Maxima framework learning cross-process practical course students question answer: ClassNotFoundException when unmarshalling

hi, fans and friends!
Today, I would like to share a problem that a student friend encountered when learning cross-process practical courses
: when learning to use cross-process Messenger for data transfer, at this time the student wants to use Messenger to transfer an object, of course, the object must be Parcelable , the specific code is as follows:

sender:

    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);
    }

Receiver:

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);
        }
    };

But unfortunately the error is reported as follows:

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) 

The root cause of the error is this ClassNotFoundException, that is, com.example.servicedemo.StudentInfo cannot be found. Why is this? Obviously, our apk can use it and compile without errors. Why?

Actually look at the stack
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 est NestNestmreadValue(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 ) a t an d ro i d . os . P a rce l 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 )
Look at the corresponding code:

 @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;
    }

It can be seen that the corresponding StudentInfo is not loaded in Class.forName, but because if we do not pass the corresponding parcelableClassLoader by default, it will not be the classloader of our apk, so it cannot be added to the class of our app , because the system class is BootClassLoader, it can only be added to those prefabricated classes in the system, resulting in errors that cannot be found.

The solution is of course to set the corresponding ClassLoader before parsing and obtaining the parcelable object

  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")));

In this way, it can be normal when parcel parsing

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

The loader here is no longer null, so use PathClassLoader of apk

expand

1. Excuse me, the StudentInfo object written in aidl for cross-process is not needed. The additional setting of this classloader
aidl file is actually converted into a java file, so the cross-process callback is directly called to the java file corresponding to aidl, so the corresponding classloader at this time It must become PathClassLoader, because the aidl of this kind of Messenger cross-process communication belongs to the framework public, so the corresponding classloader is the corresponding BootClassLoader, not this PathClassLoader

Guess you like

Origin blog.csdn.net/learnframework/article/details/130624661