前言:前段时间突然想起来看下并行广播和串行广播,并行广播项目中经常用到,或者说是无意识地用到,因为默认就是并行的,串行广播工作三年来都没接触过,自己写个demo玩一下。
参考链接:
1.Android四大组件——BroadcastReceiver普通广播、有序广播、拦截广播、本地广播、Sticky广播、系统广播
2.Android源码解析四大组件系列(六)---广播的处理过程
demo: jiatai 的 demo for ordered broadcast
调试手机:小米Mix2(突然想到调试什么的用nexus会好一点,毕竟国产机会对framework进行优化,也可能和Android源码对不上)虚拟机一直会莫名其妙重启=-=,调试结果也和mix2不大一样。
1. 串行广播的简单demo
1.1 新建broadcastReceiver
package com.example.demo_33_broadcast_parallel; import android.app.job.JobScheduler; import android.app.job.JobService; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.util.Log; public class BroadcastReceiverList { public static String TAG = "BroadcastReceiverList"; public static class HighLevelBroadcastReceiver extends BroadcastReceiver{ /** * This method is called when the BroadcastReceiver is receiving an Intent * broadcast. During this time you can use the other methods on * BroadcastReceiver to view/modify the current result values. This method * is always called within the main thread of its process, unless you * explicitly asked for it to be scheduled on a different thread using * {@link Context#registerReceiver(BroadcastReceiver, * IntentFilter, String, Handler)}. When it runs on the main * thread you should * never perform long-running operations in it (there is a timeout of * 10 seconds that the system allows before considering the receiver to * be blocked and a candidate to be killed). You cannot launch a popup dialog * in your implementation of onReceive(). * <p> * <p><b>If this BroadcastReceiver was launched through a <receiver> tag, * then the object is no longer alive after returning from this * function.</b> This means you should not perform any operations that * return a result to you asynchronously. If you need to perform any follow up * background work, schedule a {@link JobService} with * {@link JobScheduler}. * <p> * If you wish to interact with a service that is already running and previously * bound using {@link Context#bindService(Intent, ServiceConnection, int) bindService()}, * you can use {@link #peekService}. * <p> * <p>The Intent filters used in {@link Context#registerReceiver} * and in application manifests are <em>not</em> guaranteed to be exclusive. They * are hints to the operating system about how to find suitable recipients. It is * possible for senders to force delivery to specific recipients, bypassing filter * resolution. For this reason, {@link #onReceive(Context, Intent) onReceive()} * implementations should respond only to known actions, ignoring any unexpected * Intents that they may receive. * * @param context The Context in which the receiver is running. * @param intent The Intent being received. */ @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "high level receiver"); setResult(0, "high", null); } } public static class MidLevelBroadcastReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "middle level receiver-->getResultCode: " + getResultCode() + " getResultData: " + getResultData()); setResult(1, "middle", null); //abortBroadcast(); } } public static class LowLevelBroadcastReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "low level receiver-->getResultCode: " + getResultCode() + " getResultData: " + getResultData()); setResult(2, "low", null); } } }
其中onReceive的文档注释很有内涵,讲了以下几点:
1. onReceive一般是运行在主线程中的
2. onReceive不支持耗时操作,需要控制在10s执行完成
3.onReceive中不支持弹框操作
1.2 注册BroadcastReceiver
我就简单的在manifest里注册了一下,activity里用registerReceiver也行。
<receiver android:name=".BroadcastReceiverList$HighLevelBroadcastReceiver"> <intent-filter android:priority="3000"> <action android:name="com.jiatai.broadcast.ordered" /> </intent-filter> </receiver> <receiver android:name=".BroadcastReceiverList$MidLevelBroadcastReceiver"> <intent-filter android:priority="2000"> <action android:name="com.jiatai.broadcast.ordered" /> </intent-filter> </receiver> <receiver android:name=".BroadcastReceiverList$LowLevelBroadcastReceiver"> <intent-filter android:priority="1000"> <action android:name="com.jiatai.broadcast.ordered" /> </intent-filter> </receiver>
1.3 发送有序广播
package com.example.demo_33_broadcast_parallel; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; public class BroadcastTestActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_broadcast_test); Intent intent = new Intent("com.jiatai.broadcast.ordered"); sendOrderedBroadcast(intent, null); } }
1.4 调试
1.5 试下有序广播拦截
1.6 用虚拟机调试
用虚拟机调试的时候如果没有指定包名,广播会被framework拦截下来不会传递到应用侧,结合广播在Android 8.0里的修改,如果是隐式广播,并且是静态注册的话,会大概率收不到广播。对应解决方法是对广播的intent设置对应的包名将其改为显示广播。(我试了下如果设置对应类名则会继续收不到广播,这是否意味着类名会限制只在对应类里接收么=-=)
参考:点击打开链接
“限制在AndroidManifest.xml中注册接收隐式广播,如ACTION_PACKAGE_REPLACED ,但也有些例外如ACTION_BOOT_COMPLETED,ACTION_LOCALE_CHANGED(所有例外参考文末连接)。(注意此部分限制都是只针对targetSdkVersion为android O,或者编译的SDK为android O及以上的的,低于的则不受影响)”
对应框架代码:
else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0) || (r.intent.getComponent() == null && r.intent.getPackage() == null && ((r.intent.getFlags() & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0) && !isSignaturePerm(r.requiredPermissions))) { mService.addBackgroundCheckViolationLocked(r.intent.getAction(), component.getPackageName()); Slog.w(TAG, "Background execution not allowed: receiving " + r.intent + " to " + component.flattenToShortString()); skip = true; }可以看出如果没有指定Component和Package则会将skip设为true, 至于源码中的flag是hide类型的,第三方中调用不到。
/** * If set, the broadcast will never go to manifest receivers in background (cached * or not running) apps, regardless of whether that would be done by default. By * default they will receive broadcasts if the broadcast has specified an * explicit component or package name. * @hide */ public static final int FLAG_RECEIVER_EXCLUDE_BACKGROUND = 0x00800000;
2. 总结
有序广播适用于广播依次处理并且需要得到上一次处理结果的情况,大部分情况下并行广播够用了,另外有序广播又被拦截的风险。