Android知识体系总结2020之Android部分BroadcastReceiver篇

故事扮演 提交于 2020-02-26 03:11:42

初级~中级

1.广播是什么 & 用来解决什么问题

1.1 定义

  在Android中,它是一种广泛运用在应用程序之间传输信息的机制,Android中我们发送广播内容是一个Intent,这个Intent中可以携带我们要发送的数据。

1.2 广播的使用场景

  a.同一app内有多个进程的不同组件之间的消息通信。

  b.不同app之间的组件之间消息的通信。

2 广播的种类

2.1 无序广播

  context.sendBroadcast(Intent)方法发送的广播,不可被拦截,当然发送的数据,接收者是不能进行修改的。

2.2 有序广播

  context.sendOrderBroadcast(Intent)方法发送的广播,可被拦截,而且接收者是可以修改其中要发送的数据,修改和添加都是可以的,这就意味着优先接收者对数据修改之后,下一个接收者接受的数据是上一个接收者已经修改了的,这必须明白。

2.3 本地广播

  localBroadcastManager.sendBroadcast(Intent),只在app内传播。

  本地广播的发送和注册广播接收器都需要使用到LocalBroadcastManager类,如下所示为本地广播的发送和本地广播接收器注册的代码:

  本地广播的发送:

public static void sendLocalBroadcast(Context context,String action){

    Intent intent = new Intent(action);
    LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
    localBroadcastManager.sendBroadcast(intent);

}

  本地广播的接收器的注册:

    IntentFilter intentFilter = new IntentFilter();
    LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);

    intentFilter.addAction(new BroadcastUtil().action_next);
    nasbr = new NextAndStartBroadcastReceiver();
    localBroadcastManager.registerReceiver(nasbr, intentFilter);//注册本地广播接收器

3.广播接收器

  广播接收器是专门用来接收广播信息的,它可分为静态注册和动态注册:

3.1 静态注册

  首先你要创建一个广播接收器类,实例代码如下:

public class BootCompleteReceiver extends BroadcastReceiver {
    [@Override](https://my.oschina.net/u/1162528)
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
    }
}

  代码非常简单,我们只是在onReceive()方法中使用Toast弹出一段信息提示信息。 另外,静态的广播接收器一定要在AndroidManifest.xml文件中注册才可以使用,AndroidManifest.xml文件中注册静态广播代码如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.example.broadcasttest"
       android:versionCode="1"
       android:versionName="1.0" >
       ……
      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
      <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

      <application
           android:allowBackup="true"
           android:icon="@drawable/ic_launcher"
           android:label="@string/app_name"
           android:theme="@style/AppTheme" >
           ……

           <receiver 
               android:name=".BootCompleteReceiver" >
               <intent-filter>
                   <action android:name="android.intent.action.BOOT_COMPLETED" />
               </intent-filter>
           </receiver>
      </application>
</manifest>

  可以看到,<application>标签内出现了一个新的标签<receiver>,所有静态的广播接收器都是在这里进行注册的。它的用法其实和<activity>标签非常相似,也是通过android:name来指定具体注册哪一个广播接收器。

3.2 动态注册

  如何创建一个广播接收器呢?其实就需要新建一个类,让它继承自BroadcastReceiver,并重写父类的onReceive()方法就行了。这样有广播到来时,onReceive()方法就会得到执行,具体的逻辑就可以在这个方法中处理。来个简单的例子来理解以下,如何监听网络变化呢?其实就是新建一个广播接收器去接收来自系统网络变化的广播即可,代码如下所示:

public class MainActivity extends AppCompatActivity{

    private IntentFilter intentFilter;
    
    private NetWorkChangeReceiver netWorkChangeReceiver;

    [@Override](https://my.oschina.net/u/1162528)
    protected void onCreate(Bundle savedInstanceState){
        super.Oncreata(savedInstanceState);
        setContentView(R.layout.activity_main);

        intentFilter = new IntentFilter();
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        networkChangeReceiver = new NetWorkChangeReceiver();
        registerReceiver(networkChangeReceiver, intentFilter);//注册广播接收器

    }

    @Overrid
    protected void onDestroy(){
        unregisterReceiver(networkChangeReceiver);//一定要记得取消广播接收器的注册
        super.onDestroy();
       
    }  

    class NetworkChangeReceiver extends BroadcastReceiver{//广播接收器类

        @Override
        public void onReceiver(Context context,Intent intent){

            //这里需要权限,需要在AndroidManifest.xml中进行网络访问权限申请:
            //<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
            ConnectivityManager connectionManager = (ConnectivityManager)
            getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();

            if (networkInfo != null && networkInfo.isAvailable()) {

                  //有网
                  Toast.makeText(context, "network is available",Toast.LENGTH_SHORT).show();

            } else {

                  //无网
                  Toast.makeText(context, "network is unavailable",
                  Toast.LENGTH_SHORT).show();
            }
        }

    }


}

