終于來到了ARouter解析的第五篇了,前面陸陸續續分享了四篇ARouter框架的使用和源碼內容:
ARouter解析一:基本使用及頁面注冊源碼解析
ARouter解析二:頁面跳轉源碼分析
ARouter解析三:URL跳轉本地頁面源碼分析
ARouter解析四:發現服務和Fragment
這次分享下IoC思想和ARouter的自動注入這塊內容。IoC是控制反轉的意思,這個在后端開發中用的會比較多,也是Spring框架的核心。 控制反轉一般分為兩種類型,依賴注入(Dependency Injection,簡稱DI)和依賴查找(Dependency Lookup)。在Android開發中一般自己實現倒比較少,但并不是說不常用,很多大名鼎鼎的框架都用的這種思想,比如ButterKnife,Dagger等。我們今天先分享下IoC主要的兩種實現方式,再分析下ARouter在這方面的相關實踐。
這次分享會按照下面的步驟進行:
1.常用的注入方式
2.依賴注入和依賴查找
3.ARouter注入源碼分析
IoC這塊涉及到的內容會比較多,比如注解,反射,動態代理,我們盡量用簡單的語言描述,我之前的博客也有分享到這些內容,不明白的小伙伴可自行參考。
1.常用的注入方式
在開發過程中,肯定會涉及到類之間的依賴,一般都是直接new出來,比如:
class Hello{
public Hello(){
}
public void sayHello(){
}
}
public class Human {
public static void main(String[]args) {
Hello hello = new Hello();
hello.sayHello();
}
}
這里的hello實例就是用戶自己new出來的,這時控制權還是在用戶手中,這有幾個缺點:互相依賴, 用戶需要管理hello的聲明周期。
所以,更進一步的注入方式有構造函數或者set方法,比如下面代碼。
public class Human {
private Hello hello;
public Human(Hello hello) {
this.hello = hello;
}
public void setHello(Hello hello) {
this.hello = hello;
}
}
這會就比上面代碼更靈活點,也減少了依賴,但是還是需要用戶去調用方法設置實例。有木有更好的辦法呢?可能你也想到了注解的辦法,沒錯,我們接著往下看。
2.依賴注入和依賴查找
依賴注入和依賴查找有什么區別呢?我們平時用的依賴注入會比較多一點,依賴注入在Android上大部分是通過自定義注解來實現,其實嚴格說起來依賴注入也是通過依賴查找來實現的,依賴注入用起來會比依賴查找方便。我們先來看一個簡單的栗子,再看看ARouter的實踐。
先來看下依賴查找,其實我們上一期分享的ARouter解析四:發現服務和Fragment就是通過依賴查找來發現服務和Fragment的。看下面的栗子,helloService是通過ARouter框架來創建的,比上面直接new構造會方便很多,但是還是用戶來找到這個類的實例。
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();
}
}
那么上面的栗子用依賴注入該怎么實現?是不是更為方便了,不需要用戶再去給出實例的路徑,通過注解@Autowired
就可以得到我們需要的實例。
public class Human {
@Autowired
private HelloService helloService;
public static void main(String[]args) {
ARouter.getInstance().inject(this);
helloService.sayHello();
}
}
上面的栗子是為了講解清楚簡單拼湊的,我們來看下ARouter官方Demo的栗子。點擊依賴注入,可以給Test1Activity
傳遞對象參數
我們看下Test1Activity
的代碼,可以看出來沒有new,沒有查找,只有一個注解@Autowired
,和一行關鍵代碼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注入源碼分析
上面其實就是一行關鍵代碼ARouter.getInstance().inject(this);
,所以我們跟進去看看,來到ARouter的代理類_ARouter中,首先通過以來查找獲取AutowiredService的具體實現類,然后得到實例,這個不清楚的可以看下上篇分享,ARouter解析四:發現服務和Fragment。這里就不再解釋了。
static void inject(Object thiz) {
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
if (null != autowiredService) {
autowiredService.autowire(thiz);
}
}
我們接著看下AutowiredService
,這里會先在混存中查找是否有Test1Activity的輔助注入類,這里剛開始肯定是沒有的,所以需要去加載。輔助注入類就是Test1Activity$$ARouter$$Autowired
,不用說,這個就是編譯期間生成的。
@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.
}
}
}
通過反射得到輔助注入類的實例后就調用inject方法注入實例。我們來看下這個類的代碼。到這里就水落石出了,在跳轉時將需要傳遞的參數寫入postcard的bundle中,然后成功跳轉到目標頁面后就可以通過getIntent取出參數,然后分別給目標頁面的每個需要注入的成員變量賦值。
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);
}
}
我們再總結下整個注入的過程:
4.總結
今天我們分享了IoC的設計思想,也通過栗子說明了依賴注入和依賴查找,從上面的分析可以看出ARouter的依賴注入是在運行時生成輔助類,在運行時通過反射實例化輔助類并完成跳轉后參數的自動注入。這里面涉及到的技術還是比較多的,比如反射,注解,APT技術,IoC,依賴注入和依賴查找,代理,算是比較綜合的應用了,這些技術的我在之前的博客中都有分享過,小伙伴們可自行參考。
慣例,感謝@右傾傾的支持與理解!
后面會分享ARouter的攔截器相關的內容,感興趣的小伙伴歡迎關注。希望我的分享能對大家有點幫助,謝謝!
歡迎關注公眾號:JueCode