android依賴注入框架Dagger和Butterknife實戰
依賴注入(DependencyInjection):在類A中要用到一個B的物件(依賴),需要通過新建B的例項或其他一些主動的方式來獲取物件,然後才能呼叫。而通過外部的方式自動將B的物件分配給A(注入),實現相對被動的獲取物件,這個過程稱為依賴注入。
依賴注入的一些需要理解的概念,具體可以可再在維基百科上找到更詳細的解釋。
控制反轉:物件在被建立的時候,由一個調控系統內所有物件的外界實體,將其所依賴的物件的引用傳遞給它。也可以說,依賴被注入到物件中。
依賴注入:被動的接收物件,在類A的例項建立過程中即建立了依賴的B物件,通過型別或名稱來判斷將不同的物件注入到不同的屬性中。
依賴注入有如下實現方式:
· 基於介面。實現特定介面以供外部容器注入所依賴型別的物件。
· 基於 set方法。實現特定屬性的public set方法,來讓外部容器呼叫傳入所依賴型別的物件。
· 基於建構函式。實現特定引數的建構函式,在新建物件時傳入所依賴型別的物件。
· 基於註解。基於Java的註解功能,在私有變數前加“@Autowired”等註解,不需要顯式的定義以上三種程式碼,便可以讓外部容器傳入對應的物件。該方案相當於定義了public的set方法,但是因為沒有真正的set方法,從而不會為了實現依賴注入導致暴露了不該暴露的介面(因為set方法只想讓容器訪問來注入而並不希望其他依賴此類的物件訪問)。
依賴查詢:主動索取響應名稱的物件,獲得依賴物件的時間也可以在程式碼中自由控制。
依賴反轉原則:
1. 高層次的模組不應該依賴於低層次的模組,兩者都應該依賴於抽象介面。
2. 抽象介面不應該依賴於具體實現。而具體實現則應該依賴於抽象介面。
應用依賴反轉原則同樣被認為是應用了介面卡模式,例如:高層的類定義了它自己的介面卡介面(高層類所依賴的抽象介面)。被適配的物件同樣依賴於介面卡介面的抽象(這是當然的,因為它實現了這個介面),同時它的實現則可以使用它自身所在低層模組的程式碼。通過這種方式,高層元件則不依賴於低層元件,因為它(高層元件)僅間接的通過呼叫配置器介面多型方法的方式使用了低層元件通過介面卡介面,在這些多型方法則是由被適配物件以及它的低層模組所實現的。
Android依賴注入目的:
· 1. 減少樣板程式碼
· 2. 提高重用性,測試性,可維護性.
Android依賴注入庫
- RoboGuice
- Android Annotations
- Dragger
- ButterKnife
- Transfuse
1. ButterKnife
@Bind(R.id.title) TextView title;
@bind和viewId相結合能夠自動聯絡到layout的view(使用場景activity和fragment、viewholder等需要與view結合的地方都可以使用)
在使用@bind結合view之後,需要呼叫ButterKnife.bind(this)或者ButterKnife.bind(this,view)方式真正實現依賴注入,則可以代替findViewByID
特例:在Fragment生命週期中,onDestoryView也需要Butterknife.unbind(this)
2. Dagger
依賴注入框架,Dagger兩種主要方法:
第一,@Inject寫到物件類的建構函式上面,要用的時候直接注入物件,避免new物件,造成強耦合。且可以加上註釋@Singleton形成單例。
第二,以@module和@provides為基礎,可以任意注入物件,且可以帶參操作。Module裡面若是要有其他Module,要在includes裡寫好,@Module除了include,還有injects引數指定注入位置,library指定是否需要外部庫,complete是否是完整module(含有外部module依賴則不完整)。
最後在注入目的地,通過ObjectGraph(物件圖表)形成依賴關係,具體操作方式,ObjectGraph物件,並呼叫方法create(Module).inject(注入物件);則可以直接用@inject注入目標物件類裡。具體流程如圖所示。
(此圖摘自http://talentprince.github.io/blog/2015/01/04/android-yu-inject-yi-lai-zhu-ru-de-bu-jie-zhi-yuan/)
3. Dagger和butterknife區別
Buffer knife目的為注入到view,所以能夠在非activity裡面注入,也能注入到inflate的views裡面
Dagger能夠注入到任何你想要的物件,只要其在module類中。或者它是構造器。但是缺少對方法和欄位的注入支援。
Buffer knife只是避免樣板程式碼,findViewById,僅此而已,所以不能算是一個真正的注入。只是一個view的代言。
對於Dagger我們可以把它當做應用中的一個模組, 負責為其它模組提供例項並且注入依賴關係。那是它的基本職責。模組的建立位於我們應用中的一個點上,這樣我們可以擁有完全的控制權。Dagger Leiva說,特別適合用在低端裝置上,因為它沒有采取反射而使用了預編譯技術,因為基於反射的DI非常佔用資源和耗時。Dagger或許不是最理想的依賴注入框架,但Leiva認為,它是最高效的。
4. Buffer knife和Dragger綜合實戰
建立一個list列表,通過Dragger每次點選按鈕依賴注入新物件,實現逐漸填充list列表。
佈局檔案 activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="enter" />
<ListView
android:id="@+id/list_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/button" />
</RelativeLayout>
佈局檔案 listadapter.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:weightSum="2">
<TextView
android:id="@+id/text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView
android:id="@+id/text_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
主Activity MainActivity.xml
package com.hao.injectdemo;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.ButterKnife;
public class MainActivity extends ActionBarActivity implements View.OnClickListener {
@Bind(R.id.button)//Buffer knife注入
Button button;
@Bind(R.id.list_item)//Buffer knife注入
ListView mListView;
@Inject
ListProduct listProduct;//Dagger注入
static List<Map<String, String>> mlist = new ArrayList<Map<String, String>>();
static int count = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);//Bufferknife建立聯絡
App app = (App) getApplication();
app.inject(this);//建立依賴關係
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
listProduct.putMap();
WorkAdapter workAdapter = new WorkAdapter(MainActivity.this, mlist);
mListView.setAdapter(workAdapter);
}
}
ListProduct構造新的Map填充list(運用Dagger注入構造物件)
package com.hao.injectdemo;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
/**
* Created by hao on 7/4/2015.
*/
public class ListProduct {
public ListProduct() {
}
public Map putMap() {
Map<String, String> map = new HashMap<String, String>();
map.put("text", String.valueOf(MainActivity.count++));
map.put("text2", String.valueOf(MainActivity.count++));
MainActivity.mlist.add(map);
return map;
}
}
AppModule(Dagger關鍵Module,provides建立新物件形成ObjectGraph物件圖示)
package com.hao.injectdemo;
import dagger.Module;
import dagger.Provides;
/**
* Created by hao on 7/2/2015.
*/
@Module(injects = MainActivity.class)//module和provides配合
public class AppModule {
@Provides
ListProduct getWorkAdapter() {
return new ListProduct();
}
}
App(輔助Dagger ObjectGraph的建立)
package com.hao.injectdemo;
import android.app.Application;
import dagger.ObjectGraph;
/**
* Created by hao on 7/2/2015.
*/
public class App extends Application {
private ObjectGraph objectGraph;
public <T> void inject(T target) {
getObjectGraph().inject(target);
}
private ObjectGraph getObjectGraph() {
if (objectGraph == null) {
objectGraph = ObjectGraph.create(AppModule.class);
}
return objectGraph;
}
@Override
public void onCreate() {
}
}
WorkAdapter list填充
package com.hao.injectdemo;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.List;
import java.util.Map;
import butterknife.Bind;
import butterknife.ButterKnife;
/**
* Created by hao on 7/4/2015.
*/
public class WorkAdapter extends BaseAdapter {
private LayoutInflater mLayoutInflater = null;
List<Map<String, String>> mList;
Context mContext;
public WorkAdapter(Context mContext, List<Map<String, String>> list) {
this.mLayoutInflater = LayoutInflater.from(mContext);
this.mList = list;
this.mContext = mContext;
}
@Override
public int getCount() {
return mList.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.listadapter, null);
viewHolder = new ViewHolder(convertView, mContext);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.text1.setText(mList.get(position).get("text"));
viewHolder.text2.setText(mList.get(position).get("text2"));
return convertView;
}
static class ViewHolder {
@Bind(R.id.text)//針對其他view的Buffer knife注入
TextView text1;
@Bind(R.id.text_2)//針對其他view的Buffer knife注入
TextView text2;
public ViewHolder(View view, Context mContext) {
ButterKnife.bind(this, view);//Buffer knife建立聯絡
}
}
}
最終結果
參考連結
https://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC
https://en.wikipedia.org/wiki/Dependency_injection
Dagger介紹:
https://github.com/square/dagger
http://blog.csdn.net/zjbpku/article/details/18732295
Butterknife介紹
http://jakewharton.github.io/butterknife/
Androidannotation介紹
https://github.com/excilys/androidannotations/wiki