  动态注册广播接收器的优点以及缺点:动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大优势,但是它也存在着一个缺点,即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在onCreate()方法中的。那么有没有广播能在程序未启动的情况下就能接收到广播呢?静态注册的广播接收器就可以做到。

3.3 那些系统发送的广播有哪些?

  • 监听网络变化 android.net.conn.CONNECTIVITY_CHANGE
  • 关闭或打开飞行模式 Intent.ACTION_AIRPLANE_MODE_CHANGED
  • 充电时或电量发生变化 Intent.ACTION_BATTERY_CHANGED
  • 电池电量低 Intent.ACTION_BATTERY_LOW
  • 电池电量充足(即从电量低变化到饱满时会发出广播 Intent.ACTION_BATTERY_OKAY
  • 系统启动完成后(仅广播一次) Intent.ACTION_BOOT_COMPLETED
  • 按下照相时的拍照按键(硬件按键)时 Intent.ACTION_CAMERA_BUTTON
  • 屏幕锁屏 Intent.ACTION_CLOSE_SYSTEM_DIALOGS
  • 设备当前设置被改变时(界面语言、设备方向等) Intent.ACTION_CONFIGURATION_CHANGED
  • 插入耳机时 Intent.ACTION_HEADSET_PLUG
  • 未正确移除SD卡但已取出来时(正确移除方法:设置--SD卡和设备内存--卸载SD卡) Intent.ACTION_MEDIA_BAD_REMOVAL
  • 插入外部储存装置(如SD卡) Intent.ACTION_MEDIA_CHECKING
  • 成功安装APK Intent.ACTION_PACKAGE_ADDED
  • 成功删除APK Intent.ACTION_PACKAGE_REMOVED
  • 重启设备 Intent.ACTION_REBOOT
  • 屏幕被关闭 Intent.ACTION_SCREEN_OFF
  • 屏幕被打开 Intent.ACTION_SCREEN_ON
  • 关闭系统时 Intent.ACTION_SHUTDOWN
  • 重启设备 Intent.ACTION_REBOOT
  • ......

高级

4.源码角度分析广播机制

4.1 系统广播的源码角度分析

  a.自定义广播接收者BroadcastReceiver,并且重写onReceiver()方法。

  b.通过Binder机制向AMS(Activity Manager Service)进行注册。

  c.广播发送者通过Binder机制向AMS发送广播。

  d.AMS查找符合条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到相应的BroadcastReceiver(一般情况下是Activity)的消息队列中。

  e.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceiver()方法。

4.2 本地广播的源码角度分析

  相比于系统广播而言,本地广播更加安全,更加高效,以下是本地广播的特点以及内部的实现机制:

  特点:

  a.使用它发送的广播将只在自身app内传播,因此你不必担心泄漏隐私的数据。

  b.其他app无法对你的app发送该广播,因此你的app根本不可能收到非自身app发送的该广播,因此你不必担心有安全漏洞可以利用。

  c.比系统广播更加高效。

  内部实现机制:

  a.LocalBroadcast高效的原因:因为它内部是通过Handler实现的,它的sendBroadcast()方法含义并非和系统的sendBroadcast()一样,它的sendBroadcast()方法其实就是通过Handler发送了一个Message而已。

  b.LocalBroadcast安全的原因:既然它是通过Handler实现广播发送的,那么相比系统广播通过Binder机制实现那肯定更加高效,同时使用Handler来实现,别的app无法向我们应用发送该广播,而我们app内部发送的广播也不会离开我们的app。

  LocalBroadcast内部协作主要是靠两个Map集合:mReceivers和mActions,当然还有一个List集合mPendingBroadcasts,这个主要存储待接收的广播对象。

面试题(检测自己学的怎么样)

  • 1.广播是什么?(校招&实习)
  • 2.广播的注册方式有哪些?(校招&实习)
  • 3.广播的分类 & 特性 & 使用场景?(校招&实习)
  • 4.说说系统广播和本地广播的原理 & 区别 & 使用场景。
  • 5.有两个应用注册了一样的广播,一个是静态,一个是动态,连优先级也一样,那么当广播从系统发出来后,哪个应用先接收到广播?

注意:文章末尾面试题来自于笔者自己总结,想求答案或者交流,start 笔者GitHub项目AndroidFaceInterview

更新时间:2020-01-16

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!