在安卓开发中,我们可能会经常遇到一些线上报错,显示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 隐藏的开销
网友评论