findViewById注解、泛型、反射、MVVM
0x00 TOC:

常规

注解

泛型

反射

在线生成

DataBinding

源码实现

0x01 常规

这里findViewById作用以及用法就不再叙述了,接触过Android开发的都明白其怎么用,这次主要分析如何提高其性能,并且使用TraceView分析,如何改善或者节省代码,使开发人员变得更懒。

环境:

Mac 10.11
Android Studio2.0
SDK24

如何使用TraceView监测代码呢,在想要根据的代码片段之间使用以下两句代码,注意添加SD卡权限。当然也可以直接在DDMS中的面板里的Start Method Profiling来监测方法。此次就将代码放置在initView()方法里,以示区别。并且都是启动一次,再结束activity,然后再启动Activity去比较运行时间。

Debug.startMethodTracing("debug_test");  
Debug.stopMethodTracing();
//权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
//adb导出
adb pull sdcard/debug_test.trace  

这里采用的xml文件是这样的,其中包含了几个TextView,ImageView。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.sxau.findviewbyiddemo1.MainActivity">

    <TextView
        android:id="@+id/tv_demo1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textSize="32sp" />

    <TextView
        android:id="@+id/tv_demo2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/textview1"
        android:textSize="32sp" />

    <TextView
        android:id="@+id/tv_demo3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/textview3"
        android:textSize="32sp" />

    <EditText
        android:id="@+id/et_test"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"
        android:textSize="32sp" />

    <ImageView
        android:id="@+id/iv_test"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="@string/iv_text"
        android:src="@mipmap/ic_launcher" />

    <Button
        android:id="@+id/btn_test"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/tv_btn"
        android:textSize="32sp" />

</LinearLayout>

string.xml

<resources>
    <string name="app_name">FindViewByIdDemo1</string>
    <string name="textview1">test</string>
    <string name="textview3">test2</string>
    <string name="iv_text">图标</string>
    <string name="tv_btn">btn</string>
</resources>

常规的即使用findViewById方法。即:

textView1 = (TextView) findViewById(R.id.tv_demo1);
textView2 = (TextView) findViewById(R.id.tv_demo2);
textView3 = (TextView) findViewById(R.id.tv_demo3);
editText = (EditText) findViewById(R.id.et_test);
imageView = (ImageView) findViewById(R.id.iv_test);
button = (Button) findViewById(R.id.btn_test);

测试结果如下:

0x02 注解

如何使用注解这种的避免传统的findViewById呢。

很多开源框架已经支持注解了,就以Android Annotations框架为例。

View注解也分运行时注解和编译时注解。

注解利用的原理也不一样,有的是利用反射,比如KJFrameForAndroid, xUtils, afinal, thinkAndroid,有的是利用aapt资源打包。反射自然效率比较低,在反射部分举例分析。

ButterKnife处理注解是在编译的时候,处理了@Bind、@OnClick等这些注解,所以效率比较高。

即:

@BindView(R.id.tv_demo1)
TextView textView;
@BindView(R.id.tv_demo2)
TextView textView2;
@BindView(R.id.tv_demo3)
TextView textView3;
@BindView(R.id.iv_test)
ImageView imageView;
@BindView(R.id.btn_test)
Button button;
@BindView(R.id.et_test)
EditText editText;
//initView();
ButterKnife.bind(this);

结果如下:

0x03 泛型
private <T extends View> T $(int resId) {
    return (T) super.findViewById(resId);
}
textView1 = $(R.id.tv_demo1);
textView2 = $(R.id.tv_demo2);
textView3 = $(R.id.tv_demo3);
button = $(R.id.btn_test);
imageView = $(R.id.iv_test);
editText = $(R.id.et_test);

测试结果:

0x04 反射

前面也说过,有些注解框架实现视图注入是利用的反射机制,反射执行的效率是很低的。以xutils为例,测试代码:

@ContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {

    @ViewInject(R.id.tv_demo1)
    TextView textView1;
    @ViewInject(R.id.tv_demo2)
    TextView textView2;
    @ViewInject(R.id.tv_demo3)
    TextView textView3;
    @ViewInject(R.id.btn_test)
    Button button;
    @ViewInject(R.id.iv_test)
    ImageView imageView;
    @ViewInject(R.id.et_test)
    EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        x.view().inject(this);
    }
}

在DDMS的测试结果如下。

0x05 在线生成

Convert your Android XML layouts into a set of declarations and binds to save you all that manual typing. Enter a prefix for your fields, choose the scope paste in your XML and hit generate. Select "verbose" to find out why any fields are skipped.

http://android.lineten.net/layout.php

生成的结果如图所示。

0x06 DataBinding

DataBinding是谷歌官方推出的一款数据绑定框架。

使用起来比较方便,只需要在build.gradle文件里添加以下代码片段。

android {
    ....
    dataBinding {
        enabled = true
    }
}

具体的使用方法,网上也有很多,比如精通 Android Data Binding,以及官方文档

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="user" type="com.sxau.findviewbyiddemo5.User"/>
    </data>
    //界面代码
</layout>

然后Activity里可以使用MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);进行数据绑定。

前面的类即是根据xml文件名自动生成的。

其中可以实现单向数据绑定。

0x07 源码实现

想了那么多避免传统的findViewById的方法,有的性能差不多,有的性能比较低,但是官方基础实现永远是性能最快的。

public final View findViewById(int id) {
    if (id < 0) {
        return null;
    }
    return findViewTraversal(id);
}

其中调用了findViewTraversal()方法。

protected View findViewTraversal(@IdRes int id) {
    if (id == mID) {
        return this;
    }
    return null;
}

我们通常是在Activity里findViewById(),所以需要看Activity代码。

public View findViewById(@IdRes int id) {
    return getWindow().findViewById(id);
}

getWindow()的对象是继承ViewGroup的,在ViewGroup里,重写了findViewTraversal()方法:

protected View findViewTraversal(@IdRes int id) {
    if (id == mID) {
        return this;
    }

    final View[] where = mChildren;
    final int len = mChildrenCount;

    for (int i = 0; i < len; i++) {
        View v = where[i];

        if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
            v = v.findViewById(id);

            if (v != null) {
                return v;
            }
        }
    }

    return null;
}

其中维护了一个View数组,然后从中遍历匹配,从而找到View对象。


美景加咖啡,一杯又一杯。

Comments()

蓝瘦,香菇,Disqus被墙了。
  • 状态更新中...