(三十三) Broadcast之有序广播

前言:前段时间突然想起来看下并行广播和串行广播,并行广播项目中经常用到,或者说是无意识地用到,因为默认就是并行的,串行广播工作三年来都没接触过,自己写个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().
         * 

*

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中不支持弹框操作


1.2 注册BroadcastReceiver

我就简单的在manifest里注册了一下,activity里用registerReceiver也行。

        
            
                
            
        
        
            
                
            
        
        
            
                
            
        

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 调试

(三十三) Broadcast之有序广播_第1张图片


1.5 试下有序广播拦截

(三十三) Broadcast之有序广播_第2张图片


1.6 用虚拟机调试

用虚拟机调试的时候如果没有指定包名,广播会被framework拦截下来不会传递到应用侧,结合广播在Android 8.0里的修改,如果是隐式广播,并且是静态注册的话,会大概率收不到广播。对应解决方法是对广播的intent设置对应的包名将其改为显示广播。(我试了下如果设置对应类名则会继续收不到广播,这是否意味着类名会限制只在对应类里接收么=-=)

参考:点击打开链接

“限制在AndroidManifest.xml中注册接收隐式广播,如ACTION_PACKAGE_REPLACED ,但也有些例外如ACTION_BOOT_COMPLETED,ACTION_LOCALE_CHANGED(所有例外参考文末连接)。(注意此部分限制都是只针对targetSdkVersion为android O,或者编译的SDK为android O及以上的的,低于的则不受影响)”

(三十三) Broadcast之有序广播_第3张图片


对应框架代码:

 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. 总结

有序广播适用于广播依次处理并且需要得到上一次处理结果的情况,大部分情况下并行广播够用了,另外有序广播又被拦截的风险。

你可能感兴趣的:(Android)