ARouter解析五:IoC與依賴注入

終于來到了ARouter解析的第五篇了,前面陸陸續(xù)續(xù)分享了四篇ARouter框架的使用和源碼內(nèi)容:

ARouter解析一:基本使用及頁面注冊(cè)源碼解析
ARouter解析二:頁面跳轉(zhuǎn)源碼分析
ARouter解析三:URL跳轉(zhuǎn)本地頁面源碼分析
ARouter解析四:發(fā)現(xiàn)服務(wù)和Fragment

這次分享下IoC思想和ARouter的自動(dòng)注入這塊內(nèi)容宅倒。IoC是控制反轉(zhuǎn)的意思,這個(gè)在后端開發(fā)中用的會(huì)比較多娃循,也是Spring框架的核心征懈。 控制反轉(zhuǎn)一般分為兩種類型,依賴注入(Dependency Injection醋拧,簡稱DI)和依賴查找(Dependency Lookup)卿嘲。在Android開發(fā)中一般自己實(shí)現(xiàn)倒比較少颂斜,但并不是說不常用,很多大名鼎鼎的框架都用的這種思想腔寡,比如ButterKnife焚鲜,Dagger等。我們今天先分享下IoC主要的兩種實(shí)現(xiàn)方式,再分析下ARouter在這方面的相關(guān)實(shí)踐忿磅。

這次分享會(huì)按照下面的步驟進(jìn)行:

1.常用的注入方式

2.依賴注入和依賴查找

3.ARouter注入源碼分析

IoC這塊涉及到的內(nèi)容會(huì)比較多糯彬,比如注解,反射葱她,動(dòng)態(tài)代理撩扒,我們盡量用簡單的語言描述,我之前的博客也有分享到這些內(nèi)容吨些,不明白的小伙伴可自行參考搓谆。

1.常用的注入方式

在開發(fā)過程中,肯定會(huì)涉及到類之間的依賴豪墅,一般都是直接new出來泉手,比如:

class Hello{
    public Hello(){

    }
    
    public void sayHello(){
        
    }
}

public class Human {
    public static void main(String[]args) {
        Hello hello = new Hello();
        hello.sayHello();
    }
}

這里的hello實(shí)例就是用戶自己new出來的,這時(shí)控制權(quán)還是在用戶手中偶器,這有幾個(gè)缺點(diǎn):互相依賴斩萌, 用戶需要管理hello的聲明周期。

所以屏轰,更進(jìn)一步的注入方式有構(gòu)造函數(shù)或者set方法颊郎,比如下面代碼。

public class Human {
    private Hello hello;

    public Human(Hello hello) {
        this.hello = hello;
    }

    public void setHello(Hello hello) {
        this.hello = hello;
    }
}

這會(huì)就比上面代碼更靈活點(diǎn)霎苗,也減少了依賴姆吭,但是還是需要用戶去調(diào)用方法設(shè)置實(shí)例。有木有更好的辦法呢唁盏?可能你也想到了注解的辦法内狸,沒錯(cuò),我們接著往下看升敲。

2.依賴注入和依賴查找

依賴注入和依賴查找有什么區(qū)別呢答倡?我們平時(shí)用的依賴注入會(huì)比較多一點(diǎn),依賴注入在Android上大部分是通過自定義注解來實(shí)現(xiàn)驴党,其實(shí)嚴(yán)格說起來依賴注入也是通過依賴查找來實(shí)現(xiàn)的,依賴注入用起來會(huì)比依賴查找方便获茬。我們先來看一個(gè)簡單的栗子港庄,再看看ARouter的實(shí)踐。
先來看下依賴查找恕曲,其實(shí)我們上一期分享的ARouter解析四:發(fā)現(xiàn)服務(wù)和Fragment就是通過依賴查找來發(fā)現(xiàn)服務(wù)和Fragment的鹏氧。看下面的栗子佩谣,helloService是通過ARouter框架來創(chuàng)建的把还,比上面直接new構(gòu)造會(huì)方便很多,但是還是用戶來找到這個(gè)類的實(shí)例。

interface HelloService{
    void sayHello();
}

