国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android注解框架對比分析

2019-12-12 03:49:30
字體:
來源:轉載
供稿:網友

Java的注解(Annotation)相當于一種標記,在程序中加入注解就等于為程序打上某種標記,標記可以加在包,類,屬性,方法,本地變量上。然后你可以寫一個注解處理器去解析處理這些注解(人稱編譯時注解),也可以在程序運行時利用反射得到注解做出相應的處理(人稱運行時注解)。

開發Android程序時,沒完沒了的findViewById, setOnClickListener等等方法,已經讓大多數開發者頭疼不已。好在市面上有所謂的注解框架可以幫助開發者簡化一些過程。比較流行的有butterknife, annotations, xutils, afinal, roboguice等等。今天我們就來對比一下這些注解框架。

ButterKnife框架分析

       首先看下Butterknife,來自Jakewharton大神的力作,特點是接入簡單,依賴一個庫就好了。另外在Android Studio上還有提供一個插件,自動生成注解與類屬性。

       Butterknife目前支持的注解有: View綁定(Bind),資源綁定(BindBool, BindColor, BindDimen, BindDrawble, BindInt, BindString),事件綁定(OnCheckedChanged, OnClick, OnEditorAction, OnFocusChange, OnItemClick, OnItemLongClick, OnItemSelected, OnLongClick, OnPageChange, OnTextChanged, OnTouch)。

       Butterknife的原理是運行時注解。先來看下一個demo。

public class MainActivity extends Activity { @Bind(R.id.tv1) TextView mTv1; @Bind(R.id.tv2) TextView mTv2; @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  ButterKnife.bind(this);  mTv1.setText("tv1已經得到了控件的索引"); } @OnClick(R.id.tv2) public void tv2OnClick() {  Toast.makeText(this, "tv2被點擊了", Toast.LENGTH_SHORT).show(); }

這是一個View綁定的例子,你需要在成員變量上注解需要綁定的控件id,然后再調用ButterKnife.bind(Activity target)方法對帶注解的成員變量進行賦值。ok, 看下ButterKnife.bind()是如何工作的。

/** * Bind annotated fields and methods in the specified {@link Activity}. The current content * view is used as the view root. * * @param target Target activity for view binding. */ public static void bind(Activity target) { bind(target, target, Finder.ACTIVITY); }

由上面代碼可以看出,最終需要調用bind(Object target, Object source, Finder finder)方法。

static void bind(Object target, Object source, Finder finder) { Class<?> targetClass = target.getClass(); try {  if (debug) Log.d(TAG, "Looking up view binder for " + targetClass.getName());  ViewBinder<Object> viewBinder = findViewBinderForClass(targetClass);  if (viewBinder != null) {  viewBinder.bind(finder, target, source);  } } catch (Exception e) {  throw new RuntimeException("Unable to bind views for " + targetClass.getName(), e); } }

這個方法就是尋找或者生成對應的 ViewBinder對象,然后調用該對象的bind(Finder finder, T target, Object source)方法為被注解的變量賦值。先看下findViewBinderForClass(Class<?> cls)方法。

