如何减少dex/class文件中生成的access$x00方法

2019-01-24  本文已影响17人  扑扑兔

在安卓开发中,我们可能会经常遇到一些线上报错,显示access$x00方法报错,而实际上我们的代码中并没有这个方法。那么这些方法是怎么生成的呢?

实际上这些方法是由编译器自动生成的,举个例子,当我们在匿名内部类中试图访问外部类的private字段或方法时,此时,我们的内部类实际上并没有这些private字段或方法的访问权限。编译器此时会帮我们生成一个包访问权限的access$x00方法,通过这个方法我们就可以间接访问到类的private字段或方法

例如:以下java类

import android.widget.TextView;

public class ItemsView {

    private static String displayText(String item) {
    
         return ""; // TODO
    
    }
    
    private class ItemsAdapter {
    
        void bindItem(TextView tv, String item) {

            tv.setText(ItemsView.displayText(item));

        }

    }

} 

当我们运行

-- javac -bootclasspath %ANDROID_HOME%/platforms/android-24/android.jar ItemsView.java

-- javap -c ItemsView$ItemsAdapter

后,看到生成了如下的字节码文件

class ItemsView$ItemsAdapter {

  final ItemsView this$0;

  void bindItem(android.widget.TextView, java.lang.String);

    Code:

    0: aload_1

    1: aload_2

    2: invokestatic  #3   // Method ItemsView.access$000:(Ljava/lang/String;)Ljava/lang/String;

    5: invokevirtual #4   // Method android/widget/TextView.setText:(Ljava/lang/CharSequence;)V

    8: return

}

其中在Code 2:处,可以看到编译器帮我们生成了access$000代码。

那么如果我们想要去掉这些方法,该怎么做呢?

-- 将从内部类中调用的外部类的这些private 字段或方法,去掉private限定符即可。

去掉private限定符之后,再次运行

-- javap -c ItemsView$ItemsAdapter

得到如下结果,可以看到在Code 2:处,内部类中直接访问了外部类的方法

class ItemsView$ItemsAdapter {

    final ItemsView this$0;
    
    void bindItem(android.widget.TextView, java.lang.String);
    
    Code:
    
    0: aload_1
    
    1: aload_2
    
    2: invokestatic #3 // Method ItemsView.displayText:(Ljava/lang/String;)Ljava/lang/String;
    
    5: invokevirtual #4 // Method android/widget/TextView.setText:(Ljava/lang/CharSequence;)V
    
    8: return

}

实际用例


在笔者尝试通过修改限定符,减少access方法的实践中,发现涉及到一些Context的access引用访问,一时找不到具体定义。通过反复查看代码,思考,修改,测试,最终找到相关引用链

Context是由基类引入,例如在BaseActivity中,将Context的限定符设置为protected,此时BaseActivity的子类可以直接引用Context。如果此时子类的内部类也想访问这个Context引用呢,那么这个Context引用相对于这个子类的内部类,它是protected,还是private?

实际上当我将父类的Context限定符修改为public时,从子类的内部类中访问该Context引用时,不会生成access$x00方法;

而当Context的限定符为protected时,从子类的内部类中访问该Context引用时,生成了access$x00方法。

所以,关于这个疑问“此时Context引用相对于这个子类的内部类,它是protected,还是private?”,我的答案是private。

用例的正确解答


在仔细思考、验证之后,发现是否生成access方法,并不是猜测的

--“此时Context引用相对于这个子类的内部类,它是protected,还是private?”,我的答案是private

而是与子类和父类是否放置在同一包名下有关,在同一包名下,由protected限定符的含义,不管是否子类都可以访问,而当子类与父类不在同一包名下时,只有子类可以访问父类的protected方法/字段,子类的内部类则不能直接访问父类的protected方法/字段。

有兴趣的读者,可以自行测试

参考资料:探索 Java 隐藏的开销

上一篇 下一篇

猜你喜欢

热点阅读