此文章针对于普通手机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
Android 8.0 对特定函数做出了以下变更:
- 如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用
startService()
函数,则该函数将引发一个IllegalStateException
。- 新的
Context.startForegroundService()
函数将启动一个前台服务。现在,即使应用在后台运行,系统也允许其调用Context.startForegroundService()
。不过,应用必须在创建服务后的五秒内调用该服务的startForeground()
函数。如果不调用就会出现ANR。
Android Q限制在没有用户交互的情况下加载Activity。这一变化可以最大限度的减少对用户的打扰,保持用户对屏幕上所显示内容的可控性。
运行在Android Q上的APP仅在以下一种或多种情况下可运行Activity:
- APP存在一个可视的窗口,例如一个处于前台的Activity
- 另外一个处于前台的APP发送一个属于当前APP的PendingIntent。例如Custom Tabs provider发送一个menu item pending intent。
- 系统发送一个PendingIntent,例如点击一个通知。
- 系统给APP发送一个广播,如SECRET_CODE_ACTION。
前面我们已经知道了可以通过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" />
直接使用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踩坑记录