Search code examples
androidfirebase-realtime-databasenotificationsbroadcastreceiverbootcompleted

How to create a notification that always run even after phone reboot?


I have set an alarm manager at a method setAlarm() at MainActivity that contain a date from firebase database and set an alarm based on that. This is pretty good when I try to run this. But when I try to reboot my phone before the alarm times up, nothing have been happened.

I also have tried to create a Receiver that refer to a service. And from this service onHandleIntent(Intent intent), I call MainActivity.setAlarm(). Also i have been added a permission boot complete at android manifest. After that, I try to restart my phone and when my phone completed booting, the app has force close. I think it stopped cause I call a method at an activity class.

This is My Alarm Receiver

public class AlarmReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
    Intent notificationIntent = new Intent(context, BabyListActivity.class);

    TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
    stackBuilder.addParentStack(BabyListActivity.class);
    stackBuilder.addNextIntent(notificationIntent);

    PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

    NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

    Notification notification1 = builder.setContentTitle("1")
            .setContentText("New Notification From Demo App..")
            .setTicker("New Message Alert!")
            .setSmallIcon(R.mipmap.ic_launcher_round)
            .setContentIntent(pendingIntent).build();

    NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(0, notification1);
}

This is My startAlarm() method

public static void startAlarm(Context context){
    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

    Intent notificationIntent = new Intent("android.media.action.DISPLAY_NOTIFICATION");
    notificationIntent.addCategory("android.intent.category.DEFAULT");

    PendingIntent broadcast = PendingIntent.getBroadcast(context, 100, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.YEAR, 2017);
    cal.set(Calendar.MONTH, 8-1);
    cal.set(Calendar.DAY_OF_MONTH, 10);
    cal.set(Calendar.HOUR_OF_DAY, 10);
    cal.set(Calendar.MINUTE, 15);
    cal.set(Calendar.SECOND, 4);

    alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), broadcast);
}

This is My BootAlarmReceiver

public class BootAlarmReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {

    if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {

        // It is better to reset alarms using Background IntentService
        Intent i = new Intent(context, BootService.class);
        ComponentName service = context.startService(i);

        if (null == service) {
            // something really wrong here
            Log.e(TAG, "Could not start service ");
        }
        else {
            Log.e(TAG, "Successfully started service ");
        }

    } else {
        Log.e(TAG, "Received unexpected intent " + intent.toString());
    }
}

This is my Boot Service

public class BootService extends Service {


public BootService() {
    super("BootService");
}

@Override
public void onCreate() {
    MainActivity.startAlarm(this);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return START_NOT_STICKY;
}

@Override
public IBinder onBind(Intent intent) {
    return null;
}

}

This is my android manifest

<receiver android:name=".AlarmReceiver">
        <intent-filter>
            <action android:name="android.media.action.DISPLAY_NOTIFICATION"></action>
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </receiver>

    <service android:name=".BootService"/>

    <receiver android:name=".BootAlarmReceiver" android:enabled="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>

Can someone tell me how to solve this problem? thanks...


Solution

  • There are at least two mistakes I think. Firstly, you'd never new a Activity class, it should be managed by android framework, if you just want to invoke some methods in it, you'd better extract methods into an utility class or simply change the methods to static which is not recommended. Secondly, onHandleIntent method will run in background thread, you can't invoke methods that should be run at main thread.

    So in my opinion, for test, you can just make the startAlarm() method in MainActivity static and BootService extend Service instead of IntentService, do these changes and try again.

    change setAlarm() method like this:

    public static void startAlarm(Context context){
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    
        Intent notificationIntent = new Intent("android.media.action.DISPLAY_NOTIFICATION");
        notificationIntent.addCategory("android.intent.category.DEFAULT");
    
        PendingIntent broadcast = PendingIntent.getBroadcast(context, 100, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, 2017);
        cal.set(Calendar.MONTH, 8-1);
        cal.set(Calendar.DAY_OF_MONTH, 10);
        cal.set(Calendar.HOUR_OF_DAY, 11);
        cal.set(Calendar.MINUTE, 40);
        cal.set(Calendar.SECOND, 00);
    
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), broadcast);
    }
    

    change your BootAlarmReceiver like this:

    public class BootService extends Service {
    
    
        @Override
        public void onCreate() {
            MainActivity.startAlarm(this);
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            return START_NOT_STICKY;
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
    

    Try again!