Android 接收开机广播启动service/activity

Android 接收开机广播启动service/activity

前言:

此文章针对于普通手机APP,在没有限制之前直接可以通过接收开机广播,然后通过intent即可实现开机启动service/activity。

Intent intent = new Intent(context,XXXXX.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);//启动activity
context.startService(intent);//启动service
对于启动service:

Android 8.0 对特定函数做出了以下变更:

  • 如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用 startService() 函数,则该函数将引发一个 IllegalStateException
  • 新的 Context.startForegroundService() 函数将启动一个前台服务。现在,即使应用在后台运行,系统也允许其调用 Context.startForegroundService()。不过,应用必须在创建服务后的五秒内调用该服务的 startForeground() 函数。如果不调用就会出现ANR。
对于启动activity:

Android Q限制在没有用户交互的情况下加载Activity。这一变化可以最大限度的减少对用户的打扰,保持用户对屏幕上所显示内容的可控性。
运行在Android Q上的APP仅在以下一种或多种情况下可运行Activity:

  1. APP存在一个可视的窗口,例如一个处于前台的Activity
  2. 另外一个处于前台的APP发送一个属于当前APP的PendingIntent。例如Custom Tabs provider发送一个menu item pending intent。
  3. 系统发送一个PendingIntent,例如点击一个通知。
  4. 系统给APP发送一个广播,如SECRET_CODE_ACTION。
注:APP启动一个前台运行的Service并不代表该APP处于前台运行状态。
所以并不能启动service之后再service中直接启动activity。

解决办法:

启动service:

前面我们已经知道了可以通过startForegroundService()来启动service,然后在oncreate中调用startForeground()即可:

BootReceiver:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
     
    context.startForegroundService(intent);
} else {
     
    context.startService(intent);
}

service的onCreate():

//设置点击跳转
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);

String id = "1";
String name = "channel_name_1";
NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
     
    NotificationChannel mChannel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_DEFAULT);
    mChannel.setSound(null, null);
    notificationManager.createNotificationChannel(mChannel);
    notification = new Notification.Builder(this)
                           .setChannelId(id)
                           .setContentTitle("123")
                           .setContentIntent(pendingIntent)
                           .setAutoCancel(false)
                           .setSmallIcon(R.drawable.ic_launcher_background).build();
} else {
     
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                                                          .setContentTitle("123")
                                                             		.setContentIntent(pendingIntent)
                                                             .setPriority(Notification.PRIORITY_DEFAULT)// 设置该通知优先级
                                                             .setAutoCancel(false)
                                                             .setSmallIcon(R.drawable.ic_launcher_background);
    notification = notificationBuilder.build();
}
startForeground(1, notification);

这样即可实现在开机时启动服务,但是会在通知栏创建一条消息。

如果不实现startForeground()就会onDestory

最后记得在Mainfest.xml中添加权限:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
启动activity:

直接使用context.startactivity(intent)是不能够启动的,会报一些空指针错误之类的。

谷歌官方推荐使用全屏Intent:

BootReceiver:

Intent intent1 = new Intent(context, MainActivity2.class);
intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(context, 0,
        intent1, PendingIntent.FLAG_UPDATE_CURRENT);
String channelId = createNotificationChannel("my_channel_ID", "my_channel_NAME", NotificationManager.IMPORTANCE_HIGH, context);
NotificationCompat.Builder notification =
        new NotificationCompat.Builder(context, channelId)
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setContentTitle("Start Activity")
                .setContentText("click me")
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setCategory(NotificationCompat.CATEGORY_CALL)
                .setAutoCancel(true)

                // Use a full-screen intent only for the highest-priority alerts where you
                // have an associated activity that you would like to launch after the user
                // interacts with the notification. Also, if your app targets Android 10
                // or higher, you need to request the USE_FULL_SCREEN_INTENT permission in
                // order for the platform to invoke this notification.
                .setFullScreenIntent(fullScreenPendingIntent, true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
notificationManager.notify(100, notification.build());

createNotificationChannel:

private String createNotificationChannel(String channelID, String channelNAME, int level, Context context) {
     
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
     
        NotificationManager manager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
        NotificationChannel channel = new NotificationChannel(channelID, channelNAME, level);
        manager.createNotificationChannel(channel);
        return channelID;
    } else {
     
        return null;
    }
}

在接收到开机广播后会在通知栏创建一个消息,然后用户点击消息跳转到对应的activity。

最后记得在Mainfest.xml中添加权限:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />

总结:

随着版本的升级,android对后台限制越来越严格,想要悄无声息的启动service和activity有一定的困难。

而且普通app接收开机广播接收也很慢,在开机后10多秒才能接收到,把优先级设为最高,会有一定的效果

android:priority="2147483647"

最后:不同的手机厂商的限制也有所不同,也有可能以上方法不适用于某些手机。

在锁屏状态下也不会接收到开机广播,应该也是手机厂商做了限制。

参考文献:

Android10适配-针对从后台启动 Activity 的限制

显示有时效性的通知

Android Q 限制后台启动Activity

Android8.0 启动后台Service问题

Android 保活后台启动Service 8.0踩坑记录

你可能感兴趣的:(android,java,移动开发,app,安卓)