我开发了一个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)
有解决方案吗?
答案 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调用。