private static ViewBinder<Object> findViewBinderForClass(Class<?> cls)  throws IllegalAccessException, InstantiationException { ViewBinder<Object> viewBinder = BINDERS.get(cls); if (viewBinder != null) {  if (debug) Log.d(TAG, "HIT: Cached in view binder map.");  return viewBinder; } String clsName = cls.getName(); if (clsName.startsWith(ANDROID_PREFIX) || clsName.startsWith(JAVA_PREFIX)) {  if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");  return NOP_VIEW_BINDER; } try {  Class<?> viewBindingClass = Class.forName(clsName + ButterKnifeProcessor.SUFFIX);  //noinspection unchecked  viewBinder = (ViewBinder<Object>) viewBindingClass.newInstance();  if (debug) Log.d(TAG, "HIT: Loaded view binder class."); } catch (ClassNotFoundException e) {  if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());  viewBinder = findViewBinderForClass(cls.getSuperclass()); } BINDERS.put(cls, viewBinder); return viewBinder; }

       可以看出是利用反射生成一個ViewBinder對象出來,而且還帶有緩存,也就是說,同一個class多次調用只會反射一次。反射雖然比原生代碼慢一些,但是如果只有一次反射的話,對性能的影響完全可以忽略不計。那現在的問題就是這個反射生成的ViewBinder是什么東西, 它的bind(Finder finder, T target, Object source) 方法是如何為被注解的變量賦值的?
       上面說過Butterknife框架是編譯時注解,一般這類注解會在編譯的時候,根據注解標識,動態生成一些類或者生成一些xml都可以,在運行時期,這類注解是沒有的~~會依靠動態生成的類做一些操作,因為沒有反射,效率和直接調用方法沒什么區別~~~。在程序編譯時, Butterknife會跟據所有的注解生成對應的代碼比如說上面的MainActivity類,Butterknife會生成

public class MainActivity$ViewBinder<T extends MainActivity> implements ButterKnife.ViewBinder<T> {  public void bind(ButterKnife.Finder finder, final T target, Object source) { target.mTv1 = ((TextView)finder.castView((View)finder.findRequiredView(source, 2131492971, "field 'mTv1'"), 2131492971, "field 'mTv1'")); View localView = (View)finder.findRequiredView(source, 2131492972, "field 'mTv2' and method 'tv2OnClick'"); target.mTv2 = ((TextView)finder.castView(localView, 2131492972, "field 'mTv2'")); localView.setOnClickListener(new DebouncingOnClickListener() {  public void doClick(View paramAnonymousView) {  target.tv2OnClick();  } }); } public void unbind(T target) { target.mTv1 = null; target.mTv2 = null; }}

可以看出Bind注解到最后就是調用生成的代碼來findViewById然后給其賦值的,事件就是給view設置一個默認的事件,然后里面調用你注解的那個方法。所以在性能上,ButterKnife并不會影響到app。 Butterknife 自動生產的代碼也不多,不會對程序的包大小有什么影響。

AndroidAnnotations框架分析

      再來分析下著名的Annotations框架。該框架的原理跟Butterknife一樣,都是在編譯時生成代碼,不過annotations并不是生成代碼供對應的類調用去給帶注解的變量、方法賦值,而是直接生成一個繼承帶注解的類,這個類里面有對變量賦值,對注解方法調用的代碼。運行時,直接運行的是annotations生成的類,而不是我們寫的類。說了這么多是不是不太明白,沒關系,demo來了!先看下我們寫的類。

@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差不多,那功能呢?在這里翻譯一下官網的Features.

1)、依賴注入:注入views, extras, 系統服務,資源,...
2)、簡化線程模式:在方法上添加注釋來制定該方法是運行在UI線程還是子線程。
3)、事件綁定:在方法上添加注釋來制定該方法處理那些views的那個事件。
4)、REST client:創建一個client的接口,AndroidAnnotations會生成實現代碼,這是關于網絡方面的。
5)、清晰明了:AndroidAnnotations會在編譯時自動生成對應子類,我們可以查看相應的子類來了解程序是怎么運行的。
XUtils框架分析

    xutils框架是我們現在在用的框架,那我們就來分析一下他的注解功能。xutils的使用方式跟Butterknife一樣,都是在成員變量,方法上添加注釋,然后調用一個方法(xutils是ViewUtils.inject()方法)對成員變量賦值、事件方法設置到view上。不同的是,Butterknife是調用自動生成的代碼來賦值,而xutils是通過反射來實現的。ok,拿源碼說話。

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等。

      市面上還有很多其他的注釋框架,但是萬變不離其宗,不是反射就是自動生成代碼。反射功能雖然強大,但是不可取,不僅會拖慢速度還會破話程序的封裝性。個人認為生成代碼的方案比較好,所有的功能都在編譯時做了,并不會影響到用戶的體驗,唯一的缺點就是比反射難實現,不過我們程序不就是把難處留給自己,把快樂留給用戶么!

     最后,對上面三種框架總結一下。

上面的難易,強弱,快慢都是相對他們三個自己來說的,比如AndroidAnnotations的接入評級是難,并不代表它的接入方式很難,只是相對ButterKnife和XUtils來說比他們難。如果只想使用UI綁定,資源綁定,事件綁定的功能,推薦使用ButterKnife。以上分析純屬個人觀點,僅供參考!

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 郸城县| 天门市| 合肥市| 黄平县| 天长市| 津南区| 万山特区| 隆林| 浦江县| 治县。| 潮州市| 个旧市| 衢州市| 巴林左旗| 铜川市| 黄石市| 垣曲县| 陆良县| 晋城| 新巴尔虎右旗| 达日县| 卢湾区| 许昌市| 泽州县| 山西省| 襄樊市| 方城县| 剑河县| 随州市| 兰西县| 苏州市| 东城区| 安仁县| 交口县| 财经| 马山县| 福海县| 定州市| 海原县| 米泉市| 普兰店市|