您现在的位置是:首页 >其他 >千里马framework学习跨进程实战课程学员疑问解答:ClassNotFoundException when unmarshalling网站首页其他

千里马framework学习跨进程实战课程学员疑问解答:ClassNotFoundException when unmarshalling

learnframework 2024-06-17 10:14:38
简介千里马framework学习跨进程实战课程学员疑问解答:ClassNotFoundException when unmarshalling

hi,粉丝朋友们!
今天来分享一个学员朋友在学习跨进程实战课程时候,遇到的一个问题:
在学习使用跨进程传递的Messenger进行数据传递,这时候学员想要使用Messenger来传递一个对象,当然这个对象得是Parcelable的,具体代码如下:

发送端:

    void sendMessageToServer() throws RemoteException {
        Message toServer = Message.obtain();
        toServer.replyTo = messengerClientSend;
        toServer.what = 1;
        Bundle bundle = new Bundle();
        bundle.putString("bundleKey","bundleValue Client");
        StudentInfo info = new StudentInfo();
        info.id = "2";
        bundle.putParcelable("data",info);//给bundle装载一个StudentInfo的Parcelable对象
        toServer.setData(bundle);
        messengerServer.send(toServer);
    }

接受端:

Handler messengerHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case  1:
                   StudentInfo info = new StudentInfo();//故意new StudentInfo看看是否有这个类
                    info.id = "3";
                    //这里想远端获取一下Parcelable即StudentInfo
                    Log.i("test","MessengerService Messenger handleMessage msg = " + msg + " bundle  key value = " + msg.getData().getString("bundleKey") + " data " + (StudentInfo)(msg.getData().getParcelable("data")));
                    Messenger clientSend = msg.replyTo;
                    Message toClientMsg = Message.obtain();
                    toClientMsg.what = 2;
                   // toClientMsg.obj = "I am replay from Server";
                    try {
                        clientSend.send(toClientMsg);
                    }catch (Exception e) {
                        Log.i("test","MessengerService clientSend  error ",e);
                    }
                    break;
            }
            super.handleMessage(msg);
        }
    };

但是很不幸报错了如下:

ClassNotFoundException when unmarshalling: com.example.servicedemo.StudentInfo

2023-03-09 09:52:37.496 11398-11398/com.example.servicedemo E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.servicedemo:messenger, PID: 11398
    android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.example.servicedemo.StudentInfo
        at android.os.Parcel.readParcelableCreatorInternal(Parcel.java:4950)
        at android.os.Parcel.readParcelableInternal(Parcel.java:4807)
        at android.os.Parcel.readValue(Parcel.java:4567)
        at android.os.Parcel.readValue(Parcel.java:4347)
        at android.os.Parcel.-$$Nest$mreadValue(Unknown Source:0)
        at android.os.Parcel$LazyValue.apply(Parcel.java:4445)
        at android.os.Parcel$LazyValue.apply(Parcel.java:4404)
        at android.os.BaseBundle.getValueAt(BaseBundle.java:394)
        at android.os.BaseBundle.getValue(BaseBundle.java:374)
        at android.os.BaseBundle.getValue(BaseBundle.java:357)
        at android.os.BaseBundle.getValue(BaseBundle.java:350)
        at android.os.Bundle.getParcelable(Bundle.java:913)
        at com.example.servicedemo.MessengerService$1.handleMessage(MessengerService.java:30)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7872)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
     Caused by: java.lang.ClassNotFoundException: com.example.servicedemo.StudentInfo
        at java.lang.Class.classForName(Native Method)
        at java.lang.Class.forName(Class.java:454)
        at android.os.Parcel.readParcelableCreatorInternal(Parcel.java:4916)
        at android.os.Parcel.readParcelableInternal(Parcel.java:4807) 
        at android.os.Parcel.readValue(Parcel.java:4567) 
        at android.os.Parcel.readValue(Parcel.java:4347) 
        at android.os.Parcel.-$$Nest$mreadValue(Unknown Source:0) 
        at android.os.Parcel$LazyValue.apply(Parcel.java:4445) 
        at android.os.Parcel$LazyValue.apply(Parcel.java:4404) 
        at android.os.BaseBundle.getValueAt(BaseBundle.java:394) 
        at android.os.BaseBundle.getValue(BaseBundle.java:374) 
        at android.os.BaseBundle.getValue(BaseBundle.java:357) 
        at android.os.BaseBundle.getValue(BaseBundle.java:350) 
        at android.os.Bundle.getParcelable(Bundle.java:913) 
        at com.example.servicedemo.MessengerService$1.handleMessage(MessengerService.java:30) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loopOnce(Looper.java:201) 
        at android.os.Looper.loop(Looper.java:288) 
        at android.app.ActivityThread.main(ActivityThread.java:7872) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) 
     Caused by: java.lang.ClassNotFoundException: com.example.servicedemo.StudentInfo
        at java.lang.Class.classForName(Native Method)
        at java.lang.BootClassLoader.findClass(ClassLoader.java:1366)
        at java.lang.BootClassLoader.loadClass(ClassLoader.java:1426)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
        at java.lang.Class.classForName(Native Method) 
        at java.lang.Class.forName(Class.java:454) 
        at android.os.Parcel.readParcelableCreatorInternal(Parcel.java:4916) 
        at android.os.Parcel.readParcelableInternal(Parcel.java:4807) 
        at android.os.Parcel.readValue(Parcel.java:4567) 
        at android.os.Parcel.readValue(Parcel.java:4347) 
        at android.os.Parcel.-$$Nest$mreadValue(Unknown Source:0) 
        at android.os.Parcel$LazyValue.apply(Parcel.java:4445) 
        at android.os.Parcel$LazyValue.apply(Parcel.java:4404) 
        at android.os.BaseBundle.getValueAt(BaseBundle.java:394) 
        at android.os.BaseBundle.getValue(BaseBundle.java:374) 
        at android.os.BaseBundle.getValue(BaseBundle.java:357) 
        at android.os.BaseBundle.getValue(BaseBundle.java:350) 
        at android.os.Bundle.getParcelable(Bundle.java:913) 
        at com.example.servicedemo.MessengerService$1.handleMessage(MessengerService.java:30) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loopOnce(Looper.java:201) 
        at android.os.Looper.loop(Looper.java:288) 
        at android.app.ActivityThread.main(ActivityThread.java:7872) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) 

