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);