AndroidAnnotations 注解框架的优势对比、配置及使用教程

由于厌倦了在代码中反复敲打 findViewById 和其他千篇一律的重复代码,我也开始跟身边的人学习,决定使用注解方式编程,以帮助自己更加专注于逻辑,同时让代码看起来更整洁。网上的博客、身边的同事大多使用的是 xUtilsButterKnife(在后文会提及)实现注解,很惭愧我都没尝试过,不发表看法。

因为我都没用过,所以对我来说找一个我觉得合适的入手就 OK 了。但是哪个更易学、更高效呢?值得考虑。于是我开始在网上查,最终在 注解框架—AndroidAnnotations 一文中找到了合适的答案,以下是引用文中的一段话:

AndroidAnnotations 是一个开源框架,旨在加快Android开发的效率。通过使用它开放出来的注解 api,你几乎可以使用在任何地方, 大大的减少了无关痛痒的代码量,让开发者能够抽身其外,有足够的时间精力关注在真正的业务逻辑上面。而且通过简洁你的代码,也提高了代码的稳定性和后期的维护成本。以下 AndroidAnnotations 简称为 AA

可能会有人提出异议了,我们移动设备的性能,不比后台服务器拥有充足的内存和运算能力。当大量的使用注解的时候,会不会对APP的造成什么不良的影响,会不会影响到APP的执行性能?在这里先明确的声明,AA 不会给 APP 带来任何副作用,相反它强大易用的 api 能为你带来前所未有的编程体验。

目前主流的注解框架有 xUtils、ButterKnife、Dragger 和 Roboguice,它们的实现原理都是一致的,都是通过反射机制实现的。通过在Runtime运行期去反射类中带有注解的Field和Method,然后再去执行注解相对应的逻辑代码。大家都知道反射机制是在APP的运行期执行的,会造成执行的效率下降,执行时间变长的缺点。当在我们 APP 中大量的使用基于反射的注解,会严重影响到性能。但是AA的实现的逻辑并不是基于此。

AA 工作的原理其实也很简单,它通过使用 jdk 1.6引入的 Java Annotation Processing Tool,在编译器中加了一层额外的自动编译步骤,用来生成基于你源码的代码。生成的代码是你源码的直接子类,而且自动生成的类的名称就是父类名称后面加个下划线。比如使用了@EActivity注解的MyActivity,AA 都会自动帮你生成一个名为 MyActivity_ 的类。使用 AA 的注解在编译期间就已经自动生成了对应的子类,运行期运行的其实就是这个子类,所以说 AA 的使用不会给APP的执行性能造成负面影响。

如果您使用或接触过注解框架,看到这儿,我想您应该明白上文的意思了,如果不明白也没关系,总之我认可 AA,所以今天的主题就是介绍 AA : )

AndroidAnnotations 官网

AndroidAnnotations Github 地址

虽说 AA 相比于其他的框架有一定的优势,但是在配置方面还是比较麻烦的(IDE 是 AS),不是一句简单的 compile 就能接入的。此处省略我的摸索过程,直接向大家分享从配置到使用的相关步骤,尽量上代码少废话。

配置过程:

配置过程主要是参考 GitHub 的 Wiki 页面 ,说的已经很详细了。下面简单介绍我配置的过程,并在代码中添加注释。先面往下看,进入 app 下的 build.gradle

apply plugin: 'com.android.application'

//step 1
//添加依赖插件,定义版本号
apply plugin: 'android-apt'
def AAVersion = '4.0.0'

