問題
最近公司有個需求,需要開啓一個只有Service的APK,不需要界面也不需要啓動應用,只需要用來監聽接收開機、切換網絡以及指定廣播從而來觸發啓動Service(全是通過在AndroidMainifest.xml中靜態註冊廣播),一頓代碼擼完打包安裝後卻發現接收不到廣播了,我以爲是代碼出bug了,一頓狂找~~~,經過排除代碼沒有問題,又上網找資料,原來安卓從Android3.1開始,新安裝的程序就會被置於”stopped”狀態,並且只有在至少手動啓動一次後該程序纔會改變狀態,才能夠正常接收到指定的廣播消息。Android這樣做的目的是防止廣播無意或者不必要地開啓未啓動的APP後臺服務。
這不是坑人嗎,在未啓動的情況下想要通過應用自身完成一些操作是不可能的,而需要手動啓動一次和我們的需求背道而馳,這功能就廢了,還好找到了解決辦法,Android提供了一種藉助發送指定Flag廣播的方式,達到應用在未啓動的情況下仍然能夠收到消息的效果。
系統給Intent定義了兩個新的Flag,分別爲FLAG_INCLUDE_STOPPED_PACKAGES(表示包含未啓動的App)和FLAG_EXCLUDE_STOPPED_PACKAGES(表示不包含未啓動的App),用來控制Intent是否要對處於停止狀態的App起作用
解決辦法
注意:由於這個辦法需要在發送廣播的地方添加Flag,接收廣播的地方添加android:exported=”true”,所以實際上開機廣播與網絡切換廣播並沒有實質上解決,而是通過另一個應用啓動時發送一個自定義廣播來觸發啓動該應用的Service,有了這個自定義廣播的第一次觸發,之後再切換網絡就可以正常接收網絡廣播了(開機廣播自測吧~)
1、在AndroidMainifest.xml註冊廣播的地方添加action,同時添加android:exported=”true”,如:
<receiver android:name=".receiver.LauNetBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="intent.action.START_UPLOAD_CRASH" />.
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>
2、在發送廣播的地方添加Intent.FLAG_INCLUDE_STOPPED_PACKAGES,如:
Intent intent = new Intent();
intent.setAction("intent.action.START_UPLOAD_CRASH");
intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(intent);
OK! 應用終於能正常接收廣播了!
網絡監聽廣播會多次重複接收
完成以上問題後發現每次切換網絡都會接收到兩條網絡切換廣播,而一般我們的邏輯代碼都會放在網絡監聽裏觸發,這樣的話豈不是要每次執行兩次邏輯代碼?想了想找了個最簡單的辦法,就是加一個flag標誌判斷,下面只貼重點代碼了~
private static boolean isActived = false;//用於過濾連續收到兩次網絡連接上的通知
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(NET_ACTION)) {
//檢查網絡狀態的類型
int netWrokState = NetUtil.getNetWorkState(context);
switch (netWrokState) {
case 0:
Log.i("TAG", "移動網絡");
case 1:
Log.i("TAG", "wifi網絡");
if (!isActived) {
isActived = true;
Intent service = new Intent(context, BootService.class);
context.startService(service);
}
break;
case -1:
Log.i("TAG", "沒有網絡");
isActived = false;
break;
}
}
}
NetUtil代碼塊
public class NetUtil {
/**
* 沒有連接網絡
*/
private static final int NETWORK_NONE = -1;
/**
* 移動網絡
*/
private static final int NETWORK_MOBILE = 0;
/**
* 無線網絡
*/
private static final int NETWORK_WIFI = 1;
public static int getNetWorkState(Context context) {
// 得到連接管理器對象
ConnectivityManager connectivityManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager
.getActiveNetworkInfo();
if (activeNetworkInfo != null && activeNetworkInfo.isConnected()) {
if (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_WIFI)) {
return NETWORK_WIFI;
} else if (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_MOBILE)) {
return NETWORK_MOBILE;
}
} else {
return NETWORK_NONE;
}
return NETWORK_NONE;
}
}
Ok!這樣就把兩次連接給過濾掉了~