Android 监听设备开机/重启 & 开启AlarmManager、Service

项目需求

最近工作中遇到这样一个需求:

  • 每周五定时从本地发送通知到设备上
  • 如果用户中途重启或关机,也要求可以收到通知

问题拆分:

这个问题可以分成几个部分来解决

1. 定时任务

我是采用 AlarmManagersetRepeating 的方法来实现的。在测试的过程中发现间隔时间不固定, 但是这个功能对精度的要求不高, 可以使用。 其他的情况要好好斟酌再用哦。这里写的是每周五早上八点的闹钟。

Intent intent = new Intent(context, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 4000, 
        intent, PendingIntent.FLAG_UPDATE_CURRENT);

AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);

Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_WEEK, 6);
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);

alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
		AlarmManager.INTERVAL_DAY * 7, pendingIntent);

2. 发送本地通知

AlarmManager 到时间后会触发 AlarmReceiver 来发送本地通知

public class AlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        String channelId = "channelId";

        NotificationCompat.Builder notificationBuilder =
                new NotificationCompat.Builder(context, channelId)
                        .setSmallIcon(R.mipmap.ic_launcher_round)
                        .setContentTitle("title")
                        .setContentText("message")
                        .setAutoCancel(true)
                        .setStyle(new NotificationCompat.BigTextStyle().bigText("message”));

        NotificationManager notificationManager =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        if (notificationManager != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                NotificationChannel channel = new NotificationChannel(channelId,
                        “com.android.demo.channel",
                        NotificationManager.IMPORTANCE_DEFAULT);

                notificationManager.createNotificationChannel(channel);
            }
            notificationManager.notify(1, notificationBuilder.build());
        }
    }
}

对了,不要忘记在AndroidManifest.xml里面注册你的AlarmReceiver

<receiver android:name=".AlarmReceiver" />

如果你只是需要短时间的执行任务, 到这里就可以啦。 但是你还想要设备开机/重启后还能在后台继续执行任务的话,请继续看下去吧

3. 监听设备开机/重启状态

这里同样需要一个BroastcastReceiver来监听这个事件。首先要在AndroidManifest.xml加监听权限, 并注册BroastcastReceiver

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.demo">
    
    //...
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    
    <application>
    //... 
		<receiver
    		android:name=".RebootBroadcastReceiver"
    		android:enabled="true"
    		android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
    		<intent-filter>
        		<action android:name="android.intent.action.BOOT_COMPLETED" />
        		<category android:name="android.intent.category.DEFAULT" />
    		</intent-filter>
		</receiver>
		
	</application>
	
</manifest>

RebootBroadcastReceiver.java

public class RebootBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        String action = intent.getAction();
        if (action != null) {
            if (action.equalsIgnoreCase(Intent.ACTION_BOOT_COMPLETED)) {
            
            	//这里可以放AlarmManager的代码
            	//同1. 这里就不贴了
				
            	//或者startService()的代码
            	Intent i = new Intent(context, AlarmService.class);

				ComponentName service;
				if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    				service = context.startForegroundService(i);
				} else {
    				service = context.startService(i);
				}

				if (service == null) {
    				Log.i("RebootBroadcastReceiver", "Fail");
				} else {
    				Log.i("RebootBroadcastReceiver", "Success");
				}
            }
        } 
    }
}

4. 开启服务

要注意的是BroastcastReceiveronReceive最多可以执行5s,否则就会崩溃。所以如果业务逻辑比较麻烦,所以我用Service来处理。

public class AlarmService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();

        NotificationCompat.Builder notificationBuilder =
                new NotificationCompat.Builder(this, "devicereboot")
                        .setSmallIcon(R.mipmap.ic_launcher_round)
                        .setContentTitle(getResources().getString(R.string.app_name))
                        .setContentText("is running background.")
                        .setAutoCancel(true);

        NotificationManager notificationManager =
                (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("devicereboot",
                    "com.android.demo.devicereboot",
                    NotificationManager.IMPORTANCE_DEFAULT);
            notificationManager.createNotificationChannel(channel);
        }

        startForeground(5000, notificationBuilder.build());

		//这部分代码同1.
		//也可替换成其他业务代码
        Intent i = new Intent(this, AlarmReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 4000, i, PendingIntent.FLAG_UPDATE_CURRENT);

        AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);

        Calendar calendar = Calendar.getInstance();
		calendar.set(Calendar.DAY_OF_WEEK, 6);
		calendar.set(Calendar.HOUR_OF_DAY, 8);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY * 7, pendingIntent);
        
        //stopForeground()后,AlarmManager还在运行
        //”devicereboot“通知会显示后被取消
        stopForeground(true);
    }

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

总结:

到这里就告一段落啦, 基本满足项目需求。Android菜鸡一只,记录下平时遇到的疑点难点, 有问题的话多多指正。要加油啊!



版权声明:本文为weixin_43233524原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。