广播处理时报java.lang.ClassNotFoundException: Didn't find class的原因是什么?

QUESTION]
广播处理时报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,

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值