【问题标题】:How to communicate between Firebase Messaging Service and Activity? Android如何在 Firebase 消息服务和 Activity 之间进行通信?安卓
【发布时间】:2017-06-15 00:10:37
【问题描述】:

我知道关于如何在服务和活动之间进行通信的问题已经回答了很多次,但我也希望我自己的方式得到审查,并知道它是否可以接受并且是正确的方式,并且我如何处理它的缺点是什么。首先,我将尽可能详细地说明问题陈述。

我必须构建一个应用,在其中使用 Firebase 消息服务在两台设备之间进行通信。假设它是一个类似优步的系统。一个应用程序用于服务提供商(司机),一个应用程序用于客户(乘客)。当乘客请求与他们的位置一起旅行时,一定半径内的司机将使用 Firebase 收到带有有效负载的推送通知。 Firebase 服务在后台运行。当服务收到推送通知时,会调用onMessageReceived 方法。生成一个事件。我不是在这里使用 Firebase 来生成通知,而是在我需要使用 Firebase 推送通知的 data 字段时在设备之间实际传输数据。现在,驱动程序应用程序将在 Firebase 推送通知的有效负载中接收用户想要汽车的位置的坐标。我可以简单地使用 extras 中的这些数据启动一个活动,并向驱动程序显示收到了一个请求。

现在在客户方面,在客户提交请求后,他们将被带到下一个活动,在那里他们会看到一个加载屏幕,告诉他们等待一位司机接受他们的请求。当其中一个驱动程序接受此用户的请求时,此用户现在将收到 Firebase 推送通知,该通知的有效负载中包含指定驱动程序的信息。同样,目的不是生成任何通知,而是在设备之间传输数据。

现在您已经了解了用例,我将继续讨论这个问题。

当用户提交请求并移动到下一个等待屏幕时,就会出现问题,在该屏幕上会显示一个加载屏幕,告诉他们在请求等待被其中一位驱动程序接受时等待。当驱动程序接受请求时,正如我所说,用户将收到 Firebase 推送通知,其中驱动程序的信息包含在推送通知的有效负载中。如何在服务和 Activity 之间进行通信,告诉 Activity 停止显示加载屏幕并使用推送通知的有效负载中收到的数据填充 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 活动类的静态对象。我在onResumeonPause 方法中设置它的值。这意味着如果活动在前面,显示在屏幕上,那么staticActivity 的值才会不是null。这是onResumeonPause 方法。

@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);
    }
}

请查看此代码并告诉我这样做而不是其他解决方案的缺点,如果您还可以用相同的示例解释建议的方式,我将非常感激。

【问题讨论】:

  • 检查EventBus。这是一种更好、更简单的方法。

标签: android firebase android-asynctask static-methods android-service-binding


【解决方案1】:

你为什么使用 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"));
        }
    };

编辑

根据@xuiqzy LocalBroadcastManager 现已弃用! Google 说要改用 LiveData 或 Reactive Streams。

【讨论】:

  • 注销需要什么?阿卡,如果您不取消注册会发生什么?
  • 我在 FirebaseMessagingService 中的 onCreate 方法永远不会被调用。知道为什么吗?
  • 您是否重写了onCreate 方法?并检查您是否有错字,方法的名称是 onCreate() 而不是 OnCreate()
  • @VassilisPallas 如果活动不在前台,则活动将永远不会更新,因为 onStop 已被调用并且广播已取消注册。这种情况有其他选择吗?
  • @RedM 当应用程序在后台时,您无法更新活动。视图为空。
【解决方案2】:

使用 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)

【讨论】:

    【解决方案3】:

    在 onMessageReceived 方法中,您可以发送应用程序广播,传输驱动程序详细信息。 然后,请求接受的方法将被广播接收器的 onReceive 方法替换。这样就不需要写入共享首选项或使用基本上什么都不做的 aSyncTask ,因为 doinbackground 方法只返回 null 。您只使用了 onPreExecute 和 onPostExecute 方法,它们都在主线程上执行。

    【讨论】:

    • 我使用 AsyncTask 是因为我无法通过静态方法 requestAccepted() 访问活动的成员变量和 TextView,但我可以通过从静态方法执行的 AsyncTask 来访问
    【解决方案4】:

    为什么不简单地在 onMessageReceived 方法中创建一个 Intent,重新启动 Activity,并在 extra 中传递驱动程序的数据?

    【讨论】:

    • 因为我不希望客户看到活动重新开始。在用例中,客户实际上是在等待加载屏幕。
    • 好的,有道理。
    猜你喜欢
    • 2011-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多