Android四大组件重要性已经不言而喻了,今天谈谈的是Android中的广播机制。在咱们上学的时候,每一个班级的教室里都会装有一个喇叭,这些喇叭都是接入到学校的广播室的,一旦有什么重要的通知,就会播放一条广播来告知全校的师生。相似的工做机制其实在计算机领域也有很普遍的应用,若是你了解网络通讯原理应该会知道,在一个 IP 网络范围中最大的 IP 地址是被保留做为广播地址来使用的。好比某个网络的 IP 范围是 192.168.0.XXX,子网掩码是 255.255.255.0,那么这个网络的广播地址就是 192.168.0.255。 广播数据包会被发送到同一网络上的全部端口,这样在该网络中的每台主机都将会收到这条广播。为了方便于进行系统级别的消息通知,Android 也引入了一套相似的广播消息机制。html
为何说 Android中的广播机制更加灵活呢?这是由于 Android中的每一个应用程序均可以对本身感兴趣的广播进行注册,这样该程序就只会接收到本身所关心的广播内容,这些 播多是来自于系统的,也多是来自于其余应用程序的。Android提供了一套完整的 API, 容许应用程序自由地发送和接收广播。接收广播的方法则须要引入一个新的概念,广播接收器(Broadcast Receiver),它就是用来接收来自系统和应用中的广播。java
在Android广播中,主要分为两种类型:标准广播和有序广播。android
标准广播(Normal broadcasts)是一种彻底异步执行的广播,在广播发出以后,全部的 广播接收器几乎都会在同一时刻接收到这条广播消息,所以它们之间没有任何前后顺序可 言。这种广播的效率会比较高,但同时也意味着它是没法被截断的。标准广播的工做流程如图所示。安全
有序广播(Ordered broadcasts)则是一种同步执行的广播,在广播发出以后,同一时刻只会有一个广播接收器可以收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。因此此时的广播接收器是有前后顺序的,优先级高的广播接收器就能够先收到广播消息,而且前面的广播接收器还能够截断正在传递的广播,这样后面的广播接收器就没法收到广播消息了。性能优化
BroadcastReceiver主要包括两方面的内容,一个是广播的注册过程,另外一个是广播的发送和接收过程。那么该如何建立一个广播接收器呢?其实只须要新建一个类,让它继承自BroadcastReceiver, 并重写父类的 onReceive()方法就好了。这样当有广播到来时,onReceive()方法就会获得执行, 具体的逻辑就能够在这个方法中处理。 广播的使用方法有两个:静态方法和动态方法。网络
动态方法app
public class MainActivity extends Activity { private IntentFilter intentFilter; private NetworkChangeReceiver networkChangeReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intentFilter = new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); networkChangeReceiver = new NetworkChangeReceiver(); registerReceiver(networkChangeReceiver, intentFilter); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(networkChangeReceiver); } private class NetworkChangeReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "网络变化", Toast.LENGTH_SHORT).show(); } } }
能够看到,咱们在 MainActivity 中定义了一个内部类 NetworkChangeReceiver,这个类 是继承自 BroadcastReceiver的,并重写了父类的 onReceive()方法。这样每当网络状态发生变 化时,onReceive()方法就会获得执行,这里只是简单地使用 Toast提示了一段文本信息。框架
而后观察 onCreate()方法,首先咱们建立了一个 IntentFilter 的实例,并给它添加了一个 值为 android.net.conn.CONNECTIVITY_CHANGE 的 action,为何要添加这个值呢?由于 当网络状态发生变化时,系统发出的正是一条值为 android.net.conn.CONNECTIVITY_ CHANGE 的广播,也就是说咱们的广播接收器想要监听什么广播,就在这里添加相应的 action就好了。接下来建立了一个 NetworkChangeReceiver的实例,而后调用 registerReceiver() 方法进行注册,将 NetworkChangeReceiver 的实例和 IntentFilter 的实例都传了进去,这样 NetworkChangeReceiver就会收到全部值为android.net.conn.CONNECTIVITY_CHANGE的广 播,也就实现了监听网络变化的功能。异步
最后要记得,动态注册的广播接收器必定都要取消注册才行,这里咱们是在 onDestroy() 方法中经过调用 unregisterReceiver()方法来实现的。ide
静态方法
动态注册的广播接收器能够自由地控制注册与注销,在灵活性方面有很大的优点,可是 它也存在着一个缺点,即必需要在程序启动以后才能接收到广播,由于注册的逻辑是写在 onCreate()方法中的。那么有没有什么办法可让程序在未启动的状况下就能接收到广播 呢?这就须要使用静态注册的方式了。
这里咱们准备让程序接收一条开机广播,当收到这条广播时就能够在 onReceive()方法里 执行相应的逻辑,从而实现开机启动的功能。新建一个 BootCompleteReceiver 继承自 BroadcastReceiver,代码以下所示
public class BootCompleteReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show(); } }
能够看到,这里再也不使用内部类的方式来定义广播接收器,由于稍后咱们须要在 AndroidManifest.xml中将这个广播接收器的类名注册进去。在 onReceive()方法中,仍是简单 地使用 Toast弹出一段提示信息。
而后修改 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>
另外,监听系统开机广播也是须要声明权限的,能够看到,咱们使用
如今你已经学会了经过广播接收器来接收系统广播,接下来咱们就要学习一下如何在应用程序中发送自定义的广播。前面已经介绍过了,广播主要分为两种类型,标准广播和有序 广播。
在API文档中关于BroadcastReceiver的概述:
那么广播事件的流程如何呢,以下:
注册广播事件:注册方式有两种,一种是静态注册,就是在AndroidManifest.xml文件中定义,注册的广播接收器必需要继承BroadcastReceiver;另外一种是动态注册,是在程序中使用Context.registerReceiver注册,注册的广播接收器至关于一个匿名类。两种方式都须要IntentFIlter。
发送广播事件:经过Context.sendBroadcast来发送,由Intent来传递注册时用到的Action。
接收广播事件:当发送的广播被接收器监听到后,会调用它的onReceive()方法,并将包含消息的Intent对象传给它。onReceive中代码的执行时间不要超过10s,不然Android会弹出超时dialog。
具体作法:
在发送广播以前,咱们仍是须要先定义一个广播接收器来准备接收此广播才行,否则发 出去也是白发。所以新建一个 MyBroadcastReceiver继承自 BroadcastReceiver
public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "接收到广播消息", Toast.LENGTH_SHORT).show(); } }
这里当 MyBroadcastReceiver收到自定义的广播时,就会弹出提示语。而后在 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=".MyBroadcastReceiver" > <intent-filter> <action android:name="com.example.broadcasttest.MY_BROADCAST" /> </intent-filter> </receiver> </application> </manifest>
能够看到,这里让 MyBroadcastReceiver 接收一条值为 com.example.broadcasttest. MY_BROADCAST的广播,所以待会儿在发送广播的时候,咱们就须要发出这样的一条广播。
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST"); sendBroadcast(intent); } }); } }
能够看到,咱们在按钮的点击事件里面加入了发送自定义广播的逻辑。首先构建出了一 个 Intent对象,并把要发送的广播的值传入,而后调用了 Context的 sendBroadcast()方法将广 播发送出去,这样全部监听 com.example.broadcasttest.MY_BROADCAST 这条广播的广播接 收器就会收到消息。此时发出去的广播就是一条标准广播。
前面咱们发送和接收的广播所有都是属于系统全局广播,即发出的广播能够被其余任何的任何应用程序接收到,而且咱们也能够接收来自于其余任何应用程序的广播。这样就很容易会引发安全性的问题,好比说咱们发送的一些携带关键性数据的广播有可能被其余的应用 程序截获,或者其余的程序不停地向咱们的广播接收器里发送各类垃圾广播。
为了可以简单地解决广播的安全性问题,Android 引入了一套本地广播机制,使用这个机制发出的广播只可以在应用程序的内部进行传递,而且广播接收器也只能接收来自本应用程序发出的广播,这样全部的安全性问题就都不存在了。 本地广播的用法并不复杂,主要就是使用了一个 LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接收器的方法。下面咱们就经过具体的实例来尝试一下它的用法,修改 MainActivity中的代码,以下所示:
public class MainActivity extends Activity { private IntentFilter intentFilter; private LocalReceiver localReceiver; private LocalBroadcastManager localBroadcastManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); localBroadcastManager = LocalBroadcastManager.getInstance(this); // 获取实例 Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("com.example.broadcasttest. LOCAL_BROADCAST"); localBroadcastManager.sendBroadcast(intent);// 发送本地广播 } }); intentFilter = new IntentFilter(); intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST"); localReceiver = new LocalReceiver(); // 注册本地广播监听器 localBroadcastManager.registerReceiver(localReceiver, intentFilter); } @Override protected void onDestroy() { super.onDestroy(); localBroadcastManager.unregisterReceiver(localReceiver); } private class LocalReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "received local broadcast", Toast.LENGTH_SHORT).show(); } } }
有没有感受这些代码很熟悉?没错,其实这基本上就和咱们前面所学的动态注册广播接 收器以及发送广播的代码是同样。只不过如今首先是经过 LocalBroadcastManager的 getInstance() 方法获得了它的一个实例,而后在注册广播接收器的时候调用的是 LocalBroadcastManager 的 registerReceiver()方法,在发送广播的时候调用的是 LocalBroadcastManager的 sendBroadcast() 方法,仅此而已。
另外还有一点须要说明,本地广播是没法经过静态注册的方式来接收的。其实这也彻底 能够理解,由于静态注册主要就是为了让程序在未启动的状况下也能收到广播,而发送本地 广播时,咱们的程序确定是已经启动了,所以也彻底不须要使用静态注册的功能。
总结下使用本地广播的几点优点吧。
咱们如今知道了广播的注册有静态注册和动态注册,其中静态注册的广播在应用安装时由系统自动完成注册的。具体来讲是由PMS(PackageManagerService)来完成整个注册过程的,除了广播觉得,其余三大组件也都是应用安装时由PMS解析并注册的,这里分析下广播的动态注册过程,动态注册过程是从ContextWrapper的registerReceiver方法开始的,和Activity以及Service同样。ContextWrapper并无作实际的工做,基本将注册过程直接交给ContextImpl来完成。
@Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { return registerReceiver(receiver, filter, null, null); }
ContextImpl的registerReceiver方法调用了本身的registerReceiverInternal
方法,具体实现以下:
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission,Handler scheduler, Context context) { IIntentReceiver rd = null; if (receiver != null) { if (mPackageInfo != null && context != null) { if (scheduler == null) { scheduler = mMainThread.getHandler(); } rd = mPackageInfo.getReceiverDispatcher( receiver, context, scheduler, mMainThread.getInstrumentation(), true); } else { if (scheduler == null) { scheduler = mMainThread.getHandler(); } rd = new LoadedApk.ReceiverDispatcher( receiver, context, scheduler, null, true).getIIntentReceiver(); } } try { return ActivityManagerNative.getDefault().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId); } catch (RemoteException e) { return null; } }
系统首先从mPackageInfo获取IIntentReceiver对象,而后再采用跨进程的方式向AMS发送广播注册的请求。之因此用IIntentReceiver而不是直接采用BroadcastReceiver,这是由于上述注册过程是一个进程间通讯的过程,而BroadcastReceiver做为一个Android组件是不能直接跨进程传递的,因此须要经过IIntentReceiver来中转一下,毫无疑问,IIntentReceiver必须是一个Binder接口,它的具体实现是LoadedApk.ReceiverDispatcher,ReceiverDispatcher的内部同时保存了BroadcastReceiver和InnerReceiver,这样当接收到广播时ReceiverDispatcher能够很方便地调用BroadcastReceiver的onReceiver方法。
这里的ActivityManagerNative.getDefault()实际上就是一个AMS。具体代码以下:
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,Context context, Handler handler,Instrumentation instrumentation, boolean registered) { synchronized (mReceivers) { LoadedApk.ReceiverDispatcher rd = null; ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null; if (registered) { map = mReceivers.get(context); if (map != null) { rd = map.get(r); } } if (rd == null) { rd = new ReceiverDispatcher(r, context, handler,instrumentation, registered); if (registered) { if (map == null) { map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>(); mReceivers.put(context, map); } map.put(r, rd); } } else { rd.validate(context, handler); } rd.mForgotten = false; return rd.getIIntentReceiver(); }
因为注册的广播真正的实现过程是在AMS中,最终会把远程的InnerReceiver对象以及IntentFilter对象存储起来,这样整个广播的注册过程就完成了。
当经过send方法来发送广播时,AMS会查找出匹配的广播接收者并将广播发送给他们处理。广播的发送有几种类型:普通广播,有序广播和粘性广播。这里分析下普通广播的实现。
广播的发送和接收。其本质是一个过程的两个阶段。广播的发送仍然开始于ContextWrapper的sendBroadcast方法,之因此不是Context,那是由于Context的sendBroadcast是一个抽象方法。和广播的注册过程同样ContextWrapper的sendBroadcast方法仍然什么都不作,只是把事情交给ContextImpl去处理,ContextImpl的sendBroadcast方法源码以下:
public void sendBroadcast(Intent intent) { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false, getUserId()); } catch (RemoteException e) { } }
它直接向AMS发起了一个异步请求用于发送广播。那么AMS的broadcastIntent方法的源码以下:
public final int broadcastIntent(IApplicationThread caller, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle map, String requiredPermission, boolean serialized, boolean sticky, int userId) { synchronized(this) { intent = verifyBroadcastLocked(intent); final ProcessRecord callerApp = getRecordForAppLocked(caller); final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); int res = broadcastIntentLocked(callerApp, callerApp != null ? callerApp.info.packageName : null, intent, resolvedType, resultTo, resultCode, resultData, map, requiredPermission, serialized, sticky, callingPid, callingUid, userId); Binder.restoreCallingIdentity(origId); return res; } }
从代码上看,broadcastIntent调用了broadcastIntentLocked方法,但在AMS的broadcastIntentLocked方法里有这么一句:
// By default broadcasts do not go to stopped apps. intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
这表示在Android5.0下,默认状况下广播不会发送给已经中止的应用。FLAG_EXCLUDE_STOPPED_PACKAGES的含义是表示 不包含已经中止的应用,这个时候广播不会发送给已经中止的应用。
在broadcastIntentLocked的内部,会根据intent-filter查找出匹配的广播接收者并通过一系列的条件过滤,最终会将知足条件的广播接收者添加到BroadcastQueue中,接着BroadcastQueue将会广播发送给相应的广播接收者。
if ((receivers != null && receivers.size() > 0) || resultTo != null) { BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, requiredPermission, receivers, resultTo, resultCode, resultData, map, ordered, sticky, false); if (DEBUG_BROADCAST) Slog.v( TAG, "Enqueueing ordered broadcast " + r + ": prev had " + queue.mOrderedBroadcasts.size()); if (DEBUG_BROADCAST) { int seq = r.intent.getIntExtra("seq", -1); Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq); } boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r); if (!replaced) { queue.enqueueOrderedBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } }
下面看下BroadcastQueue中广播的发送过程的实现。以下所示:
public void scheduleBroadcastsLocked() { if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts [" + mQueueName + "]: current=" + mBroadcastsScheduled); if (mBroadcastsScheduled) { return; } mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this)); mBroadcastsScheduled = true; }
BroadcastQueue的scheduleBroadcastsLocked方法并无当即发送广播,而是发送了一个BROADCAST_INTENT_MSG类型的消息,BroadcastQueue收到消息后会调用processNextBroadcast方法,BroadcastQueue的processNextBroadcast方法对普通广播的处理方式以下:
// First, deliver any non-serialized broadcasts right away. while (mParallelBroadcasts.size() > 0) { r = mParallelBroadcasts.remove(0); r.dispatchTime = SystemClock.uptimeMillis(); r.dispatchClockTime = System.currentTimeMillis(); final int N = r.receivers.size(); if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast [" + mQueueName + "] " + r); for (int i=0; i<N; i++) { Object target = r.receivers.get(i); if (DEBUG_BROADCAST) Slog.v(TAG, "Delivering non-ordered on [" + mQueueName + "] to registered " + target + ": " + r); deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false); } addBroadcastToHistoryLocked(r); if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast [" + mQueueName + "] " + r); }
能够看到,无序广播存储在mParallelBroadcasts中,系统会遍历mParallelBroadcasts并将其中的广播发送给它们全部接收者,具体的发送过程是经过deliverToRegisteredReceiverLocked方法来实现的。
最终呢,会调用ApplicationThread的scheduleRegisteredReceiver的实现比较简单,它经过InnerReceiver来实现广播的接收。而后InnerReceiver的performReceive方法会调用LoadedApk.ReceiverDispatcher的PerformReceive方法。最终会回调到receiver.onReceive()这个方法。
很显然,这个时候BroadcastReceiver的onReceive方法被执行了,也就是说应用收到广播了,同时,onReceive方法是在广播接收者的主线程中被调用,因此不能作耗时操做,由于是在ApplicationThread的主线程上执行的。
总结一下,Android中应用程序发送广播的过程:
做为Android中四大组件之一的广播,能够应用不少场景的,好比用户异地登录强制下线,应用开机启动服务,网络状态变化通知等等,掌握好其中的定义,使用方法,背后的注册流程,发送和接收消息流程机制,对于咱们在开发时是颇有帮助的。
参考信息:
1,http://blog.csdn.net/zuolongsnail/article/details/6450156
2,《第一行代码》
源于对掌握的Android开发基础点进行整理,罗列下已经总结的文章,从中能够看到技术积累的过程。
1,Android系统简介
2,ProGuard代码混淆
3,讲讲Handler+Looper+MessageQueue关系
4,Android图片加载库理解
5,谈谈Android运行时权限理解
6,EventBus初理解
7,Android 常见工具类
8,对于Fragment的一些理解
9,Android 四大组件之 " Activity "
10,Android 四大组件之" Service "
11,Android 四大组件之“ BroadcastReceiver "
12,Android 四大组件之" ContentProvider "
13,讲讲 Android 事件拦截机制
14,Android 动画的理解
15,Android 生命周期和启动模式
16,Android IPC 机制
17,View 的事件体系
18,View 的工做原理
19,理解 Window 和 WindowManager
20,Activity 启动过程分析
21,Service 启动过程分析
22,Android 性能优化
23,Android 消息机制
24,Android Bitmap相关
25,Android 线程和线程池
26,Android 中的 Drawable 和动画
27,RecylerView 中的装饰者模式
28,Android 触摸事件机制
29,Android 事件机制应用
30,Cordova 框架的一些理解
31,有关 Android 插件化思考
32,开发人员必备技能——单元测试