この記事は Developer Advocate、 Doug Stevenson による The Firebase Blog の記事 "Using Android Architecture Components with Firebase Realtime Database (Part 1) " を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。
Google I/O 2017 では、Android プラットフォーム チームより Android Architecture Components が利用できるようになったことをお知らせしました 。Android Architecture Components は、高い安定性やメンテナンス性を備え、テストもしやすいアプリを設計する際に役立つライブラリです。提供されているツールの中でも私が特に感銘を受けたのは、Android デベロッパー共通の懸念であるアプリのアクティビティやフラグメントのライフサイクルの管理に役立つという点です。
本ブログシリーズでは、このライブラリと Firebase Realtime Database SDK を組み合わせて、アプリを構築する方法を紹介します。クライアント アプリは、データが書き込まれるたびに更新情報を伴って呼び出されるリスナーを通して、Realtime Database からデータを読み取り ます。これによって、簡単に最新のデータを使ってアプリの UI を最新に保つことができます。データベースの変更をリッスンするというこのモデルは、Android Architecture Components と非常にうまく連携します(この点は、Firestore にも同じように当てはまります。Firestore も、データの更新をリアルタイムでクライアント アプリに知らせることができます)。
Realtime Database を使う多くの Android アプリは、onStart()
ライフサイクル メソッドで変更のリッスンを開始し、onStop()
で終了します。これによって、Activity
や Fragment
が画面上に表示されている場合のみ、変更を受け取ることができるようになります。データベースから、ティッカーと今日の注目株の最新株価を表示する Activity
を考えてみましょう。この Activity
は、次のようになります。
public class MainActivity extends AppCompatActivity {
private static final String LOG_TAG = "MainActivity";
private final DatabaseReference ref =
FirebaseDatabase.getInstance().getReference("/hotstock");
private TextView tvTicker;
private TextView tvPrice;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvTicker = findViewById(R.id.ticker);
tvPrice = findViewById(R.id.price);
}
@Override
protected void onStart() {
super.onStart();
ref.addValueEventListener(listener);
}
@Override
protected void onStop() {
ref.removeEventListener(listener);
super.onStop();
}
private ValueEventListener listener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// update the UI here with values in the snapshot
String ticker = dataSnapshot.child("ticker").getValue(String.class);
tvTicker.setText(ticker);
Float price = dataSnapshot.child("price").getValue(Float.class);
tvPrice.setText(String.format(Locale.getDefault(), "%.2f", price));
}
@Override
public void onCancelled(DatabaseError databaseError) {
// handle any errors
Log.e(LOG_TAG, "Database error", databaseError.toException());
}
};
}
とても簡単です。データベース リスナーは、データベースの /hotstock
という場所にある株価の更新を受け取り、値をいくつかの TextView
オブジェクトに入れます。このような単純なケースでは、何の問題もありません。しかし、アプリが複雑になると、いくつかの差し迫った問題があることに気づくでしょう。
1.ボイラープレート
データベース内のロケーションに対して DatabaseReference
を定義し、onStart()
と onStop()
でリスナーを管理するために、たくさんの標準ボイラープレートが存在します。多くのリスナーを使うほど、ボイラープレート コードも多くなり、コードは煩雑になります。また、追加したリスナーはすべて削除しないと、データやメモリがリークする可能性があります。単純な 1 つのミスが、多額の損害やパフォーマンスの低下につながるかもしれません。
2.テストが難しく、可読性が低い
コードで行っていることは単純ですが、行ごとにロジックを確認する純粋なユニットテストを書くのは難しいことです。あらゆることが 1 つの Activity オブジェクトに詰め込まれているので、読みづらく、管理するのも難しくなります。
Android Architecture Components の効果
Architecture Components が提供するライブラリを詳しく見てみると、前述の問題に対処する際に特に役立つ ViewModel
と LiveData
という 2 つのクラスがあることがわかります。これらの仕組みがよくわからないという方は、まず ViewModel
と LiveData
をお読みください。 また、LiveData の拡張 も行いますので、その点もご覧ください。この 2 つのクラスが相互に連携する仕組みや、これらをホストする LifecycleOwner
(例: Activity や Fragment)について理解しておくことは重要です。
Firebase Realtime Database で LiveData
を拡張する
LiveData
は、監視可能なデータホルダー クラスです。これは、アクティビティ、フラグメント、サービスなどの Android アプリのコンポーネントのライフサイクルを意識しており、ライフサイクルの状態がアクティブであるアプリのコンポーネントにのみ通知します。ここでは、これを利用してデータベースの Query
または DatabaseReference
(DatabaseReference
自身が Query
である点に注意してください)の変更をリッスンし、監視している Activity に変更を通知し、UI を更新します。この通知は DataSnapshot
オブジェクトとして届き、通常はデータベース リスナーで受け取ることができます。この処理を行う拡張 LiveData
を次に示します。
public class FirebaseQueryLiveData extends LiveData<DataSnapshot> {
private static final String LOG_TAG = "FirebaseQueryLiveData";
private final Query query;
private final MyValueEventListener listener = new MyValueEventListener();
public FirebaseQueryLiveData(Query query) {
this.query = query;
}
public FirebaseQueryLiveData(DatabaseReference ref) {
this.query = ref;
}
@Override
protected void onActive() {
Log.d(LOG_TAG, "onActive");
query.addValueEventListener(listener);
}
@Override
protected void onInactive() {
Log.d(LOG_TAG, "onInactive");
query.removeEventListener(listener);
}
private class MyValueEventListener implements ValueEventListener {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
setValue(dataSnapshot);
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.e(LOG_TAG, "Can't listen to query " + query, databaseError.toException());
}
}
}
FirebaseQueryLiveData
では、コンストラクタで指定された Query
のデータが変更されるたびに、MyValueEventListener
が呼び出されて新しい DataSnapshot
が渡されます。そして、LiveData
の setValue()
メソッドを使ってオブザーバに通知します。MyValueEventListener
が onActive()
と onInactive()
で管理されている点にも注目してください。そのため、この LiveData
オブジェクトに関連付けられた Activity や Fragment が画面に表示されるたびに(STARTED または RESUMED の状態)、LiveData
オブジェクトは「アクティブ」になり、データベース リスナーが追加されます。
LiveData
が提供する大きなメリットは、関連付けられた Activity の状態に基づいてデータベース リスナーを管理できることです。FirebaseQueryLiveData
は自分の仕事をいつ設定してどのように破棄するかを正確に認識しているので、リークが発生する可能性がありません。このクラスは、 すべての 種類の Firebase クエリに再利用することができます。 FirebaseQueryLiveData クラスは、非常に再利用性が高いクラスなのです!
これで、データベースへの変更を読み取って通知する LiveData
オブジェクトができました。次に必要になるのは、これを Activity と接続するために使う ViewModel
オブジェクトです。まずは、それを実現する仕組みについて説明しましょう。
FirebaseQueryLiveData
を管理する ViewModel
の実装
ViewModel
の実装には、ホストとなる Activity で使われる LiveData
オブジェクトが含まれています。ViewModel
オブジェクトは Activity の構成変更 (ユーザーが端末を回転させた場合など)が起こっても失われることはないので、LiveData
メンバー オブジェクトも保持されます。ホスト Activity と ViewModel のライフタイムの関係を図示すると、次のようになります。
次に示す ViewModel
実装は、Realtime Database のロケーション /hotstock
をリッスンする FirebaseQueryLiveData
を公開しています。
public class HotStockViewModel extends ViewModel {
private static final DatabaseReference HOT_STOCK_REF =
FirebaseDatabase.getInstance().getReference("/hotstock");
private final FirebaseQueryLiveData liveData = new FirebaseQueryLiveData(HOT_STOCK_REF);
@NonNull
public LiveData<DataSnapshot> getDataSnapshotLiveData() {
return liveData;
}
}
この ViewModel 実装では、LiveData
オブジェクトが公開されている点に注目してください。これによって、HotStockViewModel
を使う Activity が、基となるデータ(データベースの /hotstock
)に対するすべての変更を積極的に監視できるようになります。
Activity で LiveData
と ViewModel
を組み合わせる
これで、LiveData
と ViewModel
の実装が完成したので、Activity からこれらを使うようにします。先ほどの Activity を LiveData
と ViewModel
を使うようにリファクタリングしたものを以下に示します。
public class MainActivity extends AppCompatActivity {
private TextView tvTicker;
private TextView tvPrice;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvTicker = findViewById(R.id.ticker);
tvPrice = findViewById(R.id.price);
// Obtain a new or prior instance of HotStockViewModel from the
// ViewModelProviders utility class.
HotStockViewModel viewModel = ViewModelProviders.of(this).get(HotStockViewModel.class);
LiveData<DataSnapshot> liveData = viewModel.getDataSnapshotLiveData();
liveData.observe(this, new Observer<DataSnapshot>() {
@Override
public void onChanged(@Nullable DataSnapshot dataSnapshot) {
if (dataSnapshot != null) {
// update the UI here with values in the snapshot
String ticker = dataSnapshot.child("ticker").getValue(String.class);
tvTicker.setText(ticker);
Float price = dataSnapshot.child("price").getValue(Float.class);
tvPrice.setText(String.format(Locale.getDefault(), "%.2f", price));
}
}
});
}
}
コードが 20 行ほど短くなり、読みやすく、管理しやすくなりました!
onCreate() の中では、次の短いコードを使って HotStockViewModel
インスタンスを取得しています。
HotStockViewModel viewModel = ViewModelProviders.of(this).get(HotStockViewModel.class);
ViewModelProviders
は Architecture Components のユーティリティ クラスで、与えられたライフサイクル コンポーネントに基づいて ViewModel
インスタンスを管理します。上の行から得られる HotStockViewModel
オブジェクトは、次の
いずれか となります。Activity として指定されたクラスの ViewModel が存在しない場合、新しく作成されます。 または 構成変更が起こる 前の Activity のインスタンスから取得されます。
HotStockViewModel
のインスタンスがあれば、単にオブザーバを追加するだけで LiveData
の変更に反応する Activity を作ることができます。そして、データベース内にある基となるデータが変更されるたびに、オブザーバが UI を更新します。
この方式によるメリット
DatabaseReference
から ValueEventListener
を削除し忘れることはありません。これを忘れると、Activity のリークの原因になります。LiveData
オブジェクトはライフサイクルを意識しているので、いくつのオブザーバによって監視されているかを把握しています。そのため、オブザーバがなくなった(「非アクティブ」になった)時点で自動的にデータベース リスナーが削除されます。
Activity に構成変更が発生しても、
即座に 最新の DataSnapshot
の監視が始まります。監視には、直前の Activity インスタンスで使われていた LiveData
が使われます。そのデータの描画を始めるために、サーバーとの間でもう一往復が発生するのを待つ必要は ありません
。また、LiveData
では onSaveInstanceState() を使う 必要はありません。
それぞれの目的が明確になるので、個々のクラスのテストがしやすくなり、可読性も上がります。
MainActivity は、UI の描画する役割を担います。
ViewModel は、MainActivity のすべての UI データを保持します。
FirebaseQueryLiveData
は、実際のデータを保持します。また、UI がアクティブかどうかに応じて、アプリがデータベース内のデータをリッスンするかどうかを決定します。
さらに改善するには
新しい Activity の実装を見てみると、Firebase Realtime Database の細かい処理を行う部分は、ほとんどなくなっていることがわかります。そういった部分は、DataSnapshot
を扱う部分を除き、FirebaseQueryLiveData
に移動しています。理想を言えば、Realtime Database を参照している部分はすべて Activity から削除したいところです。そうすれば、データが実際にどこからやってくるのかを意識する必要はなくなります。Firestore への移行を考える場合は、これが重要になります。まったくではないにしろ、ほとんど Activity を変更しなくてもよくなるからです。
もう 1 点微妙な問題があります。実は、構成変更が起こるたびに、リスナーが削除、再追加されています。リスナーを再追加するということは、実質的にデータを再取得するためにサーバーとの間を一往復しなければならないということです。ユーザーの限られたモバイルデータを消費することがないように、これは避けた方がよいでしょう。ディスク永続性 を有効にする方法もありますが、さらによい方法もあります(その秘訣はこのシリーズで紹介するので、お楽しみに!)。
以上の 2 つの問題は、今後の投稿で解決したいと思いますので、ぜひ Firebase ブログに注目してください。また、Twitter で @Firebase をフォローしてください!パート 2 は、こちら からご覧いただけます。
Reviewed by Khanh LeViet - Developer Relations Team
3 件のコメント :
Firebase Realtime Database ini sangat bermanfaat sekali untuk merancang aplikasi yang sangat stabil, dapat dipelihara, dan mudah diuji dan mudah-mudahan bisa lebih ditingkatkan lagi untuk fitur yang lainnya.
Segera daftarkan diri anda untuk bergabung dengan kami permainan poker online terbaik dan terpercaya se-Asia. Salah satunya situs poker online terpercaya adalah Pokerace99. Banyak promo-promo menarik yang selalu menunggu anda dan para pencinta poker online Asia. Masih kurang puas dengan promonya? Tenang saja, kami sebagai agen poker resmi Pokerrepublik menyediakan layanan live chat 24 jam nonstop untuk membantu anda apabila mengalami kesulitan dalam bermain game poker online kami. Jadi tunggu apa lagi? Buruan Daftar Sekarang Juga hanya di Poker Ace!
Firebase Realtime Database ini sangat bermanfaat sekali untuk merancang aplikasi yang sangat stabil, dapat dipelihara, dan mudah diuji dan mudah-mudahan bisa lebih ditingkatkan lagi untuk fitur yang lainnya.
Poker online adalah salah satu permainan yang mudah dimenangkan apabila sudah mengetahui cara bermain yang baik dan juga strategi yang tepat. Mainkan game terbaru Poker Republik sekarang, rasakan sensasi bermain SUPER10 dan raih kemenangan Jutaan Rupiah! Jadi ayo Daftar Sekarang juga dan jangan sampai ketinggalan Promo-promo menarik seperti Kejutan Angpao Berlimpah Imlek 2019 dan berhadiah 13 HP VIVO V, 2 ACER SWIFT3, dan Tiket Undian berhadah Jutaan Rupiah. Gak bakal nyesel deh kalo bermain di Pokerrepublik!
Firebase Realtime Database ini sangat bermanfaat sekali untuk merancang aplikasi yang sangat stabil, dapat dipelihara, dan mudah diuji dan mudah-mudahan bisa lebih ditingkatkan lagi untuk fitur yang lainnya.
Promo Cashback di Situs Poker Online Terbaik Dan Terpercaya. Semua jenis permainan judi online yang ada pada situs ini dijamin aman dan berjalan fairpaly tanpa kecurangan sedikitpun. Salah satu situs judi online terpercaya ini adalah Afapoker. Permainan dijamin berjalan secara natural dan juga aman tanpa adanya bot. Daftarkan diri anda di Afapoker Sekarang Juga! Dan nikmati Promo-promo besar Lainnya!
コメントを投稿