深入分析安卓(Android)中的注解
歸納而言,Android中的注解大概有以下好處
1、提高我們的開(kāi)發(fā)效率
2、更早的發(fā)現(xiàn)程序的問(wèn)題或者錯(cuò)誤
3、更好的增加代碼的描述能力
4、更加利于我們的一些規(guī)范約束
5、提供解決問(wèn)題的更優(yōu)解
準(zhǔn)備工作
默認(rèn)情況下,Android中的注解包并沒(méi)有包括在framework中,它獨(dú)立成一個(gè)單獨(dú)的包,通常我們需要引入這個(gè)包.
dependencies { compile 'com.android.support:support-annotations:22.2.0' }
但是如果我們已經(jīng)引入了 appcompat 則沒(méi)有必要再次引用 support-annotations ,因?yàn)?appcompat 默認(rèn)包含了對(duì)其引用.
替代枚舉
在最早的時(shí)候,當(dāng)我們想要做一些值得限定實(shí)現(xiàn)枚舉的效果,通常是
1、定義幾個(gè)常量用于限定
2、從上面的常量選取值進(jìn)行使用
一個(gè)比較描述上面問(wèn)題的示例代碼如下
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 } //調(diào)用 setColor(COLOR_RED)
然而上面的還是有不盡完美的地方
1、setColor(COLOR_RED) 與 setColor(0) 效果一樣,而后者可讀性很差,但卻可以正常運(yùn)行
2、setColor方法可以接受枚舉之外的值,比如 setColor(3) ,這種情況下程序可能出問(wèn)題
一個(gè)相對(duì)較優(yōu)的解決方法就是使用Java中的Enum.使用枚舉實(shí)現(xiàn)的效果如下
// ColorEnum.java public enum ColorEmun { RED, GREEN, YELLOW } public void setColorEnum(ColorEmun colorEnum) { //some code here } setColorEnum(ColorEmun.GREEN);
然而Enum也并非最佳,Enum因?yàn)槠湎啾确桨敢坏某A縼?lái)說(shuō),占用內(nèi)存相對(duì)大很多而受到曾經(jīng)被Google列為不建議使用,為此Google特意引入了一些相關(guān)的注解來(lái)替代枚舉.
Android中新引入的替代枚舉的注解有 IntDef 和 StringDef ,這里以 IntDef 做例子說(shuō)明一下.
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; }
1、聲明必要的int常量
2、聲明一個(gè)注解為L(zhǎng)ightColors
3、使用@IntDef修飾LightColors,參數(shù)設(shè)置為待枚舉的集合
4、使用@Retention(RetentionPolicy.SOURCE)指定注解僅存在與源碼中,不加入到class文件中
Null相關(guān)的注解
和Null相關(guān)的注解有兩個(gè)
@Nullable 注解的元素可以是Null
@NonNull 注解的元素不能是Null
上面的兩個(gè)可以修飾如下的元素
1、成員屬性
2、方法參數(shù)
3、方法的返回值
@Nullable private String obtainReferrerFromIntent(@NonNull Intent intent) { return intent.getStringExtra("apps_referrer"); }
NonNull檢測(cè)生效的條件
1、顯式傳入null
2、在調(diào)用方法之前已經(jīng)判斷了參數(shù)為null時(shí)
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 }
區(qū)間范圍注解
Android中的IntRange和FloatRange是兩個(gè)用來(lái)限定區(qū)間范圍的注解,
float currentProgress; public void setCurrentProgress(@FloatRange(from=0.0f, to=1.0f) float progress) { currentProgress = progress; }
如果我們傳入非法的值,如下所示
setCurrentProgress(11);
就會(huì)得到這樣的錯(cuò)誤
Value must be >=0.0 and <= 1.0(was 11)
長(zhǎng)度以及數(shù)組大小限制
限制字符串的長(zhǎng)度
private void setKey(@Size(6) String key) { }
限定數(shù)組集合的大小
private void setData(@Size(max = 1) String[] data) { } setData(new String[]{"b", "a"});//error occurs
限定特殊的數(shù)組長(zhǎng)度,比如3的倍數(shù)
private void setItemData(@Size(multiple = 3) String[] data) { }
權(quán)限相關(guān)
在Android中,有很多場(chǎng)景都需要使用權(quán)限,無(wú)論是Marshmallow之前還是之后的動(dòng)態(tài)權(quán)限管理.都需要在manifest中進(jìn)行聲明,如果忘記了,則會(huì)導(dǎo)致程序崩潰. 好在有一個(gè)注解能輔助我們避免這個(gè)問(wèn)題.使用RequiresPermission注解即可.
@RequiresPermission(Manifest.permission.SET_WALLPAPER) public void changeWallpaper(Bitmap bitmap) throws IOException { }
資源注解
在Android中幾乎所有的資源都可以有對(duì)應(yīng)的資源id.比如獲取定義的字符串,我們可以通過(guò)下面的方法
public String getStringById(int stringResId) { return getResources().getString(stringResId); }
使用這個(gè)方法,我們可以很容易的獲取到定義的字符串,但是這樣的寫法也存在著風(fēng)險(xiǎn).
getStringById(R.mipmap.ic_launcher)
如果我們?cè)诓恢榛蛘呤韬銮闆r下,傳入這樣的值,就會(huì)出現(xiàn)問(wèn)題. 但是如果我們使用資源相關(guān)的注解修飾了參數(shù),就能很大程度上避免錯(cuò)誤的情況.
public String getStringById(@StringRes int stringResId) { return getResources().getString(stringResId); }
在Android中資源注解如下所示
AnimRes
AnimatorRes
AnyRes
ArrayRes
AttrRes
BoolRes
ColorRes
DimenRes
DrawableRes
FractionRes
IdRes
IntegerRes
InterpolatorRes
LayoutRes
MenuRes
PluralsRes
RawRes
StringRes
StyleRes
StyleableRes
TransitionRes
XmlRes
Color值限定
上面部分提到了 ColorRes ,用來(lái)限定顏色資源id,這里我們將使用 ColorInt ,一個(gè)用來(lái)限定Color值的注解. 在較早的TextView的setTextColor是這樣實(shí)現(xiàn)的.
public void setTextColor(int color) { mTextColor = ColorStateList.valueOf(color); updateTextColors(); }
然而上面的方法在調(diào)用時(shí)常常會(huì)出現(xiàn)這種情況
myTextView.setTextColor(R.color.colorAccent);
如上,如果傳遞過(guò)去的參數(shù)為color的資源id就會(huì)出現(xiàn)顏色取錯(cuò)誤的問(wèn)題,這個(gè)問(wèn)題在過(guò)去還是比較嚴(yán)重的.好在 ColorInt 出現(xiàn)了,改變了這一問(wèn)題.
public void setTextColor(@ColorInt int color) { mTextColor = ColorStateList.valueOf(color); updateTextColors(); }
當(dāng)我們?cè)俅蝹魅隒olor資源值時(shí),就會(huì)得到錯(cuò)誤的提示.
CheckResult
這是一個(gè)關(guān)于返回結(jié)果的注解,用來(lái)注解方法,如果一個(gè)方法得到了結(jié)果,卻沒(méi)有使用這個(gè)結(jié)果,就會(huì)有錯(cuò)誤出現(xiàn),一旦出現(xiàn)這種錯(cuò)誤,就說(shuō)明你沒(méi)有正確使用該方法。
@CheckResult public String trim(String s) { return s.trim(); }
線程相關(guān)
Android中提供了四個(gè)與線程相關(guān)的注解
@UiThread,通??梢缘韧谥骶€程,標(biāo)注方法需要在UIThread執(zhí)行,比如View類就使用這個(gè)注解
@MainThread 主線程,經(jīng)常啟動(dòng)后創(chuàng)建的第一個(gè)線程
@WorkerThread 工作者線程,一般為一些后臺(tái)的線程,比如AsyncTask里面的doInBackground就是這樣的.
@BinderThread 注解方法必須要在BinderThread線程中執(zhí)行,一般使用較少.
一些示例
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()); }
注意,這種情況下不會(huì)出現(xiàn)錯(cuò)誤提示
new Thread(){ @Override public void run() { super.run(); updateViews(); } }.start();
雖然updateViews會(huì)在一個(gè)新的工作者線程中執(zhí)行,但是在compile時(shí)沒(méi)有錯(cuò)誤提示.
因?yàn)樗呐袛嘁罁?jù)是,如果updateView的線程注解(這里為@UiThread)和run(沒(méi)有線程注解)不一致才會(huì)錯(cuò)誤提示.如果run方法沒(méi)有線程注解,則不提示.
CallSuper
重寫的方法必須要調(diào)用super方法
使用這個(gè)注解,我們可以強(qiáng)制方法在重寫時(shí)必須調(diào)用父類的方法 比如Application的 onCreate , onConfigurationChanged 等.
Keep
在Android編譯生成APK的環(huán)節(jié),我們通常需要設(shè)置minifyEnabled為true實(shí)現(xiàn)下面的兩個(gè)效果
1、混淆代碼
2、刪除沒(méi)有用的代碼
但是出于某一些目的,我們需要不混淆某部分代碼或者不刪除某處代碼,除了配置復(fù)雜的Proguard文件之外,我們還可以使用@Keep注解 .
@Keep public static int getBitmapWidth(Bitmap bitmap) { return bitmap.getWidth(); }
ButterKnife
ButterKnife是一個(gè)用來(lái)綁定View,資源和回調(diào)的提高效率的工具.作者為Jake Wharton. ButterKnife的好處
1、使用BindView替代繁瑣的findViewById和類型轉(zhuǎn)換
2、使用OnClick注解方法來(lái)替換顯式聲明的匿名內(nèi)部類
3、使用BindString,BindBool,BindDrawable等注解實(shí)現(xiàn)資源獲取
一個(gè)摘自Github的示例
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... } }
ButterKnife工作原理
以BindView注解使用為例,示例代碼為
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); } }
1.程序在compile時(shí),會(huì)根據(jù)注解自動(dòng)生成兩個(gè)類,這里為 MainActivity_ViewBinder.class
和 MainActivity_ViewBinding.class
2.當(dāng)我們調(diào)用 ButterKnife.bind(this);
時(shí),會(huì)查找當(dāng)前類對(duì)應(yīng)的ViewBinder
類,并調(diào)用bind
方法,這里會(huì)調(diào)用到 MainActiivty_ViewBinder.bind
方法.
3.MainActiivty_ViewBinder.bind
方法實(shí)際上是調(diào)用了findViewById
然后在進(jìn)行類型轉(zhuǎn)換,賦值給MainActivity
的myTextView
屬性
ButterKnife的bind方法
public static Unbinder bind(@NonNull Activity target) { return getViewBinder(target).bind(Finder.ACTIVITY, target, target); }
ButterKnife的 getViewBinder 和 findViewBinderForClass
@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) { //如果內(nèi)存集合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 { //使用反射創(chuàng)建實(shí)例 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) { //如果沒(méi)有找到,對(duì)父類進(jìn)行查找 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); } //加入內(nèi)存集合,便于后續(xù)的查找 BINDERS.put(cls, viewBinder); return viewBinder; }
MainActivity_ViewBinder的反編譯源碼
➜ 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 // 創(chuàng)建ViewBinding實(shí)例 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 //調(diào)用上面的重載方法 7: invokevirtual #5 // Method bind:(Lbutterknife/internal/Finder;Lcom/example/admin/androidannotationsample/MainActivity;Ljava/lang/Object;)Lbutterknife/Unbinder; 10: areturn }
MainActivity_ViewBinding的反編譯源碼
➜ 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 //調(diào)用Finder.findRequireViewAsType找到View,并進(jìn)行類型轉(zhuǎn)換,并復(fù)制給MainActivity中對(duì)一個(gè)的變量 12: ldc #4 // int 2131427412 14: ldc #5 // String field 'myTextView' 16: ldc #6 // class android/widget/TextView // 內(nèi)部實(shí)際調(diào)用了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 // 解除綁定,設(shè)置對(duì)應(yīng)的變量為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 }
Finder的源碼
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(); } }; //查找對(duì)應(yīng)的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."); } //來(lái)自ViewBinding的調(diào)用 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); }
Otto
Otto Bus 是一個(gè)專為Android改裝的Event Bus,在很多項(xiàng)目中都有應(yīng)用.由Square開(kāi)源共享.
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); } }
Otto 的工作原理
1、使用@Produce和@Subscribe標(biāo)記方法
2、當(dāng)調(diào)用bus.register方法,去檢索注冊(cè)對(duì)象的標(biāo)記方法,并cache映射關(guān)系
3、當(dāng)post事件時(shí),將事件與handler方法對(duì)應(yīng)加入事件隊(duì)列
4、抽取事件隊(duì)列,然后調(diào)用handler處理
如下為對(duì)Otto如何利用注解的分析
register的源碼
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); } } } } }
HandlerFinder源碼
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); } };
具體查找實(shí)現(xiàn)
/** 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; }
總結(jié)
以上就是關(guān)于Android中注解的一些總結(jié),文章部分內(nèi)容參考自 Support Annotations ,希望能幫助大家對(duì)注解有基礎(chǔ)的認(rèn)識(shí),并運(yùn)用到實(shí)際的日常開(kāi)發(fā)之中。如有有疑問(wèn)歡迎大家留言討論。
- 自定義Android注解系列教程之注解變量
- Android基于注解的6.0權(quán)限動(dòng)態(tài)請(qǐng)求框架詳解
- Android 反射注解與動(dòng)態(tài)代理綜合使用詳解
- Android注解使用之ButterKnife 8.0詳解
- Android中封裝SDK時(shí)常用的注解總結(jié)
- Android AOP 注解詳解及簡(jiǎn)單使用實(shí)例(三)
- Android AOP之注解處理解釋器詳解(二)
- Android AOP注解Annotation詳解(一)
- Android注解框架對(duì)比分析
- Android注解ButterKnife的基本使用
- Android 中的注解詳細(xì)介紹
- Android 中的注解深入探究
- Android注解基礎(chǔ)介紹快速入門與解讀
相關(guān)文章
android 圖片操作(縮放移動(dòng)) 實(shí)例代碼
android 圖片操作(縮放移動(dòng)) 實(shí)例代碼,需要的朋友可以參考一下2013-06-06Android中Item實(shí)現(xiàn)點(diǎn)擊水波紋效果
這篇文章主要給大家介紹了關(guān)于Android中Item實(shí)現(xiàn)點(diǎn)擊水波紋效果的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11Matrix的set,pre,post調(diào)用順序詳解
下面小編就為大家?guī)?lái)一篇Matrix的set,pre,post調(diào)用順序詳解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04深入分析Android NFC技術(shù) android nfc開(kāi)發(fā)
本篇文章我們對(duì)android開(kāi)發(fā)中nfc技術(shù)做了全面的原理分析以及實(shí)現(xiàn)過(guò)程,需要的讀者們一起參考一下吧。2017-11-11Android 多種dialog的實(shí)現(xiàn)方法(推薦)
下面小編就為大家分享一篇Android 多種dialog的實(shí)現(xiàn)方法(推薦),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01