Androidで開発しててAPI通信しようとしたらまず思いつくのは、AsyncTaskですよね。
自分でスレッドを起こしてなんやかんやするよりはるかに便利です。
APIからニュース記事を取ってきて画面に表示するイメージで使い方を簡単に説明します。
コツ: AsyncTaskはモデルから使うこと
AsyncTaskはモデル層の中で使います。今回は説明を簡単にするため、シングルトンにしてあります。
refreshが呼ばれたら、AsyncTaskを起動してデータ通信をします。
アンチパターンとしては、ActivityやFragmentの中で直接AsyncTaskを呼び出してしまうというのがあります。するとActivityやFragmentの状態を管理しなければいけないためにフラグが増えていきます。これは便利ではありません。
import android.os.AsyncTask;
import java.util.Observable;
public class NewsModel extends Observable {
private static NewsModel ourInstance = new NewsModel();
public static NewsModel getInstance() {
return ourInstance;
}
private boolean busy;
private AsyncTask<Void, Void, NewsEntity> task;
private NewsEntity entity;
public enum Event {
ENTITY_CHANGED, BUSY_STATE_CHANGED,
}
private NewsModel() {
}
public void refresh() {
if (isBusy()) return;
setBusy(true);
task = new MyTask();
task.execute();
}
public boolean isBusy() {
return busy;
}
public void setBusy(boolean busy) {
if (this.busy == busy) return;
this.busy = busy;
setChanged();
notifyObservers(Event.BUSY_STATE_CHANGED);
}
public NewsEntity getEntity() {
return entity;
}
public void setEntity(NewsEntity entity) {
this.entity = entity;
setChanged();
notifyObservers(Event.ENTITY_CHANGED);
}
private class MyTask extends AsyncTask<Void, Void, NewsEntity> {
@Override
protected NewsEntity doInBackground(Void... params) {
// TODO ここで通信処理を行う
return new NewsEntity();
}
@Override
protected void onPostExecute(NewsEntity newsEntity) {
super.onPostExecute(newsEntity);
setBusy(false);
setEntity(newsEntity);
}
}
}
コツ:モデルからの通知で画面を更新すること
ニュース記事を表示するActivityかFragmentを作成します。Fragmentの方が何かと便利ですね。
通信完了などでモデルの状態が変わったら、通知を受け取って画面をリフレッシュします。この例では簡単のためObserverを使ってますが、EventBusなどのライブラリーを使うとさらに便利です。
アンチパターンとしては、AsyncTaskにActivityやFragmentの中で定義したコールバックを渡しているのを時々見かけます。AsyncTaskが終わった時にActivityやFragmentがもういなくなっていたらどうするのでしょうか?これは便利ではない使い方です。
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.Observable;
import java.util.Observer;
public class NewsFragment extends Fragment {
private NewsModel model = NewsModel.getInstance();
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
// ここは適切に実装してください。
return inflater.inflate(R.layout.activity_main, container, false);
}
@Override
public void onStart() {
super.onStart();
resetContent();
model.refresh();
model.addObserver(observer);
}
@Override
public void onStop() {
model.deleteObserver(observer);
super.onStop();
}
private Observer observer = new Observer() {
@Override
public void update(Observable observable, Object data) {
if ( !(data instanceof NewsModel.Event)) return;
switch ((NewsModel.Event)data) {
case BUSY_STATE_CHANGED:
resetProgressBar();
break;
case ENTITY_CHANGED:
resetContent();
break;
}
}
};
private void resetContent() {
// TODO: コンテンツの差し替え
}
private void resetProgressBar() {
// TODO: プログレスバーの表示・非表示の切り替え
}
}
まとめ
AsyncTaskはシンプルで便利です。
ただし、ActivityやFragmentから直接呼び出すべきではないです。
- コツ: AsyncTaskはモデルから使うこと
- コツ: モデルからの通知で画面を更新すること
この二つを守っていれば、とても簡単に使えます。