//step 2
buildscript {
    //指定远程仓库
    repositories {
        mavenCentral()
    }
    //依赖库
    dependencies {
        // 记得保持版本号与您根目录下 build.gradle 中所依赖的版本号一致
        classpath 'com.android.tools.build:gradle:2.0.0'
        // 保持最新的版本号,当前是1.8
        // 最新版查看地址-http://mvnrepository.com/artifact/com.neenbedankt.gradle.plugins/android-apt
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

//step 3
apt {
    arguments {
        androidManifestFile variant.outputs[0]?.processResources?.manifestFile
    }
}

// step 4
dependencies {
    // ......
    // 具体的依赖
    apt "org.androidannotations:androidannotations:$AAVersion"
    compile "org.androidannotations:androidannotations-api:$AAVersion"
}

android {
    //.......
}

上手使用

配置讲完了,开始讲使用。平时我虽然喜欢搬砖,但是在这儿必须尊重别人的成果,所以不会直接复制粘贴。大家可以参考 Android 最火快速开发框架AndroidAnnotations使用详解 一文,其中对于 AA 各个注解标签的使用讲解还是蛮全的,我就是从这儿挑的几个常用的写的 demo。同时,文章开头引用的文章也讲解了如何使用,都差不多,二者掺和着看吧。

实例

下面是我自己写的 demo,涉及了开发中常用的简单操作,再加上在使用过程中灵活应变、举一反三,还是蛮容易上手的。对于其他未提及的用法,我还在摸索中,如果有比较常用但我没有提及的,欢迎您留言补充,我会及时更新…

先看看 .xml 界面,注意 id 即可,后面会说:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">

    <TextView  android:id="@+id/textShow" android:layout_width="wrap_content" android:layout_height="wrap_content" />

    <Button  android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="12dp" android:text="Click Me" />

</LinearLayout>

接下来就是 Activity 的 .java 代码,老规矩直接在代码中注释,实际开发是不会写这么多的,此处是为了更直观,不要嫌乱 : )

//引入 Activity 的布局,相当于 setContentView(int id)
@EActivity(R.layout.activity_main)
//全屏
@Fullscreen
//移除标题栏
@WindowFeature(Window.FEATURE_NO_TITLE)
//同理,对于Fragment:@EFragment,普通类:@EBean
public class MainActivity extends AppCompatActivity {
    /** * 引入控件 * 注:变量名与 xml 中声明的 id 一致时,不需要加 R.id.textShow **/
     @ViewById
     TextView textShow;

    /** * 变量名与 xml 中声明的 id 不一致时,需要加 R.id.button **/
     @ViewById(R.id.button)
     Button mButton;

    //引入 String 资源,id 的使用同上
    @StringRes
    String btText;

    //引入色值、动画。。。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //查看 MainActivity_ 的 onCreate() 方法,其
        //先执行 super.onCreate(savedInstanceState)方法
        //再执行setContentView(R.layout.activity_main) 方法
        //所以在 MainActivity 的 onCreate() 方法中不要涉及任何与 layout 控件相关的操作
    }

    /** * 开始使用控件 * 注意:不要在 onCreate() 方法中调用通过注解得到的控件 * 因为 onCreate() 在执行时 view 还没有注入,会导致空指针异常 */
    @AfterViews
    void initView() {
        textShow.setText("AA 注解的使用");
        mButton.setText(btText);
    }

    //点击事件,同理,长按:@LongClick,触摸:@Touch
    @Click(R.id.button)
    void btClick() {
        Toast.makeText(MainActivity.this, "start", Toast.LENGTH_SHORT).show();
        //模拟在后台执行耗时操作
        doInBack();
    }

    @Background
    /** * 子线程执行耗时操作,比如网络请求 */
    void doInBack() {
        try {
            //模拟耗时操作
            Thread.sleep(2000);

            //错误示范:直接更新 UI 会报错,原因你懂的
            //mButton.setText("Why Click Me");

            //正确的写法
            doInUiThread();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @UiThread
    /** * 在子线程中更新 UI 线程 */
    void doInUiThread() {
        mButton.setText("Why Click Me");
    }
}

不止于此

对于上文 .java 文件的代码意思,相信我们都能够理解,但是其实际上是如何工作的呢?如果在学习的过程中,不追问自己一句,仅仅止于此,我想是不会有机会理解其背后的原理的(代码层级)。

在说之前,先给出几点提醒:
1. androidannotations 在使用过程中是通过生成新的类继承自现有的类实现工作的,比如 MainActivity_ extends MainActivity
2. 在 Mainfest 中注册 MainActivity_ 而不是 MainActivity。同时,在别处使用 MainActivity 时也是如此
3. 成员变量或方法不要声明为私有,否则会在编译时出错(子类无法继承父类的私有变量)

接下来要说的会涉及第一点提醒。现在,我们以代码中的 doInUiThread() 方法为例,按住 Ctrl(Windows 系统) 并单击方法名,进入子类 MainActivity_ 的 doInUiThread() 方法中查看其实现原理,代码如下:

@Override
    void doInUiThread() {
        UiThreadExecutor.runTask("", new Runnable() {

            @Override
            public void run() {
                MainActivity_.super.doInUiThread();
            }
        }
        , 0L);
    }

先不管 UiThreadExecutor 类中静态方法 runTask() 的实现逻辑和具体参数,只要知道其内部本质是通过调用 Handle 发送 Runnable 对象,实现在子线程中发送消息(Runnable 在传递过程中会被封装为 Message)到主线程的消息队列,从而更新主线程的UI。

明白了 runTask() 的实现逻辑,再来看 MainActivity_.super.doInUiThread() 这句,MainActivity_.super 还用说?当然是 MainActivity 啦。所以,这句话等价于调用我们在 MainActivity 中写在 doInUiThread() 中的逻辑。如此一来,顺利实现在子线程中更新UI线程。

写在最后

我所了解的 AA 就这些了,本文是我的学习过程,难免有不全或不足之处,而且在实际项目中会遇到哪些问题还不得而知,所以在后期的使用过程中我会随时根据我的认识更新本文,以渐渐优化。如果文中有任何不足或错误之处,或者您有更好的意见或建议,不妨给我留言指点一二,让我有更多的机会去学习!谢谢!

你可能感兴趣的:(注解,框架,annotation,android)