Android Lifecycle 在Api 26以下导致的ClassNotFoundException异常

Android Lifecycle 在Api 26以下导致的ClassNotFoundException异常因为在 Api 26 以下 EditText中没有定义这个方法,SDK也中也没有TextClassifier 类,所以在 Api 26 以下会报NoClassDefFoundError。 也就不会加载TextClassifier,自然不会报NoClassDefFoundErro…

记一次线上Crash,重新学习Lifecycle。

应用环境

compileSdkVersion = 28
minSdkVersion = 21
targetSdkVersion = 28
buildToolsVersion = "28.0.3"
lifecycle = android 28 (lifecycle:2.0.0源码略微不同)

问题

线上报出Android 8.0( < Api 26)以下系统上Crash,具体异常日志如下:

java.lang.IllegalArgumentException: The observer class has some methods that use newer APIs which are not available in the current OS version. 
Lifecycles cannot access even other methods so you should make sure that your observer classes only access framework classes that are available 
in your min API level OR use lifecycle:compiler annotation processor.
	at androidx.lifecycle.ClassesInfoCache.getDeclaredMethods(ClassesInfoCache.java:72)
	at androidx.lifecycle.ClassesInfoCache.createInfo(ClassesInfoCache.java:122)
	... 省略一堆没有用的日志 ...
	at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:170)
	... 省略一堆没有用的日志 ...
    at com.xxx.XXXEditText.initListener(XXXEditText.java:60)
Caused by: java.lang.NoClassDefFoundError: android.view.textclassifier.TextClassifier
	at libcore.reflect.InternalNames.getClass(InternalNames.java:55)
	at java.lang.Class.getDexCacheType(Class.java:479)
	at java.lang.reflect.ArtMethod.getDexCacheType(ArtMethod.java:191)
	at java.lang.reflect.ArtMethod.getReturnType(ArtMethod.java:145)
	at java.lang.reflect.Method.getReturnType(Method.java:184)
	at java.lang.Class.getDeclaredMethods(Class.java:771)
	at androidx.lifecycle.ClassesInfoCache.getDeclaredMethods(ClassesInfoCache.java:70)
	... 29 more
Caused by: java.lang.ClassNotFoundException: Didn't find class "android.view.textclassifier.TextClassifier" on path: DexPathList[[zip file "/data/app/xxx.apk"],nativeLibraryDirectories=[/data/app/cxxx/lib/arm, /vendor/lib, /system/lib]]
	at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
	... 省略一堆没有用的日志 ...

问题代码

只是在EditText中注册了生命周期监听,在Android 8.0( < Api 26)以下就会触发上面的异常,而且必现;

class XXXEditText extends AppCompatEditText implements LifecycleObserver {
    public void initListener(LifecycleOwner owner) {
    	owner.getLifecycle().addObserver(this);
    }
    
    @OnLifecycleEvent(Event.ON_DESTROY)
    public void destroyed() {
        // to do something
        removeListener();
    }
}

异常抛出点

androidx.lifecycle.ClassesInfoCache#getDeclaredMethods
异常抛出具体代码:

boolean hasLifecycleMethods(Class klass) {
    省略一堆...
    Method[] methods = getDeclaredMethods(klass);
    for (Method method : methods) {
        OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
        if (annotation != null) { 省略一堆... return true; }
    }
    mHasLifecycleMethods.put(klass, false);
    return false;
}
private Method[] getDeclaredMethods(Class klass) {
    try {
        return klass.getDeclaredMethods();
    } catch (NoClassDefFoundError e) {
        throw new IllegalArgumentException("The observer class has some methods that use 省略一堆...", e);
    }
}

当看到这,基本就问题很明显了,Lifecycle处理注解方式的生命周期回调,会利用getDeclaredMethods来查找当前类下的所有方法是否有OnLifecycleEvent这个注解,如果有,将相关信息缓存起来,以备使用;

