广播处理时报java.lang.ClassNotFoundException: Didn't find class的原因是什么?
[ANSWER]
由于广播处理不是即时的,比如很多静态注册的广播接收者,在广播发出可能要经过一定的延迟才触发广播接收者在onReceive中处理这个广播
存在一种特殊情况,假设应用A中静态注册了一个TEST_BROADCAST广播接收者,一个非应用A的进程P发了一个TEST_BROADCAST广播,之后这个广播被加入到BroadcastQueue中等待被处理,但是此时应用A发生更新操作,应用更新完之后,这个时候才轮到应用A中注册的广播接收者处理TEST_BROADCAST广播,为了处理广播,这个时候应用A会启动自己所在的进程,但是这个时候,由于应用A更新了,但是启动进程使用的packageinfo信息未能及时更新导致找不到class,因此报以下问题:
java.lang.ClassNotFoundException: Didn't find class
具体原因分析如下:
1.在AMS的 broadcastIntentLocked方法中发送广播查询receiver时,receiver列表是到PackageManagerService中去获取的,每查询到一个receiver便会new一个ResolveInfo保存到receiver列表中
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
.queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);
2.在AMS的 broadcastIntentLocked 方法中将广播加入对应的BroadcastQueue
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
//BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastQueue queue = broadcastQueueForIntent(callerPackage,intent);
// 实例化BroadcastRecord对象
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
+ ": prev had " + queue.mOrderedBroadcasts.size());
if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
"Enqueueing broadcast " + r.intent.getAction());
boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
// 将BroadcastRecord对象加入到BroadcastQueue中
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}
BroadcastRecord作为这个广播的载体,记录着发广播的应用相关package和进程信息,权限信息等等,其中还有一个比较重要的就是receivers信息,也就是哪些广播接收者可以接收处理这个广播,这个receivers列表中包含两种广播接收者,一种是动态注册的广播接收者,一种是静态注册的广播接收者,动态注册的广播接收者是用BroadcastFilter来代表的,而静态注册的广播接收者是用ResolveInfo来表示的
3.广播分发处理时,如果是静态注册的广播接收者,那么这个receiver是通过ResolveInfo来表示的
ResolveInfo info =
(ResolveInfo)nextReceiver;
ComponentName component = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
如果ResolveInfo所表示的receiver所在的应用进程没有启动,那个这个时候就去启动这个应用进程
if ((r.curApp=mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
"broadcast", r.curComponent,
(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
== null) {
这个时候问题出现了,info.activityInfo.applicationInfo 数据中保存的其实是应用A更新之前的相关应用信息,由于应用A更新了,但是启动进程使用的packageinfo信息未能及时更新导致找不到class
解决方案:
针对上面存在的问题,可以在发现receiver所在的应用进程没有启动之前判断应用信息是否有更新,如果当前应用信息和PMS中的应用信息不一致,说明应用更新过,则跳过当前receiver的处理,具体改动如下:
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
old mode 100644
new mode 100755
index 68e2405..13bb908
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -33,6 +33,7 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.PackageInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -1077,7 +1078,22 @@ public final class BroadcastQueue {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Need to start app ["
+ mQueueName + "] " + targetProcess + " for broadcast " + r);
- if ((r.curApp=mService.startProcessLocked(targetProcess,
+
+ boolean packageUpdated = false;
+ PackageInfo pi = null;
+ try {
+ pi = AppGlobals.getPackageManager().getPackageInfo(
+ info.activityInfo.applicationInfo.packageName, 0, UserHandle.myUserId());
+ if (pi != null && !info.activityInfo.applicationInfo.sourceDir.equals(pi.applicationInfo.sourceDir)) {
+ Slog.e(TAG,"seal---package = " + info.activityInfo.applicationInfo.packageName +
+ " is updated application info in packagemanager,but in startProcess still use the old ,so dont start the process");
+ packageUpdated = true;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG,"seal---getPackageInfo error:", e);
+ }
+
+ if (packageUpdated || (r.curApp=mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
"broadcast", r.curComponent,