报错根本原因是这个ClassNotFoundException,即找不到com.example.servicedemo.StudentInfo,这个是为啥?明明我们apk自己都可以用它而且编译也不会出错,到底是为啥阿?

其实来看一下堆栈
at android.os.Parcel.readParcelableCreatorInternal(Parcel.java:4916)
at android.os.Parcel.readParcelableInternal(Parcel.java:4807)
at android.os.Parcel.readValue(Parcel.java:4567)
at android.os.Parcel.readValue(Parcel.java:4347)
at android.os.Parcel.-$ N e s t Nest NestmreadValue(Unknown Source:0)
at android.os.Parcel L a z y V a l u e . a p p l y ( P a r c e l . j a v a : 4445 ) a t a n d r o i d . o s . P a r c e l LazyValue.apply(Parcel.java:4445) at android.os.Parcel LazyValue.apply(Parcel.java:4445)atandroid.os.ParcelLazyValue.apply(Parcel.java:4404)
at android.os.BaseBundle.getValueAt(BaseBundle.java:394)
at android.os.BaseBundle.getValue(BaseBundle.java:374)
at android.os.BaseBundle.getValue(BaseBundle.java:357)
at android.os.BaseBundle.getValue(BaseBundle.java:350)
at android.os.Bundle.getParcelable(Bundle.java:913)
看一下对应的代码:

 @Nullable
    private <T> Parcelable.Creator<T> readParcelableCreatorInternal(
            @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
        String name = readString();
      //省略部分
        try {
            // If loader == null, explicitly emulate Class.forName(String) "caller
            // classloader" behavior.
            ClassLoader parcelableClassLoader =
                    (loader == null ? getClass().getClassLoader() : loader);
            // Avoid initializing the Parcelable class until we know it implements
            // Parcelable and has the necessary CREATOR field. http://b/1171613.
            Class<?> parcelableClass = Class.forName(name, false /* initialize */,
                    parcelableClassLoader);
    		//省略部分
        } catch (IllegalAccessException e) {
         
        } catch (ClassNotFoundException e) {
            Log.e(TAG, "Class not found when unmarshalling: " + name, e);
            throw new BadParcelableException(
                    "ClassNotFoundException when unmarshalling: " + name, e);
        } catch (NoSuchFieldException e) {
           
        }
    		//省略部分
        return (Parcelable.Creator<T>) creator;
    }

可以看出这里其实是在Class.forName时候没有加载到对应的StudentInfo,但是因为这里如果默认我们没有传递对应的parcelableClassLoader,就会导致它就不是我们apk的classloader,所以加在不到我们app的class,因为系统类是BootClassLoader,只能加在系统预制那些class,从而导致出错找不到。

解决办法当然就是在解析获取parcelable对象前要设置对应的ClassLoader

  Bundle data = msg.getData();
                    StudentInfo info = new StudentInfo();
                    info.id = "3";
                    data.setClassLoader(getClassLoader());//把当前apk的classloader设置进去
                    Log.i("test","MessengerService Messenger handleMessage msg = " + msg + " bundle  key value = " + msg.getData().getString("bundleKey") + " data " + (StudentInfo)(msg.getData().getParcelable("data")));

这样在parcel解析时候就可以正常了

  ClassLoader parcelableClassLoader =
                    (loader == null ? getClass().getClassLoader() : loader);

这里的loader已经不为null,所以使用是apk的PathClassLoader

扩展

1、请问为跨进程写在aidl里面的StudentInfo对象就不需要,额外设置这个clasloader
aidl文件实际转化成了java文件,所以跨进程回调是直接调用到了aidl对应的java文件,所以这个时候对应的classloader肯定就变成了PathClassLoader,因为这类的Messenger跨进程通讯的aidl属于framework公共,所以对应的classloader就是对应的BootClassLoader,不会是这个PathClassLoader

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。