@Route(path = "/service/hello")
class HelloServiceImpl implements HelloService {
    Context mContext;

    @Override
    public void sayHello() {
        Toast.makeText(mContext, "Hello ", Toast.LENGTH_SHORT).show();
    }
}

public class Human {
    public static void main(String[]args) {
        HelloService helloService = ARouter.getInstance().navigation(HelloService.class);
        helloService.sayHello();
    }
}

那么上面的栗子用依賴注入該怎么實(shí)現(xiàn)吊履?是不是更為方便了安皱,不需要用戶再去給出實(shí)例的路徑,通過注解@Autowired就可以得到我們需要的實(shí)例艇炎。

public class Human {
    @Autowired
    private HelloService helloService;

    public static void main(String[]args) {
        ARouter.getInstance().inject(this);
        helloService.sayHello();
    }
}

上面的栗子是為了講解清楚簡單拼湊的酌伊,我們來看下ARouter官方Demo的栗子。點(diǎn)擊依賴注入缀踪,可以給Test1Activity傳遞對(duì)象參數(shù)

依賴注入1.png
依賴注入2.png

我們看下Test1Activity的代碼,可以看出來沒有new居砖,沒有查找,只有一個(gè)注解@Autowired驴娃,和一行關(guān)鍵代碼ARouter.getInstance().inject(this);

@Route(path = "/test/activity1")
public class Test1Activity extends AppCompatActivity {

    @Autowired
    String name;

    @Autowired
    int age;

    @Autowired(name = "boy")
    boolean girl;

    @Autowired
    TestParcelable pac;

    @Autowired
    TestObj obj;

    private long high;

    @Autowired
    String url;

    @Autowired
    HelloService helloService;

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

        ARouter.getInstance().inject(this);

        String params = String.format(
                "name=%s,\n age=%s,\n girl=%s,\n high=%s,\n url=%s,\n pac=%s,\n obj=%s",
                name,
                age,
                girl,
                high,
                url,
                pac,
                obj
        );
        helloService.sayHello("Hello moto.");

        ((TextView)findViewById(R.id.test)).setText("I am " + Test1Activity.class.getName());
        ((TextView)findViewById(R.id.test2)).setText(params);
    }
}

那么源碼是怎么做到的呢奏候?我們接著往下看。

3.ARouter注入源碼分析

上面其實(shí)就是一行關(guān)鍵代碼ARouter.getInstance().inject(this);唇敞,所以我們跟進(jìn)去看看鼻由,來到ARouter的代理類_ARouter中,首先通過以來查找獲取AutowiredService的具體實(shí)現(xiàn)類厚棵,然后得到實(shí)例蕉世,這個(gè)不清楚的可以看下上篇分享,ARouter解析四:發(fā)現(xiàn)服務(wù)和Fragment婆硬。這里就不再解釋了狠轻。

static void inject(Object thiz) {
        AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
        if (null != autowiredService) {
            autowiredService.autowire(thiz);
        }
}

我們接著看下AutowiredService,這里會(huì)先在混存中查找是否有Test1Activity的輔助注入類,這里剛開始肯定是沒有的彬犯,所以需要去加載向楼。輔助注入類就是Test1Activity$$ARouter$$Autowired,不用說谐区,這個(gè)就是編譯期間生成的湖蜕。

@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
    private LruCache<String, ISyringe> classCache;
    private List<String> blackList;

    @Override
    public void init(Context context) {
        classCache = new LruCache<>(66);
        blackList = new ArrayList<>();
    }

    @Override
    public void autowire(Object instance) {
        String className = instance.getClass().getName();
        try {
            if (!blackList.contains(className)) {
                ISyringe autowiredHelper = classCache.get(className);
                if (null == autowiredHelper) {  // No cache.
                    autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
                }
                autowiredHelper.inject(instance);
                classCache.put(className, autowiredHelper);
            }
        } catch (Exception ex) {
            blackList.add(className);    // This instance need not autowired.
        }
    }
}

