Reputation: 51
I've have a simple app to schedule a notification 5 minutes after a button is pressed. It works fine for me. But if I restart the phone within that 5 minutes I don't get the notification. I have done a research on Alarm Manager and Scheduling Notifications on device reboots. I have 5 classes in my App.
They are:
I have 2 classes that extends BroadCast Receivers. They are:
From my research and other stack overflow questions I learned that in order to receive notifications after the device is rebooted I should create a class that extends BroadCast Receiver. It will run whatever code is in the onReceive()
method whenever the phone is booted up.
This is my 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");
}
});
}
}
This is my NotificationUtil.class. This is the class that handles all the Notifications and this is the class I call on Button Click in the 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();
}
}
This is my NotificationPublisher.class. This is the class responsible for sending the Notification.
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();
}
}
}
This is my DeviceBootReceiver.class. I'm trying to implement my code here to receive Notification after the device is rebooted.
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);
}
}
}
After restarting my phone I get the message "Application has Stopped Working". I'm not sure where I'm going wrong.
This is my manifest file:
<?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>
From what I understood the class that extends BroadCast Receivers gets called on Device Reboot. So I tried passing my notification
and notificationID
from my NotificationUtil.class to my DeviceBootReceiver.class like this using Intent.putExtra
Intent deviceBootIntent = new Intent(context,DeviceBootReceiver.class);
deviceBootIntent.putExtra(DeviceBootReceiver.NOTIFICATION_ID, 1);
deviceBootIntent.putExtra(DeviceBootReceiver.NOTIFICATION, notification);
Then in my DeviceBootReceiver.class under the onReceive
method I get the values using getExtra
. I further pass the 2 values I got to my NotificationPublisher.class.
This "WAS" my earlier implementation of my DeviceBootReceiver.class. Since this didn't work I tried a new implementation as given above.
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);
}
}
}
What I expected was, since the DeviceBootReceiver.class is called on startup the 2 values will be sent to my NotificationPublisher.class and I'll get my Notification on startup. Instead the Application crashes with my Old and New Implementation in the onReceive
method.
Can someone who has experience with receiving Notification on Device reboot help me with this? Please go through my code and try to implement it in your project to understand what I have done. I have uploaded all the codes here in this question.
These are my crash logs:
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
Upvotes: 3
Views: 2783
Reputation: 3180
ACTION_BOOT_COMPLETED
is on the implicit broadcast whitelist, and so you can register for it in the manifest even at and after Android 8.0.
This answer was originally answered by @CommonsWare at this thread
All in all, don't forget to test it once implemented.
Upvotes: 1
Reputation: 75788
As part of the Android 8.0 (API level 26)
Background Execution Limits, apps that target the API level 26 or higher can no longer register broadcast receivers for implicit broadcasts in their manifest. However, several broadcasts are currently exempted from these limitations. Apps can continue to register listeners for the following broadcasts, no matter what API level the apps target.
Even though these implicit broadcasts still work in the background, you should avoid registering listeners for them.
You can use scheduled jobs
instead.
FYI
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)
NullPointerException
is thrown when an application attempts to use an object reference that has the null value.
Upvotes: 1