Android IOC 註解技術

手寫IOC 註解框架,由於代碼中有詳細註釋,這裏就基本只上代碼。

1. 先寫一個setContentView()、findViewById()、onClick()事件註解:

 1 //----------setContentView()註解----------
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 @Target(ElementType.TYPE) //將註解寫到類上面
 9 @Retention(RetentionPolicy.RUNTIME) //程序運行時執行
10 public @interface ContentView {
11     int value() default -1;
12 }
13 
14 //----------findViewById()註解----------
15 
16 import java.lang.annotation.ElementType;
17 import java.lang.annotation.Retention;
18 import java.lang.annotation.RetentionPolicy;
19 import java.lang.annotation.Target;
20 
21 @Target(ElementType.FIELD)
22 @Retention(RetentionPolicy.RUNTIME)
23 public @interface ViewInject {
24     int value() default -1;
25 }
26 
27 //----------onClick()事件註解----------
28 
29 import java.lang.annotation.ElementType;
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 import java.lang.annotation.Target;
33 
34 /**
35  * 該註解在另一個註解上面使用
36  */
37 @Target(ElementType.ANNOTATION_TYPE)
38 @Retention(RetentionPolicy.RUNTIME)
39 public @interface EventBase {
40 
41     // 1.setOnClickListener 訂閱
42     String listenerSetter();
43 
44     // 2.new.View.OnClickListener() 事件
45     Class<?> listenerType();
46 
47     // 3.onClick(View v) 事件處理
48     String callbackMethod();
49 }
50 
51 import android.view.View;
52 
53 import java.lang.annotation.ElementType;
54 import java.lang.annotation.Retention;
55 import java.lang.annotation.RetentionPolicy;
56 import java.lang.annotation.Target;
57 
58 @Target(ElementType.METHOD)
59 @Retention(RetentionPolicy.RUNTIME)
60 @EventBase(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class, callbackMethod = "onClick")
61 public @interface OnClick {
62     int[] value() default -1;
63 }

 

2.定義一個公共類 InjectUtils.java 這個類相當也是第三方,需要在BaseActivity中聲明。

import com.sprsoft.iocframe.annotation.ContentView;
import com.sprsoft.iocframe.annotation.EventBase;
import com.sprsoft.iocframe.annotation.ViewInject;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 這就是第三方
 */
public class InjectUtils {

    public static void inject(Object context) {
        //注入的功能在這裏完成
        injectLayout(context);
        injectView(context);
        injectClick(context);
    }

