終于來到了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ù)
我們看下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è)注入的過程:
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