前言:前段时间突然想起来看下并行广播和串行广播,并行广播项目中经常用到,或者说是无意识地用到,因为默认就是并行的,串行广播工作三年来都没接触过,自己写个demo玩一下。
参考链接:
1.Android四大组件——BroadcastReceiver普通广播、有序广播、拦截广播、本地广播、Sticky广播、系统广播
2.Android源码解析四大组件系列(六)---广播的处理过程
demo: jiatai 的 demo for ordered broadcast
调试手机:小米Mix2(突然想到调试什么的用nexus会好一点,毕竟国产机会对framework进行优化,也可能和Android源码对不上)虚拟机一直会莫名其妙重启=-=,调试结果也和mix2不大一样。
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().
*
*
If this BroadcastReceiver was launched through a tag,
* then the object is no longer alive after returning from this
* function. 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}.
*
* 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}.
*
*
The Intent filters used in {@link Context#registerReceiver}
* and in application manifests are not 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中不支持弹框操作
我就简单的在manifest里注册了一下,activity里用registerReceiver也行。
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);
}
}
用虚拟机调试的时候如果没有指定包名,广播会被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;
有序广播适用于广播依次处理并且需要得到上一次处理结果的情况,大部分情况下并行广播够用了,另外有序广播又被拦截的风险。