その中でも困りそうなのが、AsyncTaskが非推奨になったことです。
AsyncTaskを使うと、AndroidStudioに取り消し線がつくのが気に入らないので、今のうちにイイ感じの実装方法を覚えていおたほうがいいかな。と思います。
AsyncTaskを機能分割すると、流れ的に大きく3つ。
- doInBackground(Params... params) バックグラウンド処理
- onProgressUpdate(Progress... values) 進捗状況をUIで表現
-
onPostExecute(Result
result) 1.の結果を処理する(成功したらDBに登録とか)
このうち、1,3は必須。2は場合によってって感じかな。と思います。
で、よさげな実装案は、次の通り。
- java.util.concurrent.ExecutorServiceでバックグラウンド処理
- JetPackのLiveDataで進捗状況をUIで表現
- JetPackのLiveDataで1.の結果を処理する
1,3はイイ感じかなと思います。2はちょっとダサいけどできなくはない。じゃあ実際にどうするか。というと、
- JetPack LiveDataの利用準備
- LiveData用モデルの作成
- LiveDataの更新コールバックを受け取るリスナーの設定
- ExecutorServiceを使った非同期処理の実行
という感じかな。と思います。じゃあ、実装例。
app/build.gradleに以下追記。ADDってなっている部分。
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.navigation:navigation-fragment:2.2.2'
implementation 'androidx.navigation:navigation-ui:2.2.2'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
//<<ADD
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
//>>ADD
}
2.LiveData用モデルの作成(クラスの用意)
↓のような感じのクラスを作る。
4.ExecutorServiceを使った非同期処理の実行
LiveDataを使うことでObserverパターンを実現しています。
public class MyViewModel extends ViewModel {
private MutableLiveData<String> currentName = new MutableLiveData<String>();
public MutableLiveData<String> getCurrentValue() {
return currentName;
}
}
3.LiveDataの更新コールバックを受け取るリスナーの設定4.ExecutorServiceを使った非同期処理の実行
public class FirstFragment extends Fragment {
//2.LiveData用モデルの作成(変数定義)
private MyViewModel model;
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//2.LiveData用モデルの作成(インスタンス生成)
model = new ViewModelProvider(this).get(MyViewModel.class);
//3.LiveDataの更新コールバックを受け取るリスナーの設定
final Observer<String> nameObserver = new Observer<String>() {
@Override
public void onChanged(@Nullable final String newValue) {
// 画面の更新
TextView textView = view.findViewById(R.id.textview);
textView.setText(newValue);
}
};
model.getCurrentValue().observe(this, nameObserver);
view.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//4.ExecutorServiceを使った非同期処理の実行
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(new Runnable() {
@Override
public void run() {
//10秒経過後、ワーカースレッドより、モデルの値を更新
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
String anotherName = "3秒経過 " + new Date();
//他のスレッドから情報更新する場合、postValueで値をセットする必要がある
model.getCurrentValue().postValue(anotherName);
}
});
}
});
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_first, container, false);
}
}
また、postValueを使うことで、スレッドを意識する必要がなくなります。
Handlerを使えば、同じことはできますが、スレッドの入れ替えを隠蔽することで、コードがわかりやすくなりますね(個人的な感覚)。
また、値の取り回しが複数になる場合、面倒なので、更新通知用のフィールドを設けるのもありかなと思います。
「2.JetPackのLiveDataを使う」は、ちょっとダサいですけど、MyViewModelに進捗状況を表す数字を持たせ、postValueすればできるかな。と思います。
・・・でもダサいですね。
0 件のコメント:
コメントを投稿