У меня есть простое приложение для планирования уведомлений через 5 минут после нажатия кнопки. Он отлично работает для меня. Но если я перезагружаю телефон в течение этих 5 минут, я не получаю уведомление. Я провел исследование Alarm Manager и планирование уведомлений при перезагрузке устройства. У меня в приложении 5 классов.
Это:
- MainActivity.class
- NotificationUtil.class
- NotificationPublisher.class
- NotificationView.class
- DeviceBootReceiver.class
У меня есть 2 класса, расширяющих приемники BroadCast. Это:
- NotificationPublisher.class
- DeviceBootReceiver.class
Из моих исследований и других вопросов о переполнении стека я узнал, что для получения уведомлений послеустройство перезагружено, я должен создать класс, который расширяет BroadCast Receiver. Он будет запускать любой код в методе onReceive()
при каждой загрузке телефона.
Это мой класс MainActivity.class
public class MainActivity extends AppCompatActivity {
private Button button;
private Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.notification);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
NotificationUtil.createNotification(MainActivity.this,NotificationView.class,"Notification","You have a new Task");
}
});
}
}
Это мой класс NotificationUtil.class. Это класс, который обрабатывает все уведомления, и это класс, который я вызываю по нажатию кнопки в MainActivity.
public class NotificationUtil
{
public static void createNotification(Context context,Class<?> cls, String title, String content)
{
Intent intent = new Intent(context,cls);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
sendNotification(getNotification(pendingIntent,context,title,content),context);
}
private static void sendNotification(Notification notification,Context context)
{
Intent notificationIntent = new Intent(context, NotificationPublisher.class);
Intent deviceBootIntent = new Intent(context,DeviceBootReceiver.class);
notificationIntent.putExtra(NotificationPublisher.NOTIFICATION_ID,1);
notificationIntent.putExtra(NotificationPublisher.NOTIFICATION,notification);
deviceBootIntent.putExtra(DeviceBootReceiver.NOTIFICATION_ID, 1);
deviceBootIntent.putExtra(DeviceBootReceiver.NOTIFICATION, notification);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,notificationIntent,PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 60 * 1000,pendingIntent);
}
private static Notification getNotification(PendingIntent pendingIntent, Context context, String title, String content)
{
NotificationCompat.Builder builder = new NotificationCompat.Builder(context,"ChannelID");
builder.setSmallIcon(R.drawable.notification_bell);
builder.setContentTitle(title);
builder.setContentText("You have a Notification");
builder.setSubText("Tap To View");
builder.setStyle(new NotificationCompat.BigTextStyle().bigText(content));
builder.setContentIntent(pendingIntent);
return builder.build();
}
}
Это мой NotificationPublisher.class. Это класс, отвечающий за отправку Уведомления.
public class NotificationPublisher extends BroadcastReceiver {
public static String NOTIFICATION_ID = "notification_id";
public static String NOTIFICATION = "notification";
@Override
public void onReceive(final Context context, Intent intent) {
try
{
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = intent.getParcelableExtra(NOTIFICATION);
int notificationId = intent.getIntExtra(NOTIFICATION_ID, 1);
notificationManager.notify(notificationId, notification);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Это мой DeviceBootReceiver.class. Я пытаюсь реализовать свой код здесь, чтобы получать уведомления после перезагрузки устройства.
public class DeviceBootReceiver extends BroadcastReceiver {
public static String NOTIFICATION_ID = "notification_id";
public static String NOTIFICATION = "notification";
public void onReceive(Context context, Intent intent)
{
if(Objects.requireNonNull(intent.getAction()).equalsIgnoreCase("android.intent.action.BOOT_COMPLETED"))
{
Notification notification = intent.getParcelableExtra(NOTIFICATION);
int notificationID = intent.getIntExtra(NOTIFICATION_ID,1);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(notificationID, notification);
}
}
}
После перезагрузки телефона я получаю сообщение «Приложение перестало работать». Я не уверен, где я ошибаюсь.
Это мой файл манифеста:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.notificationtest">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".NotificationView"
android:parentActivityName=".MainActivity"/>
<receiver android:name=".NotificationPublisher"/>
<receiver android:name=".DeviceBootReceiver"
android:enabled="true"
android:exported="true"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.ACTION_BOOT_COMPLETED" />
<action android:name="android.intent.action.REBOOT" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.ACTION_SHUTDOWN" />
</intent-filter>
</receiver>
</application>
</manifest>
Из того, что я понял, класс, который расширяет BroadCast Receivers, вызывается при перезагрузке устройства. Поэтому я попытался передать мои notification
и notificationID
из моего NotificationUtil.class в мой DeviceBootReceiver.class следующим образом, используя Intent.putExtra
Intent deviceBootIntent = new Intent(context,DeviceBootReceiver.class);
deviceBootIntent.putExtra(DeviceBootReceiver.NOTIFICATION_ID, 1);
deviceBootIntent.putExtra(DeviceBootReceiver.NOTIFICATION, notification);
Затем в моем DeviceBootReceiver.class по методу onReceive
, который яполучить значения, используя getExtra
. Далее я передаю 2 значения, которые я получил, в свой NotificationPublisher.class.
Это «БЫЛО» моя более ранняя реализация моего DeviceBootReceiver.class. Так как это не сработало, я попробовал новую реализацию, как указано выше.
public class DeviceBootReceiver extends BroadcastReceiver {
public static String NOTIFICATION_ID = "notification_id";
public static String NOTIFICATION = "notification";
public void onReceive(Context context, Intent intent)
{
if(Objects.requireNonNull(intent.getAction()).equals("android.intent.action.BOOT_COMPLETED"))
{
Intent pushIntent = new Intent(context, NotificationPublisher.class);
Notification notification = intent.getParcelableExtra(NOTIFICATION);
int notificationID = intent.getIntExtra(NOTIFICATION_ID,1);
pushIntent.putExtra(NotificationPublisher.NOTIFICATION_ID,notificationID);
pushIntent.putExtra(NotificationPublisher.NOTIFICATION,notification);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,pushIntent,PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 60 * 1000,pendingIntent);
}
}
}
То, что я ожидал, было то, что, поскольку DeviceBootReceiver.class вызывается при запуске, 2 значения будут отправлены на мой NotificationPublisher.class, и я получу уведомление при запуске. Вместо этого приложение падает с моей старой и новой реализацией в методе onReceive
.
Может ли кто-нибудь, кто имеет опыт получения уведомлений о перезагрузке устройства, помочь мне с этим? Пожалуйста, пройдите мой код и попробуйте внедрить его в свой проект, чтобы понять, что я сделал. Я загрузил все коды здесь в этом вопросе.
Это мои журналы сбоев:
10-14 17:26:03.088 5438-5438/? I/SELinux: Function: selinux_android_load_priority [0], There is no sepolicy file.
10-14 17:26:03.088 5438-5438/? I/SELinux: Function: selinux_android_load_priority [1], There is no sepolicy version file.
10-14 17:26:03.088 5438-5438/? I/SELinux: Function: selinux_android_load_priority , priority is 3. priority version is VE=SEPF_SM-G357FZ_4.4.4_A042
10-14 17:26:03.088 5438-5438/? E/SELinux: [DEBUG] get_category: variable seinfocat: default sensitivity: NULL, cateogry: NULL
10-14 17:26:03.088 5438-5438/? E/SELinux: seapp_context_lookup: str_security_ctx is null
10-14 17:26:03.088 5438-5438/? E/dalvikvm: >>>>> Normal User
10-14 17:26:03.088 5438-5438/? E/dalvikvm: >>>>> com.example.notificationtest [ userId:0 | appId:10181 ]
10-14 17:26:03.088 5438-5438/? E/SELinux: [DEBUG] get_category: variable seinfocat: default sensitivity: NULL, cateogry: NULL
10-14 17:26:03.088 5438-5438/? E/SELinux: seapp_context_lookup: str_security_ctx is null
10-14 17:26:03.088 5438-5438/? D/dalvikvm: Late-enabling CheckJNI
10-14 17:26:03.088 5438-5438/? I/libpersona: KNOX_SDCARD checking this for 10181
10-14 17:26:03.088 5438-5438/? I/libpersona: KNOX_SDCARD not a persona
10-14 17:26:03.138 5438-5438/? D/TimaKeyStoreProvider: in addTimaSignatureService
10-14 17:26:03.148 5438-5438/? D/TimaKeyStoreProvider: Cannot add TimaSignature Service, License check Failed
10-14 17:26:03.148 5438-5438/? D/ActivityThread: Added TimaKesytore provider
10-14 17:26:03.168 5438-5438/com.example.notificationtest D/DisplayManagerGlobal: getDisplayInfo: displayId=0, info=DisplayInfo{"Built-in Screen", app 480 x 800, real 480 x 800, largest app 800 x 762, smallest app 480 x 442, 60.0 fps, rotation0, density 240 (217.714 x 216.17) dpi, layerStack 0, type BUILT_IN, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}
10-14 17:26:03.168 5438-5438/com.example.notificationtest D/DisplayManagerGlobal: getDisplayInfo: displayId=0, info=DisplayInfo{"Built-in Screen", app 480 x 800, real 480 x 800, largest app 800 x 762, smallest app 480 x 442, 60.0 fps, rotation0, density 240 (217.714 x 216.17) dpi, layerStack 0, type BUILT_IN, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}
10-14 17:26:03.168 5438-5438/com.example.notificationtest D/ActivityThread: handleBindApplication:com.example.notificationtest
10-14 17:26:03.168 5438-5438/com.example.notificationtest D/DisplayManagerGlobal: getDisplayInfo: displayId=0, info=DisplayInfo{"Built-in Screen", app 480 x 800, real 480 x 800, largest app 800 x 762, smallest app 480 x 442, 60.0 fps, rotation0, density 240 (217.714 x 216.17) dpi, layerStack 0, type BUILT_IN, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}
10-14 17:26:03.168 5438-5438/com.example.notificationtest D/DisplayManagerGlobal: getDisplayInfo: displayId=0, info=DisplayInfo{"Built-in Screen", app 480 x 800, real 480 x 800, largest app 800 x 762, smallest app 480 x 442, 60.0 fps, rotation0, density 240 (217.714 x 216.17) dpi, layerStack 0, type BUILT_IN, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}
10-14 17:26:03.238 5438-5438/com.example.notificationtest D/AndroidRuntime: Shutting down VM
10-14 17:26:03.238 5438-5438/com.example.notificationtest W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x416a7d58)
10-14 17:26:03.238 5438-5438/com.example.notificationtest E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.notificationtest, PID: 5438
java.lang.RuntimeException: Unable to start receiver com.example.notificationtest.NotificationPublisher: java.lang.NullPointerException
at android.app.ActivityThread.handleReceiver(ActivityThread.java:2551)
at android.app.ActivityThread.access$1700(ActivityThread.java:155)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1319)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5426)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at android.app.NotificationManager.notify(NotificationManager.java:150)
at android.app.NotificationManager.notify(NotificationManager.java:120)
at com.example.notificationtest.NotificationPublisher.onReceive(NotificationPublisher.java:23)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:2544)
at android.app.ActivityThread.access$1700(ActivityThread.java:155)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1319)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5426)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084)
at dalvik.system.NativeStart.main(Native Method)
10-14 17:26:07.708 5438-5438/com.example.notificationtest I/Process: Sending signal. PID: 5438 SIG: 9