写在前面
相信很多人看到标题startActivity可能会轻蔑一笑,启动Activity不是Android开发里基础得不能再基础的内容吗? 但相信点进来的各位看官都是追求优雅编码的开发者:) 。我要用一个优雅的姿势,最小的代码来启动一个Activity。
startActivity的原生式
启动Activity原生方式是new 一个Intent,将需要传递的参数put进Intent,再调用Activity#startActivity:
Intent intent = new Intent(this, TestActivity.class);
intent.putExtra("age", age);
intent.putExtra("name", name);
startActivity(intent);
startActivity的改进式
在开发当中,往往需要知道TargetActivity要接收的参数有哪些,难道我们每次启动TargetActivity都要打开TargetActivity.Java看看它需要哪些参数,而且在TargetActivity取出参数的时候还得记住每一参数的key? 为什么不让厨师列一份食材清单,而不是每次需要炒菜时都去问厨师需要什么食材呢。聪明的开发者想到了在TargetActivity加一个静态方法,将启动Activity的工作都放到这个方法中。
////------TargetActivity.java
public static void startTargetActivity(Activity srcActivity, int age, String name) {
Intent intent = new Intent(srcActivity, TestActivity.class);
intent.putExtra("age", age);
intent.putExtra("name", name);
srcActivity.startActivity(intent);
}
////-------SrcActivity.java
TestActivity.startTargetActivity(this,age,name);
这样启动Activity的控制权就交由TargetActivity,这样的确省了不少重复的代码。然而故事并没有结束,这种方式仍有不少痛点。
创建Intent对象,putExtra等模板代码还是免不了。参数不多还好,假如参数多了呢?
用Intent#getXxxExtra从Intent中逐个取出参数,这样的代码也是免不了,而且还是得关注每个参数的key。
假如参数一多,启动Activity的静态方法的参数列表就变得会非常长。一大串参数,看着就不舒服。
对于startActivity的调用方来说,假如有些参数它不想传,那就只能传一个null。就像这样,迷之传null:(
TestActivity.startTargetActivity(this,null,null,null,null,null);
为了解决上述痛点,以下优雅式应运而生。
startActivity的优雅式
既然一次调用需要参数灵活,又不想写一大堆重载函数,我们想到用建造者模式。就像RxJava那样的链式调用就是我们需要的:
XXX.from(MainActivity.this)
.gotoTargetActivity()
.age(18)
.name("tlh")
.go();
为了实现这种优雅调用,我受dagger和butterKnift的启发写了一个基于编译时注解的开源库AutoGo。
具体用法:
在目标Activity(以下简称TargetActivity)的字段用@IntentValue标注,注解参数将作为传进Intent的key,如果不传,字段名将作为key。调用AutoGo.assign(activity);就可以自动为被@IntentValue标注的字段赋值。
标注好后最好Make Project(Ctrl+F9),编译一下代码,以产生辅助代码。
public class TargetActivity extends AppCompatActivity {
//the annotated field should not be private
@IntentValue("myName") String name;
@IntentValue int age;
......
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
AutoGo.assign(this);
}
在调用方用链式调用的方式传参,启动Activity。
AutoGo.from(SrcActivity.this)
.gotoTargetActivity()
.age(18)
.myName("tlh")
.go();
原理
有朋友可能会问,这货使用注解会影响性能的吧? 放心,请不要谈注解色变。注解大概分成两种,一种是运行时注解,也就是在运行时起作用,因为在运行时需要通过反射获取被注解Element(如字段,类,方法)的信息,会有一定的性能影响;一种是编译期注解,这种注解一般是不会在添加到class文件的,对代码性能几乎没有影响,熟知的dagger,butterKnife,greenDao就是基于编译期注解。
在代码编译时,会有一个注解处理器,负责解析被注解标注的Element的信息,比如这里我需要获取字段名作为key,和字段类型,所以就用@IntentValue标注,在编译时这些被标注的字段会交给注解处理器来处理。而我们的工作就是编写注解处理器,解析并收集这些字段信息,生成相关辅助代码。生成的代码可以在build目录中找到:
此外,AutoGo这个项目还支持更优雅地使用SharedPreference和Bundle来存储数据。喜欢不妨点个star :)
项目源码—Github : https://github.com/TellH/AutoGo