??我們?cè)趯?shí)際的開(kāi)發(fā)過(guò)程中,請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)是必不可少的。市場(chǎng)上所有的APP都不可避免的會(huì)用到請(qǐng)求數(shù)據(jù)。這一章主要是介紹下自用的一些比較常見(jiàn)的請(qǐng)求方式。例如:HttpURLConnection,HttpClient(目前被拋棄了),OKHttp,Volley,xutils請(qǐng)求,還有一些rxandroid不怎么用,這里就不介紹了。
1. 初步認(rèn)識(shí)HTTP協(xié)議
??Http協(xié)議內(nèi)容對(duì)于我們來(lái)說(shuō),寫(xiě)很多本書(shū)都可以了,我們這邊只是初步認(rèn)識(shí)下,了解一些協(xié)議的內(nèi)容。在Http協(xié)議中,主要的就是兩個(gè)端,客戶端(Client)和服務(wù)端(Server)。一個(gè)完整的http需要需要經(jīng)歷兩個(gè)過(guò)程:客戶端發(fā)送請(qǐng)求到服務(wù)端,服務(wù)端響應(yīng)并返回結(jié)果到客戶端。如下圖所示:
打開(kāi)瀏覽器,查看客戶端的請(qǐng)求(以百度為例):F12->Network->F5->Name第一個(gè)->Headers
-
客戶端——>服務(wù)端:
客戶端向服務(wù)器發(fā)送請(qǐng)求主要包含以下信息:請(qǐng)求的Url地址、請(qǐng)求頭以及可選的請(qǐng)求體,打開(kāi)百度首頁(yè)。
請(qǐng)求的URl(Request URL)
??上圖的中的url地址就是:百度的地址,后面可以沒(méi)有參數(shù),也可以通過(guò)?和&添加鍵值對(duì),一般情況下,URL的長(zhǎng)度不能超過(guò)2048個(gè)字符,即2KB,超過(guò)此限制的話服務(wù)器可能就不識(shí)別。請(qǐng)求頭(Request Headers)
??請(qǐng)求頭其實(shí)也是一些鍵值對(duì),不過(guò)這些鍵值通常都是W3C定義了的一些標(biāo)準(zhǔn)的Http請(qǐng)求頭的名稱,請(qǐng)求頭包含了客戶端想告訴服務(wù)端的一些元數(shù)據(jù)信息,注意是元數(shù)據(jù),而不是數(shù)據(jù),比如請(qǐng)求頭User-Agent會(huì)告訴服務(wù)器這條請(qǐng)求來(lái)自于什么瀏覽器,再比如請(qǐng)求頭Accept-Encoding會(huì)告訴服務(wù)器客戶端支持的壓縮格式。除了這些標(biāo)準(zhǔn)的請(qǐng)求頭,我們還可以添加自定義的請(qǐng)求頭。請(qǐng)求體(Request Body)
??之前我們提到,URL的最大長(zhǎng)度就是2048個(gè)字符,如果我們發(fā)送的數(shù)據(jù)很大,超過(guò)了2KB怎么辦?我們可以將很大的數(shù)據(jù)放到請(qǐng)求體中,GET請(qǐng)求不支持請(qǐng)求體,只有POST請(qǐng)求才能設(shè)置請(qǐng)求體。請(qǐng)求體中可以放置任意的字節(jié)流,從而可以很方便地發(fā)送任意格式的數(shù)據(jù),服務(wù)端只需要讀取該輸入流即可。服務(wù)端——>客戶端:
服務(wù)器接收數(shù)據(jù)請(qǐng)求之后會(huì)返回相應(yīng)的處理信息給客戶端
- 響應(yīng)頭(Response Headers)
??響應(yīng)頭包含了服務(wù)器想要告訴客戶端的一些元數(shù)據(jù)信息,注意不是數(shù)據(jù),是元數(shù)據(jù),比如通過(guò)響應(yīng)頭Content-Encoding告訴客戶端服務(wù)器所采用的壓縮格式,響應(yīng)頭Content-Type告訴客戶端響應(yīng)體是什么格式的數(shù)據(jù),再比如服務(wù)端可以通過(guò)多個(gè)Set-Cookie響應(yīng)頭向客戶端寫(xiě)入多條Cookie信息,等等。剛剛提到的幾個(gè)請(qǐng)求頭都是W3C規(guī)定的標(biāo)準(zhǔn)的請(qǐng)求頭名稱,我們也可以在服務(wù)端向客戶端寫(xiě)入自定義的響應(yīng)頭。
-
響應(yīng)體 (Response Body)
??響應(yīng)體是服務(wù)端向客戶端傳輸?shù)膶?shí)際的數(shù)據(jù)信息,本質(zhì)就是一堆字節(jié)流,可以表示文本,也可以表示圖片或者其他格式的信息,如下所示:
響應(yīng)體的內(nèi)容
- GET vs POST
??Http協(xié)議支持的操作有GET、POST、HEAD、PUT、TRACE、OPTIONS、DELETE,其中最最常用的還是GET和POST操作,下面我們看一下GET和POST的區(qū)別。
GET:
- GET請(qǐng)求可以被緩存。
- 鍵值對(duì)添加在URL上,隨著GET請(qǐng)求一起發(fā)送,一旦URL被攔截,發(fā)送的鍵值對(duì)信息被暴露,安全性低。
- 發(fā)送到數(shù)據(jù)長(zhǎng)度有限,不能超過(guò)2048個(gè)字符
- 只能獲取數(shù)據(jù),不能參與其他的操作。
POST:
- POST請(qǐng)求從不會(huì)被緩存。
- POST請(qǐng)求的URL中追加鍵值對(duì)參數(shù),不過(guò)這些鍵值對(duì)參數(shù)不是隨著URL發(fā)送的,而是被放入到請(qǐng)求體中發(fā)送的,這樣安全性稍微好一些。
- 可以用POST請(qǐng)求發(fā)送敏感信息
- 由于可以在請(qǐng)求體中發(fā)送任意的數(shù)據(jù),所以理論上POST請(qǐng)求不存在發(fā)送數(shù)據(jù)大小的限制。
- 當(dāng)執(zhí)行增減、刪除、修改等操作時(shí),應(yīng)該使用POST請(qǐng)求,而不應(yīng)該使用GET請(qǐng)求。
到這里就是協(xié)議的一些初步認(rèn)識(shí)。
2. HttpURLConnection
??在過(guò)去,Android上發(fā)送HTTP主要是HttpURLConnection和HttpClient這兩種方式。不過(guò)因?yàn)镠ttpClient存在一系列的問(wèn)題(API數(shù)量過(guò)多,向下兼容困難等),所以Android團(tuán)隊(duì)不建議我們使用這種方式,在Android 6.0系統(tǒng)中被完全移除,所以這個(gè)功能就別用了。這里介紹下HttpURLConnection的用法,HttpURLConnection的主要代碼就這些:
URL url= new URL("https://www.baidu.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//HttpURLConnection默認(rèn)就是用GET發(fā)送請(qǐng)求,所以下面的setRequestMethod可以省略
connection.setRequestMethod("GET");
//HttpURLConnection默認(rèn)也支持從服務(wù)端讀取結(jié)果流,所以下面的setDoInput也可以省略
connection.setDoInput(true);
//禁用網(wǎng)絡(luò)緩存
connection.setUseCaches(false);
//在對(duì)各種參數(shù)配置完成后,通過(guò)調(diào)用connect方法建立TCP連接,但是并未真正獲取數(shù)據(jù)
//conn.connect()方法不必顯式調(diào)用,當(dāng)調(diào)用conn.getInputStream()方法時(shí)內(nèi)部也會(huì)自動(dòng)調(diào)用connect方法
connection.connect();
connection.setConnectTimeout(3000);
connection.setReadTimeout(3000);
//調(diào)用getInputStream方法后,服務(wù)端才會(huì)收到請(qǐng)求,并阻塞式地接收服務(wù)端返回的數(shù)據(jù)
InputStream inputStream = connection.getInputStream();
//對(duì)獲取的輸入流進(jìn)行讀取
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder builder=new StringBuilder();
String line;
while((line= reader.readLine())!=null){
builder.append(line);
}
??首先得到一個(gè)URL對(duì)象,然后傳入需要傳入的地址,再調(diào)用openConnection()的方法,打開(kāi)連接。接下來(lái)就是自己的一些要求定制,設(shè)置POST或者GET的方式發(fā)送請(qǐng)求,設(shè)置連接超時(shí)時(shí)間,設(shè)置讀取超時(shí)時(shí)間。得到一個(gè)讀取builder.toString()的字符串。然后對(duì)這個(gè)字符串進(jìn)行處理操作,別忘了,在對(duì)異常處理完成之后記得添加關(guān)閉操作。下面是貼出完整的代碼:
1.activity_de_fault_http_urlconnection.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.demo.myhttpurlconnection.defaulthtu.DeFaultHttpURLConnectionActivity">
<TextView
android:id="@+id/tv_post_show"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="16sp"
/>
</android.support.constraint.ConstraintLayout>
- DeFaultHttpURLConnectionActivity ,請(qǐng)求網(wǎng)絡(luò)是耗時(shí)操作,所以要另開(kāi)線程,然后再在主線程中顯示。
public class DeFaultHttpURLConnectionActivity extends AppCompatActivity {
private TextView tv_post_show;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_de_fault_http_urlconnection);
tv_post_show = (TextView) findViewById(R.id.tv_post_show);
sendRequestWithHttpURLConnection();
}
private void sendRequestWithHttpURLConnection() {
//開(kāi)啟線程來(lái)請(qǐng)求網(wǎng)絡(luò)
new Thread(new Runnable() {
private BufferedReader reader;
private HttpURLConnection connection;
@Override
public void run() {
try {
URL url= new URL("https://www.baidu.com");
connection = (HttpURLConnection) url.openConnection();
//HttpURLConnection默認(rèn)就是用GET發(fā)送請(qǐng)求,所以下面的setRequestMethod可以省略
connection.setRequestMethod("GET");
//HttpURLConnection默認(rèn)也支持從服務(wù)端讀取結(jié)果流,所以下面的setDoInput也可以省略
connection.setDoInput(true);
//禁用網(wǎng)絡(luò)緩存
connection.setUseCaches(false);
//在對(duì)各種參數(shù)配置完成后,通過(guò)調(diào)用connect方法建立TCP連接,但是并未真正獲取數(shù)據(jù)
//conn.connect()方法不必顯式調(diào)用,當(dāng)調(diào)用conn.getInputStream()方法時(shí)內(nèi)部也會(huì)自動(dòng)調(diào)用connect方法
connection.connect();
connection.setConnectTimeout(3000);
connection.setReadTimeout(3000);
//調(diào)用getInputStream方法后,服務(wù)端才會(huì)收到請(qǐng)求,并阻塞式地接收服務(wù)端返回的數(shù)據(jù)
InputStream inputStream = connection.getInputStream();
//對(duì)獲取的輸入流進(jìn)行讀取
reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder builder=new StringBuilder();
String line;
while((line= reader.readLine())!=null){
builder.append(line);
}
showRespone(builder.toString());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(reader!=null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (connection!=null){
connection.disconnect();
}
}
}
}).start();
}
private void showRespone(final String s) {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_post_show.setText(s);
}
});
}
}
2. OkHttp
??OkHttp是目前市場(chǎng)上使用的比較多的一個(gè)網(wǎng)絡(luò)請(qǐng)求方式了。它不僅在接口封裝上做的簡(jiǎn)單易用,底層的實(shí)現(xiàn)也很特別。比起HttpURLConnection有過(guò)之而無(wú)不及。
2.1 使用OkHttp
在使用OkHttp 之前,我們需要添加依賴包:
compile 'com.squareup.okhttp3:okhttp:3.9.0'
??添加完依賴之后會(huì)自動(dòng)下載兩個(gè)庫(kù),一個(gè)是OkHttp庫(kù),另一個(gè)是Okio庫(kù),后者是hi前者的通信基礎(chǔ)。代碼很簡(jiǎn)單首先是創(chuàng)建一個(gè)OkHttpClient實(shí)例,然后再創(chuàng)建一個(gè)Request對(duì)象,自己添加想要的方法來(lái)豐富這個(gè)內(nèi)容。之后調(diào)用OkHttpClient的newCall(request)方法來(lái)獲得返回的對(duì)象。
public class OkHttpActivity extends AppCompatActivity {
private TextView tv_ok_show;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ok_http);
tv_ok_show = (TextView) findViewById(R.id.tv_ok_show);
new Thread(new Runnable() {
@Override
public void run() {
//創(chuàng)建一個(gè)OkHttpClient實(shí)例
OkHttpClient client = new OkHttpClient();
//需要發(fā)送請(qǐng)求就需要?jiǎng)?chuàng)建空的Request對(duì)象,在這里面添加方法來(lái)豐富這個(gè)內(nèi)容
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
//發(fā)出請(qǐng)求之后還需要獲取返回的數(shù)據(jù)
try {
Response response = client.newCall(request).execute();
//這個(gè)就是得到的返回?cái)?shù)據(jù)
String responseData = response.body().string();
tv_ok_show.setText(responseData);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
這里只是列出了一些最基本的用法,還有很多需要自己去探究。
布局文件很簡(jiǎn)單就一個(gè)TextView,這里就不貼出來(lái)了,效果如上圖所示。
3. Volley
3.1 Volley的基本用法
??Volley這種網(wǎng)絡(luò)請(qǐng)求方式也是很普遍的。很多程序員喜歡用這種數(shù)據(jù)通訊庫(kù),這里簡(jiǎn)單介紹下:
實(shí)現(xiàn)一個(gè)Volley的請(qǐng)求操作只需要三步:
- 創(chuàng)建一個(gè)RequestQueue對(duì)象
- 創(chuàng)建一個(gè)StringRequest對(duì)象
- 將StringRequest對(duì)象添加到RequestQueue對(duì)象中
下面是對(duì)這三步的分析:
1). 創(chuàng)建一個(gè)RequestQueue對(duì)象,RequestQueue是一個(gè)緩存隊(duì)列,可以緩存所有的HTTP請(qǐng)求,然后按照一定的算法并發(fā)地發(fā)出這些請(qǐng)求。RequestQueue內(nèi)部的設(shè)計(jì)就是非常合適高并發(fā)的,因此我們不必為每一次HTTP請(qǐng)求都創(chuàng)建一個(gè)RequestQueue對(duì)象,這是非常浪費(fèi)資源的,基本上在每一個(gè)需要和網(wǎng)絡(luò)交互的Activity中創(chuàng)建一個(gè)RequestQueue對(duì)象就足夠了。
//創(chuàng)建一個(gè)RequestQueue的對(duì)象
RequestQueue requestQueue = Volley.ewRequestQueue(getApplicationContext());
2). 創(chuàng)建一個(gè)StringRequest對(duì)象,這里new出一個(gè)StringRequest對(duì)象,構(gòu)造函數(shù)有四個(gè),第一個(gè)是設(shè)置GET或者POST的請(qǐng)求方式,第二個(gè)是URL地址,第三個(gè)是返回?cái)?shù)據(jù)成功的響應(yīng),第四個(gè)是返回?cái)?shù)據(jù)失敗的響應(yīng)。
StringRequest stringRequest = new StringRequest(Request.Method.GET,"http://www.weather.com.cn/data/cityinfo/101010100.html",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.e("tag","responese="+response);
tv_volley_show.setText(response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("tag","error="+error);
}
});
注意:以下是Volley的異常列表:
- AuthFailureError:如果在做一個(gè)HTTP的身份驗(yàn)證,可能會(huì)發(fā)生這個(gè)錯(cuò)誤。
- NetworkError:Socket關(guān)閉,服務(wù)器宕機(jī),DNS錯(cuò)誤都會(huì)產(chǎn)生這個(gè)錯(cuò)誤。
- NoConnectionError:和NetworkError類似,這個(gè)是客戶端沒(méi)有網(wǎng)絡(luò)連接。
- ParseError:在使用JsonObjectRequest或JsonArrayRequest時(shí),如果接收到的JSON是畸形,會(huì)產(chǎn)生異常。
- SERVERERROR:服務(wù)器的響應(yīng)的一個(gè)錯(cuò)誤,最有可能的302或者4xx或5xx HTTP狀態(tài)代碼。
- TimeoutError:Socket超時(shí),服務(wù)器太忙或網(wǎng)絡(luò)延遲會(huì)產(chǎn)生這個(gè)異常。默認(rèn)情況下,Volley的超時(shí)時(shí)間為2.5秒。如果得到這個(gè)錯(cuò)誤可以使用RetryPolicy。
3). 將StringRequest對(duì)象添加大RequestQueue對(duì)象中
//將StringRequest對(duì)象添加大RequestQueue對(duì)象中
requestQueue.add(stringRequest);
當(dāng)然在使用GET請(qǐng)求的時(shí)候,我們會(huì)直接拼接到URL上,如果是post請(qǐng)求的話,需要添加參數(shù)的時(shí)候怎么辦?這時(shí)候就需要重寫(xiě)Volley的getParams()的方法。
//創(chuàng)建一個(gè)StringRequest對(duì)象
StringRequest stringRequest = new StringRequest(Request.Method.GET,"http://www.baidu.com",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.e("tag","responese="+response);
tv_volley_show.setText(response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("tag","error="+error);
}
}){
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> map = new HashMap<String, String>();
map.put("params1", "value1");
map.put("params2", "value2");
return map;
}
};
3.2 Volley 中的 JsonRequest的用法
??客戶端以普通的post方式進(jìn)行提交,服務(wù)端返回json串。類似于StringRequest,JsonRequest也是繼承自Request類的,不過(guò)由于JsonRequest是一個(gè)抽象類,因此我們無(wú)法直接創(chuàng)建它的實(shí)例,那么只能從它的子類入手了。JsonRequest有兩個(gè)直接的子類,JsonObjectRequest和JsonArrayRequest,從名字上你應(yīng)該能就看出它們的區(qū)別了吧?一個(gè)是用于請(qǐng)求一段JSON數(shù)據(jù)的,一個(gè)是用于請(qǐng)求一段JSON數(shù)組的。
至于它們的用法也基本上沒(méi)有什么特殊之處,先new出一個(gè)JsonObjectRequest對(duì)象,如下所示:
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("http://www.weather.com.cn/data/cityinfo/101010100.html", null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.d("TAG", response.toString());
tv_volley_show.setText(response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
requestQueue.add(jsonObjectRequest);
返回的結(jié)果:
{"weatherinfo":{"city":"北京","cityid":"101010100","temp1":"-2℃","temp2":"16℃","weather":"晴","img1":"n0.gif","img2":"d0.gif","ptime":"18:00"}}
??服務(wù)器返回給我們的數(shù)據(jù)確實(shí)是JSON格式的,并且onResponse()方法中攜帶的參數(shù)也正是一個(gè)JSONObject對(duì)象,之后只需要從JSONObject對(duì)象取出我們想要得到的那部分?jǐn)?shù)據(jù)就可以了。
3.3 注意:
??注意,當(dāng)設(shè)置提交方式為POST的時(shí)候,我們必須會(huì)添加一些參數(shù)進(jìn)來(lái)拼接,但是會(huì)出一個(gè)小問(wèn)題,就是調(diào)用getParams()添加參數(shù)沒(méi)有作用。這時(shí)候我們必須得構(gòu)成JSONObject當(dāng)做實(shí)參傳入JsonObjectRequest對(duì)象里 ,所以getParams()這個(gè)方法是無(wú)效的。這時(shí)候需要這樣寫(xiě):
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
Map<String, String> map = new HashMap<String, String>();
map.put("name1", "value1");
map.put("name2", "value2");
JSONObject jsonObject = new JSONObject(params);
JsonRequest<JSONObject> jsonRequest = new JsonObjectRequest(Method.POST,httpurl, jsonObject,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.d(TAG, "response -> " + response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, error.getMessage(), error);
}
})
};
requestQueue.add(jsonRequest);
3.4 實(shí)際項(xiàng)目中這樣操作
1.首先定義一個(gè)NormalRequest類
public class NormalRequest extends Request<JSONObject> {
private Map<String,String> mMap;
private Response.Listener<JSONObject> mListener;
public NormalRequest(String url, Response.Listener<JSONObject> listener, Response.ErrorListener errorListener,Map<String, String> map) {
super(Method.POST,url, errorListener);
this.mListener= listener;
this.mMap= map;
}
//mMap是已經(jīng)按照前面的方式,設(shè)置了參數(shù)的實(shí)例
@Override
protected Map<String, String> getParams() throws AuthFailureError {
return mMap;
}
//此處為response返回值需要json數(shù)據(jù),和JsonObjectRequest類一樣即可
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONObject(jsonString),HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
@Override
protected void deliverResponse(JSONObject response) {
mListener.onResponse(response);
}
}
2.然后調(diào)用的時(shí)候
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext);
//因?yàn)闆](méi)找到有用的接口,所以拼接的時(shí)候參數(shù)要自己添加
//實(shí)際調(diào)用
Map<String, String> params = new HashMap<String, String>();
params.put("name1", "value1");
params.put("name2", "value2");
Request<JSONObject> request = new NormalRequest(url,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.d("TAG", response.toString());
tv_volley_show.setText(response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d("TAG", error.toString());
}
}, params);
requestQueue.add(request);
4. xutils
??xUtils 是一個(gè)工具類的集合體,其中包含許多實(shí)用的Android工具,主要包含四大模塊:
1、ViewUtils的模塊
2、HttpUtils的模塊
3、BitmapUtils的模塊
4、DBUtils的模塊
4.1 ViewUtils 模塊
Android中的ioc框架,完全注解方式就可以進(jìn)行UI、資源和事件綁定;新的事件綁定方式,使用混淆工具混淆后仍可正常工作;目前支持常用的20種事件綁定。
【備注:】
??控制反轉(zhuǎn)(Inversion of Control,英文縮寫(xiě)為IoC)是一個(gè)重要的面向?qū)ο缶幊痰姆▌t,用來(lái)削減計(jì)算機(jī)程序的耦合問(wèn)題,也是輕量級(jí)的Spring框架的核心。
??控制反轉(zhuǎn)一般分為兩種類型,依賴注入(Dependency Injection,簡(jiǎn)稱DI)和依賴查找(Dependency Lookup)。依賴注入應(yīng)用比較廣泛。
?? IoC中最基本的Java技術(shù)就是“反射”編程。通俗的說(shuō)反射就是根據(jù)給出的類名(字符串)來(lái)生成對(duì)象。這種編程方式可以讓對(duì)象在生成時(shí)才決定要生成哪一種對(duì)象。反射的應(yīng)用是很廣泛的,象Hibernate、Spring中都是用“反射”做為最基本的技術(shù)手段。
??在過(guò)去,反射編程方式相對(duì)于正常的對(duì)象生成方式要慢10幾倍,這也許也是當(dāng)時(shí)為什么反射技術(shù)沒(méi)有普遍應(yīng)用開(kāi)來(lái)的原因。但經(jīng)SUN改良優(yōu)化后,反射方式生成對(duì)象和通常對(duì)象生成方式,速度已經(jīng)相差不大了(但依然有一倍以上的差距)。
4.1.1 Viewutils模塊的使用
完全注解方式就可以進(jìn)行UI綁定和事件綁定。無(wú)需findViewById和setClickListener等。
- View控件注解:
// xUtils的view注解要求必須提供id,以使代碼混淆不受影響。
@ViewInject(R.id.textView)
TextView textView;
//@ViewInject(vale=R.id.textView, parentId=R.id.parentView)
//TextView textView;
- 字符串資源的注解
@ResInject(id = R.string.label, type = ResType.String)
private String label;
- View控件的OnClick回調(diào)方法的注解:
// 取消了之前使用方法名綁定事件的方式,使用id綁定不受混淆影響
// 支持綁定多個(gè)id @OnClick({R.id.id1, R.id.id2, R.id.id3})
// or @OnClick(value={R.id.id1, R.id.id2, R.id.id3}, parentId={R.id.pid1, R.id.pid2, R.id.pid3})
// 更多事件支持參見(jiàn)ViewCommonEventListener類和包c(diǎn)om.lidroid.xutils.view.annotation.event。
@OnClick(R.id.test_button)
public void testButtonClick(View v) { // 方法簽名必須和接口中的要求一致
...
}
4、在Activity中注入:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ViewUtils.inject(this); //注入view和事件
}
5、在Fragment中注入:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.bitmap_fragment, container, false); // 加載fragment布局
ViewUtils.inject(this, view); //注入view和事件
}
6、在PreferenceFragment中注入:
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ViewUtils.inject(this, getPreferenceScreen()); //注入view和事件
}
7、其他重載
// inject(View view);
// inject(Activity activity)
// inject(PreferenceActivity preferenceActivity)
// inject(Object handler, View view)
// inject(Object handler, Activity activity)
// inject(Object handler, PreferenceGroup preferenceGroup)
// inject(Object handler, PreferenceActivity preferenceActivity)
4.2 HttpUtils模塊
4.2.1 HttpUtils的功能
- 支持同步,異步方式的請(qǐng)求;
- 支持大文件上傳,上傳大文件不會(huì)OOM;
- 支持GET,POST,PUT,MOVE,COPY,DELETE,HEAD,OPTIONS,TRACE,CONNECT請(qǐng)求;
- 下載支持301/302重定向,支持設(shè)置是否根據(jù)Content-Disposition重命名下載的文件;
- 返回文本內(nèi)容的請(qǐng)求(默認(rèn)只啟用了GET請(qǐng)求)支持緩存,可設(shè)置默認(rèn)過(guò)期時(shí)間和針對(duì)當(dāng)前請(qǐng)求的過(guò)期時(shí)間。
4.2.2 HttpUtils使用:
- 普通GET方法:
HttpUtils http = new HttpUtils();
http.send(HttpRequest.HttpMethod.GET,
"http://www.lidroid.com",
new RequestCallBack<String>(){
@Override
//第一個(gè)參數(shù):下載或上傳文件的總長(zhǎng)度,第二個(gè)參數(shù):下載或上傳文件的當(dāng)前進(jìn)度,第三個(gè)參數(shù):是否為上傳
public void onLoading(long total, long current, boolean isUploading) {
testTextView.setText(current + "/" + total);
}
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
textView.setText(responseInfo.result);
}
@Override
public void onStart() {
}
@Override
public void onFailure(HttpException error, String msg) {
}
});
【回調(diào)方法說(shuō)明:】
1). onStart() 開(kāi)始執(zhí)行之前的回調(diào)方法
2). onSuccess(ResponseInfo<T> responseInfo) 請(qǐng)求成功的回調(diào)方法
3). onFailure(HttpException error, String msg) 請(qǐng)求失敗的回調(diào)方法
4). onLoading(long total, long current, boolean isUploading) 正在執(zhí)行的回調(diào)方法
5). onCancelled() 取消請(qǐng)求的回調(diào)方法
- 使用HttpUtils上傳文件 或者 提交數(shù)據(jù) 到服務(wù)器(post方法)
RequestParams params = new RequestParams();
params.addHeader("name", "value");
params.addQueryStringParameter("name", "value");
// 只包含字符串參數(shù)時(shí)默認(rèn)使用BodyParamsEntity,
// 類似于UrlEncodedFormEntity("application/x-www-form-urlencoded")。
params.addBodyParameter("name", "value");
// 加入文件參數(shù)后默認(rèn)使用MultipartEntity("multipart/form-data"),
// 如需"multipart/related",xUtils中提供的MultipartEntity支持設(shè)置subType為"related"。
// 使用params.setBodyEntity(httpEntity)可設(shè)置更多類型的HttpEntity(如:
// MultipartEntity,BodyParamsEntity,FileUploadEntity,InputStreamUploadEntity,StringEntity)。
// 例如發(fā)送json參數(shù):params.setBodyEntity(new StringEntity(jsonStr,charset));
params.addBodyParameter("file", new File("path"));
...
HttpUtils http = new HttpUtils();
http.send(HttpRequest.HttpMethod.POST,
"uploadUrl....",
params,
new RequestCallBack<String>() {
@Override
public void onStart() {
testTextView.setText("conn...");
}
@Override
public void onLoading(long total, long current, boolean isUploading) {
if (isUploading) {
testTextView.setText("upload: " + current + "/" + total);
} else {
testTextView.setText("reply: " + current + "/" + total);
}
}
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
testTextView.setText("reply: " + responseInfo.result);
}
@Override
public void onFailure(HttpException error, String msg) {
testTextView.setText(error.getExceptionCode() + ":" + msg);
}
});
【文件上傳的send()方法參數(shù)說(shuō)明:】
第一個(gè)參數(shù):請(qǐng)求方法,在此使用HttpMethod.POST
第二個(gè)參數(shù):上傳請(qǐng)求的網(wǎng)絡(luò)路徑
第三個(gè)參數(shù):上傳的數(shù)據(jù),包含基本信息和上傳文件信息
第四個(gè)參數(shù):請(qǐng)求的回調(diào)接口
- 使用HttpUtils下載文件:【支持?jǐn)帱c(diǎn)續(xù)傳,隨時(shí)停止下載任務(wù),開(kāi)始任務(wù)】
HttpUtils http = new HttpUtils();
HttpHandler handler = http.download("http://apache.dataguru.cn/httpcomponents/httpclient/source/httpcomponents-client-4.2.5-src.zip",
"/sdcard/httpcomponents-client-4.2.5-src.zip",
true, // 如果目標(biāo)文件存在,接著未完成的部分繼續(xù)下載。服務(wù)器不支持RANGE時(shí)將從新下載。
true, // 如果從請(qǐng)求返回信息中獲取到文件名,下載完成后自動(dòng)重命名。
new RequestCallBack<File>() {
@Override
public void onStart() {
testTextView.setText("conn...");
}
@Override
public void onLoading(long total, long current, boolean isUploading) {
testTextView.setText(current + "/" + total);
}
@Override
public void onSuccess(ResponseInfo<File> responseInfo) {
testTextView.setText("downloaded:" + responseInfo.result.getPath());
}
@Override
public void onFailure(HttpException error, String msg) {
testTextView.setText(msg);
}
});
...
//調(diào)用cancel()方法停止下載
handler.cancel();
【文件下載download()方法參數(shù)說(shuō)明:】
第一個(gè)參數(shù)url:網(wǎng)絡(luò)資源地址
第二個(gè)參數(shù)target: 本地存儲(chǔ)位置,如/mnt/sdcard/aa.mp3
第三個(gè)參數(shù)autoResume:是否繼續(xù)下載(斷點(diǎn)續(xù)傳)
第四個(gè)參數(shù)autoRename:從請(qǐng)求返回信息中獲取文件名,下載完成后是否重命名
第五個(gè)參數(shù):異步請(qǐng)求的回調(diào)接口
四、BitmapUtils模塊:
(一)、簡(jiǎn)介:
1、加載Bitmap的時(shí)候無(wú)需考慮Bitmap加載過(guò)程中出現(xiàn)的OOM和Android容器快速滑動(dòng)時(shí)候出現(xiàn)的圖片錯(cuò)位等現(xiàn)象;
2、支持加載網(wǎng)絡(luò)圖片和本地圖片;
3、內(nèi)存管理使用LRU算法,更好的管理bitmap內(nèi)存;
4、可配置線程加載線程數(shù)量,緩存大小,緩存路徑,加載顯示動(dòng)畫(huà)等。
4.3 BitmapUtils 使用:
1、加載網(wǎng)絡(luò)圖片
BitmapUtils bitmapUtils = new BitmapUtils(this);
bitmapUtils.display(testImageView, "http://bbs.lidroid.com/static/image/common/logo.png");
2、加載本地圖片(路徑以/開(kāi)頭, 絕對(duì)路徑)
BitmapUtils bitmapUtils = new BitmapUtils(this);
bitmapUtils.display(testImageView, "/sdcard/test.jpg");
3、加載assets中的圖片(路徑以assets開(kāi)頭)
BitmapUtils bitmapUtils = new BitmapUtils(this);
bitmapUtils.display(testImageView, "assets/img/wallpaper.jpg");
4、使用ListView等容器展示圖片時(shí)可通過(guò)PauseOnScrollListener控制滑動(dòng)和快速滑動(dòng)過(guò)程中時(shí)候暫停加載圖片
BitmapUtils bitmapUtils = new BitmapUtils(this);
listView.setOnScrollListener(new PauseOnScrollListener(bitmapUtils, false, true));
listView.setOnScrollListener(new PauseOnScrollListener(bitmapUtils, false, true, customListener));
- BitmapUtils中的config系列方法:
1). BitmapGlobalConfig配置
線程加載線程數(shù)量 bUtils.configThreadPoolSize(5); //配置線程池大小
配置緩存
路徑:/data/data/{package}/cache/xx
bUtils.configMemoryCacheEnabled(true)
bUtils.configDefaultCacheExpiry(100*1024); //100k
加載顯示動(dòng)畫(huà) bUtils.configDefaultImageLoadAnimation(Animation)
2). BitmapDisplayConfig配置
圖片寬高
bUtils.configDefaultBitmapMaxSize(int w,int h)
bUtils.configDefaultBitmapMaxSize(BitmapSize bs)
new BitmapSize(int w,int h) 指定寬和高
BitmapCommonUtils.getScreenSize(context) 依據(jù)屏幕尺寸
默認(rèn)顯示圖片
bUtils.configDefaultLoadingImage(int resId)
bUtils.configDefaultLoadingImage(Bitmap b)
bUtils.configDefaultLoadingImage(Drawable d)
下載失敗圖片
bUtils.configDefaultLoadFailedImage(int resId)
bUtils.configDefaultLoadFailedImage(Bitmap b)
bUtils.configDefaultLoadFailedImage(Drawable d)
圖片保存質(zhì)量
bUtils.configDefaultBitmapConfig(Bitmap.Config.RGB_565);
4.4 DbUtils模塊:
4.4.1 DbUtils的功能
- Android中的ORM框架,一行代碼就可以進(jìn)行增刪改查;
- 支持事務(wù),默認(rèn)關(guān)閉;
- 可通過(guò)注解自定義表名,列名,外鍵,唯一性約束,NOT NULL約束,CHECK約束等(需要混淆的時(shí)候請(qǐng)注解表名和列名);
- 支持綁定外鍵,保存實(shí)體時(shí)外鍵關(guān)聯(lián)實(shí)體自動(dòng)保存或更新;
- 自動(dòng)加載外鍵關(guān)聯(lián)實(shí)體,支持延時(shí)加載;
- 支持鏈?zhǔn)奖磉_(dá)查詢,更直觀的查詢語(yǔ)義,參考下面的介紹或sample中的例子。
4.4.2 DbUtils 使用
DbUtils db = DbUtils.create(this);
User user = new User(); //這里需要注意的是User對(duì)象必須有id屬性,或者有通過(guò)@ID注解的屬性
user.setEmail("wyouflf@qq.com");
user.setName("wyouflf");
db.save(user); // 使用saveBindingId保存實(shí)體時(shí)會(huì)為實(shí)體的id賦值
// 查找
Parent entity = db.findById(Parent.class, parent.getId());
List<Parent> list = db.findAll(Parent.class);//通過(guò)類型查找
Parent Parent = db.findFirst(Selector.from(Parent.class).where("name","=","test"));
// IS NULL
Parent Parent = db.findFirst(Selector.from(Parent.class).where("name","=", null));
// IS NOT NULL
Parent Parent = db.findFirst(Selector.from(Parent.class).where("name","!=", null));
// WHERE id<54 AND (age>20 OR age<30) ORDER BY id LIMIT pageSize OFFSET pageOffset
List<Parent> list = db.findAll(Selector.from(Parent.class)
.where("id" ,"<", 54)
.and(WhereBuilder.b("age", ">", 20).or("age", " < ", 30))
.orderBy("id")
.limit(pageSize)
.offset(pageSize * pageIndex));
// op為"in"時(shí),最后一個(gè)參數(shù)必須是數(shù)組或Iterable的實(shí)現(xiàn)類(例如List等)
Parent test = db.findFirst(Selector.from(Parent.class).where("id", "in", new int[]{1, 2, 3}));
// op為"between"時(shí),最后一個(gè)參數(shù)必須是數(shù)組或Iterable的實(shí)現(xiàn)類(例如List等)
Parent test = db.findFirst(Selector.from(Parent.class).where("id", "between", new String[]{"1", "5"}));
DbModel dbModel = db.findDbModelAll(Selector.from(Parent.class).select("name"));//select("name")只取出name列
List<DbModel> dbModels = db.findDbModelAll(Selector.from(Parent.class).groupBy("name").select("name", "count(name)"));
...
List<DbModel> dbModels = db.findDbModelAll(sql); // 自定義sql查詢
db.execNonQuery(sql) // 執(zhí)行自定義sql
核心代碼:
private void initDbUtils() {
DaoConfig config = new DaoConfig(getApplicationContext());
config.setDbName("db_student.db");
config.setDbVersion(3);
config.setDbUpgradeListener(new DbUpgradeListener() {
@Override
public void onUpgrade(DbUtils db, int oldVersion, int newVersion) {
if (newVersion > oldVersion) {
try {
db.dropDb();
} catch (DbException e) {
e.printStackTrace();
}
}
}
});
dbUtils = DbUtils.create(config);
dbUtils.configAllowTransaction(true);
dbUtils.configDebug(true);
try {
dbUtils.createTableIfNotExist(Student.class);
} catch (DbException e) {
e.printStackTrace();
}
}
DbUtils中常用的CRUD操作方法:
1、insert:
dbUtils.save(student);
2、delete:
方法1: dbUtils.delete(Student.class, WhereBuilder.b("_id", "=", id));
方法2: dbUtils.deleteById(Student.class, id);
方法3: dbUtils.execNonQuery("delete from tb_student where _id=" + id);
方法4: dbUtils.delete(student);
3、select:
dbUtils.findAll(Student.class);
dbUtils.findAll(Selector.from( Student.class).where("_id", "=", id));
dbUtils.findById();
dbUtils.execQuery();
4、update:
dbUtils.update(student);
dbUtils.update(student, WhereBuilder.b("_id", "=", id), "username", "password", "age");
4.5 其他功能:
4.5.1 輸出日志LogUtils:
1、自動(dòng)添加TAG,格式: className.methodName(L:lineNumber)
2、可設(shè)置全局的LogUtils.allowD = false,LogUtils.allowI = false...,控制是否輸出log。
3、自定義log輸出LogUtils.customLogger = new xxxLogger();
LogUtils.d("wyouflf");
4.5.2 權(quán)限:
1、使用xUtils快速開(kāi)發(fā)框架需要有以下權(quán)限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
4.5.3 混淆時(shí)注意事項(xiàng):
1、添加Android默認(rèn)混淆配置${sdk.dir}/tools/proguard/proguard-android.txt
2、不要混淆xUtils中的注解類型,添加混淆配置:-keep class * extends java.lang.annotation.Annotation { *; }
對(duì)使用DbUtils模塊持久化的實(shí)體類不要混淆,或者注解所有表和列名稱@Table(name="xxx"),@Id(column="xxx"),@Column(column="xxx"),@Foreign(column="xxx",foreign="xxx");
參考文章:http://blog.csdn.net/iispring/article/details/51474529
參考文章:http://blog.csdn.net/qq_35550937/article/details/52069302