1. 程式人生 > >Android注入框架你應該知道的一切------打造自己的注入框架

Android注入框架你應該知道的一切------打造自己的注入框架

前言

Java的所有框架基本都是基於反射的,所以有句話是這麼說的,無反射,無框架。所以Android的注入框架也是基於反射的,接下來就簡單的介紹一下Android的注入框架你應該知道的一切。

註解簡介

註解(Annotation)在Java裡面是比較重要的一部分,但是通常很少接觸到這一部分,這裡就簡單的過一下。現在我們簡單的寫一個註解然後解釋它。
通過Eclipse右鍵->New->Annotation然後敲入下面的程式碼。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface
TestAnnotation {
int vauls(); String test(); }

可以看到Target這個標註我們定義為FIELD就是類裡面的屬性的意思,Retention這個標註是表示是執行時的註解。每個註解的意思大家可以收一下自己看看。然後看看我們怎麼使用這個註解。我們隨便在某一個類裡面宣告一個物件。如下

    @TestAnnotation(test="hello",vauls=12)
    private Button button3;

這樣就宣告好了我們的註解。然後就是註解的使用。也簡單的看一下怎麼使用的。

    Class<?> clas = getClass();
        //獲取屬性
Field fields[] = clas.getDeclaredFields(); for (Field field : fields) { //獲取註解 TestAnnotation testAnnotation = field.getAnnotation(TestAnnotation.class); if (testAnnotation != null) { //得到註解裡面的值 String test = testAnnotation.test(); int
id = testAnnotation.vauls(); System.out.println(test + id); } }

就這樣簡單的使用,如果需要深入的理解註解可以在查一下註解的資料。

關於注入框架你應該知道的一切

打造自己的注入框架

首先說一下我們這次要實現怎麼樣的一個東西,注入View和注入Onclick事件,首先我們先解決注入View的問題.

View的注入

首先我們還是新建一個註解,敲入以下程式碼。

package com.edsheng.inject;

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

/***
 * Copyright (c) 2015, TNT All Rights Reserved.
 * View註解在宣告VIew控制元件的時候進行註解就行了
 * @author bobo
 * @date 2015-6-9
 * @filename ViewInject.java
 * 
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject
{
    int value();
}

然後我們在新建一個ViewInjectUtile類,在裡面實現這樣的一個方法。

/***
     * 注入控制元件View 
     * 簡單說一下注入控制元件的流程 
     * 1:根據Filed遍歷所有的Filed 
     * 2:得到我們需要的註解
     * 3:根據註解拿到id
     * 4:通過反射去呼叫找查方法 
     * 5:最後通過反射賦值
     * 
     * @param activity
     */
    private static void injectView(Activity activity)
    {
        Class<?> cls = activity.getClass();
        Field field[] = cls.getDeclaredFields();// 獲取所有的filed
        for (Field field2 : field)
        {
            ViewInject inject = field2.getAnnotation(ViewInject.class);// 獲取註解
            if (inject != null)
            {
                int id = inject.value(); // 得到id
                try
                {
                    // findViewById
                    Method method = cls.getMethod("findViewById", int.class);
                    Object resView = method.invoke(activity, id);// 得到控制元件
                    field2.setAccessible(true);
                    field2.set(activity, resView);// 賦值給View
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }

            }
        }

    }

註釋都寫的很清楚我就不解釋了,這樣就很簡單的實現了View的注入,使用也很簡單。

事件的注入

我們還是新建一個註解來完成我們的事件注入,敲入以下程式碼。

/***
 * 
 * Copyright (c) 2015, TNT All Rights Reserved.
 * 方法的註解類在需要回調OnlickLisenler的時候進行註解就行了
 * 
 * @author bobo
 * @date 2015-6-9
 * @filename MethodInject.java
 * 
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodInject
{
    int[] value();
}

然後在ViewInjectUtile這個類裡面實現這樣一個方法。

/**
     * 注入監聽的方法 所有的框架基本都是基於反射來實現的,不是有一句話麼?無反射無框架。 
     * 簡單的說一下這個流程 
     * 1:在我們的acitvity裡面注入方法
     * 2:生成動態代理 
     * 3:通過東動態代理去回撥我們注入的方法
     * 
     * @param activity
     */
    private static void injectMethod(Activity activity)
    {
        Class<?> cls = activity.getClass();
        Method methods[] = cls.getMethods();// 獲取這個類的public方法
        for (Method method : methods)
        {
            MethodInject meathdInject = method.getAnnotation(MethodInject.class); // 獲取方法上的註解
            if (meathdInject != null)
            {// 當有註解的時候生成動態代理
                Object proxy = (Object) Proxy.newProxyInstance(View.OnClickListener.class.getClassLoader(), new Class<?>[] { View.OnClickListener.class }, new DynaHanlder(activity, method));
                int ids[] = meathdInject.value();// 獲取註解裡面的id
                try
                {

                    Method findviewbyid = cls.getMethod("findViewById", int.class);// 得到方法
                    for (int id : ids)
                    {
                        Object view = findviewbyid.invoke(activity, id);// 根據方法獲取view
                        Method onclickMethod = view.getClass().getMethod("setOnClickListener", View.OnClickListener.class);
                        onclickMethod.invoke(view, proxy);// 呼叫setOnClickListener方法回撥在動態類裡面
                    }
                }

                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }

        }
    }

這裡需要注意的就是動態類的生成和代理,我們把View.OnClickListener這個的介面通過代理和反射來回調給註解的地方,我們來看看這個DynaHanlder怎麼實現的。

public static class DynaHanlder implements InvocationHandler
    {
        Object target = null;
        Method method = null;

        public DynaHanlder(Object target, Method method)
        {
            super();
            this.target = target;
            this.method = method;
        }

        /**
         * 這個函式就是動態註冊的回撥方法
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            // 這裡呼叫動注入的方法
            return this.method.invoke(target, args);
        }

    }

也很簡單就是保持我們的方法的應用當通過代理回撥我們的時候我們也通過反射去呼叫我們的方法。
最後再給外部暴露一個介面方法。

//外部呼叫介面
static public void inject(Activity activity)
    {
        injectView(activity);
        injectMethod(activity);
    }

最後來看看我們怎麼使用它吧。

public class MainActivity extends Activity
{

    @ViewInject(R.id.button)
    private Button button;
    @ViewInject(R.id.button2)
    private Button button2;


    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mainactivity);
        ViewInjectUtile.inject(this);
        button.setText("fuck");
        button2.setText("asdfasdf");
    }

    @MethodInject({ R.id.button, R.id.button2 })
    public void onClick(View v)
    {
        switch (v.getId())
        {
        case R.id.button:
            // System.out.println("asdfasdf");
            Toast.makeText(this, "R.id.button", 0).show();
            break;
        case R.id.button2:

            Toast.makeText(this, "R.id.button2", 0).show();
            // System.out.println("asdf");
            break;
        default:
            break;
        }
    }

當點選按鈕會回撥我們的方法,注入一開始就幫我們完成了id與控制元件的繫結,這就是注入框架主要的精髓了,需要更好更強大的框架還需要自己慢慢完成。這裡貼上原始碼地址:傳送門