サービスとアクティビティの間の通信方法に関する質問は何度も回答されていますが、これを行う独自の方法をレビューし、それが受け入れられる適切な方法であるかどうか、そして何が処理方法の欠点。最初に、できる限り詳細に問題ステートメントを述べます。
Firebase Messaging Serviceを使用して2つのデバイス間で通信するアプリを作成する必要があります。 Uberのようなシステムだとしましょう。 1つのアプリはサービスプロバイダー(ドライバー)用で、もう1つのアプリは顧客(乗客)用です。乗客が自分の場所で旅行することを要求すると、特定の半径内のドライバーは、Firebaseを使用してペイロードを含むプッシュ通知を受け取ります。 Firebaseサービスはバックグラウンドで実行されています。サービスがプッシュ通知を受信すると、onMessageReceived
メソッドが呼び出されます。イベントが生成されます。ここではFirebaseを使用して通知を生成していませんが、Firebase Push通知のdata
フィールドを使用する必要がある場合、実際にデバイス間でデータを転送します。これで、ドライバーアプリは、Firebase Push通知のペイロードでユーザーが車を望む場所の座標を受け取ります。このデータを追加してアクティビティを開始するだけで、ドライバーにリクエストが受信されたことを示すことができます。
顧客側では、顧客がリクエストを送信した後、次のアクティビティに進み、ドライバーの1人がリクエストを受け入れるのを待つようなローディング画面が表示されます。ドライバーの1つがこのユーザーの要求を受け入れると、このユーザーは、指定されたドライバーの情報をプッシュ通知のペイロードに含むFirebaseプッシュ通知を受け取ります。繰り返しますが、目的は通知を生成することではなく、デバイス間でデータを転送することです。
ユースケースを理解したので、次に問題に進みます。
この問題は、ユーザーがリクエストを送信し、次の待機画面に移動すると、ドライバーの1人がリクエストを受け入れるのを待っている間、待機するよう指示するロード画面が表示されるときに発生します。ドライバーがリクエストを受け入れると、ユーザーがプッシュ通知のペイロードにドライバーの情報を含むFirebaseプッシュ通知を受け取ることになります。ロード画面の表示を停止し、プッシュ通知のペイロードで受信したデータでTextViewを埋めるようにアクティビティに伝えるために、サービスとアクティビティの間で通信する方法を教えてください。
これは私がこれをどのように処理したかです。 AwaitingDriver
という名前のアクティビティがあるとします。このアクティビティには、ドライバーのデータが入力されるTextViewがあります。ただし、現在、リクエストはまだ受け入れられていないため、アクティビティには読み込み画面が表示されています。これで、ユーザーは、バックグラウンドで実行されているサービスのドライバーの情報を含むプッシュ通知を受信しますが、何らかの方法でアクティビティに接続されることはありません。これが私のonMessageReceived
メソッドです
_ @Override
public void onMessageReceived(RemoteMessage remoteMessage){
SharedPreferences rideInfoPref = getSharedPreferences(getString(R.string.rideInfoPref), MODE_PRIVATE);
SharedPreferences.Editor rideInfoPrefEditor = rideInfoPref.edit();
String msgType = remoteMessage.getData().get("MessageType");
if (msgType.equals("RequestAccepted")){
rideInfoPrefEditor.putBoolean(getString(R.string.is_request_accepted), true);
rideInfoPrefEditor.putString(getString(R.string.driver_phone), remoteMessage.getData().get("DriverPhone"));
rideInfoPrefEditor.putString(getString(R.string.driver_lat), remoteMessage.getData().get("DriverLatitude"));
rideInfoPrefEditor.putString(getString(R.string.driver_lng), remoteMessage.getData().get("DriverLongitude"));
rideInfoPrefEditor.commit();
AwaitingDriver.requestAccepted(); // A static method in AwaitingDriver Activity
}
}
_
ここで、AwaitingDriver.requestAccepted()
はAwaitingDriver
アクティビティの静的メソッドです。 AwaitingDriver
アクティビティ自体の中には、顧客に待機するように指示する進捗ダイアログが表示されています。ここに、メソッドAwaitingDriver.requestAccepted()
が実行しているものがあります。
_public static void requestAccepted(){
try{
awaitingDriverRequesting.dismiss(); //ProgressDialog for telling user to wait
}catch (Exception e){
e.printStackTrace();
}
if (staticActivity != null){
staticActivity.new TaskFindSetValues().execute();
}
}
_
ここで、staticActivity
は、このクラス内で宣言されたAwaitingDriver
アクティビティクラスの静的オブジェクトです。 onResume
およびonPause
メソッドで値を設定しています。アクティビティが前面にあり、画面に表示されている場合、staticActivity
の値はnull
ではありません。 onResume
およびonPause
メソッドを以下に示します。
_@Override
public void onResume(){
super.onResume();
staticActivity = this;
Boolean accepted = rideInfoPref.getBoolean(getString(R.string.is_request_accepted), false);
if (accepted){
new TaskFindSetValues().execute();
}
}
@Override
protected void onPause(){
super.onPause();
staticActivity = null;
}
_
ここで、TaskFindSetValues
はAwaitingDriver
アクティビティクラス内で定義されたAsyncTaskです。 TaskFindSetValues
のコードは次のとおりです
_public class TaskFindSetValues extends AsyncTask<String, Void, String>{
String phone;
String lat;
String lng;
@Override
protected void onPreExecute(){
SharedPreferences pref = getSharedPreferences(getString(R.string.rideInfoPref), MODE_PRIVATE);
phone = pref.getString(getString(R.string.driver_phone), "");
lat = pref.getString(getString(R.string.driver_lat), "");
lng = pref.getString(getString(R.string.driver_lng), "");
}
@Override
protected String doInBackground(String... arg0){
return null;
}
@Override
protected void onPostExecute(String returnValue){
awaitingDriverPhone.setText(phone); //setting values to the TextViews
awaitingDriverLat.setText(lat);
awaitingDriverLng.setText(lng);
}
}
_
このコードを確認して、他の解決策の代わりにこれを行うことの欠点について教えてください。同じ例で提案された方法を説明することができれば、本当に感謝しています。
なぜAsyncTaskを使用しているのですか?意味がありません。とにかく、アクティビティと通信する場合は、BroadcastReceiver
を使用して実行できます。
public class MyFirebaseMessagingService extends FirebaseMessagingService{
private LocalBroadcastManager broadcaster;
@Override
public void onCreate() {
broadcaster = LocalBroadcastManager.getInstance(this);
}
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Intent intent = new Intent("MyData");
intent.putExtra("phone", remoteMessage.getData().get("DriverPhone"));
intent.putExtra("lat", remoteMessage.getData().get("DriverLatitude"));
intent.putExtra("lng", remoteMessage.getData().get("DriverLongitude"));
broadcaster.sendBroadcast(intent);
}
}
そしてあなたの活動で
@Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver((mMessageReceiver),
new IntentFilter("MyData")
);
}
@Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
awaitingDriverRequesting.dismiss();
awaitingDriverPhone.setText(intent.getExtras().getString("phone")); //setting values to the TextViews
awaitingDriverLat.setText(intent.getExtras().getDouble("lat"));
awaitingDriverLng.setText(intent.getExtras().getDouble("lng"));
}
};
OnMessageReceivedメソッドでは、アプリケーションのブロードキャストを送信して、ドライバーの詳細を転送できます。要求が受け入れられたメソッドは、ブロードキャストレシーバーのonReceiveメソッドに置き換えられます。そうすれば、doinbackgroundメソッドがnullを返すだけなので、共有設定に書き込むか、基本的に何もしないaSyncTaskを使用する必要はありません。 onPreExecuteメソッドとonPostExecuteメソッドのみを使用しています。どちらもメインスレッドで実行されます。
バックグラウンド通信にイベントバスを使用します。最高です。
OnMessageReceived()関数呼び出しからイベントをスローするだけです
Eventbus.getDefault().post(Throw(value))
イベントのイベントモデルを作成します。すべてのイベントモデルは一意である必要があります。
class Throw(val value :Object) {}
その後、アクティビティで目的の関数をイベントモデルに登録するだけです。イベントを起動すると受信します。実装が簡単で、理解しやすいです。
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
EventBus.getDefault().unregister(this)
super.onStop()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onThrowEvent(t : Throw) {
// Do your staff here
}
バックグラウンドでイベントをキャッチすることもできます。 ThreadModeを変更するだけです。試してみることをお勧めします
@Subscribe(threadMode = ThreadMode.ASYNC)
@Subscribe(threadMode = ThreadMode.BACKGROUND)
OnMessageReceivedメソッド内でインテントを作成し、アクティビティを再起動し、ドライバーのデータをエクストラに渡すだけではどうですか?