深入学习BroadcastReceiver

xiaoxiao2021-02-28  11

介绍

Android 应用可以发送或接收源于系统或其他App的广播,类似于发布—订阅模式。一般来说,广播主要用于应用内或跨应用的的通信。

注册方式

Context.register(动态注册)

1.创建BroadcastReceiver实例,代码如下:

private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { } };

2.创建IntentFilter,并使用Context.registerReceiver(BroadcastReceiver,IntentFilter)注册,代码如下:

private void register(String action){ IntentFilter filter = new IntentFilter(action); registerReceiver(broadcastReceiver,filter); }

动态注册的BroadcastReceiver的生命周期取决于Context,如果是使用Activity Context注册,则Activity销毁时,广播也会销毁。如果使用Application的Context注册,则广播的生命周期与应用同步,应用销毁时,广播才会销毁。

3.销毁广播时,使用unRegister(BroadcastReceiver),代码如下:

unregisterReceiver(broadcastReceiver);

注: 1.如果在onCreate使用activity的context注册receiver,应该在onDestroy中移除注册。因为receiver持有activity的context引用,导致activity销毁时无法正常释放内存,从而造成内存泄漏(Memory Leak)。

2.如果在onResume中注册receiver,为避免多次注册,应该在onPause中移除注册。不要在onSaveInstanceState移除注册,因为这个方法在用户自然退出时,不会调用。

Manifest-Register(静态注册)

1.创建广播类,集成BroadcastReceiver,实现onReceiver方法:

public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { System.out.println("系统开机"); } }

2.在Manifest.xml中指定广播的action:

<receiver android:name=".BootReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver>

针对静态注册广播,系统会为收到的每一条广播创建一个新的BroadcastReceiver实例,该实例仅在onReceiver执行期间有效。当该方法结束时,系统会将之视为不可用,然后销毁。

goAsync()

BroadcastReceiver的状态(正在运行与否)会影响它所在的进程是否会被系统杀死。如,当进程正在执行receiver(也就是说在onReceive中正在执行代码)时,它会被视为前台进程。除非系统内存极度紧缺,否则系统会一直保持它的运行。

然而,一旦onReceive() 返回,BroadcastReceiver就不再活跃。receiver 的持有进程的重要性就和它所持有的其他组件的重要性一致了。如果进程仅持有一个Manifest-receiver(对于用户从未或最近没有使用的app来说很常见),当onReceive()返回时,系统会将该进程视为低优先级进程,并可能会为更重要的进程获取资源而将之杀死。

因此,不应该在broadcast receiver中启动长久运行的后台进程。onReceive()执行完毕后,系统会在任意时间杀死进程回收内存。于此同时,中止该进程中运行的线程。为避免这种情况,应该调用goAsync()(使得receiver返回时不会被中止,仍处于activie状态,从而保证进程优先级,为后台线程执行费时操作争取更多时间)或从receiver中使用JobScheduler安排JobService,以便系统了解到进程正在执行活跃的工作。可参考”进程和应用的生命周期”

用法如下:

public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, final Intent intent) { System.out.println("系统开机"); final PendingResult pendingResult = goAsync(); AsyncTask<String,Integer,String> asyncTask = new AsyncTask<String, Integer, String>() { @Override protected String doInBackground(String... params) { StringBuilder sb = new StringBuilder(); sb.append("Action: " + intent.getAction() + "\n"); sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n"); // Must call finish() so the BroadcastReceiver can be recycled. pendingResult.finish(); return sb.toString(); } }; asyncTask.execute(); } }

广播处理完毕后,必须调用PendingResult.finish()方法。关于PendingResult.finish()方法,官方解释如下:

/** * Finish the broadcast. The current result will be sent and the * next broadcast will proceed. */

意思是结束广播,当前的结果将被发送然后处理下一条广播。测试发现,如果不调用finish ()方法,该receiver无法继续接收广播。结合上面说的goAsync()使得广播保持active状态,不被中止来看,当前广播没有被销毁。

广播安全

发送安全

1.指定权限:为避免应用内广播发送到应用外或应用接收到应用外的广播,可以给广播指定权限,只有特定权限的应用才能接收到广播。如:

sendBroadcast(new Intent("com.chaos.message"), Manifest.permission.SEND_SMS);

2.指定包名:setPackage(String)可以设定只有特定包名的应用才能接受广播。如:

Intent intent = new Intent("com.chaos.message"); intent.setPackage("com.chaos.test"); sendBroadcast(intent, Manifest.permission.SEND_SMS);

3.使用LocalBroadcastManager

接收安全

1.设定接收权限:

<receiver android:name=".BootReceiver" android:permission="android.permission.RECEIVE_BOOT_COMPLETED"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver>

2.android:export = “false”,export设置为false,该receiver将不会接受应用外的广播。

3.使用LocalBroadcastManager

转载请注明原文地址: https://www.6miu.com/read-450130.html

最新回复(0)