| 注册
请输入搜索内容

热门搜索

Java Linux MySQL PHP JavaScript Hibernate jQuery Nginx
aele0878
8年前发布

探究 Android 中的注解

   <p>注解是我们经常接触的技术,Java有注解,Android也有注解,本文将试图介绍Android中的注解,以及ButterKnife和Otto这些基于注解的库的一些工作原理.</p>    <p>归纳而言,Android中的注解大概有以下好处</p>    <ul>     <li>提高我们的开发效率</li>     <li>更早的发现程序的问题或者错误</li>     <li>更好的增加代码的描述能力</li>     <li>更加利于我们的一些规范约束</li>     <li>提供解决问题的更优解</li>    </ul>    <h2>准备工作</h2>    <p>默认情况下,Android中的注解包并没有包括在framework中,它独立成一个单独的包,通常我们需要引入这个包.</p>    <pre>  <code class="language-java">dependencies {      compile 'com.android.support:support-annotations:22.2.0'  }</code></pre>    <p>但是如果我们已经引入了 appcompat 则没有必要再次引用 support-annotations ,因为 appcompat 默认包含了对其引用.</p>    <h2>替代枚举</h2>    <p>在最早的时候,当我们想要做一些值得限定实现枚举的效果,通常是</p>    <ul>     <li>定义几个常量用于限定</li>     <li>从上面的常量选取值进行使用</li>    </ul>    <p>一个比较描述上面问题的示例代码如下</p>    <pre>  <code class="language-java">public static final int COLOR_RED = 0;  public static final int COLOR_GREEN = 1;  public static final int COLOR_YELLOW = 2;    public void setColor(int color) {      //some code here  }  //调用  setColor(COLOR_RED)  </code></pre>    <p>然而上面的还是有不尽完美的地方</p>    <ul>     <li>setColor(COLOR_RED) 与 setColor(0) 效果一样,而后者可读性很差,但却可以正常运行</li>     <li>setColor方法可以接受枚举之外的值,比如 setColor(3) ,这种情况下程序可能出问题</li>    </ul>    <p>一个相对较优的解决方法就是使用Java中的Enum.使用枚举实现的效果如下</p>    <pre>  <code class="language-java">// ColorEnum.java  public enum ColorEmun {      RED,      GREEN,      YELLOW  }    public void setColorEnum(ColorEmun colorEnum) {      //some code here  }    setColorEnum(ColorEmun.GREEN);  </code></pre>    <p>然而Enum也并非最佳,Enum因为其相比方案一的常量来说,占用内存相对大很多而受到曾经被Google列为不建议使用,为此Google特意引入了一些相关的注解来替代枚举.</p>    <p>Android中新引入的替代枚举的注解有 IntDef 和 StringDef ,这里以 IntDef 做例子说明一下.</p>    <pre>  <code class="language-java">public class Colors {      @IntDef({RED, GREEN, YELLOW})      @Retention(RetentionPolicy.SOURCE)      public @interface LightColors{}        public static final int RED = 0;      public static final int GREEN = 1;      public static final int YELLOW = 2;  }  </code></pre>    <ul>     <li>声明必要的int常量</li>     <li>声明一个注解为LightColors</li>     <li>使用@IntDef修饰LightColors,参数设置为待枚举的集合</li>     <li>使用@Retention(RetentionPolicy.SOURCE)指定注解仅存在与源码中,不加入到class文件中</li>    </ul>    <h2>Null相关的注解</h2>    <p>和Null相关的注解有两个</p>    <pre>  <code class="language-java">@Nullable 注解的元素可以是Null  @NonNull 注解的元素不能是Null</code></pre>    <p>上面的两个可以修饰如下的元素</p>    <ul>     <li>成员属性</li>     <li>方法参数</li>     <li>方法的返回值</li>    </ul>    <pre>  <code class="language-java">@Nullable  private String obtainReferrerFromIntent(@NonNull Intent intent) {      return intent.getStringExtra("apps_referrer");  }  </code></pre>    <p>NonNull检测生效的条件</p>    <ul>     <li>显式传入null</li>     <li>在调用方法之前已经判断了参数为null时</li>    </ul>    <pre>  <code class="language-java">setReferrer(null);//提示警告    //不提示警告  String referrer = getIntent().getStringExtra("apps_referrer");  setReferrer(referrer);    //提示警告  String referrer = getIntent().getStringExtra("apps_referrer");  if (referrer == null) {      setReferrer(referrer);  }    private void setReferrer(@NonNull String referrer) {      //some code here  }  </code></pre>    <h2>区间范围注解</h2>    <p>Android中的IntRange和FloatRange是两个用来限定区间范围的注解,</p>    <pre>  <code class="language-java">float currentProgress;    public void setCurrentProgress(@FloatRange(from=0.0f, to=1.0f) float progress) {      currentProgress = progress;  }  </code></pre>    <p>如果我们传入非法的值,如下所示</p>    <pre>  <code class="language-java">setCurrentProgress(11);  </code></pre>    <p>就会得到这样的错误</p>    <pre>  <code class="language-java">Value must be >=0.0 and <= 1.0(was 11)  </code></pre>    <h2>长度以及数组大小限制</h2>    <p>限制字符串的长度</p>    <pre>  <code class="language-java">private void setKey(@Size(6) String key) {  }  </code></pre>    <p>限定数组集合的大小</p>    <pre>  <code class="language-java">private void setData(@Size(max = 1) String[] data) {  }  setData(new String[]{"b", "a"});//error occurs  </code></pre>    <p>限定特殊的数组长度,比如3的倍数</p>    <pre>  <code class="language-java">private void setItemData(@Size(multiple = 3) String[] data) {  }  </code></pre>    <h2>权限相关</h2>    <p>在Android中,有很多场景都需要使用权限,无论是Marshmallow之前还是之后的动态权限管理.都需要在manifest中进行声明,如果忘记了,则会导致程序崩溃. 好在有一个注解能辅助我们避免这个问题.使用RequiresPermission注解即可.</p>    <pre>  <code class="language-java">@RequiresPermission(Manifest.permission.SET_WALLPAPER)      public void changeWallpaper(Bitmap bitmap) throws IOException {  }  </code></pre>    <h2>资源注解</h2>    <p>在Android中几乎所有的资源都可以有对应的资源id.比如获取定义的字符串,我们可以通过下面的方法</p>    <pre>  <code class="language-java">public String getStringById(int stringResId) {      return getResources().getString(stringResId);  }  </code></pre>    <p>使用这个方法,我们可以很容易的获取到定义的字符串,但是这样的写法也存在着风险.</p>    <pre>  <code class="language-java"> getStringById(R.mipmap.ic_launcher)  </code></pre>    <p>如果我们在不知情或者疏忽情况下,传入这样的值,就会出现问题. 但是如果我们使用资源相关的注解修饰了参数,就能很大程度上避免错误的情况.</p>    <pre>  <code class="language-java">public String getStringById(@StringRes  int stringResId) {      return getResources().getString(stringResId);  }  </code></pre>    <p>在Android中资源注解如下所示</p>    <ul>     <li>AnimRes</li>     <li>AnimatorRes</li>     <li>AnyRes</li>     <li>ArrayRes</li>     <li>AttrRes</li>     <li>BoolRes</li>     <li>ColorRes</li>     <li>DimenRes</li>     <li>DrawableRes</li>     <li>FractionRes</li>     <li>IdRes</li>     <li>IntegerRes</li>     <li>InterpolatorRes</li>     <li>LayoutRes</li>     <li>MenuRes</li>     <li>PluralsRes</li>     <li>RawRes</li>     <li>StringRes</li>     <li>StyleRes</li>     <li>StyleableRes</li>     <li>TransitionRes</li>     <li>XmlRes</li>    </ul>    <h2>Color值限定</h2>    <p>上面部分提到了 ColorRes ,用来限定颜色资源id,这里我们将使用 ColorInt ,一个用来限定Color值的注解. 在较早的TextView的setTextColor是这样实现的.</p>    <pre>  <code class="language-java">public void setTextColor(int color) {      mTextColor = ColorStateList.valueOf(color);      updateTextColors();  }  </code></pre>    <p>然而上面的方法在调用时常常会出现这种情况</p>    <pre>  <code class="language-java">myTextView.setTextColor(R.color.colorAccent);  </code></pre>    <p>如上,如果传递过去的参数为color的资源id就会出现颜色取错误的问题,这个问题在过去还是比较严重的.好在 ColorInt 出现了,改变了这一问题.</p>    <pre>  <code class="language-java">public void setTextColor(@ColorInt int color) {      mTextColor = ColorStateList.valueOf(color);      updateTextColors();  }  </code></pre>    <p>当我们再次传入Color资源值时,就会得到错误的提示.</p>    <h2>CheckResult</h2>    <p>这是一个关于返回结果的注解,用来注解方法,如果一个方法得到了结果,却没有使用这个结果,就会有错误出现,一旦出现这种错误,就说明你没有正确使用该方法。</p>    <pre>  <code class="language-java">@CheckResult  public String trim(String s) {      return s.trim();  }  </code></pre>    <h2>线程相关</h2>    <p>Android中提供了四个与线程相关的注解</p>    <ul>     <li>@UiThread,通常可以等同于主线程,标注方法需要在UIThread执行,比如View类就使用这个注解</li>     <li>@MainThread 主线程,经常启动后创建的第一个线程</li>     <li>@WorkerThread 工作者线程,一般为一些后台的线程,比如AsyncTask里面的doInBackground就是这样的.</li>     <li>@BinderThread 注解方法必须要在BinderThread线程中执行,一般使用较少.</li>    </ul>    <p>一些示例</p>    <pre>  <code class="language-java">new AsyncTask<Void, Void, Void>() {          //doInBackground is already annotated with @WorkerThread          @Override          protected Void doInBackground(Void... params) {              return null;              updateViews();//error          }      };    @UiThread  public void updateViews() {      Log.i(LOGTAG, "updateViews ThreadInfo=" + Thread.currentThread());  }  </code></pre>    <p>注意,这种情况下不会出现错误提示</p>    <pre>  <code class="language-java">new Thread(){      @Override      public void run() {          super.run();          updateViews();      }  }.start();  </code></pre>    <p>虽然updateViews会在一个新的工作者线程中执行,但是在compile时没有错误提示.</p>    <p>因为它的判断依据是,如果updateView的线程注解(这里为@UiThread)和run(没有线程注解)不一致才会错误提示.如果run方法没有线程注解,则不提示.</p>    <h2>CallSuper</h2>    <p>重写的方法必须要调用super方法</p>    <p>使用这个注解,我们可以强制方法在重写时必须调用父类的方法 比如Application的 onCreate , onConfigurationChanged 等.</p>    <h2>Keep</h2>    <p>在Android编译生成APK的环节,我们通常需要设置minifyEnabled为true实现下面的两个效果</p>    <ul>     <li>混淆代码</li>     <li>删除没有用的代码</li>    </ul>    <p>但是出于某一些目的,我们需要不混淆某部分代码或者不删除某处代码,除了配置复杂的Proguard文件之外,我们还可以使用@Keep注解 .</p>    <pre>  <code class="language-java">@Keep  public static int getBitmapWidth(Bitmap bitmap) {      return bitmap.getWidth();  }  </code></pre>    <h2>ButterKnife</h2>    <p>ButterKnife是一个用来绑定View,资源和回调的提高效率的工具.作者为Jake Wharton. ButterKnife的好处</p>    <ul>     <li>使用BindView替代繁琐的findViewById和类型转换</li>     <li>使用OnClick注解方法来替换显式声明的匿名内部类</li>     <li>使用BindString,BindBool,BindDrawable等注解实现资源获取</li>    </ul>    <p>一个摘自Github的示例</p>    <pre>  <code class="language-java">class ExampleActivity extends Activity {    @BindView(R.id.user) EditText username;    @BindView(R.id.pass) EditText password;      @BindString(R.string.login_error) String loginErrorMessage;      @OnClick(R.id.submit) void submit() {      // TODO call server...    }      @Override public void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      setContentView(R.layout.simple_activity);      ButterKnife.bind(this);      // TODO Use fields...    }  }  </code></pre>    <h3>ButterKnife工作原理</h3>    <p>以BindView注解使用为例,示例代码为</p>    <pre>  <code class="language-java">public class MainActivity extends AppCompatActivity {      @BindView(R.id.myTextView)      TextView myTextView;        @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);          ButterKnife.bind(this);      }  }  </code></pre>    <p>1.程序在compile时,会根据注解自动生成两个类,这里为 <strong>MainActivity_ViewBinder.class</strong> 和 <strong>MainActivity_ViewBinding.class</strong></p>    <p>2.当我们调用 ButterKnife.bind(this); 时,会查找当前类对应的ViewBinder类,并调用bind方法,这里会调用到 MainActiivty_ViewBinder.bind 方法.</p>    <p>3.MainActiivty_ViewBinder.bind方法实际上是调用了findViewById然后在进行类型转换,赋值给MainActivity的myTextView属性</p>    <p>ButterKnife的bind方法</p>    <pre>  <code class="language-java">public static Unbinder bind(@NonNull Activity target) {      return getViewBinder(target).bind(Finder.ACTIVITY, target, target);  }  </code></pre>    <p>ButterKnife的 getViewBinder 和 findViewBinderForClass</p>    <pre>  <code class="language-java">@NonNull @CheckResult @UiThread    static ViewBinder<Object> getViewBinder(@NonNull Object target) {      Class<?> targetClass = target.getClass();      if (debug) Log.d(TAG, "Looking up view binder for " + targetClass.getName());      return findViewBinderForClass(targetClass);    }      @NonNull @CheckResult @UiThread    private static ViewBinder<Object> findViewBinderForClass(Class<?> cls) {     //如果内存集合BINDERS中包含,则不再查找      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.") || clsName.startsWith("java.")) {        if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");        return NOP_VIEW_BINDER;      }      //noinspection TryWithIdenticalCatches Resolves to API 19+ only type.      try {        //使用反射创建实例        Class<?> viewBindingClass = Class.forName(clsName + "_ViewBinder");        //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());      } catch (InstantiationException e) {        throw new RuntimeException("Unable to create view binder for " + clsName, e);      } catch (IllegalAccessException e) {        throw new RuntimeException("Unable to create view binder for " + clsName, e);      }      //加入内存集合,便于后续的查找      BINDERS.put(cls, viewBinder);      return viewBinder;    }  </code></pre>    <p>MainActivity_ViewBinder的反编译源码</p>    <pre>  <code class="language-java">➜  androidannotationsample javap -c MainActivity_ViewBinder  Warning: Binary file MainActivity_ViewBinder contains com.example.admin.androidannotationsample.MainActivity_ViewBinder  Compiled from "MainActivity_ViewBinder.java"  public final class com.example.admin.androidannotationsample.MainActivity_ViewBinder implements butterknife.internal.ViewBinder<com.example.admin.androidannotationsample.MainActivity> {    public com.example.admin.androidannotationsample.MainActivity_ViewBinder();      Code:         0: aload_0         1: invokespecial #1                  // Method java/lang/Object."<init>":()V         4: return      public butterknife.Unbinder bind(butterknife.internal.Finder, com.example.admin.androidannotationsample.MainActivity, java.lang.Object);      Code:         0: new           #2                  // class com/example/admin/androidannotationsample/MainActivity_ViewBinding         3: dup         4: aload_2         5: aload_1         6: aload_3                           // 创建ViewBinding实例         7: invokespecial #3                  // Method com/example/admin/androidannotationsample/MainActivity_ViewBinding."<init>":(Lcom/example/admin/androidannotationsample/MainActivity;Lbutterknife/internal/Finder;Ljava/lang/Object;)V        10: areturn      public butterknife.Unbinder bind(butterknife.internal.Finder, java.lang.Object, java.lang.Object);      Code:         0: aload_0         1: aload_1         2: aload_2         3: checkcast     #4                  // class com/example/admin/androidannotationsample/MainActivity         6: aload_3                           //调用上面的重载方法         7: invokevirtual #5                  // Method bind:(Lbutterknife/internal/Finder;Lcom/example/admin/androidannotationsample/MainActivity;Ljava/lang/Object;)Lbutterknife/Unbinder;        10: areturn  }  </code></pre>    <p>MainActivity_ViewBinding的反编译源码</p>    <pre>  <code class="language-java">➜  androidannotationsample javap -c MainActivity_ViewBinding  Warning: Binary file MainActivity_ViewBinding contains com.example.admin.androidannotationsample.MainActivity_ViewBinding  Compiled from "MainActivity_ViewBinding.java"  public class com.example.admin.androidannotationsample.MainActivity_ViewBinding<T extends com.example.admin.androidannotationsample.MainActivity> implements butterknife.Unbinder {    protected T target;      public com.example.admin.androidannotationsample.MainActivity_ViewBinding(T, butterknife.internal.Finder, java.lang.Object);      Code:         0: aload_0         1: invokespecial #1                  // Method java/lang/Object."<init>":()V         4: aload_0         5: aload_1         6: putfield      #2                  // Field target:Lcom/example/admin/androidannotationsample/MainActivity;         9: aload_1        10: aload_2        11: aload_3                           //调用Finder.findRequireViewAsType找到View,并进行类型转换,并复制给MainActivity中对一个的变量        12: ldc           #4                  // int 2131427412        14: ldc           #5                  // String field 'myTextView'        16: ldc           #6                  // class android/widget/TextView                                              // 内部实际调用了findViewById        18: invokevirtual #7                  // Method butterknife/internal/Finder.findRequiredViewAsType:(Ljava/lang/Object;ILjava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;        21: checkcast     #6                  // class android/widget/TextView        24: putfield      #8                  // Field com/example/admin/androidannotationsample/MainActivity.myTextView:Landroid/widget/TextView;        27: return      public void unbind();      Code:         0: aload_0         1: getfield      #2                  // Field target:Lcom/example/admin/androidannotationsample/MainActivity;         4: astore_1         5: aload_1         6: ifnonnull     19         9: new           #9                  // class java/lang/IllegalStateException        12: dup        13: ldc           #10                 // String Bindings already cleared.        15: invokespecial #11                 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V        18: athrow        19: aload_1        20: aconst_null                       // 解除绑定,设置对应的变量为null        21: putfield      #8                  // Field com/example/admin/androidannotationsample/MainActivity.myTextView:Landroid/widget/TextView;        24: aload_0        25: aconst_null        26: putfield      #2                  // Field target:Lcom/example/admin/androidannotationsample/MainActivity;        29: return  }  </code></pre>    <p>Finder的源码</p>    <pre>  <code class="language-java">package butterknife.internal;    import android.app.Activity;  import android.app.Dialog;  import android.content.Context;  import android.support.annotation.IdRes;  import android.view.View;    @SuppressWarnings("UnusedDeclaration") // Used by generated code.  public enum Finder {    VIEW {      @Override public View findOptionalView(Object source, @IdRes int id) {        return ((View) source).findViewById(id);      }        @Override public Context getContext(Object source) {        return ((View) source).getContext();      }        @Override protected String getResourceEntryName(Object source, @IdRes int id) {        final View view = (View) source;        // In edit mode, getResourceEntryName() is unsupported due to use of BridgeResources        if (view.isInEditMode()) {          return "<unavailable while editing>";        }        return super.getResourceEntryName(source, id);      }    },    ACTIVITY {      @Override public View findOptionalView(Object source, @IdRes int id) {        return ((Activity) source).findViewById(id);      }        @Override public Context getContext(Object source) {        return (Activity) source;      }    },    DIALOG {      @Override public View findOptionalView(Object source, @IdRes int id) {        return ((Dialog) source).findViewById(id);      }        @Override public Context getContext(Object source) {        return ((Dialog) source).getContext();      }    };      //查找对应的Finder,如上面的ACTIVITY, DIALOG, VIEW    public abstract View findOptionalView(Object source, @IdRes int id);        public final <T> T findOptionalViewAsType(Object source, @IdRes int id, String who,        Class<T> cls) {      View view = findOptionalView(source, id);      return castView(view, id, who, cls);    }      public final View findRequiredView(Object source, @IdRes int id, String who) {      View view = findOptionalView(source, id);      if (view != null) {        return view;      }      String name = getResourceEntryName(source, id);      throw new IllegalStateException("Required view '"          + name          + "' with ID "          + id          + " for "          + who          + " was not found. If this view is optional add '@Nullable' (fields) or '@Optional'"          + " (methods) annotation.");    }      //来自ViewBinding的调用    public final <T> T findRequiredViewAsType(Object source, @IdRes int id, String who,        Class<T> cls) {      View view = findRequiredView(source, id, who);      return castView(view, id, who, cls);    }      public final <T> T castView(View view, @IdRes int id, String who, Class<T> cls) {      try {        return cls.cast(view);      } catch (ClassCastException e) {        String name = getResourceEntryName(view, id);        throw new IllegalStateException("View '"            + name            + "' with ID "            + id            + " for "            + who            + " was of the wrong type. See cause for more info.", e);      }    }      @SuppressWarnings("unchecked") // That's the point.    public final <T> T castParam(Object value, String from, int fromPos, String to, int toPos) {      try {        return (T) value;      } catch (ClassCastException e) {        throw new IllegalStateException("Parameter #"            + (fromPos + 1)            + " of method '"            + from            + "' was of the wrong type for parameter #"            + (toPos + 1)            + " of method '"            + to            + "'. See cause for more info.", e);      }    }      protected String getResourceEntryName(Object source, @IdRes int id) {      return getContext(source).getResources().getResourceEntryName(id);    }      public abstract Context getContext(Object source);  }  </code></pre>    <h2>Otto</h2>    <p>Otto Bus 是一个专为Android改装的Event Bus,在很多项目中都有应用.由Square开源共享.</p>    <pre>  <code class="language-java">public class EventBusTest {      private static final String LOGTAG = "EventBusTest";      Bus mBus  = new Bus();        public void test() {          mBus.register(this);      }        class NetworkChangedEvent {        }        @Produce      public NetworkChangedEvent sendNetworkChangedEvent() {          return new NetworkChangedEvent();      }          @Subscribe      public void onNetworkChanged(NetworkChangedEvent event) {          Log.i(LOGTAG, "onNetworkChanged event=" + event);      }  }  </code></pre>    <h2>Otto 的工作原理</h2>    <ul>     <li>使用@Produce和@Subscribe标记方法</li>     <li>当调用bus.register方法,去检索注册对象的标记方法,并cache映射关系</li>     <li>当post事件时,将事件与handler方法对应加入事件队列</li>     <li>抽取事件队列,然后调用handler处理</li>    </ul>    <p>如下为对Otto如何利用注解的分析</p>    <p>register的源码</p>    <pre>  <code class="language-java">public void register(Object object) {      if (object == null) {        throw new NullPointerException("Object to register must not be null.");      }      enforcer.enforce(this);      //查找object中的Subscriber      Map<Class<?>, Set<EventHandler>> foundHandlersMap = handlerFinder.findAllSubscribers(object);      for (Class<?> type : foundHandlersMap.keySet()) {        Set<EventHandler> handlers = handlersByType.get(type);        if (handlers == null) {          //concurrent put if absent          Set<EventHandler> handlersCreation = new CopyOnWriteArraySet<EventHandler>();          handlers = handlersByType.putIfAbsent(type, handlersCreation);          if (handlers == null) {              handlers = handlersCreation;          }        }        final Set<EventHandler> foundHandlers = foundHandlersMap.get(type);        if (!handlers.addAll(foundHandlers)) {          throw new IllegalArgumentException("Object already registered.");        }      }        for (Map.Entry<Class<?>, Set<EventHandler>> entry : foundHandlersMap.entrySet()) {        Class<?> type = entry.getKey();        EventProducer producer = producersByType.get(type);        if (producer != null && producer.isValid()) {          Set<EventHandler> foundHandlers = entry.getValue();          for (EventHandler foundHandler : foundHandlers) {            if (!producer.isValid()) {              break;            }            if (foundHandler.isValid()) {              dispatchProducerResultToHandler(foundHandler, producer);            }          }        }      }    }  </code></pre>    <p>HandlerFinder源码</p>    <pre>  <code class="language-java">interface HandlerFinder {      Map<Class<?>, EventProducer> findAllProducers(Object listener);      Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener);      //Otto注解查找器    HandlerFinder ANNOTATED = new HandlerFinder() {      @Override      public Map<Class<?>, EventProducer> findAllProducers(Object listener) {        return AnnotatedHandlerFinder.findAllProducers(listener);      }        @Override      public Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener) {        return AnnotatedHandlerFinder.findAllSubscribers(listener);      }    };  </code></pre>    <p>具体查找实现</p>    <pre>  <code class="language-java">/** This implementation finds all methods marked with a {@link Subscribe} annotation. */    static Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener) {      Class<?> listenerClass = listener.getClass();      Map<Class<?>, Set<EventHandler>> handlersInMethod = new HashMap<Class<?>, Set<EventHandler>>();        Map<Class<?>, Set<Method>> methods = SUBSCRIBERS_CACHE.get(listenerClass);      if (null == methods) {        methods = new HashMap<Class<?>, Set<Method>>();        loadAnnotatedSubscriberMethods(listenerClass, methods);      }      if (!methods.isEmpty()) {        for (Map.Entry<Class<?>, Set<Method>> e : methods.entrySet()) {          Set<EventHandler> handlers = new HashSet<EventHandler>();          for (Method m : e.getValue()) {            handlers.add(new EventHandler(listener, m));          }          handlersInMethod.put(e.getKey(), handlers);        }      }        return handlersInMethod;    }  </code></pre>    <p>以上就是关于Android中注解的一些总结,文章部分内容参考自 <a href="/misc/goto?guid=4958965626898206615" rel="nofollow,noindex">Support Annotations</a> ,希望能帮助大家对注解有基础的认识,并运用到实际的日常开发之中.</p>    <p> </p>    <p>来自:http://droidyue.com/blog/2016/08/14/android-annnotation/</p>    <p> </p>    
 本文由用户 aele0878 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
 转载本站原创文章,请注明出处,并保留原始链接、图片水印。
 本站是一个以用户分享为主的开源技术平台,欢迎各类分享!
 本文地址:https://www.open-open.com/lib/view/open1471219143607.html
Android Android开发 移动开发 butterknife