前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android必知必会的四大组件--Broadcast Receiver

Android必知必会的四大组件--Broadcast Receiver

作者头像
ClericYi
发布2020-06-23 15:18:31
7130
发布2020-06-23 15:18:31
举报
文章被收录于专栏:ClericYi's Blog

前言

广播,在我们的应用中起着一个非常重要的角色。就比如说我们经常使用的IntentIntentFilter,就有着广播的作用。

在我的 helper 项目中就集成了网络广播的动态注册。

生命周期

因为没有直接的图示可以上,而且Broadcast中并没有onCreate()onDestroy()这样方法,只能通过官方文档验证。

图中的圈红框的加粗文字大概意思就是,使用静态广播进行注册,那么每接受到一次信息,他就不复存在了,也就是需要重建。其它方式构建的生命周期,与关联的Activity中的具体操作相关。

在Android 8.0以后已经不在支持静态广播了

两种广播

代码语言:javascript
复制
代码语言:javascript
复制
public class NetworkReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION) && App.getInstance() != null) {
             App.getInstance().notifyObservers(isNetConnected(context));
         }
     }
}
代码语言:javascript
复制

虽然是两种广播形式,但是他们同样要干一件事情,就是继承BroadcastReceiver,并重写onReceive()方法。

广播的类型主要分为5类:

  • 普通广播(Normal Broadcast
  • 系统广播(System Broadcast):当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作时会自动进行系统广播。
  • 有序广播(Ordered Broadcast):sendOrderedBroadcast(Intent),会按照优先级进入的顺序进行排序发送
  • 粘性广播(Sticky Broadcast):Android5.0 后已经失效
  • App应用内广播(Local Broadcast

全局广播

这个广播同样可以使用在应用内,但是这种广播的安全性有待质疑。

代码语言:javascript
复制
代码语言:javascript
复制
// 消息传递
sendBroadcast(Intent);
  • 静态广播注册
代码语言:javascript
复制
<receiver android:name="com.clericyi.basehelper.network.NetworkReceiver">
  <intent-filter>
    <action android:name="android.intent.action.BATTERY_LOW"/>
  </intent-filter>
</receiver>
  • 动态广播注册

和静态广播不同的地方,动态广播注册完以后需要进行注销操作。

代码语言:javascript
复制
代码语言:javascript
复制
// 注册
networkReceiver = new NetworkReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
registerReceiver(networkReceiver, intentFilter);

// 注销(如果没有注销,将会发生内存泄漏)
unregisterReceiver(networkReceiver);

应用内广播

  1. 优点:
    • 发送的广播只会在自己的App内传播,不会泄漏给其他App,保障了数据的安全性。
    • 无法接受到其他App的广播,也就省去各种麻烦事。
    • 相较于全局广播效率更高。
  2. 使用方法:
代码语言:javascript
复制
代码语言:javascript
复制
//注册
networkReceiver = new NetworkReceiver();
localBroadcastManager = LocalBroadcastManager.getInstance(this); // --> 以单例模式进行创建
localBroadcastManager.registerReceiver(networkReceiver, new IntentFilter("需要去过滤的信息"));

// 发送消息
localBroadcastManager.sendBroadcast(Intent);

// 注销
localBroadcastManager.unregisterReceiver(networkReceiver);

LocalBroadcastManager源码导读

为什么要导读LocalBroadcastManager源码呢?

其实是想让读者们知道LocalBroadcastManager使用并不是Binder机制来完成通信的。

就上述的内容中我们知道,一共有两个主要方法:

(1)getInstance()

(2)sendBroadcast(Intent)

(1)getInstance()

代码语言:javascript
复制
代码语言:javascript
复制
public static LocalBroadcastManager getInstance(@NonNull Context context) {
        synchronized (mLock) {
            if (mInstance == null) {
                mInstance = new LocalBroadcastManager(context.getApplicationContext()); // 1 -->
            }
            return mInstance;
        }
    }
// 由注释1直接调用的方法    
private LocalBroadcastManager(Context context) {
        mAppContext = context;
        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);
                }
            }
        };
    }
代码语言:javascript
复制

那这里就是很清楚的知道,这是一个以DCL的方式,来直接完成对单例的创建,而在构造函数中,定义了一个Handler

那我们就来做一个猜测,我们在应用内的广播本质其实是基于一个Handler的一异步传输机制。为了验证!!我们就需要去了解他的sendBroadcast(Intent)方法。

(2)sendBroadcast(Intent)

代码语言:javascript
复制
代码语言:javascript
复制
public boolean sendBroadcast(@NonNull Intent intent) {
        synchronized (mReceivers) {
            // 拿到传递过来的Intent中保存的数据
            final String action = intent.getAction();
            final String type = intent.resolveTypeIfNeeded(
                    mAppContext.getContentResolver());
            final Uri data = intent.getData();
            final String scheme = intent.getScheme();
            final Set<String> categories = intent.getCategories();

            // 。。。。。

            // 获取配置的Action
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            if (entries != null) {
                if (debug) Log.v(TAG, "Action list: " + entries);

                ArrayList<ReceiverRecord> receivers = null;
                // 。。。。。对变量receivers的一些列操作。

                // 存在接受对象时,将数据通过Handler的方式传递出去。
                if (receivers != null) {
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }

通过代码已经成功验证了,其实LocalBroadcast最终基于的数据传输机制就是我们的Handler,这就是和应用间广播最大的不同之处了。

总结

Q1:动态广播和静态广播的区别?

  • 静态广播:广播一直存在,消耗资源较大,耗电量大。
  • 动态广播:广播的生命周期较为灵活,资源消耗少。响应速度快于静态广播。

Q2:未启动的App能不能用广播调起?

Android在3.1以后将新安装的应用置为“STOPPED”状态,只有当应用启动过一次之后这个状态才会改变。

为了调起我们的App,应该在Intent中加入这样的代码。

代码语言:javascript
复制
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);

其他知识点们:

  1. 广播同样会引发ANR的惨状,广播的耗时操作时长不允许超过10s。而且广播内一般也不会像ServiceActivity一样会使用Thread来完成我们的耗时操作。
  2. 全局和应用内的广播两者的注册方式其实相似,但是针对的场景不同。如果需要网络、电池等服务,你就需要全局广播;如果你只需要应用内通信,那么你只需要应用内广播。
  3. 应用内广播(LocalBroadcast)使用的Handler的消息传输机制;应用间广播或者说是进程间广播(Broadcast)使用的则是Binder的机制。
  4. 在onResume()注册、onPause()注销,因为onPause()在App死亡前一定会被执行,从而保证广播在App死亡前一定会被注销,从而防止内存泄露。

注意点!!

对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:

  • 对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext;
  • 对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context;
  • 对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context。
  • 对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context;
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DevGW 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档