一、androidannotations
- 依赖注入(Dependency injection): 支持view, extras, system service, resource等等
- 简单的线程模型(Simplified threading model): 进行方法注解以让该方法在UI线程或后台线程进行执行
- 事件绑定(Event binding): 进行方法注解以让方法执行view的时间而不用再添加一些监听
- REST client: 创建一个接口,AndroidAnnotations用来实现
- 没有你想象的复杂: AndroidAnnotations 只是在在编译时生成相应子类
- 编译检测: 提供的多种注解,用于检测代码编译时可能存在的异常,并给开发者相关提示,提高代码质量
-
不影响应用性能: 仅 50kb,在编译时完成,不会对运行时有性能影响。
AndroidAnnotations来实现这些美好的功能,只需要不到150kb的大小
由于 androidannotations 支持的功能要复杂的多,不仅仅包含 UI 注入,还包含线程切换,网络请求等等,因此它的注解解析逻辑也要复杂得多
该框架的原理跟Butterknife一样,都是在编译时生成代码,不过annotations并不是生成代码供对应的类调用去给带注解的变量、方法赋值,而是直接生成一个继承带注解的类,这个类里面有对变量赋值,对注解方法调用的代码。运行时,直接运行的是annotations生成的类,而不是我们写的类。
@EActivity(R.layout.content_main)
public class MainActivity extends Activity {
@ViewById(R.id.myInput)
EditText myInput;
@ViewById(R.id.myTextView)
TextView textView;
@Click
void myButton() {
String name = myInput.getText().toString();
textView.setText("Hello "+name);
}
}
再看下annotations生成的类。
public final class MainActivity_
extends MainActivity
implements HasViews, OnViewChangedListener
{
private final OnViewChangedNotifier onViewChangedNotifier_ = new OnViewChangedNotifier();
@Override
public void onCreate(Bundle savedInstanceState) {
OnViewChangedNotifier previousNotifier = OnViewChangedNotifier.replaceNotifier(onViewChangedNotifier_);
init_(savedInstanceState);
super.onCreate(savedInstanceState);
OnViewChangedNotifier.replaceNotifier(previousNotifier);
setContentView(layout.content_main);
}
private void init_(Bundle savedInstanceState) {
OnViewChangedNotifier.registerOnViewChangedListener(this);
}
@Override
public void setContentView(int layoutResID) {
super.setContentView(layoutResID);
onViewChangedNotifier_.notifyViewChanged(this);
}
@Override
public void setContentView(View view, LayoutParams params) {
super.setContentView(view, params);
onViewChangedNotifier_.notifyViewChanged(this);
}
@Override
public void setContentView(View view) {
super.setContentView(view);
onViewChangedNotifier_.notifyViewChanged(this);
}
public static MainActivity_.IntentBuilder_ intent(Context context) {
return new MainActivity_.IntentBuilder_(context);
}
public static MainActivity_.IntentBuilder_ intent(Fragment supportFragment) {
return new MainActivity_.IntentBuilder_(supportFragment);
}
@Override
public void onViewChanged(HasViews hasViews) {
myInput = ((EditText) hasViews.findViewById(id.myInput));
textView = ((TextView) hasViews.findViewById(id.myTextView));
{
View view = hasViews.findViewById(id.myButton);
if (view!= null) {
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
MainActivity_.this.myButton();
}
}
);
}
}
}
}
方法调用链:onCreate(Bundle saveInstanceState) ----> setContentView() ----> onViewChangedNotifier_.notifyViewChanged(),而onViewChanagedNotifier_.notifyViewChanged()方法最终会调用onViewChanged(HasViews hasViews)方法,在此方法中有对变量赋值,事件方法设置的代码,注意看自动生成的类的名字,发现规律了吧,就是我们写的类的名字后面加上一个''符号,现在知道为什么用Annotations框架,我们的AndroidManifest.xml中对Activity 的配置,Activity的名字要多加一个''符号了吧。因为真正加载的是AndroidAnnotations生成的代码。写到这里大家发现没,annotations框架里面一个反射都没有,没错这个框架没有用到反射,没有初始化,所有的工作在编译时都做了,不会对我们的程序造成任何速度上的影响。
那Annotations支持哪些注解呢?既然Annotations性能上跟Butterknife差不多,那功能呢?
1、依赖注入:注入views, extras, 系统服务,资源,...
2、简化线程模式:在方法上添加注释来制定该方法是运行在UI线程还是子线程。
3、事件绑定:在方法上添加注释来制定该方法处理那些views的那个事件。
4、REST client:创建一个client的接口,AndroidAnnotations会生成实现代码,这是关于网络方面的。
5、清晰明了:AndroidAnnotations会在编译时自动生成对应子类,我们可以查看相应的子类来了解程序是怎么运行的。
二、xUtils
- 支持功能较多:orm, http(s), image, view注解, 但依然很轻量级(246K), 并且特性强大
- 使用方便:只需要引入Jar包,使用方式和ButterKnife类似,在方法和成员变量上添加注解
- 兼容低版本:最低兼容android 2.2 (api level 8)
- 性能一般:注解是在运行时通过反射处理,性能较低
关键源码:
private static void injectObject(Object handler, ViewFinder finder) {
Class<?> handlerType = handler.getClass();
// inject ContentView
.......
// inject view
Field[] fields = handlerType.getDeclaredFields();
if (fields != null && fields.length > 0) {
for (Field field : fields) {
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {
try {
View view = finder.findViewById(viewInject.value(), viewInject.parentId());
if (view != null) {
field.setAccessible(true);
field.set(handler, view);
}
} catch (Throwable e) {
LogUtils.e(e.getMessage(), e);
}
} else {
ResInject resInject = field.getAnnotation(ResInject.class);
...... // 跟viewInject类似
} else {
PreferenceInject preferenceInject = field.getAnnotation(PreferenceInject.class);
...... // 跟viewInject类似
}
}
}
}
// inject event
Method[] methods = handlerType.getDeclaredMethods();
if (methods != null && methods.length > 0) {
for (Method method : methods) {
Annotation[] annotations = method.getDeclaredAnnotations();
if (annotations != null && annotations.length > 0) {
for (Annotation annotation : annotations) {
Class<?> annType = annotation.annotationType();
if (annType.getAnnotation(EventBase.class) != null) {
method.setAccessible(true);
try {
// ProGuard:-keep class * extends java.lang.annotation.Annotation { *; }
Method valueMethod = annType.getDeclaredMethod("value");
Method parentIdMethod = null;
try {
parentIdMethod = annType.getDeclaredMethod("parentId");
} catch (Throwable e) {
}
Object values = valueMethod.invoke(annotation);
Object parentIds = parentIdMethod == null ? null : parentIdMethod.invoke(annotation);
int parentIdsLen = parentIds == null ? 0 : Array.getLength(parentIds);
int len = Array.getLength(values);
for (int i = 0; i < len; i++) {
ViewInjectInfo info = new ViewInjectInfo();
info.value = Array.get(values, i);
info.parentId = parentIdsLen > i ? (Integer) Array.get(parentIds, i) : 0;
EventListenerManager.addEventMethod(finder, info, annotation, handler, method);
}
} catch (Throwable e) {
LogUtils.e(e.getMessage(), e);
}
}
}
}
}
}
}
可以看到上面用到了很多反射,虽然现在的反射速度也很快了,但是还是不能跟原生代码相比,一旦注释用的多了,这初始化速度会越来越慢。通过上面注释处理的代码可以看出,xUtils支持的注释目前主要有UI, 资源,事件,SharedPreference绑定。跟xUtils一样是运行时利用反射去解析注释的框架还有Afinal, Roboguice等框架。
现在网上还有很多其他的注释框架,但是不是反射就是自动生成代码。反射功能虽然强大,但是不太可取,不仅会拖慢速度还会破话程序的封装性。个人认为生成代码的方案比较好,所有的功能都在编译时做了,并不会影响到用户的体验,唯一的缺点就是实现难度比较高。
三、ButterKnife
- 强大的View绑定,Click事件处理功能以及资源内容,简化代码,提升开发效率;
- 方便的处理Adapter里的ViewHolder绑定问题;
- 运行时不会影响APP效率,使用配置方便;
- 代码清晰,可读性强。
示例代码:
class ExampleActivity extends Activity {
@BindView(R.id.title) TextView title;
@BindView(R.id.subtitle) TextView subtitle;
@BindView(R.id.footer) TextView footer;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
}
}
与缓慢的反射相比,Butter Knife使用再编译时生成的代码来执行View的查找,因此不必担心注解的性能问题。调用bind来生成这些代码。
上面的例子,生成的代码大致如下所示:
public void bind(ExampleActivity activity) {
activity.subtitle = (android.widget.TextView) activity.findViewById(2130968578);
activity.footer = (android.widget.TextView) activity.findViewById(2130968579);
activity.title = (android.widget.TextView) activity.findViewById(2130968577);
}
这里只是举了一个例子来说明butterKnife的使用,再详细的内容,会再另外出文章进行详解。
四、EventBus
EventBus 2.x: 采用反射的方式对整个注册的类的所有方法进行扫描来完成注册,对性能会有些影响
EventBus 3.0: 提供了EventBusAnnotationProcessor注解处理器来在编译期通过读取@Subscribe()注解并解析、处理其中所包含的信息,然后生成java类来保存所有订阅者关于订阅的信息,这样就比在运行时使用反射来获得这些订阅者的信息速度要快。