(三十三) 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().
         * <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. 总结

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

猜你喜欢

转载自blog.csdn.net/sinat_20059415/article/details/80296863