注册

Android面试经:Broadcast需要注意哪些细节

前言


在android中,广播也是我们经常使用的组件,但是因为大部分使用场景简单,所以关注不多,今天就让我们来探索一下Broadcast。


注册


这个是常识了,两种注册方式:静态注册(menifast)和动态注册,不展开说了。


这里注意动态注册后,我们一般会手动进行注销,不过如果没有手动注销,当context对象被销毁时,Broadcast会自动注销,但是我们还是及时注销释放资源。


线程及ANR


默认Broadcast都是运行在主线程,而且android对它的运行(onReceive)有一个时间限制——10秒,即ANR时间,所以不要在onReceive执行耗时操作。


但是Broadcast其实可以运行在其他线程(这时候就没有时间限制了),但是必须是动态注册才可以,Context的registerReceiver其实是一系列函数,其中就有

public abstract Intent registerReceiver(BroadcastReceiver receiver,
IntentFilter filter, @Nullable String broadcastPermission,
@Nullable Handler scheduler)

这里可以传入一个Handler,这个Handler就可以是其他线程创建的,这样就可以在其他线程运行Broadcast。


官方说明如下:



This method is always called within the main thread of its process, unless you


explicitly asked for it to be scheduled on a different thread using
{@link android.content.Context#registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)}. When it runs on the main
thread you should
never perform long-running operations in it (there is a timeout of
10 seconds that the system allows before considering the receiver to
be blocked and a candidate to be killed). You cannot launch a popup dialog
in your implementation of onReceive().



那么既然onReceive中不能执行耗时操作,我们是否可以在onReceive中开启一个新的线程来处理?


在onReceive中开启新的线程,因为与其生命周期有关,所以下面与生命周期一起来说。


生命周期


Broadcast生命周期很简单,只有onReceive,当它在执行onReceive时是活跃状态,当执行完成则处于失活状态。根据网上资料:


拥有一个活跃状态的广播接收器的进程被保护起来而不会被杀死,但仅拥有失活状态组件的进程则会在其它进程需要它所占有的内存的时候随时被杀掉。 


而根据Broadcast的官方文档,当onReceive执行完这个Broadcast对象不再是alive状态,所以可以随时被回收销毁。所以不能在onReceive中进行异步操作,即开启新的线程,因为当onReceive执行完处于失活状态,它和这个新的线程可能随时被销毁,导致不可预计的程序问题。如果想在onReceive中执行一些异步操作,那么可以使用JobService,或者service。官方文档如下:



If this BroadcastReceiver was launched through a <receiver> tag,
then the object is no longer alive after returning from this
function.
This means you should not perform any operations that
return a result to you asynchronously. If you need to perform any follow up
background work, schedule a {@link android.app.job.JobService} with
{@link android.app.job.JobScheduler}.


If you wish to interact with a service that is already running and previously
bound using {@link android.content.Context#bindService(Intent, ServiceConnection, int) bindService()},
you can use {@link #peekService}.



所以说当Broadcast执行完onReceive后就可以随时被销毁了,当然动态注册不一样,因为它是手动创建的,所以还需要关心它的引用可达性。


同时,Broadcast的创建也一样,动态注册的时候我们手动创建,所以是一个对象。


而静态注册的时候,应该与activity等组件类似,(binder机制中)先通过intent条件查找创建Broadcast对象,经过测试每次都是重新创建。比如我们在menifast中静态注册一个Broadcast,然后通过一个按钮发送这个广播,在Broadcast的onReceive中打印自己的对象的toString,发现每次点击都是一个新的对象来执行。所以给Broadcast设置类变量,防止重复接收不会起作用,因为每次都是一个新的对象。


如果在onReceive中执行耗时操作,如下:

public class MyBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e("ssss", this.toString() + ":start");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("ssss", this.toString() + ":end");
}
}

再反复点击按钮发送广播,就会发现这些广播会按顺序执行,当上一个执行完才开始执行下一个(因为是在一个线程中)。


作者:BennuCTech
链接:https://juejin.cn/post/7244808577209516093
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册