大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
介绍
BroadcastReceiver 是 Android 的四大组件之一,它作用于应用内、进程间重要的一种通信方式,能够将某个消息通过广播的形式传递给订阅的广播接收器中,下面我们就来分析一下 广播注册到接收到消息 Android 源码到底做了些什么?
源码解析
registerReceiver
时序图
代码讲解
我们跟着上面时序图来讲解代码
先在 MainActivity registerReceiver 广播接收者
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注册广播接收者
registerReceiver(new ReceiverB(),filterB);
}
...代码省略...
}
点击 registerReceiver 我们发现并不是 Activity 里面的方法,而是 Activity 的父类 ContextWrapper
ContextWrapper registerReceiver
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient {
....代码省略...
}
public class ContextThemeWrapper extends ContextWrapper {
....代码省略...
}
public class ContextWrapper extends Context {
@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
}
这里的成员变量 mBase 是 Context,看过 Application 应用启动那块的源码知道 ContextImp 继承了 Context ,那么我们看继承类具体的 registerReceiver 方法吧。
ContextImp registerReceiver
class ContextImpl extends Context {
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler, int flags) {
return registerReceiverInternal(receiver, getUserId(),
filter, broadcastPermission, scheduler, getOuterContext(), flags);
}
}
继续看 registerReceiverInternal
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context, int flags) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
//获取 ActivityThread H
scheduler = mMainThread.getHandler();
}
//获取 IIntentReceiver 对象,通过它与 AMS 交互,并且通过 Handler 传递消息
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 {
//调用 AMS 的 registerReceiver
final Intent intent = ActivityManager.getService().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
broadcastPermission, userId, flags);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
注册广播接收器的函数最终进入到了 ContextImpl 的 registerReceiverInternal 这个函数,这里的成员变量 mPackageInfo 是一个 LoadApk 实例,它是用来负责处理广播的接收。
ActivityThread H
mMainThread.getHandler() 获取的这个 Handler 是用来分发 AMS 发过来的广播。
private class H extends Handler {
...
public void handleMessage(Message msg) {
case RECEIVER:
handleReceiver((ReceiverData)msg.obj);
maybeSnapshot();
break;
}
....
}
继续看 handleReceiver
private void handleReceiver(ReceiverData data) {
....
//应用 Application 局部变量
Application app;
//广播局部变量
BroadcastReceiver receiver;
//应用 Context
ContextImpl context;
try {
//制作 Applicaiton
app = packageInfo.makeApplication(false, mInstrumentation);
//拿到上下文
context = (ContextImpl) app.getBaseContext();
if (data.info.splitName != null) {
context = (ContextImpl) context.createContextForSplit(data.info.splitName);
}
java.lang.ClassLoader cl = context.getClassLoader();
data.intent.setExtrasClassLoader(cl);
data.intent.prepareToEnterProcess();
data.setExtrasClassLoader(cl);
//通过反射进行实例化广播
receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
} catch (Exception e) {
try {
if (localLOGV) Slog.v(
sCurrentBroadcastIntent.set(data.intent);
receiver.setPendingResult(data);
//onReceive 进行调用
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
...
} finally {
sCurrentBroadcastIntent.set(null);
}
....
}
mPackageInfo.getReceiverDispatcher 函数实现
//LoadeApk
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>();
//以Context 为 key,map 为 values 存储到 mReveivers 中
mReceivers.put(context, map);
}
map.put(r, rd);
}
} else {
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}
在 LoadedAPK 类中的 getReceivcerDispatcher 函数中,首先看下 r 是不是已经实例化了,如果没有就创建一个,并且以 r 为 key 值保存在一个 HM 集合中,而这个 map 又被存储在了 mReceivers 中,这样只要给定一个 Activity 和 BroadcastReceiver ,就可以查看 LoadedAPK 里面是否已经存在相应的广播接收发布器了。现在在回到 ContextImpl.registerReceiverInternal 函数,获得了 IIntentReceiver 类型的 Binder 对象后,就开始注册到 AMS 中了,具体代码看下面小点。
AMS registerReceiver
/** * 这里是通过 Binder 通知调用 * @return Intent */
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
int flags) {
enforceNotIsolatedCaller("registerReceiver");
ArrayList<Intent> stickyIntents = null;
ProcessRecord callerApp = null;
final boolean visibleToInstantApps
= (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
int callingUid;
int callingPid;
boolean instantApp;
synchronized(this) {
if (caller != null) {
//1. 获取进ProcessRecord
callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {
...代码省略...
}
...代码省略...
//2. 根据 Action 查找匹配的 sticky 接收器
Iterator<String> actions = filter.actionsIterator();
if (actions == null) {
ArrayList<String> noAction = new ArrayList<String>(1);
noAction.add(null);
actions = noAction.iterator();
}
...代码省略...
//3. 获取 ReceiverList
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
try {
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
} else if (rl.uid != callingUid) {
...代码省略...
}
//4. 构建 BroadcastFilter 对象并且添加到 ReceiverList 中
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId, instantApp, visibleToInstantApps);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
mReceiverResolver.addFilter(bf);
...代码省略...
return sticky;
}
}
- 根据上面注释 1 可知,获取进程对应的 pid,uid;
- 注释 2 获取 IntentFilter 的所有 Action;
- 注释 3 把广播接收器的 receiver 保存到了一个 ReceiverList 中,这个列表的宿主进程是 rl.app,就是 MainActivity 所在的进程。
- 注释 4 只是把广播接收器保存起来,但是还没有和 filter 关联起来,这里就创建一个 BroadcastFilter 来把广播接收器列表 rl 和 filter 关联起来,然后保存在 AMS 成员变量 mReceiverResolver 中,这样,就将广播接收器和要接收广播类型的接收器 filter 保存在 AMS 中了,以后就能接到到相应的广播并做处理了。
接下来我们看下 sendBroadcast() 。
onReceive
时序图
在 Activity 通过 sendBroadcast 发送一个广播最后 Binder 发送给 AMS , AMS 根据这个广播的 Action 类型找到相应的广播接收器,然后把这个广播放进自己的消息队列中,完成第一部分广播异步分发。
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
intent = new Intent(intent);
......代码省略....
if (intent.getComponent() == null) {
......代码省略....
} else {
//查询到该 Intent 对应的 BroadcastFilter 也就是接收器列表
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false /*defaultOnly*/, userId);
}
}
......代码省略....
if (!replaced) {
queue.enqueueParallelBroadcastLocked(r);
//处理广播分发
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
......代码省略....
return ActivityManager.BROADCAST_SUCCESS;
}
AMS 在消息循环中处理这个广播,并通过 Binder 机制把这个广播分发给注册的 ReceiverDispatch ,ReceiverDispatch 把这个广播放进 MainActivity 所在进程的消息队列中,完成第二部分异步消息分发。
public void scheduleBroadcastsLocked() {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
+ mQueueName + "]: current="
+ mBroadcastsScheduled);
if (mBroadcastsScheduled) {
return;
}
//通过 Handler 分发
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
private final class BroadcastHandler extends Handler {
......
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_INTENT_MSG: {
if (DEBUG_BROADCAST) Slog.v(
TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
//处理下一个广播
processNextBroadcast(true);
} break;
....
}
}
}
ReceiverDispatch 的内部类 Args 在 MainActivity 所在的线程消息循环中处理这个广播,最终是将这个广播分发给注册的 Receiver 实例的 onReceiver 处理。
public final class LoadedApk {
...
static final class ReceiverDispatcher {
final class Args extends BroadcastReceiver.PendingResult {
....
public final Runnable getRunnable() {
return () -> {
....
try {
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess();
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
//回调到接收广播的 onReceiver
receiver.onReceive(mContext, intent);
} catch (Exception e) {
...
};
}
}
}
...
}
总结
注册跟接收源码分析就到这里差不多了,简单来说广播就是一个订阅 – 发布的过程,通过一些 map 存储 BroadcastReceiver ,key 就是封装了这些广播的信息类,如 Action 之类的,当发布一个广播时通过 AMS 到这个 map 中查询注册了这个广播的 IntentFilter 的 BroadcastReceiver , 然后通过 ReceiverDispatch 将广播分发给各个订阅的对象,从而完成了整个通信过程。
《如何成为一位Android架构师?(架构视频+面试专题文档+学习笔记)》
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/197284.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...