Android IOC框架實(shí)現(xiàn)

寫在前面

這篇文章是我以前寫在CSDN上的辆床,由于感覺CSDN上寫作體驗(yàn)感覺不好佳晶,現(xiàn)在準(zhǔn)備把以前的一些文章轉(zhuǎn)到簡(jiǎn)書


以上都是廢話。讼载。宵晚。。维雇。

什么是IOC?

IOC框架稱為控制控制反轉(zhuǎn)框架也稱為依賴注入框架晒他,依賴注入(DI)和控制反轉(zhuǎn)(IOC)是從不同的角度的描述的同一件事情吱型,就是指通過引入IOC容器,利用依賴關(guān)系注入的方式陨仅,實(shí)現(xiàn)對(duì)象之間的解耦津滞。

IOC相關(guān)概念

在寫一個(gè)框架之前铝侵,我們需要了解下IOC的相關(guān)概念。
簡(jiǎn)單來說触徐,對(duì)象和IOC容器的關(guān)系就像電腦和外設(shè)之間的關(guān)系咪鲜,其中電腦相當(dāng)于對(duì)象,而IOC容器相當(dāng)于一個(gè)個(gè)不同的電腦外設(shè)撞鹉。外設(shè)具有不同的功能疟丙,并且他們和電腦之間都遵循著某種協(xié)議(如USB2.0協(xié)議),因此它們?cè)诒舜霜?dú)立的同時(shí)卻又能相互聯(lián)系鸟雏。
輸入功能原本屬于電腦的享郊,但是外設(shè)鍵盤卻能通過電腦提供的相關(guān)接口來控制電腦的輸入功能,這就是IOC概念里面的控制反轉(zhuǎn)孝鹊。由于電腦和外設(shè)間都遵循著USB協(xié)議炊琉,鍵盤便可以實(shí)現(xiàn)隨意切換,卻不影響鍵盤的輸入功能又活,這就是IOC概念里面的依賴注入苔咪。

電腦和外設(shè)

好了,了解完IOC協(xié)議后柳骄,便可以開始繪制相應(yīng)的架構(gòu)圖了团赏。

IOC框架圖

IOC框架圖

這是我用StartUml繪制的UML圖。

如這個(gè)圖所示夹界,整個(gè)IOC框架可以分為5個(gè)部分
1馆里、ModuleListener,IOC的核心接口可柿,相當(dāng)于電腦的USB協(xié)議
2鸠踪、AbsModule,這就是IOC容器复斥,IOC容器功能都在這個(gè)類里面實(shí)現(xiàn)
3营密、IOCProxy,對(duì)象的靜態(tài)代理目锭,相當(dāng)于電腦的USB接口
4评汰、ModuleFactory,IOC容器的享元工廠痢虹,用于創(chuàng)建IOC容器
5被去、AbsActivity,具體的對(duì)象

有了概念奖唯,有了圖紙惨缆,現(xiàn)在終于可以愉快的碼代碼了

功能實(shí)現(xiàn)

ModuleListener

如上文所說的那樣,ModuleListener是整個(gè)IOC框架的IOC協(xié)議,它本質(zhì)上就是一個(gè)接口坯墨,定義了幾個(gè)方法寂汇,僅此而已,是的捣染,所謂的IOC協(xié)議就是那么簡(jiǎn)單骄瓣。
代碼如下:

public interface ModuleListener {
    /**
     * 無參的回調(diào)
     *
     * @param method 方法名
     */
    public void callback(String method);

    /**
     * 帶參數(shù)的回調(diào)
     *
     * @param method        方法名
     * @param dataClassType 參數(shù)類型
     * @param data          數(shù)據(jù)
     */
    public void callback(String method, Class<?> dataClassType, Object data);

    /**
     * 統(tǒng)一接口的回調(diào),回調(diào)接口為dataCallback
     *
     * @param result        返回碼
     * @param data          回調(diào)數(shù)據(jù)
     */
    public void callback(int result, Object data);
}

