AndroidでViewModelを使用するメリット

2019年9月6日

本稿ではAndroidでViewModelを使用するメリットを説明します。

 

ViewModelのメリット

今までは画面回転などでActivityが破棄される場合を考慮して実装していたのが、
ViewModelを使うことで、その部分を考慮せずに記述できるようになります。
(ライフサイクルを意識せずに記述していくことが可能になります)

また、ViewとViewModelの役割が明確に分かれていき、
FatなActivityやFragmentになるのを防ぐことができます。

 

LiveDataのメリット

ViewModelとあわせて使用することの多いLiveDataのメリットも説明します。
LiveDataLには以下のようなメリットがあります。

  • メモリリークしない
  • データの状態を受け取りUIに自動反映できる
  • ActivityがBackStackにある時はデータ通知されない
  • 自分でライフサイクル管理しなくていい
  • BackStackがActiveになった瞬間に最新のデータを受け取れる
  • シングルトンでラップすれば色々な場所からLiveDataを読み込める

ViewModelとLiveDataLiveDataを使用したサンプル

Activityの実装

public class MainActivity{
    private ViewModel mViewModel = null;
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        try {
            mViewModel = ViewModelProviders.of(this).get(ViewModel.class);
    
            //LiveDataに既にObserversがあるかどうかを確認。
            // LiveDataに既にデータセットがある場合Observersに配信される。
            if (mViewModel.getItems().hasObservers()) {
                return;
            }
            mViewModel.getItems().observe(this, new Observer<List() {
            @Override
            public void onChanged(@Nullable final List result) {
                // UI更新
                if (result != null) {
                    //ViewModelのバックグラウンド処理を実行
                    mViewModel.loadItems();
                } else {
                       // エラー処理
                }
             }
        });
        } catch (IllegalArgumentException e) {
        // エラー処理
    }
}
}

 

ViewModelの実装

public class ViewModel extends AndroidViewModel {
    private final MutableLiveData<List> mLiveData = new MutableLiveData<>();
    public ViewModel(@NonNull Application application) {
        super(application);
    }
    public LiveData<List> getItems() {
        return mLiveData;
    }
    public void loadItems() {
        new ViewModel.LoadTask(mLiveData).execute();
    }
    private static class LoadTask extends AsyncTask<Void, Void, Void> {
        private final MutableLiveData<List> mBackgroundLiveData;
        LoadTask(MutableLiveData<List> liveData) {
            mBackgroundLiveData = liveData;
        }
        @Override
        protected Void doInBackground(Void... voids) {
            Context context = SampleApplication.get().getApplicationContext();
            Uri uri = Uri.parse("content://com.hogehoge.smple.provider/categories").buildUpon().build();
            ContentResolver resolver = context.getContentResolver();
            Cursor cursor = resolver.query(uri, null, null, null, null);
        
            if (cursor == null) {
                return null;
            }
            List categories = new ArrayList<>();
            if (cursor.moveToFirst()) {
                do {
                    try {
                        categories.add(Category.class.getConstructor(Cursor.class).newInstance(cursor));
                    } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                    //エラー
                    }
                } while (cursor.moveToNext());
            }
            cursor.close();
            if (categories.isEmpty()) {
                mBackgroundLiveData.postValue(null);
                return null;
            }
            mBackgroundLiveData.postValue(categories);
            return null;
        }
    }
}