Android 中的注解詳細(xì)介紹
注解是我們經(jīng)常接觸的技術(shù),Java有注解,Android也有注解,本文將試圖介紹Android中的注解,以及ButterKnife和Otto這些基于注解的庫(kù)的一些工作原理.
歸納而言,Android中的注解大概有以下好處
- 提高我們的開發(fā)效率
- 更早的發(fā)現(xiàn)程序的問(wèn)題或者錯(cuò)誤
- 更好的增加代碼的描述能力
- 更加利于我們的一些規(guī)范約束
- 提供解決問(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)閍ppcompat默認(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)
然而上面的還是有不盡完美的地方
setColor(COLOR_RED)與setColor(0)效果一樣,而后者可讀性很差,但卻可以正常運(yùn)行
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; }
- 聲明必要的int常量
- 聲明一個(gè)注解為L(zhǎng)ightColors
- 使用@IntDef修飾LightColors,參數(shù)設(shè)置為待枚舉的集合
- 使用@Retention(RetentionPolicy.SOURCE)指定注解僅存在與源碼中,不加入到class文件中
Null相關(guān)的注解
和Null相關(guān)的注解有兩個(gè)
@Nullable 注解的元素可以是Null
@NonNull 注解的元素不能是Null
上面的兩個(gè)可以修飾如下的元素
成員屬性
方法參數(shù)
方法的返回值
@Nullable private String obtainReferrerFromIntent(@NonNull Intent intent) { return intent.getStringExtra("apps_referrer"); }
NonNull檢測(cè)生效的條件
顯式傳入null
在調(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è)效果
混淆代碼
刪除沒(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的好處
- 使用BindView替代繁瑣的findViewById和類型轉(zhuǎn)換
- 使用OnClick注解方法來(lái)替換顯式聲明的匿名內(nèi)部類
- 使用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開源共享.
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 的工作原理
- 使用@Produce和@Subscribe標(biāo)記方法
- 當(dāng)調(diào)用bus.register方法,去檢索注冊(cè)對(duì)象的標(biāo)記方法,并cache映射關(guān)系
- 當(dāng)post事件時(shí),將事件與handler方法對(duì)應(yīng)加入事件隊(duì)列
- 抽取事件隊(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; }
以上就是關(guān)于Android中注解的一些總結(jié),文章部分內(nèi)容參考自 Support Annotations ,希望能幫助大家對(duì)注解有基礎(chǔ)的認(rèn)識(shí),并運(yùn)用到實(shí)際的日常開發(fā)之中.
以上就對(duì)Android 注解資料的整理,后續(xù)繼續(xù)補(bǔ)充,謝謝大家對(duì)本站的支持!
相關(guān)文章
Android Studio無(wú)法執(zhí)行Java類的main方法問(wèn)題及解決方法
這篇文章主要介紹了Android Studio無(wú)法執(zhí)行Java main方法的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03Android倒計(jì)時(shí)功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android倒計(jì)時(shí)功能的實(shí)現(xiàn)代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12Android啟動(dòng)頁(yè)出現(xiàn)白屏、黑屏的解決方案
這篇文章主要給大家介紹了關(guān)于Android啟動(dòng)頁(yè)出現(xiàn)白屏、黑屏的解決方案,這一個(gè)需求是每位Android開發(fā)者都需要的,最近發(fā)現(xiàn)了一個(gè)不錯(cuò)的解決方法,所以分享給大家,文中給出了詳細(xì)的介紹,需要的朋友可以參考下。2017-12-12Android開發(fā) -- 控件的顯示與隱藏 setVisibility View.VISIBLE View.INVISI
本文簡(jiǎn)單介紹在Android開發(fā)中控件的顯示與隱藏幾種常見(jiàn)的屬性,給大家一個(gè)參考,希望對(duì)大家學(xué)習(xí)有所幫助。2016-06-06Android自定義SeekBar滑動(dòng)顯示數(shù)字
這篇文章主要為大家詳細(xì)介紹了Android自定義SeekBar滑動(dòng)顯示數(shù)字,使用FrameLayout結(jié)合SeekBar滑動(dòng)時(shí),數(shù)值顯示,滑動(dòng)停止時(shí)顯示數(shù)字,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09Android開發(fā)手冊(cè)Button按鈕實(shí)現(xiàn)點(diǎn)擊音效
這篇文章主要為大家介紹了Android開發(fā)手冊(cè)Button按鈕實(shí)現(xiàn)點(diǎn)擊音效示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06分析Android多主題顏色的相關(guān)問(wèn)題
這篇文章總結(jié)了在Android開發(fā)多主題顏色的時(shí)候會(huì)遇到的一些問(wèn)題,然后給出解決方案,讓大家可以解決問(wèn)題,有需要的下面一起來(lái)看看吧。2016-08-08Android頁(yè)面之間進(jìn)行數(shù)據(jù)回傳的方法分析
這篇文章主要介紹了Android頁(yè)面之間進(jìn)行數(shù)據(jù)回傳的方法,結(jié)合實(shí)例形式分析了Android頁(yè)面之間進(jìn)行數(shù)據(jù)的傳遞與處理技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06解決Eclipse啟動(dòng)出錯(cuò):Failed to create the Java Virtual Machine
這篇文章主要介紹了解決Eclipse啟動(dòng)出錯(cuò):Failed to create the Java Virtual Machine的相關(guān)資料,這里說(shuō)明出錯(cuò)原因及查找錯(cuò)誤和解決辦法,需要的朋友可以參考下2017-07-07