AbsModule

AbsModule耍攘,這就是IOC框架的IOC容器榕栏,IOC容器的功能都應(yīng)該在它的子類里面實(shí)現(xiàn)。
AbsModule依賴于ModuleListener接口少漆,它持有ModuleListener的引用臼膏。其實(shí)說白了它就是一個(gè)觀察者對(duì)象,僅此而已....
代碼如下:

public class AbsModule {
    public String TAG = "";
    private Context mContext;
    private ModuleListener mModuleListener;

    public AbsModule(Context context) {
        mContext = context;
        init();
    }

    /**
     * 初始化一些東西
     */
    private void init() {
        String className = getClass().getName();
        String arrays[] = className.split("\\.");
        TAG = arrays[arrays.length - 1];
    }

    public void setModuleListener(ModuleListener moduleListener) {
        if (moduleListener == null)
            throw new NullPointerException("ModuleListener為空");
        this.mModuleListener = moduleListener;
    }

    public Context getContext() {
        return mContext;
    }

    /**
     * 統(tǒng)一的回調(diào)
     *
     * @param result 返回碼
     * @param data   回調(diào)數(shù)據(jù)
     */
    protected void callback(int result, Object data) {
        mModuleListener.callback(result, data);
    }

    /**
     * module回調(diào)
     *
     * @param method 回調(diào)的方法名
     */
    @Deprecated
    protected void callback(String method) {
        mModuleListener.callback(method);
    }

    /**
     * 帶參數(shù)的module回調(diào)
     *
     * @param method        回調(diào)的方法名
     * @param dataClassType 回調(diào)數(shù)據(jù)類型
     * @param data          回調(diào)數(shù)據(jù)
     */
    @Deprecated
    protected void callback(String method, Class<?> dataClassType, Object data) {
        mModuleListener.callback(method, dataClassType, data);
    }
}

IOCProxy

類如其名示损,IOCProxy本身就是一個(gè)靜態(tài)代理渗磅,代理本身其實(shí)很簡(jiǎn)單,說白了检访,代理就是我們平時(shí)所說的“黑中介”始鱼,在這里,我們的代理僅僅只實(shí)現(xiàn)了兩個(gè)功能:
1脆贵、給IOC對(duì)象設(shè)置觀察者医清,這個(gè)觀察者是繼承于AbsModule的子類。
2卖氨、中轉(zhuǎn)AbsModule回調(diào)的數(shù)據(jù)会烙,將數(shù)據(jù)再次回調(diào)到AbsActivity的相應(yīng)接口。
代碼如下:

