Android 广播分析

广播的定义

BroadcastReceiver,本质上是一个全局的监听器,属于Android四大组件之一。

广播的使用场景

  • 不同组件之间通信(包含应用内/不同应用之间)
  • 多线程通信
  • 系统信息变化监听(网络变化、sd卡插拔、来电)

广播分类

  • 无序广播: Context.sendBroadcast(@RequiresPermission Intent intent,@Nullable String receiverPermission)
  • 有序广播: Context.sendOrderedBroadcast(@RequiresPermission Intent intent,@Nullable String receiverPermission);
    有序、无序,是针对广播接收者而言的。有序广播的接收顺序是按照 Priority 的大小来的,如果两个广播接收者的 Priority 一样大,那么动态注册的广播接收者优先接收到广播。先接收的广播接收者可以对广播进行截断,即后接收的广播接收者不再接收到此广播;先接收的广播接收者可以对广播进行修改,那么后接收的广播接收者将接收到被修改后的广播。
  • 内部广播: Local Broadcast 只在自身app 内传播,安全性,效率性更高
  • 系统广播: 无需开发者手动发送广播,系统产生变化的时候,会自动发送相关的广播,开发者只需要接收就可以了。

广播的实现机制

广播使用了观察者模式,基于 发布/订阅 的模型,是广播发送者和广播接收者最大程度的解耦,方便集成和扩展。

实现流程:

  • 自定义广播接收者BroadcastReceiver,并复写onRecvice()方法;
  • 广播接收者通过Binder机制向AMS(Activity Manager Service)进行注册;
  • 广播发送者通过Binder机制向AMS发送广播;
  • AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;
  • 消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。

注意:广播发送者和广播接收者的执行是异步的,发送者不会关心接收者什么时候能接收到。广播接收者是在UI线程的,所以不能进行耗时操作,否则会导致 anr

无序/有序广播使用

  • 继承 BroascastReceiver ,重写 onReceive 方法,在接收到广播以后,该回调会被执行
  • 在 Manifest.xml 文件中静态注册或者是在代码中动态注册
public class LoginBroadcastReceiver extends BroadcastReceiver {

  //接收到广播后自动调用该方法
  @Override
  public void onReceive(Context context, Intent intent) {
   //写入接收广播后的操作,由于是在主线程,所以不能执行耗时操作
    }
}

静态注册

通常我们使用极光推送的时候就需要静态注册。静态注册的广播接收者一旦注册以后,就会一直执行下去,和 Activity 的生命周期没有任何关系。

<receiver 
    android:enabled=["true" | "false"]
    //此broadcastReceiver能否接收其他App的发出的广播
    //默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
    android:exported=["true" | "false"]

    //继承BroadcastReceiver子类的类名
    android:name=".mBroadcastReceiver"

    //具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
    android:permission="string"

    //BroadcastReceiver运行所处的进程
    //默认为app的进程,可以指定独立的进程
    //注:Android四大基本组件都可以通过此属性指定自己的独立进程
    android:process="string" >

    //用于指定此广播接收器将接收的广播类型
    //本示例中给出的是用于接收网络状态改变时发出的广播
     <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

动态注册

在代码中通过调用Context的registerReceiver()方法进行动态注册BroadcastReceiver

private LoginBroadcastReceiver mBroadcastReceiver;
// 为什么要在 onResume()方法中注册呢,因为 onCreate()方法不一定能得到调用,比如 onRestart()方法执行的生命周期内就不会再调用 onCreate()
@Override
  protected void onResume() {
      super.onResume();

    //实例化LoginBroadcastReceiver子类 &  IntentFilter
     mBroadcastReceiver = new LoginBroadcastReceiver();
     IntentFilter intentFilter = new IntentFilter();

    //设置接收广播的类型
     intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);

    //调用Context的registerReceiver()方法进行动态注册
     registerReceiver(mBroadcastReceiver, intentFilter);
 }


//注册广播后,要记得销毁广播,否则可能导致内存泄漏。为什么要在 onPause()方法中销毁呢?因为 onStop()\onDestroy()方法可能会因为Activity被系统意外杀掉而得不到调用,就可能导致内存泄漏,而 onPause()方法是一定会得到调用的。
//即在onPause()中unregisterReceiver(mBroadcastReceiver)
 @Override
 protected void onPause() {
     super.onPause();
      //销毁在onCreate()方法中的广播
     unregisterReceiver(mBroadcastReceiver);
     }
}

应用内广播

如何将全局广播设置成应用内广播呢?

1、注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收;
2、在广播发送和接收时,增设相应权限permission,用于权限验证;
3、发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。

如何使用系统封装的 LocalBroadcastManager?

//注册应用内广播接收器
//步骤1:实例化BroadcastReceiver子类 & IntentFilter  
LoginBroadcastReceiver = new LoginBroadcastReceiver (); 

