Android应用在安装后未启动的情况下无法收到开机等各类广播

时间:2021-08-29 20:27:21

问题

最近公司有个需求,需要开启一个只有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!这样就把两次连接给过滤掉了~