import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class IOCProxy implements ModuleListener {
    private static final String TAG = "IOCProxy";
    private static final String mMethod = "dataCallback";

    private Object mObj;

    /**
     * 初始化靜態(tài)代理
     */
    public static IOCProxy newInstance(Object obj, AbsModule module) {
        return new IOCProxy(obj, module);
    }

    /**
     * 被代理對(duì)象
     */
    private IOCProxy(Object obj, AbsModule module) {
        this.mObj = obj;
        if (module != null) {
            module.setModuleListener(this);
        }
    }
    
    /**
     * 動(dòng)態(tài)切換不同的觀察者
     * @param module
     */
    public void changeModule(AbsModule module) {
        module.setModuleListener(this);
    }

    /**
     * 統(tǒng)一的數(shù)據(jù)回調(diào)
     *
     * @param result 返回碼
     * @param data   回調(diào)數(shù)據(jù)
     */
    @Override
    public void callback(int result, Object data) {
        synchronized (this) {
            try {
                Method m = mObj.getClass().getDeclaredMethod(mMethod, int.class, Object.class);
                m.setAccessible(true);
                m.invoke(mObj, result, data);
            } catch (NoSuchMethodException e) {
                Log.e(TAG, "無法找到" + mMethod + "方法");
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 數(shù)據(jù)回調(diào)
     *
     * @param method 方法名
     */
    @Override
    @Deprecated
    public void callback(String method) {
        synchronized (this) {
            try {
                Method m = mObj.getClass().getDeclaredMethod(method);
                m.setAccessible(true);
                m.invoke(mObj);
            } catch (NoSuchMethodException e) {
                Log.e(TAG, "無法找到" + method + "方法");
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 帶參數(shù)的回調(diào)
     *
     * @param method        方法名
     * @param dataClassType 參數(shù)類型,如 int.class
     * @param data          數(shù)據(jù)
     */
    @Override
    @Deprecated
    public void callback(String method, Class<?> dataClassType, Object data) {
        synchronized (this) {
            try {
                Method m = mObj.getClass().getDeclaredMethod(method, dataClassType);
                m.setAccessible(true);
                m.invoke(mObj, data);
            } catch (NoSuchMethodException e) {
                Log.e(TAG, "無法找到" + method + "方法");
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}

在這個(gè)類里筒捺,博主根據(jù)以前的需求寫了幾種不同的代理回調(diào)柏腻。
但是,我強(qiáng)烈建議你使用帶有返回碼的統(tǒng)一回調(diào)接口系吭,該方法規(guī)定了AbsActivity的回調(diào)函數(shù)五嫂,實(shí)現(xiàn)了數(shù)據(jù)流的統(tǒng)一,一致性總歸是好的肯尺,便于我們后期維護(hù)沃缘。

ModuleFactory

ModuleFactory是一個(gè)享元工廠,IOC容器的構(gòu)建都是由它來完成了则吟,享元實(shí)現(xiàn)不同對(duì)象的容器之間的共享槐臀。
代碼如下:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class ModuleFactory {

    private static final String TAG = "ModuleFactory";

    private static Map<String, AbsModule> mModules = new HashMap<>();

    /**
     * 獲取Module
     */
    protected static <T extends AbsModule> T getModule(Context context, Class<T> clazz) {
        T module = (T) mModules.get(String.valueOf(clazz.hashCode()));
        if (module == null) {
            return newInstanceModule(context, clazz);
        }
        return module;
    }

    /**
     * 構(gòu)造一個(gè)新的Module
     */
    private static <T extends AbsModule> T newInstanceModule(Context context, Class<T> clazz) {
        Class[] paramTypes = {Context.class};
        Object[] params = {context};
        try {
            Constructor<T> con = clazz.getConstructor(paramTypes);
            T module = con.newInstance(params);
            mModules.put(String.valueOf(clazz.hashCode()), module);
            return module;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
}

AbsActivity

寫了這么久,終于快到尾聲了...
AbsActivity是IOC框架的對(duì)象氓仲,Activity也沒啥好說的水慨,大家都懂的败匹,直接放代碼:

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public abstract class AbsActivity extends AppCompatActivity {
    private static IOCProxy mProxy;
    private IOCProxy mNewProxy;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mNewProxy = IOCProxy.newInstance(this, null);
    }

    /**
     * 利用反射來對(duì)代理進(jìn)行重指向
     */
    private void setProxy() {
        mProxy = mNewProxy;
    }

    /**
     * 獲取Module
     */
    protected static <T extends AbsModule> T getModule(AbsActivity activity, Class<T> clazz) {
        Method m = ReflectionUtil.getMethod(activity.getClass(), "setProxy", new Class[]{});
        try {
            m.invoke(activity);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        T module = ModuleFactory.getModule(activity, clazz);
        mProxy.changeModule(module);
        return module;
    }

    /**
     * 統(tǒng)一的回調(diào)接口
     *
     * @param result 返回碼,用來判斷是哪個(gè)接口進(jìn)行回調(diào)
     * @param data   回調(diào)數(shù)據(jù)
     */
    protected abstract void dataCallback(int result, Object data);
}

總結(jié)

到現(xiàn)在為止讥巡,框架核心部分已經(jīng)完成了,現(xiàn)在讓我們整理下整個(gè)框架到底是怎樣運(yùn)作的舔哪!
老規(guī)矩欢顷,還是用圖來說明,沒什么比圖更讓人易懂的捉蚤。
下圖是整個(gè)框架的流程圖:


框架流程圖

圖不難懂抬驴,我就不廢話了,我直接給出例子就行了缆巧,大家都是碼農(nóng)布持,我相信沒幾個(gè)碼農(nóng)喜歡看文字的,都喜歡看代碼直接干的陕悬。题暖。

例子

Module1

import android.content.Context;

import com.example.yuyu.blogframedemo.frame.AbsModule;

/**
 * Created by yuyu on 2015/9/6.
 */
public class Module1 extends AbsModule{
    public Module1(Context context) {
        super(context);
    }

    public void module1Test(){
        callback(100, "我是Module1111111");
    }

    public void cusomCallback(){
        callback("myCallback", String.class, "我是Module11的自定義回調(diào)......");
    }
}

Module2

import android.content.Context;

import com.example.yuyu.blogframedemo.frame.AbsModule;

/**
 * Created by yuyu on 2015/9/6.
 */
public class Module2 extends AbsModule{
    public Module2(Context context) {
        super(context);
    }

    public void module2Test(){
        callback(101, "我是Module22222222");
    }
}

MainActivity


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

import com.example.yuyu.blogframedemo.R;
import com.example.yuyu.blogframedemo.frame.AbsActivity;

/**
 * Created by yuyu on 2015/9/6.
 */
public class MainActivity extends AbsActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    /**
     * 回調(diào)到自定義的方法
     * @param data
     */
    private void myCallback(String data){
        show(data);
    }

    /**
     * 數(shù)據(jù)回調(diào),當(dāng)然你也可以回調(diào)到指定的方法里面
     * @param result 返回碼捉超,用來判斷是哪個(gè)接口進(jìn)行回調(diào)
     * @param data   回調(diào)數(shù)據(jù)
     */
    @Override
    protected void dataCallback(int result, Object data) {
        show(String.valueOf(data));
    }

    public void onClick(View view){
        switch (view.getId()){
            case R.id.module1:
                getModule(this, Module1.class).module1Test();
                break;
            case R.id.module2:
                getModule(this, Module2.class).module2Test();
                break;
            case R.id.custom_module:
                getModule(this, Module1.class).cusomCallback();
                break;
        }
    }

    private void show(String msg){
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}
效果圖

DEMO

IOC升級(jí)版胧卤,MVVM框架(實(shí)現(xiàn)了6.0權(quán)限封裝、不需要xml的填充布局拼岳、module注入)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末枝誊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子惜纸,更是在濱河造成了極大的恐慌叶撒,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耐版,死亡現(xiàn)場(chǎng)離奇詭異祠够,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)椭更,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門哪审,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人虑瀑,你說我怎么就攤上這事湿滓。” “怎么了舌狗?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵叽奥,是天一觀的道長。 經(jīng)常有香客問我痛侍,道長朝氓,這世上最難降的妖魔是什么魔市? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮赵哲,結(jié)果婚禮上待德,老公的妹妹穿的比我還像新娘。我一直安慰自己枫夺,他們只是感情好将宪,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著橡庞,像睡著了一般较坛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扒最,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天丑勤,我揣著相機(jī)與錄音,去河邊找鬼吧趣。 笑死法竞,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的再菊。 我是一名探鬼主播爪喘,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼纠拔!你這毒婦竟也來了播赁?” 一聲冷哼從身側(cè)響起先舷,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后旺入,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體门怪,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坐梯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年郎汪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劝萤。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渊涝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出床嫌,到底是詐尸還是另有隱情跨释,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布厌处,位于F島的核電站鳖谈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏阔涉。R本人自食惡果不足惜缆娃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一捷绒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贯要,春花似錦暖侨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至显押,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間傻挂,已是汗流浹背乘碑。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留金拒,地道東北人兽肤。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像绪抛,于是被迫代替她去往敵國和親资铡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容