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