Androidの非同期処理にはローダを使う

AsyncTask を用いた非同期処理はユーザビリティには欠かせないという話でしたが,AsyncTask にも欠点があります.それを回避するためにローダを使ったほうがいいようです.

AsyncTask の欠点

アクティビティが読まれると,非同期処理をするため新しいタスクを生成しますが,例えばスマホの向きが変わってアクティビティが更新されると,またタスクを生成します.

しかも,前回の処理が終わるのを待ってからそれを破棄して新しいタスクを作るので,かなり効率が悪くなります.

そのため,タスクを破棄して再生成するのではなく,アクティビティの開始や終了に左右されずに処理できるローダを使います.

実装方法

パッケージのインポート

MyActivity.java
import android.content.Loader;
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;

パッケージは似た名前のがいくつかあって,自動補完でもなんとかなりません.

インターフェイスを実装する

MyActivity.java
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<D> { ... }

メソッドを実装する

インターフェイスを実装すると,インターフェイス内に用意されていたメソッドをすべて書くか,MainActivity.class 自体を abstract クラスにしなければなりません.

MyActivity.java
@Override
public Loader<D> onCreateLoader(int id, Bundle args) {
    return new ExampleLoader();
}

@Override
public void onLoadFinished(Loader<D> loader, D data) {
    //何らかの処理
}

@Override
public void onLoaderReset(Loader<D> loader) {
    //何らかの処理
}

処理内容は AsyncTaskLoader を継承したクラスの loadInBackground() メソッドに書きます.

ExampleLoader.java
import android.content.AsyncTaskLoader;
import android.content.Context;

public class ExampleLoader extends AsyncTaskLoader<D> {
    public ExampleLoader(Context context) {
        super(context);
    }

    @Override
    protected void onStartLoading() {
        forceLoad();
    }

    @Override
    public D loadInBackground() {
        //何らかの処理
        return 結果;
    }
}

ローダを初期化する

アクティビティなら onCreate() 内でローダを初期化します.一つのアクティビティもしくはフラグメントごとに一つ適用できます.

MyActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity);

    LoaderManager loaderManager = getLoaderManager();
    loaderManager.initLoader(0, null, this);
}

initLoader() の第一引数が ID で,ローダは ID によって管理します.

同一 ID を初期化しようとしたとき,存在しなければ新規作成,存在すれば以前のローダを再利用することで,アクティビティが更新されたときにも以前の処理結果を用いることができます.