//步骤2:设置接收广播的类型 
IntentFilter intentFilter = new IntentFilter(); 
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);

//步骤3:实例化LocalBroadcastManager的实例
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);

//步骤4:调用LocalBroadcastManager单一实例的registerReceiver()方法进行动态注册 
manager.registerReceiver(mBroadcastReceiver, intentFilter);



//取消注册应用内广播接收器
manager.unregisterReceiver(mBroadcastReceiver);



//发送应用内广播
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
manager.sendBroadcast(intent);

广播接收器回调中的Context

  • 对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext;(TODO)
  • 对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context;
  • 对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context;
  • 对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context

LocalBroadcastManager源码解读

官方在文档中说:

You know that the data you are broadcasting won't leave your app, so don't need to worry about 
leaking private data.

It is not possible for other applications to send these broadcasts to your app, so you don't need to 
worry about having security holes they can exploit(开发).

It is more efficient than sending a global broadcast through the system.

本地广播只能在自身 app 内传播,所以不用担心隐私数据泄漏
外界不可能发送广播给我们的本地广播接收者,所以不用担心安全漏洞
本地广播的速度比系统广播更快

那么系统是如何实现来保证以上三点的呢?进源码去看看

public final class LocalBroadcastManager {
    // 广播接收者静态内部类,封装了广播接收者和IntentFilter信息
    private static final class ReceiverRecord{
    final IntentFilter filter;
    final BroadcastReceiver receiver;}

    // 静态内部类,封装了 Intent 和 ReceiverRecord 集合
    private static final class BroadcastRecord{
     final Intent intent;
     final ArrayList<ReceiverRecord> receivers;}

    private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers= new HashMap<>();
    private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
    private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();

     private final Handler mHandler;

    private static LocalBroadcastManager mInstance;

    @NonNull
    public static LocalBroadcastManager getInstance(@NonNull Context context) {
        synchronized (mLock) {
            if (mInstance == null) {
                // 不持有某一个 Activity 这样的 context ,避免内存泄漏
                mInstance = new LocalBroadcastManager(context.getApplicationContext());
            }
            return mInstance;
        }
    }

    // 私有构造方法,供单例使用
    private LocalBroadcastManager(Context context) {
        mAppContext = context;
        // 内部使用 Handler发送消息,而不是全局广播那种 binder 机制
        mHandler = new Handler(context.getMainLooper()) {

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_EXEC_PENDING_BROADCASTS:
                        executePendingBroadcasts();
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        };
    }

executePendingBroadcasts()方法通过遍历的方式,调用广播接收者的 onReceive() 方法,实现广播接收

 private void executePendingBroadcasts() {
     ...
     for (int i=0; i<brs.length; i++) {
       final BroadcastRecord br = brs[i];
       final int nbr = br.receivers.size();
       for (int j=0; j<nbr; j++) {
           final ReceiverRecord rec = br.receivers.get(j);
           if (!rec.dead) {
               rec.receiver.onReceive(mAppContext, br.intent);
           }
       }
   }
 }

发送广播

 public boolean sendBroadcast(@NonNull Intent intent) {
    ...

    if (receivers != null) {
        for (int i=0; i<receivers.size(); i++) {
            receivers.get(i).broadcasting = false;
        }
        mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
        // 使用 Handler 发送消息,进一步触发 executePendingBroadcasts()方法中的 onReceive()方法
        if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
            mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
        }
        return true;
    }

}

注册广播,将广播的信息保存到 mReceivers,和 mActions 中,方便发送广播的时候轮询获取广播信息

    public void registerReceiver(@NonNull BroadcastReceiver receiver,
            @NonNull IntentFilter filter) {
        synchronized (mReceivers) {
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<>(1);
                mReceivers.put(receiver, filters);
            }
            filters.add(entry);
            for (int i=0; i<filter.countActions(); i++) {
                String action = filter.getAction(i);
                ArrayList<ReceiverRecord> entries = mActions.get(action);
                if (entries == null) {
                    entries = new ArrayList<ReceiverRecord>(1);
                    mActions.put(action, entries);
                }
                entries.add(entry);
            }
        }
    }

注销广播的时候,就是把 mReceivers/mActions中的数据删除,这里不再贴代码了。

从上面整个源码中,我们发现整个 LocalBroadcastManager 设计的非常精巧,巧妙的使用 Handler 来发送消息和处理消息,达到发送广播和回调广播接收者 onReceive() 的目的。代码清晰、逻辑简单,因为使用 Handler 发送消息,所以本地广播只能在应用内发送消息,避免了私密数据的泄漏,同时,别人也不能给我们的广播发送消息,最后就是 Handler 的速度比传统的 binder 模式的全局广播更加轻便、快速。而且是一次教科书级别的 Handler的使用。

猜你喜欢

转载自blog.csdn.net/xiao6gui/article/details/80808698