AppCompatEditText下有TextClassifier相关的使用,如下:

@RequiresApi(api = 26)
public void setTextClassifier(@Nullable TextClassifier textClassifier) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P || mTextClassifierHelper == null) {
        super.setTextClassifier(textClassifier);
        return;
    }
    mTextClassifierHelper.setTextClassifier(textClassifier);
}

@RequiresApi(api = 26)
public TextClassifier getTextClassifier() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P || mTextClassifierHelper == null) {
        return super.getTextClassifier();
    }
    return mTextClassifierHelper.getTextClassifier();
}

异常原因

TextClassifier 是 Api 28 增加提供文本分类相关功能的Api,AppCompatEditText 在 Api 26、Api 27上利用 TextClassifierHelper 来做兼容,但并不支持 Api 26 以下,
因为在 Api 26 以下 EditText中没有定义这个方法,SDK也中也没有TextClassifier 类,所以在 Api 26 以下会报NoClassDefFoundError

异常解决办法

  1. 在高版本中( >= Api 26)不要使用Lifecycle的注解方式,改用FullLifecycleObserverGenericLifecycleObserver来完成生命周期监听;
  2. app/build.gradle中增加 APT 编译依赖;
// kapt - for kotlin 
// annotationProcessor - for java
kapt "androidx.lifecycle:lifecycle-common-java8:last-version"
或
kapt "androidx.lifecycle:lifecycle-compiler:last-version"

增加 APT 怎么能解决 NoClassDefFoundError

增加APT编译依赖,这个解决方案是在 Stackoverflow 上看到的,正常情况下,APT只会生成代码,并不会像ASM一样修改源码,
那么问题来了:为什么增加APT就可以解决这个问题呢?lifecycle-compiler这个APT的作用是什么呢?

  1. 先看了一下 lifecycle-compiler 源码,难道APT真能改源码或者字节码?其实里面很简单,就是生成个Java文件,看来原因不在lifecycle-compiler中;
  2. 再看一下lifecycle-compiler生成的代码是个啥,发现没啥,就是个代理类;
public class XXXEditText_LifecycleAdapter implements GeneratedAdapter {
   final XXXEditText mReceiver;

   XXXEditText_LifecycleAdapter(XXXEditText receiver) {
       this.mReceiver = receiver;
   }

   @Override
   public void callMethods(LifecycleOwner owner, Lifecycle.Event event, boolean onAny, MethodCallsLogger logger) {
       boolean hasLogger = logger != null;
       if (onAny) { return; }
       if (event == Lifecycle.Event.ON_DESTROY) {
     	    if (!hasLogger || logger.approveCall("removeListener", 1)) {
            	// to do something
                mReceiver.removeListener();
     	    }
     	    return;
       }
   }
}

APT能解决的真相

重读Lifecycle源码

// androidx.lifecycle.LifecycleRegistry#addObserver
// 添加观察者
public void addObserver(@NonNull LifecycleObserver observer) {
     State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
     ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
     ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
     ...
}

// androidx.lifecycle.LifecycleRegistry.ObserverWithState#ObserverWithState
// 保存观察者和状态信息
ObserverWithState(LifecycleObserver observer, State initialState) {
    // 利用观察者作为key查找回调 ???
    mLifecycleObserver = Lifecycling.getCallback(observer);
    mState = initialState;
}

