Broadcast发送流程分析

Posted 胖虎

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Broadcast发送流程分析相关的知识,希望对你有一定的参考价值。

android Source Tools : androidxref.com

Original stack trace:

android.app.RemoteServiceException : can’t deliver broadcast
at android.app.ActivityThread H . h a n d l e M e s s a g e ( A c t i v i t y T h r e a d . j a v a : 2038 ) a t a n d r o i d . o s . H a n d l e r . d i s p a t c h M e s s a g e ( H a n d l e r . j a v a : 107 ) a t a n d r o i d . o s . L o o p e r . l o o p ( L o o p e r . j a v a : 214 ) a t a n d r o i d . a p p . A c t i v i t y T h r e a d . m a i n ( A c t i v i t y T h r e a d . j a v a : 7682 ) a t j a v a . l a n g . r e f l e c t . M e t h o d . i n v o k e ( M e t h o d . j a v a ) a t c o m . a n d r o i d . i n t e r n a l . o s . R u n t i m e I n i t H.handleMessage(ActivityThread.java:2038) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7682) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit H.handleMessage(ActivityThread.java:2038)atandroid.os.Handler.dispatchMessage(Handler.java:107)atandroid.os.Looper.loop(Looper.java:214)atandroid.app.ActivityThread.main(ActivityThread.java:7682)atjava.lang.reflect.Method.invoke(Method.java)atcom.android.internal.os.RuntimeInitMethodAndArgsCaller.run(RuntimeInit.java:516)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)

conclusion: By the above Log information and the following process analysis, we can only conclude that when Step7 was about to distribute the broadcast to the registered BroadcastReceiver, the remote Binder was found dead, but we don’t know why


eg. Android SDK 29 source code for analysis

First. We have found the problem log occurs in the android system code

com.android.server.am.BroadcastQueue.java Line 600

“app.scheduleCrash(“can’t deliver broadcast”);” in the method “performReceiveLocked”

I Found Google developers offering this code comments here (“Failed to call into the process. It’s either dying or wedged. Kill It gently”)

In order to understand their approach, I analyzed the transmission process of Broadcast. The process analysis is as follows


Step1. Context.sendBroadcast(Intent) [can other app] -> in fact is ContextImpl.sendBroadcast(Intent)
function body contracted version

intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntent(…)


This operation will leave current app process, calling AMS is broadcast


Step2. ActivityManagerService.broadcastIntent(…) -> code final call method “ActivityManagerService.broadcastIntentLocked”
function body contracted version

// Figure out who all will receive this broadcast.
List receivers = null;
List registeredReceivers = null;
// Need to resolve the intent to interested receivers…
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false /defaultOnly/, userId);


BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(…);

queue.enqueueOrderedBroadcastLocked®;
queue.scheduleBroadcastsLocked();


This operation will find the receivers of the broadcast
Get the foreground or background queues from AMS
Construct a BroadcastRecord class object and put it in a BroadcastQueue(This is our focus class, due to this bug that appears here)


Step3. BroadcastQueue.scheduleBroadcastsLocked()
function body contracted version
// Set when we current have a BROADCAST_INTENT_MSG in flight.
if (mBroadcastsScheduled)
return;

mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;

We can see that this is just to activate the handler if it’s not already activated, because the broadcast we are sending is already waiting in the queue


Step4. BroadcastQueue.BroadcastHandler#handleMessage()
function body contracted version

switch (msg.what)
case BROADCAST_INTENT_MSG:
processNextBroadcast(true);
break;
case BROADCAST_TIMEOUT_MSG:
synchronized (mService)
broadcastTimeoutLocked(true);

break;



From the code we can clearly see that the final call to processNextBroadcast() method


Step5. BroadcastQueue#processNextBroadcast() -> final call to processNextBroadcastLocked()
function body contracted version
// 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();
for (int i=0; i<N; i++)
Object target = r.receivers.get(i);
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);

addBroadcastToHistoryLocked®;



We see the real distribution of the broadcast


Step6. BroadcastQueue#deliverToRegisteredReceiverLocked()
function body contracted version

performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);


Send this broadcast


Step7. BroadcastQueue#performReceiveLocked()[★★★bug happend method body★★★]

function body compelete verion
//Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null)
if (app.thread != null)
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
try
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
// TODO: Uncomment this when (b/28322359) is fixed and we aren’t getting
// DeadObjectException when the process isn’t actually dead.
// catch (DeadObjectException ex)
// Failed to call into the process. It’s dying so just let it die and move on.
// throw ex;
catch (RemoteException ex)
// Failed to call into the process. It’s either dying or wedged. Kill it gently.
synchronized (mService)
Slog.w(TAG, "Can’t deliver broadcast to " + app.processName
+ " (pid " + app.pid + “). Crashing it.”);
app.scheduleCrash(“can’t deliver broadcast”);

throw ex;

else
// Application has died. Receiver doesn’t exist.
throw new RemoteException(“app.thread must not be null”);



This bug is happend here, we will go to next method


Step8. ActivityThread#scheduleRegisteredReceiver(IIntentReceiver receiver,…)
function body compelete verion
updateProcessState(processState, false);
receiver.performReceive(…);

receiver is IIntentReceiver impl


Step9. LoadedApk.ReceiverDispatcher.InnerReceiver#performReceive()
function body compelete verion
if (rd != null)
// Rc. rd -> class LoadedApk.ReceiverDispatcher object
rd.performReceive(…);
else
// The activity manager dispatched a broadcast to a registered
// receiver in this process, but before it could be delivered the
// receiver was unregistered. Acknowledge the broadcast on its
// behalf so that the system’s broadcast sequence can continue.
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
“Finishing broadcast to unregistered receiver”);
IActivityManager mgr = ActivityManager.getService();
try
mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
catch (RemoteException e)
throw e.rethrowFromSystemServer(); // Rc. We need to think about this place



Normally we want rd to be valid


Step10. LoadedApk.ReceiverDispatcher#performReceive()
function body contracted version

final Args args = new Args(intent, resultCode, data, extras, ordered, sticky, sendingUser);

mActivityThread.post(args.getRunnable())


Step11. LoadedApk.Args#getRunnable()
function body contracted version

ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess();
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);


Done send broadcast

以上是关于Broadcast发送流程分析的主要内容,如果未能解决你的问题,请参考以下文章

Android系统广播(Broadcast)注册,发送,接收流程解析

CSipIm断网重连崩溃原因分析

广播——broadcast

android framework开发之广播broadcast源码分析2-千里马

android framework开发之广播broadcast源码分析2-千里马

求助 有个android手机建立热点后发送组播信息的问题