    /**
     * 佈局
     *
     * @param context
     */
    private static void injectLayout(Object context) {
        Class<?> clazz = context.getClass();
        ContentView contentView = clazz.getAnnotation(ContentView.class);
        if (contentView != null) {
            int layoutID = contentView.value();
            //反射執行setContentView(this);
            try {
                Method method = context.getClass().getMethod("setContentView", int.class);
                method.invoke(context, layoutID);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 控件注入
     *
     * @param context
     */
    private static void injectView(Object context) {
        Class<?> aClass = context.getClass();
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            if (viewInject != null) {
                int valueId = viewInject.value();
                try {
                    Method method = aClass.getMethod("findViewById", int.class);
                    View view = (View) method.invoke(context, valueId);
                    field.setAccessible(true);
                    field.set(context, view);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 事件
     * 涉及事件三要素:事件源、事件、事件處理
     *
     * @param context
     */
    private static void injectClick(Object context) {
        Class<?> clazz = context.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            //這就寫死了
            //OnClick annotation = method.getAnnotation(OnClick.class);
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                //得到OnClick上一級的EventBase
                Class<?> annotionClass = annotation.annotationType();
                EventBase eventBase = annotionClass.getAnnotation(EventBase.class);
                //如果沒有EventBase則當前方法不是一個事件處理程序
                if (eventBase == null) {
                    continue;
                }
                //否則是事件處理回調的函數,是的開始得到相關的事件要素
                //1.訂閱關係
                String listenerSetter = eventBase.listenerSetter();
                //2.事件
                Class<?> listenerType = eventBase.listenerType();
                //3.事件處理程序
                String callbackMethod = eventBase.callbackMethod();
                try {
                    //反射得到ID 根據ID得到相應VIEW
                    Method valueMethod = annotionClass.getDeclaredMethod("value");
                    int[] viewId = (int[]) valueMethod.invoke(annotation);
                    //對每一個ID進行事件綁定
                    for (int id : viewId) {
                        //找到ID對應的對象
                        Method findId = clazz.getMethod("findViewById", int.class);
                        View view = (View) findId.invoke(context, id);
                        if (view == null) {
                            continue;
                        }
                        //得到ID對應的View以後,開始在View上執行監聽
                        //用代理執行View.OnClickListener()方法
                        //需要執行activity上的OnClick方法
                        ListenerInvocationHandler listenerHandler = new ListenerInvocationHandler(context, method);
                        //去View.OnClickListener() == listenerType代理這個接口
                        Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, listenerHandler);
                        //執行方法
                        Method onClickMethod = view.getClass().getMethod(listenerSetter, listenerType);
                        onClickMethod.invoke(view, proxy);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

3.去實現一個事件代理類(ListenerInvocationHandler)

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 事件代理類
 * public void onClick(View v){}
 */
public class ListenerInvocationHandler implements InvocationHandler {
    private Object activity;
    private Method activityMethod;

    public ListenerInvocationHandler(Object activity, Method activityMethod) {
        this.activity = activity;
        this.activityMethod = activityMethod;
    }

    /**
     * 點擊按鈕後執行這個方法
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在這裏調用被註解的onClick方法
        return activityMethod.invoke(activity, args);
    }
}

  

4. BaseActivity.java中初始化

import android.content.Intent;
import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

import com.sprsoft.iocframe.utils.InjectUtils;

public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InjectUtils.inject(this);
    }

    /**
     * 根據傳入的Class
     *
     * @param pClass
     */
    public void openNewActivity(Class<?> pClass) {
        Intent _Intent = new Intent();
        _Intent.setClass(this, pClass);
        startActivity(_Intent);
    }
}

 5.實現上面的以後下來就開始調用了。

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.sprsoft.iocframe.annotation.ContentView;
import com.sprsoft.iocframe.annotation.OnClick;
import com.sprsoft.iocframe.annotation.OnLongClick;
import com.sprsoft.iocframe.annotation.ViewInject;
import com.sprsoft.practical.R;
import com.sprsoft.practical.base.BaseActivity;

/**
 * IOC 注入框架設計(註解)
 */
@ContentView(R.layout.activity_ioc)
public class IOCActivity extends BaseActivity {

    @ViewInject(R.id.edt_old_password)
    EditText edtOldPassword;

    @ViewInject(R.id.edt_new_password)
    EditText edtNewPassword;

    @ViewInject(R.id.edt_reset_password)
    EditText edtResetPassword;

    @ViewInject(R.id.btn_password_save)
    Button btnPasswordSave;

    @ViewInject(R.id.btn_password_cancel)
    Button btnPasswordCancel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Toast.makeText(this, btnPasswordSave.getText().toString(), Toast.LENGTH_SHORT).show();
    }

    @OnClick({R.id.btn_password_save, R.id.btn_password_cancel})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_password_cancel:
                Toast.makeText(this, "取消成功", Toast.LENGTH_SHORT).show();
                break;
            case R.id.btn_password_save:
                Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show();
                break;
        }
    }

    @OnLongClick({R.id.btn_password_save, R.id.btn_password_cancel})
    public boolean onLongClick(View view) {
        switch (view.getId()) {
            case R.id.btn_password_cancel:
                Toast.makeText(this, "長按取消成功", Toast.LENGTH_SHORT).show();
                break;
            case R.id.btn_password_save:
                Toast.makeText(this, "長按保存成功", Toast.LENGTH_SHORT).show();
                break;
        }
        return true;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章