// androidx.lifecycle.Lifecycling#getCallback
// 查找Callback,其实Callback也是个LifecycleObserver
static GenericLifecycleObserver getCallback(Object object) {
    // 如果注册的是FullLifecycleObserver,那么返回一个包装过的LifecycleObserver
    if (object instanceof FullLifecycleObserver) {
        return new FullLifecycleObserverAdapter((FullLifecycleObserver) object);
    }
    // 如果注册的是GenericLifecycleObserver,那么直接注册的观察者
    if (object instanceof GenericLifecycleObserver) {
        return (GenericLifecycleObserver) object;
    }
    // 如果不是以上两种,那么继续往下走
    // 解决方案1就是因为不会再往下走,所以不会再利用反射去找EditText中使用了OnLifecycleEvent注解的方法
    final Class<?> klass = object.getClass();
    int type = getObserverConstructorType(klass);
    // 如果构造函数类型是生成的Callback,也就是使用APT生成的代码,那么后面肯定是用生成的Callback来完成调用;
    if (type == GENERATED_CALLBACK) {
        // 省略一堆...
        return new CompositeGeneratedAdaptersObserver(adapters);
    }
    // 否则,使用反射
    return new ReflectiveGenericLifecycleObserver(object);
}

// androidx.lifecycle.Lifecycling#getObserverConstructorType
private static int getObserverConstructorType(Class<?> klass) {
    ...
    int type = resolveObserverCallbackType(klass);
    sCallbackCache.put(klass, type);
    return type;
}

// androidx.lifecycle.Lifecycling#resolveObserverCallbackType
private static int resolveObserverCallbackType(Class<?> klass) {
    // 大概就是利用className.replace(".", "_") + "_LifecycleAdapter"通过反射创建一个观察者对象
    // 如果创建成功,就直接返回了,不会再往下走;
    // 解决方案2生效原因:到这就明确知道,如果使用APT,那么就不会往下利用反射处理注解了,就不会调用到getDeclaredMethods
    Constructor<? extends GeneratedAdapter> constructor = generatedConstructor(klass);
    if (constructor != null) {
        sClassToAdapters.put(klass, Collections.<Constructor<? extends GeneratedAdapter>>singletonList(constructor));
        return GENERATED_CALLBACK;
    }
    // 否则反射处理注解方法
    boolean hasLifecycleMethods = ClassesInfoCache.sInstance.hasLifecycleMethods(klass);
    if (hasLifecycleMethods) {
        return REFLECTIVE_CALLBACK;
    }
    ...
    boolean hasLifecycleMethods = ClassesInfoCache.sInstance.hasLifecycleMethods(klass);
    ...
    return REFLECTIVE_CALLBACK;
}

// androidx.lifecycle.ClassesInfoCache#hasLifecycleMethods
// 回到异常抛出点
boolean hasLifecycleMethods(Class klass) {
    省略一堆...
    Method[] methods = getDeclaredMethods(klass);
    for (Method method : methods) {
        OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
        if (annotation != null) { 省略一堆... return true; }
    }
    mHasLifecycleMethods.put(klass, false);
    return false;
}

其实就是因为注册Observer时,会优先使用APT生成的LifecycleAdapter;
所以,当使用了APT,就不会再利用反射去处理使用了注解OnLifecycleEvent方法,
也就不会加载TextClassifier,自然不会报NoClassDefFoundError。

Lifecycle处理步骤

Lifecycle处理观察者回调的步骤如下:

获取Callback (Lifecycling.getCallback()) 
	⬇
注册的Observer是否为 FullLifecycleObserver 
	⬇
如果是,返回包装后的Observer,否则继续查找
	⬇
注册的Observer是否为 GenericLifecycleObserver
	⬇
如果是,直接返回Observer,否则继续查找
	⬇
判断XXX_LifecyclerAdapter class是否存在,如果存在,反射创建这个class对象,并返回包装后的Observer
	⬇
中间还会经历一些从缓存中查找的步骤
	⬇
反射处理使用了注解OnLifecycleEvent方法信息

最后的建议

Lifecycle注册观察者建议直接使用FullLifecycleObserver或GenericLifecycleObserver;

直接使用LifecycleObserver的注解方式,收益并不高,还会用到反射,如果使用了APT还会增加额外的编译、包体积和方法数成本,
并且使用LifecycleObserver并不方便。

今天的文章Android Lifecycle 在Api 26以下导致的ClassNotFoundException异常分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/22384.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注