什么是依賴注入?
依賴是指一個對象持有其他對象的引用。依賴注入則是將這些依賴對象傳遞給被依賴對象,而不是被依賴對象自己創建這些對象。
public class MyClass{ private AnotherClass mAnotherObject; public MyClass(){ mAnotherObject = new AnotherClass(); } }
通過傳遞對象的方式,所傳遞對象的更改不會影響代碼。
public class MyClass{ private MyInterface mAnotherObject; public MyClass(MyInterface anotherObject){ mAnotherObject = anotherObject; } }
依賴注入可以簡化代碼編寫,並提供一個可適配的環境,方便進行單元測試以及功能模塊的配置。
開發中可能會遇到這樣的麻煩。
我們將通過一個例子來理解依賴注入的應用場景:某Android應用需要一個列表來顯示用戶的好友。
public class FriendListFragment{ private FriendListAPI mFriendListAPI; ...... public FriendListFragment(){ mFriendListAPI = new FriendListAPI(); } private void getFriendList(){ mFriendListAPI.getFriendList(new Callback(){ public void onSuccess(List<User> list){ ...... } ...... }); } } public class FriendListAPI{ private OkHttpClient mHttpClient; public FriendListAPI(){ mHttpClient= new OkHttpClient(); //接下來各種Http配置 ...... } }
代碼寫好了,運行程序試試。可是,后台API沒有准備好或者沒有數據怎么辦?自己添點測試數據試試吧。在FriendListFragment里面添加一個生成測試數據的方方法buildTestData(),並替換getFriendList()方法。等后台API准備好后再改回來。
我們想測試網絡有延遲或錯誤的時候,程序是否會出現異常。這需要通過配置OkHttpClient參數來實現測試場景,於是又要更改FriendListAPI中相關HttpClient配置代碼,測試完后再修改回來。
這樣對代碼進行多次修改,很容易出錯。因此,對於多次使用的模塊,我們可以通過注入的方式,將引用傳入需要使用的類中,而不是自己創建。通過編寫兩個API,一個是直接請求后台數據,另一個則只是一些靜態測試數據。需要測試的時候注入可生成測試數據的API,測試完后則切換為正式API。
public class FriendListFragment{ private FriendListAPI mFriendListAPI; ...... public FriendListFragment(FriendListAPI friendListAPI){ mFriendListAPI = friendListAPI; } } public class FriendListAPI{ private OkHttpClient mHttpClient; public FriendListAPI(HttpClient okHttpClient){ mHttpClient= okHttpClient; ...... } }
現在引入一個稍微復雜的場景,更多的Fragment需要使用FriendListAPI,我們需要在兩個不同的地方進行注入,因此產生了許多重復代碼。

因此,我們需要一個容器,它知道什么地方需要注入,注入什么樣的對象。
Dagger解決方案。
這里簡單的展示輕量級依賴注入庫Dagger實現的注入。
首先定義模塊:
public class MyModule{ @Provides @Singleton OkHttpClient provideOkHttpClient(){ //這里可進行各種Http配置 return new OkHttpClient(); } @Provides @Singleton FriendListAPI provideFriendListAPI(){ return newFriendListAPI(); } }
初始化模塊以及依賴對象圖。
public class MyApplication extends Application{ private ObjectGraph graph; @Override public void onCreate() { super.onCreate(); graph = ObjectGraph.create(getModules().toArray()); } protected List<Object> getModules() { return Arrays.asList( new MyModule(this)); } public void inject(Object object) { graph.inject(object); } }
最后添加注入點並進行注入。
public abstract class BaseActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ((MyApplication) getApplication()).inject(this); } } public class FriendListFragment{ @Inject FriendListAPI mFriendListAPI; ...... } public class FriendListAPI{ @Inject OkHttpClient mHttpClient; ...... }
如需進行單元測試,或使用可生成測試數據的模擬API,則再編寫一個模塊,在初始模塊和依賴對象圖時替換即可。
現有的依賴注入性能?
依賴注入雖能簡化代碼編寫,方便單元測試,可是由於當前基於反射的依賴注入框架(
Guice、
RoboGuice)性能並不好。原因是他們會在程序運行的時候需要掃描代碼中的注解,並需要花費內存映射到內存中。
這里推薦使用Dagger是因為它使用了編譯時注解,也就是說在編譯代碼的時候,Dagger就已經完成傳統依賴注入框架在運行時所執行的任務。
什么時候需要依賴注入?
當你需要將配置數據注入到一個或多個模塊時。在開發過程中前端訪問后台服務器地址會分為測試服務器和正式服務器,以及各種第三方分享key和ID,依賴注入都是非常好的選擇。
當需要將同一依賴注入到多個模塊時。如加載圖片以及圖片存儲管理組件(Picasso, Android-Universal-Image-Loader)。
當需要對同一依賴注入不同的實現時。為方便開發和單元測試,后台API可有正式API和模擬API,通過依賴注入方便切換環境。
當同一模塊需要注入不同的配置時。
參考資料:
http://square.github.io/dagger/