天天看點

【Android架構進階〖00〗】ThinkAndroid注解機制

由于項目需要,開始研究ThinkAndroid。

個人認為該架構的注解機制十分新穎,是以先研究這個,順便學習下 Java 的annotation。

粗略的看了看,該機制在BaseActivity中初始化。而BaseActivity是所有Activity的基類。

對BaseActivity進行了代碼剖離,發現在BaseActivity中在onCreate函數裡啟動注解機制。

  • 首先注入布局資源(綁定layout布局)
  • 其次注入成員資源(綁定元件資源)
  • 然後注入成員變量(初始化普通變量)

暫時先搞清楚第二個,怎麼綁定元件資源的:

我寫了個小demo,MyAnno

  • 【Android架構進階〖00〗】ThinkAndroid注解機制
    【Android架構進階〖00〗】ThinkAndroid注解機制

InjectView.java

package com.myanno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * 自定義注解,擷取資源ID
 * @使用方法
 * 				@InjectView(id = R.id.settingIv)
 * 				private ImageView imgSetting; 
 * 
 * @author 		[email protected]
 * @date		2013-10-29
 * @description @Retention: 定義注解的保留政策
 *				@Retention(RetentionPolicy.SOURCE) 	//注解僅存在于源碼中,在class位元組碼檔案中不包含
 *				@Retention(RetentionPolicy.CLASS)  	//預設的保留政策,注解會在class位元組碼檔案中存在,但運作時無法獲得,
 *				@Retention(RetentionPolicy.RUNTIME)	//注解會在class位元組碼檔案中存在,在運作時可以通過反射擷取到
 *				@Inherited							//說明子類可以繼承父類中的該注解
 *
 *				@Target(ElementType.TYPE)   		//接口、類、枚舉、注解
 *				@Target(ElementType.FIELD) 			//字段、枚舉的常量
 *				@Target(ElementType.METHOD) 		//方法
 * 				@Target(ElementType.PARAMETER) 		//方法參數
 *				@Target(ElementType.CONSTRUCTOR)  	//構造函數
 *				@Target(ElementType.LOCAL_VARIABLE)	//局部變量
 *				@Target(ElementType.ANNOTATION_TYPE)//注解
 *				@Target(ElementType.PACKAGE) 		//包   
 */

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView
{
	/** View的ID */
	public int id() default -1;
}

           

MainActivity.java

package com.myanno;

import java.lang.reflect.Field;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends Activity {

	/** 注解綁定UI元素 */
	@InjectView(id=R.id.myimgview)
	ImageView myimageview;
	
	@InjectView(id=R.id.mytext)
	TextView mytext;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		//初始化注解綁定的成員變量
		injectView(this);
		
		//直接使用UI元素
		mytext.setText("Text0");
		myimageview.setImageResource(R.drawable.junny);
	}
	
	/** 執行個體化@InjectView 注解的成員*/
	public void injectView(Activity activity)
	{
		Field[] fields = activity.getClass().getDeclaredFields();//得到Activity中的所有定義的字段
		if (fields != null && fields.length > 0)
		{
			for (Field field : fields)
			{
				if (field.isAnnotationPresent(InjectView.class))//方法傳回true,如果指定類型的注解存在于此元素上
				{
					Log.i("Field", field.toString());
					
					InjectView mInjectView = field.getAnnotation(InjectView.class);	//獲得該成員的annotation
					int viewId = mInjectView.id();	//獲得該注解的id
					View view=activity.findViewById(viewId);//獲得ID為viewID的元件對象
					
					Log.i("Field", String.valueOf(viewId));
					Log.i("Field", view.getClass().toString());
					
					try
					{
						field.setAccessible(true);//設定類的私有成員變量可以被通路
						field.set(activity, view);//field.set(object,value)===object.fieldValue = value
					} catch (Exception e) { e.printStackTrace();}
				}
				else
					Log.i("Field", "該字段沒有被注解");
			}
		}
	}
}
           

布局檔案 activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000"
    android:orientation="vertical"
    tools:ignore="ContentDescription" >
    <ImageView
        android:id="@+id/myimgview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>
    <TextView
        android:id="@+id/mytext"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center"
        android:background="@android:color/darker_gray"
        android:textSize="30sp" />
</LinearLayout>
           

實驗結果

  • 【Android架構進階〖00〗】ThinkAndroid注解機制

即注入成功了 

【Android架構進階〖00〗】ThinkAndroid注解機制

需要說下我遇到的問題,在以上的基礎上,如果将布局檔案裡的TextView 和ImageView兩個布局換個位置,這時候再運作下,會出現空指針異常。

将Log向上翻會發現一個警告

  • 【Android架構進階〖00〗】ThinkAndroid注解機制
    【Android架構進階〖00〗】ThinkAndroid注解機制

即非法參數異常

定位到這一行

field.set(activity, view);
           

通過觀察Log列印的日志

  • 【Android架構進階〖00〗】ThinkAndroid注解機制
    【Android架構進階〖00〗】ThinkAndroid注解機制

發現第一二行是對的,即獲得注解的類型和ID(ImageView 2131230720)都是正确的,但是通過findViewById擷取view的時候,Log第三行顯示的卻是TextView。

百思不得其解,最後無賴之下,清理一下項目,搞定。

我不知道是怎麼回事,暫且推測為資源緩存吧。