带操作按钮的FCM推送通知不起作用

时间:2019-05-28 10:10:33

标签: android firebase firebase-cloud-messaging

我开发了一个Android应用程序,用户可以在其中接收地震信息。如果用户设备的位置靠近地震位置,则通知中将具有2个按钮,以确认它们是否安全或已从发生地震的疏散中撤离。用户在通知中按下两个按钮中的1个(安全或已抽空)时,我陷入了一个过程。

这是我的代码:

MyFirebaseInstanceService.java

private void showNotification(String title, String body) {
    NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    String NOTIFICATION_CHANNEL_ID = "com.example.yohan.notifgempafcm";

    Intent amanIntent = setIntent(body, "SAFE");
    Intent evakuasiIntent = setIntent(body, "EVACUATED");

    PendingIntent pendingIntentAman = PendingIntent.getBroadcast(this, 0, amanIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    PendingIntent pendingIntentEvakuasi = PendingIntent.getActivity(this, 1, evakuasiIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "Notification",
                NotificationManager.IMPORTANCE_DEFAULT);
        notificationChannel.enableLights(true);
        notificationChannel.setDescription("Info Gempa");
        notificationChannel.setLightColor(Color.BLUE);
        if (title.contains("WASPADA GEMPA")) {
            notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000});
        }
        notificationManager.createNotificationChannel(notificationChannel);
    }

    NotificationCompat.Action actAman = new NotificationCompat.Action.Builder(android.R.drawable.ic_secure, "AMAN", pendingIntentAman).build();
    NotificationCompat.Action actEvakuasi = new NotificationCompat.Action.Builder(android.R.drawable.ic_partial_secure, "EVAKUASI", pendingIntentEvakuasi).build();

    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
    notificationBuilder.setAutoCancel(true)
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle(title)
            .setContentText(body)
            .setContentInfo("Info");
    if (title.contains("WASPADA GEMPA")) {
        notificationBuilder.addAction(actAman);
        notificationBuilder.addAction(actEvakuasi);
    }
    final Notification notification = notificationBuilder.build();
    notificationManager.notify(11111, notification);
}

public Intent setIntent(String body, String status){
    Intent intent = new Intent(this, NotificationActionReceiver.class);
    intent.putExtra("tanggal", (body.split(" ")[2] + " " + body.split(" ")[3]));
    intent.putExtra("token", token);
    intent.putExtra("status", status);
    return intent;
}

NotificationActionReceiver.java

public class NotificationActionReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String tanggal = intent.getStringExtra("tanggal");
        String token = intent.getStringExtra("token");
        String status = intent.getStringExtra("status");
        konfirmasi(context, tanggal, token, status);
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.cancel(11111);
    }

    public void konfirmasi(final Context context, String tanggal, String token, String status){
        HashMap<String, String> data = pushData(tanggal, token, status);
        PostResponseAsyncTask konfirm = new PostResponseAsyncTask(context, data, new AsyncResponse() {
            @Override
            public void processFinish(String s) {
                if (s.equals("UPDATED")){
                    Toast.makeText(context, "Konfirmasi berhasil disimpan", Toast.LENGTH_SHORT).show();
                }
            }
        });
        konfirm.execute(LoginActivity.URL + "notifikasi/changeStatus");
    }

    public HashMap<String, String> pushData(String tanggal, String token, String status){
        HashMap<String, String> data = new HashMap<>();
        data.put("datetime", tanggal);
        data.put("token", token);
        data.put("konfirmasi", status);
        return data;
    }
}

我希望(示例)如果用户在通知中按下SAFE按钮,它将确保用户的确认状态从NOTIFIED(默认)到数据库服务器中的SAFE,等等上。但它返回一个错误:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.yohan.notifgempafcm, PID: 3821
    java.lang.RuntimeException: Unable to start receiver com.example.yohan.notifgempafcm.NotificationActionReceiver: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
        at android.app.ActivityThread.handleReceiver(ActivityThread.java:2621)
        at android.app.ActivityThread.access$1700(ActivityThread.java:153)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1382)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5293)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
     Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
        at android.view.ViewRootImpl.setView(ViewRootImpl.java:569)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:282)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
        at android.app.Dialog.show(Dialog.java:298)
        at com.kosalgeek.genasync12.PostResponseAsyncTask.onPreExecute(PostResponseAsyncTask.java:151)
        at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:591)
        at android.os.AsyncTask.execute(AsyncTask.java:539)
        at com.example.yohan.notifgempafcm.NotificationActionReceiver.konfirmasi(NotificationActionReceiver.java:36)
        at com.example.yohan.notifgempafcm.NotificationActionReceiver.onReceive(NotificationActionReceiver.java:21)
        at android.app.ActivityThread.handleReceiver(ActivityThread.java:2614)
        at android.app.ActivityThread.access$1700(ActivityThread.java:153) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1382) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:135) 
        at android.app.ActivityThread.main(ActivityThread.java:5293) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:372) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) 

有解决方案吗?

1 个答案:

答案 0 :(得分:0)

在这种情况下,容易出错的代码是

  Toast.makeText(context, "Konfirmasi berhasil disimpan", Toast.LENGTH_SHORT).show();

BroadcastReceiver最多可以运行10秒。如果您访问后的上下文将无效(这是您的情况)。而且如果这是当时Process中唯一的东西,那么这个Process将被杀死,不管您的API是否被调用。因此,您不应该直接在#onReceive()内部执行长时间运行的操作。
Documentation

要使用您的代码解决此问题,您可以使用goAsync()。从该函数返回后,这将使广播保持活动状态。使用此方法之前,请阅读#goAsync()的文档。

Here is an example of goAsync()

为此,您可以启动IntentService来自动处理后台线程中的API调用。