注解库汇总

一、androidannotations

  1. 依赖注入(Dependency injection): 支持view, extras, system service, resource等等
  2. 简单的线程模型(Simplified threading model): 进行方法注解以让该方法在UI线程或后台线程进行执行
  3. 事件绑定(Event binding): 进行方法注解以让方法执行view的时间而不用再添加一些监听
  4. REST client: 创建一个接口,AndroidAnnotations用来实现
  5. 没有你想象的复杂: AndroidAnnotations 只是在在编译时生成相应子类
  6. 编译检测: 提供的多种注解,用于检测代码编译时可能存在的异常,并给开发者相关提示,提高代码质量
  7. 不影响应用性能: 仅 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

  1. 支持功能较多:orm, http(s), image, view注解, 但依然很轻量级(246K), 并且特性强大
  2. 使用方便:只需要引入Jar包,使用方式和ButterKnife类似,在方法和成员变量上添加注解
  3. 兼容低版本:最低兼容android 2.2 (api level 8)
  4. 性能一般:注解是在运行时通过反射处理,性能较低

关键源码:

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

  1. 强大的View绑定,Click事件处理功能以及资源内容,简化代码,提升开发效率;
  2. 方便的处理Adapter里的ViewHolder绑定问题;
  3. 运行时不会影响APP效率,使用配置方便;
  4. 代码清晰,可读性强。

示例代码:

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类来保存所有订阅者关于订阅的信息,这样就比在运行时使用反射来获得这些订阅者的信息速度要快。

你可能感兴趣的:(注解库汇总)