zengjingfang / AndroidBox

Android开发知识、经验、资料等总结,作为个人的开发知识体系
Apache License 2.0
16 stars 3 forks source link

.EventBus.register异常: Didn't find class "android.os.PersistableBundle" #22

Open zengjingfang opened 6 years ago

zengjingfang commented 6 years ago
java.lang.NoClassDefFoundError: android/os/PersistableBundle
    at java.lang.Class.getDeclaredMethods(Native Method)
    at java.lang.Class.getPublicMethodsRecursive(Class.java:894)
    at java.lang.Class.getMethods(Class.java:877)
    at org.greenrobot.eventbus.SubscriberMethodFinder.findUsingReflectionInSingleClass(SubscriberMethodFinder.java:157)
    at org.greenrobot.eventbus.SubscriberMethodFinder.findUsingInfo(SubscriberMethodFinder.java:88)
    at org.greenrobot.eventbus.SubscriberMethodFinder.findSubscriberMethods(SubscriberMethodFinder.java:64)
    at org.greenrobot.eventbus.EventBus.register(EventBus.java:136)
    at com.xtc.watch.view.location.view.impl.LocationMainActivity.onCreate(LocationMainActivity.java:300)
    at android.app.Activity.performCreate(Activity.java:5355)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1089)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2171)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2257)
    at android.app.ActivityThread.access$800(ActivityThread.java:140)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1202)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:136)
    at android.app.ActivityThread.main(ActivityThread.java:5113)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:780)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:596)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.ClassNotFoundException: Didn't find class "android.os.PersistableBundle" on path: DexPathList[[zip file "/data/app/com.xtc.watch-1.apk", zip file "/data/data/com.xtc.watch/code_cache/secondary-dexes/com.xtc.watch-1.apk.classes2.zip", zip file "/data/data/com.xtc.watch/code_cache/secondary-dexes/com.xtc.watch-1.apk.classes3.zip", zip file "/data/data/com.xtc.watch/code_cache/secondary-dexes/com.xtc.watch-1.apk.classes4.zip"],nativeLibraryDirectories=[/data/app-lib/com.xtc.watch-1, /vendor/lib, /system/lib]]
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
    ... 22 more

代码

  @override
  public void onSaveInstanceState(Bundle outState) {
       super.onSaveInstanceState(outState);
      EventBus.getDefault().register(MainActivity.this);
  }
    @Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);
        LogUtil.i(TAG, "xxx onSaveInstanceState...");
    }
zengjingfang commented 6 years ago

源码追踪


    private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            methods = findState.clazz.getDeclaredMethods();
          //  第一步:methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
           // 第二步 Plan B:methods = findState.clazz.getMethods(); 
            methods = findState.clazz.getMethods();// line:157 崩溃处
            findState.skipSuperClasses = true;
        }
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException("@Subscribe method " + methodName +
                            "must have exactly 1 parameter but has " + parameterTypes.length);
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
    }

资料搜索 eventbus documentation

A java.lang.NoClassDefFoundError is throw when a subscriber class is registered. What can I do? First a bit of background to help you understand what’s going on here: Some Android versions seem to have a bug with reflection when calling getDeclaredMethods or getMethods. The exception is thrown if the class has methods with a parameter that is unavailable to the API level of the device. For example, the class PersistableBundle was added in API level 21. Along with the new class some new life cycle methods were introduced in the class Activity having PersistableBundle as a parameter, for example onCreate (Bundle savedInstanceState, PersistableBundle persistentState). Now, if you override this method and you try to register this Activity to EventBus on a older device, we have exactly the scenario described to cause to bug. Understanding why this happens will help to resolve the issue easily.

Here are a couple suggestions how to fix the scenario (check in given the order):

Maybe you overwrote a life cycle method with PersistableBundle just by accident. In that case just change to the method without PersistableBundle, for example onCreate (Bundle savedInstanceState).

Use EventBus 3 with an subscriber index. This will avoid reflection and thus the problem altogether. As a positive side effect, registering subscribers and thus app startup time will be much faster.

Remove the offending method from your subscriber class. Either pull out the event handler methods into a new subscriber class, or pull out the offending method into a non-subscriber class.

If the offending method is public, make it non-public. This works because of some “plan b” logic EventBus applies: EventBus first calls getDeclaredMethods, which will fail. Next, EventBus will try again using the getMethods (“plan b”). The latter will succeed because getMethods only returns public methods. However, keep in mind that is the least efficient way in terms of performance (2 reflection calls instead of 1 with getMethods considering the entire class hierarchy).

解读

zengjingfang commented 6 years ago

解决方案一:

该方法的 public -> protected

@override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}

@override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}

解决方案二:

如果重写了这个方法,就去掉

public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState){
}

解决方案三:

使用 EventBus 3