我知道关于如何在服务和活动之间进行通信的问题已被多次回答,但我也希望我自己的方式对此进行审核,并知道它是否是可接受的和正确的方法来执行此操作以及什么是我如何处理它的弊端。 首先,我将尽可能详细地陈述问题陈述。
我必须构建一个应用程序,我使用Firebase Messaging Service在两个设备之间进行通信。 让我们说它是一个类似Uber的系统。 一个应用程序用于服务提供商(驱动程序),一个用于客户(乘客)。 当乘客请求使用其位置旅行时,某个半径范围内的驾驶员将使用Firebase获得有效负载的推送通知。 Firebase服务正在后台运行。 当服务收到推送通知时,将调用onMessageReceived
方法。 生成一个事件。 我不是在这里使用Firebase来生成通知,但实际上在我需要使用Firebase推送通知的data
字段时在设备之间传输数据。 现在,驱动程序应用程序将在Firebase推送通知的有效负载中接收用户想要汽车的坐标。 我可以简单地在附加内容中使用此数据启动活动,并向驱动程序显示已收到请求。
现在在客户方面,在客户提交请求之后,他们将被带到下一个活动,在那里他们将被显示一种加载屏幕,告诉他们等待其中一个驱动程序接受他们的请求。 当其中一个驱动程序接受此用户的请求时,此用户现在将在推送通知的有效负载中收到带有指定驱动程序信息的Firebase推送通知。 同样,目的不是生成任何通知,而是在设备之间传输数据。
既然您了解了用例,我将继续讨论该问题。
当用户提交请求并进入下一个等待屏幕时会出现问题,在该屏幕上显示一个加载屏幕,告诉他们在请求等待其中一个驱动程序接受时等待。 当驱动程序接受请求时,正如我所说,用户将在推送通知的有效负载中收到带有驱动程序信息的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? 没有意义。 无论如何,如果你想与Activity通信,你可以使用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方法替换。 这样就不需要写共享首选项或使用aSyncTask,因为doinbackground方法只返回null,所以基本上什么都不做。 您只使用onPreExecute和onPostExecute方法,这两种方法都在主线程上执行。
使用Eventbus进行后台通信。 这是最好的。
只需从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方法中创建一个intent,重新启动活动,并在额外的?中传递驱动程序的数据?