一.背景
今天组里面更新了compileSdkVersion到26了,闲来无事我也跟着一起更新了,并且安装了sdk-26的虚拟机,项目完美运行。无意间看到了 Activity 内部中 findViewById 代码,强转的 TextView 都变成了灰色,手贱看了下提示:Casting 'findViewById(R.id.tvRegister) to 'TextView' is redundant。第一反应居然是:我擦最后一个单词居然不认识,好尴尬。联想到由于刚刚更新了sdk,有可能是
android-26 源代码中的新特性导致以后都不需要再强转初始化 View 了。
二.分析源码
activity 中的 findViewById其实最后是通过mWindow.findViewById 来实现的;而 activity中的mWindow其实代表了 Window 对象(真实实现的是PhoneWindow),可以看到:
public <T extends View> T findViewById(@IdRes int id) {
return getDecorView().findViewById(id);
}
最后是定位 DecorView 的 findViewById,现在知道了其实Activity的findViewById其实就是调用了 View 的 findViewById方法。 对比android-25和android-26中源于该内容的源码:
//androi-25
public final View findViewById(@IdRes int id) {
if (id < 0) {
return null;
}
return findViewTraversal(id);
}
protected View findViewTraversal(@IdRes int id) {
if (id == mID) {
return this;
}
return null;
}
//andorid-26
public final <T extends View> T findViewById(@IdRes int id) {
if (id == NO_ID) {
return null;
}
return findViewTraversal(id);
}
protected <T extends View> T findViewTraversal(@IdRes int id) {
if (id == mID) {
return (T) this;
}
return null;
}
骚年们看到了吧,android-26中其实对findViewById 返回了一个泛型对象,而这个泛型对象其实就是真实的控件类型,这就是在Activity不需要再强转的根本原因。
三.解析代码
泛型的返回使得在activity中初始化控件变得更加简单,但是这一段泛型代码你之前见过吗?在哪里见过你呢,好像真的没见过,那就写个demo测试下。
class View {
private int mId;
public View(int id) {
mId = id;
}
public View findViewById1(int id) {
if (mId == id) {
return this;
}
return null;
}
public <T extends View> T findViewById2(int id) {
if (mId == id) {
return (T) this;
}
return null;
}
}
class TextView extends View {
public TextView(int id) {
super(id);
}
}
class ImageView extends View {
public ImageView(int id) {
super(id);
}
}
public class {
public static void main(String[] args) {
int resId = 10;
TextView tv = new TextView(resId);
TextView tv1 = tv.findViewById1(resId);
ImageView iv2 = tv.findViewById2(resId);
}
}
这里主要解释下findViewById2方法。因为我们要返回具体的实现类,所以返回类型肯定是View的子类,比如TextView。但现在返回的是泛型,所以不知道具体返回的是什么类型(有可能是TextView、ImageView)。只有在=赋值的时候才会对该泛型进行赋值为真实对象。
这就存在的一个问题:由于返回类型是泛型,所以不管你复制给那个实例的引用,在编译时候都不会报错误。所以上面代码通过findViewById2获取返回赋值给 ImageView ,在编译的时候是顺利通过的,但是在运行的时候其实是强转错误。
虽然暂时不清楚为啥在这种情况下可以使用泛型(感觉上这和泛型没有一点关系,只是类的继承相关),所以暂时还是保持这个疑问吧。