1. 静态广播唤醒
广播的exported属性和enabled属性
- exported默认为true表示这个广播可以接收来自其他app发送的广播,只要条件满足,exported设置成false表示只能是这个app内发送的广播才能接收,即使是receiver的进程和发送广播的进程不是同一个,但是只要都是属于一个app的就可以正常接收,有时候会遇到发送者和接收者不在一个进程,广播没办法正常接收,这是因为receiver所在的进程是死的,如果通过某种方式把receiver所在的进程唤醒,那么即使exported为false也能正常接收
- enabled为true表示广播可用,为false表示禁用广播,禁用后广播将无法接收
静态的系统广播
静态的系统广播,例如:开机广播,用户开屏广播,USB插入和拔出广播等这类广播在app运行期间可以用静态注册的广播正常接收,但是在app被杀死后就无法收到了,android系统做了屏蔽,把被杀死的app的系统静态广播都过滤了,所以想让app被杀死后仍然通过静态注册的广播接收系统广播是做不到的
自定义广播
我们一般发广播都是局限在app内部,所以通常都是这么发的:
Intent intent = new Intent();
intent.setAction("my.broadcast.test");
sendBroadcast(intent);
或者这么发:
Intent intent = new Intent(context, TestBroadcastReceiver.class);
sendBroadcast(intent);
上面这两种广播的发送方式在app被杀死后都无法收到广播
但是采用下面这种方式发送广播即使app被杀死后,静态广播也能正常收到:
发送广播方的app,包名:com.syncpush.demo
Intent intent = new Intent();
Context c = null;
try {
c = createPackageContext("com.example.broadcasttest", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
// intent.setPackage(getPackageName());
// intent.setComponent(pkgName, className);
// intent.setComponent(pkgNameContext, className);
intent.setClassName(c, "com.example.broadcasttest.TestBroadcastReceiver");
// intent.setClassName("com.example.broadcasttest", "com.example.broadcasttest.TestBroadcastReceiver");
intent.setAction("my.broadcast.test");
intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(intent);
intent.setClassName(“com.example.broadcasttest”, “com.example.broadcasttest.TestBroadcastReceiver”)声明接收的广播或者用intent.setClassName(c, “com.example.broadcasttest.TestBroadcastReceiver”),但是这个Context是接收广播方app的Context,所以通过createPackageContext(“com.example.broadcasttest”, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY)根据包名来获取到app的Context
接收广播方的app,包名com.example.broadcasttest
public class TestBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Toast.makeText(context, "action:" + action, Toast.LENGTH_LONG).show();
Log.i("TestBroadcastReceiver", "action:" + action);
}
}
// 配置文件
<receiver android:name="com.example.broadcasttest.TestBroadcastReceiver"
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT" /> <!-- 手机开屏广播 -->
</intent-filter>
<intent-filter>
<action android:name="my.broadcast.test" /> <!-- 自定义广播 -->
</intent-filter>
</receiver>
接收放广播的配置要把exported设置成true,否则就无法收到app以外的广播发送,只能收到app内部的广播发送
广播唤醒的缺陷
以上通过广播唤醒在一些手机上可以正常唤醒app,例如小米3;但是在魅族手机上就没办法唤醒了,需要到安全中心把app的自启动权限开启后才能正常唤醒,由此可见,一些手机厂商可能对于静态广播的接收做了一些优化导致静态广播还是没办法被接收,所以会唤醒失败
关于intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
这个网上说是设置了就能保证即使app被杀死后,也能正常接收广播,但是我在小米手机上测了下,没有用,即使没设置,但是采用intent.setClassName()后,app杀死后也能正常接收广播,后来怀疑是不是默认就是Intent.FLAG_INCLUDE_STOPPED_PACKAGES,于是就intent.setFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES),意思是app被杀死后就不接收广播,但是广播照样能正常接收,所以跟intent.setFlags()貌似没关系,但是小米手机系统是定制的,我不敢保证他们是不是对这方面做了修改,目前没有在原生的android系统上试过intent.setFlags()的有效性
2. Service唤醒
service唤醒我认为是最可靠的,在有些软件或者手机上(华为)有禁止app唤醒的选项,如果用户把这个开关打开,那么service唤醒也会失效,除了这个,目前都可以正常通过service唤醒
所谓service唤醒无非就是通过Intent来startService,但是需要指定packageName,否则会在你当前的app进程启动新的service,而不会启动另一个app里面的service,示例代码如下:
Intent serviceIntent = new Intent();
serviceIntent.setComponent(new ComponentName(componentPckName, serviceName));
serviceIntent.setPackage(context.getPackageName());
serviceIntent.setAction("action");
context.startService(serviceIntent);