通過反射得到輔助注入類的實(shí)例后就調(diào)用inject方法注入實(shí)例。我們來看下這個(gè)類的代碼宋列。到這里就水落石出了昭抒,在跳轉(zhuǎn)時(shí)將需要傳遞的參數(shù)寫入postcard的bundle中,然后成功跳轉(zhuǎn)到目標(biāo)頁面后就可以通過getIntent取出參數(shù)炼杖,然后分別給目標(biāo)頁面的每個(gè)需要注入的成員變量賦值灭返。

public class Test1Activity$$ARouter$$Autowired implements ISyringe {
    private SerializationService serializationService;

    @Override
    public void inject(Object target) {
        serializationService = ARouter.getInstance().navigation(SerializationService.class);
        ;
        Test1Activity substitute = (Test1Activity) target;
        substitute.name = substitute.getIntent().getStringExtra("name");
        substitute.age = substitute.getIntent().getIntExtra("age", 0);
        substitute.girl = substitute.getIntent().getBooleanExtra("boy", false);
        substitute.pac = substitute.getIntent().getParcelableExtra("pac");
        if (null != serializationService) {
            substitute.obj = serializationService.json2Object(substitute.getIntent().getStringExtra("obj"), TestObj.class);
        } else {
            Log.e("ARouter::", "You want automatic inject the field 'obj' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
        }
        substitute.url = substitute.getIntent().getStringExtra("url");
        substitute.helloService = ARouter.getInstance().navigation(HelloService.class);
    }
}

我們?cè)倏偨Y(jié)下整個(gè)注入的過程:

依賴注入.png

4.總結(jié)

今天我們分享了IoC的設(shè)計(jì)思想,也通過栗子說明了依賴注入和依賴查找坤邪,從上面的分析可以看出ARouter的依賴注入是在運(yùn)行時(shí)生成輔助類熙含,在運(yùn)行時(shí)通過反射實(shí)例化輔助類并完成跳轉(zhuǎn)后參數(shù)的自動(dòng)注入。這里面涉及到的技術(shù)還是比較多的艇纺,比如反射怎静,注解邮弹,APT技術(shù),IoC蚓聘,依賴注入和依賴查找腌乡,代理,算是比較綜合的應(yīng)用了或粮,這些技術(shù)的我在之前的博客中都有分享過导饲,小伙伴們可自行參考。

慣例氯材,感謝@右傾傾的支持與理解渣锦!

后面會(huì)分享ARouter的攔截器相關(guān)的內(nèi)容,感興趣的小伙伴歡迎關(guān)注氢哮。希望我的分享能對(duì)大家有點(diǎn)幫助袋毙,謝謝!

歡迎關(guān)注公眾號(hào):JueCode

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末冗尤,一起剝皮案震驚了整個(gè)濱河市听盖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌裂七,老刑警劉巖皆看,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異背零,居然都是意外死亡腰吟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門徙瓶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毛雇,“玉大人,你說我怎么就攤上這事侦镇×榇” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵壳繁,是天一觀的道長震捣。 經(jīng)常有香客問我,道長氮趋,這世上最難降的妖魔是什么伍派? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮剩胁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘祥国。我一直安慰自己昵观,他們只是感情好晾腔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著啊犬,像睡著了一般灼擂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上觉至,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天剔应,我揣著相機(jī)與錄音,去河邊找鬼语御。 笑死峻贮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的应闯。 我是一名探鬼主播纤控,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼碉纺!你這毒婦竟也來了船万?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤骨田,失蹤者是張志新(化名)和其女友劉穎耿导,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體态贤,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舱呻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抵卫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狮荔。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖介粘,靈堂內(nèi)的尸體忽然破棺而出殖氏,到底是詐尸還是另有隱情,我是刑警寧澤姻采,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布雅采,位于F島的核電站,受9級(jí)特大地震影響慨亲,放射性物質(zhì)發(fā)生泄漏婚瓜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一刑棵、第九天 我趴在偏房一處隱蔽的房頂上張望巴刻。 院中可真熱鬧,春花似錦蛉签、人聲如沸胡陪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柠座。三九已至邑雅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間妈经,已是汗流浹背淮野。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吹泡,地道東北人骤星。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像荞胡,于是被迫代替她去往敵國和親妈踊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

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