NoClassDefFoundError ServiceConnection$-CC 崩溃 (系统应用调用到 onBindingDied 或者系统接口的 default 方法时报错)

错误log: E/AndroidRuntime( 2991): java.lang.NoClassDefFoundError: Failed resolution of: Landroid/content/ServiceConnection$-CC;

解决方法
如果是系统应用,且使用了compileOnly framework.jar ,就用压缩包工具删除内部的 android/content/ServiceConnection 类,其他的类似的报错去删除其对应的接口类。

原因:
1.framework.jar 存在这个类时,会导致编译出的 apk 包含 ServiceConnection.onBindingDied 方法,且方法内会调用 android/content/ServiceConnection$-CC.onBindingDied 方法。在某些情况下触发 onBindingDied 时(比如覆盖安装被绑定的服务) 会出现 NoClassDefFoundError: Failed resolution of: Landroid/content/ServiceConnection$-CC 导致崩溃。

通过反编译和写测试代码初步验证 -CC 这种是 java 8 接口中 写 default 方法在编译时会生成的代码
 

// 带 default 方法的测试接口
public interface TestConnection {
    void bind();
    default void bindingTest(){
        Log.d("TestConnection","bindingTest");
    }
}

//另外一个测试类
private val testConnection = object :TestConnection{
    override fun bind() {
        Log.d(TAG,"bind")
    }
}
//编译生成apk后反编译查看代码。
public final class DewarpingHandle$testConnection$1 implements TestConnection {
		@Override 
		//可以看到编译自动增加了这个方法
		public /* synthetic */ void bindingTest() {
				TestConnection.CC.$default$bindingTest(this);
		}
  
    DewarpingHandle$testConnection$1() {
    }
    
    @Override
    public void bind() {
        Log.d("test", "bind");
    }

}

public interface TestConnection {
    void bind();

    void bindingTest();
		//这里是反编译工具自动把-CC改成了CC
    /* renamed from: com.xxxxxxxx.TestConnection$-CC  reason: invalid class name */
    public final /* synthetic */ class CC {
        public static void $default$bindingTest(TestConnection _this) {
            Log.d("TestConnection", "bindingTest");
        }
    }
}

可以看到, interface 的接口中写 default 接口,其实是编译时在内部生成一个 -CC 的类,在实例化接口的时候,默认增加了该 default 方法,并直接调用到 TestConnection.-CC 类。从而实现了 “default“ 功能。

3.目前很多系统应用比较特殊,需要调用到系统的隐藏方法,而要在 AS 内编译通过,就必须要 compileOnly framework.jar 并且通过 options.bootstrapClasspath 让编译器优先找 framework.jar 这种做法。而这种情况会导致把系统接口误认为是普通接口,做这种生成 -CC 类 的操作,从而实现适配。但是实际上,系统编译生成 framework 的时候 实际已经满足 Java 8 , 不需要做这种操作,所以系统里面的 ServiceConnection 自然也没有 -CC 这种中间类。从而导致了 NoClassDefFoundError: Failed resolution of: Landroid/content/ServiceConnection$-CC 的报错。

4.但是从 framework.jar 删除这个类后,编译器直接调用到SDK内的类和方法。猜测编译器直接调用 SDK 的时候能已知系统支持 Java 8 ,支持 default 写法,所以不再需要去写这种 -CC 类去适配。从而生成的 apk 中也没有去增加 onBindingDied 方法,更没有去调用 ServiceConnection$-CC.onBindingDied 方法了。
 

猜你喜欢

转载自blog.csdn.net/yzwfeng/article/details/132814584