02.Broadcast总结

87 阅读3分钟

1、Broadcast 广播

功能:可以跨进程的通信方式,用于应用间或应用内通信。

种类:

  • 标准广播/无序广播:异步接收,不可取消。

    Intent().also {
                        it.setAction(Constants.Broadcast.NormalBroadcast)
                        //设置包名,限定在该包名内广播
    
                        sendBroadcast(it)
                    }
    
  • 有序广播:同步接收,按照优先级(priority)从高到低接收广播,可取消。

    Intent().also {
                        it.setAction(Constants.Broadcast.OrderBroadcast)
                        //sendOrderedBroadcast(it,null)
                        //指定广播接收器
                        sendOrderedBroadcast(it,null,broadcastReceiver,null,0,null,null)
                    }
    
  • 本地广播:只在应用内传播。

    private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) }
    //注册本地广播接收器
    localBroadcastManager.registerReceiver(broadcastReceiver, intentFilter)
    //发送本地广播
    Intent().also {
                        it.setAction(Constants.Broadcast.LocalBroadcast)
                        localBroadcastManager.sendBroadcast(it)
                    }
    
  • 粘性广播(Android5.0已被废弃)


2、BroadcastReceiver 广播接收器

功能:用于接收广播信息

注册方式:

  • 静态注册,创建广播接收器后在Manifest清单中注册,程序未启动也能接收到广播

    <receiver
                android:name=".UserPresentReceiver"
                android:enabled="true"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.USER_PRESENT" />
                </intent-filter>
            </receiver>
    
  • 动态注册,创建广播接收器,需要启动程序后注册后才能接收到广播,调用context.registerReceiver(broadcastReceiver, intentFilter)进行注册,调用 context.unregisterReceiver(broadcastReceiver)注销广播接收器

    //动态注册广播接收器
    class MainActivity : AppCompatActivity() {
        private val broadcastReceiver: BroadcastReceiver by lazy { UserPresentReceiver() }  //广播接收器
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val intentFilter = IntentFilter().apply {   //过滤器
                addAction(Intent.ACTION_USER_PRESENT)   //添加一个用户回到前台的活动
            }
            registerReceiver(broadcastReceiver, intentFilter)   //注册广播接收器
        }
    
        override fun onDestroy() {
            super.onDestroy()
            unregisterReceiver(broadcastReceiver)   //注销广播接收器
        }
    }
    
    //广播接收器
    class UserPresentReceiver : BroadcastReceiver() {
        private val TAG = javaClass.simpleName
    
        override fun onReceive(context: Context, intent: Intent) {
            when (intent.action) {
                Intent.ACTION_USER_PRESENT -> "screen on,user present!".logd(TAG)
            }
        }
    }
    

3、广播实现机制

系统广播实现机制

  1. 实现广播接收器BroadcastReceiver,重写onReceiver()方法;

  2. 广播接收器通过Binder机制向AMS进行注册;

  3. 广播发送者通过Binder机制向AMS发送广播;

  4. AMS查询符合条件(IntentFilter/Permission等)的BroasdcastReceiver,将广播发送到对应的BroadcastReceiver的消息队列中;

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

本地广播实现机制: 本地广播主要通过Handler进行发送,其实是通过Handler发送Message。


4、系统变更

Android8.0(API26)开始,大多数广播不能使用隐式接收器接收,即所有隐式广播不能使用使用静态注册的方式来接收。静态注册的广播接收器无法接收隐式广播,默认情况下我们发出的广播都是隐式的,需要通过intentFilter.setPackage(String)指定目标程序,让它变成显式的广播,否则静态注册的广播接收器无法接收到这条广播。


5、对进程状态的影响:

当执行onReceiver()时是前台进程,当进程只有一个静态广播接收器在运行且从onReceiver返回,则变为后台进程。系统可能回收后台进程,同时回收该进程的派生线程。因此不要在广播接收器中直接启动长时间运行的后台线程,可以调用 goAsync()或者使用 JobScheduler从接收器调度 JobService,这样系统就会知道该进程将继续活跃地工作。

//goAsync()的使用
class UserPresentReceiver : BroadcastReceiver() {
    private val TAG = javaClass.simpleName

    override fun onReceive(context: Context, intent: Intent) {
        when (intent.action) {
            Intent.ACTION_USER_PRESENT -> {
                "screen on,user present!".logd(TAG)
                Task(goAsync() as PendingResult).execute()
            }
        }

    }

    private inner class Task(val pendingIntent: PendingResult) :
        AsyncTask<String, Int, String>() {
        override fun doInBackground(vararg p0: String?): String {
            VUiKit.sleep(8000)
            return "screen on,await!"
        }

        override fun onPostExecute(result: String?) {
            super.onPostExecute(result)
            result?.logd(TAG)
            pendingIntent.finish()
        }
    }
}