Android應用開發-網路程式設計(一)
網路圖片檢視器
1. 確定圖片的網址
2. 傳送http請求
URL url = new URL(address); // 獲取客戶端和伺服器的連線物件,此時還沒有建立連線 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 設定請求方式,注意必須大寫 conn.setRequestMethod("GET"); // 設定連線和讀取超時 conn.setConnectTimeout(5000); conn.setReadTimeout(5000); // 傳送請求,與伺服器建立連線 conn.connect();// 如果響應碼為200,說明請求成功 if(conn.getResponseCode() == 200){ }
3. 伺服器的圖片是以流的形式返回給瀏覽器的
InputStream is = conn.getInputStream(); // 拿到伺服器返回的輸入流 Bitmap bm = BitmapFactory.decodeStream(is);// 把流裡的資料讀取出來,並構造成點陣圖物件
4. 把點陣圖物件顯示至ImageView
ImageView iv = (ImageView) findViewById(R.id.iv); iv.setImageBitmap(bm);
需要新增許可權
<uses-permission android:name="android.permission.INTERNET"/>
網路請求
主執行緒阻塞
在Android中,主執行緒被阻塞會導致UI停止重新整理,使用者體驗將非常差,若主執行緒阻塞時間過長,就會丟擲ANR(Application Not Responding,即應用無響應)異常。因此任何耗時操作都不應該在主執行緒進行,否則可能使主執行緒阻塞。因為網路請求屬於耗時操作,如果網速很慢,執行緒會被阻塞,所以網路請求的程式碼不能寫在主執行緒中。
訊息傳遞機制
-
主執行緒又稱UI執行緒,因為只有在主執行緒中才能重新整理UI。如果需要在子執行緒中重新整理UI,需要藉助Handler的訊息傳遞機制
-
主執行緒建立時,系統會為主執行緒建立一個Looper(ActivityThread中的main方法中依次呼叫Looper.prepareMainLooper(),Looper.loop()),而Looper物件在初始化時會建立一個與之關聯的MessageQueue
-
如果是子執行緒的話,需要我們自己在子執行緒呼叫Looper.prepare()來為子執行緒建立一個Looper(Looper物件在初始化時仍會建立一個與之關聯的MessageQueue),然後再呼叫Looper.loop()來啟動這個Looper
-
Looper.loop()使用一個死迴圈不斷的取出MessageQueue中的Message,然後將Message分發給曾經發送它的Handler進行處理(如果MessageQueue中沒有Message,loop()方法會暫時阻塞,實際上Android系統的UI執行緒始終處於loop死迴圈中,一旦退出這個訊息迴圈,App也就退出了)
-
Handler收到Message後會回撥它的handleMessage()來處理這條Message。如果這個handleMessage()方法執行在主執行緒中,就可以重新整理UI
/** * 呼叫預設的構造器new一個Handler會將它與所在的執行緒關聯起來 * 如果Handler關聯的執行緒沒有Looper,就會丟擲如下異常 * java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() * 這裡是在主執行緒中直接new一個Handler,不會拋異常,且handleMessage()可以重新整理UI */ android.os.Handler handler = new android.os.Handler(){ public void handleMessage(Message msg) { } };
-
在子執行緒中向Handler所線上程的MessageQueue裡傳送Message
Message msg = handler.obtainMessage();// 這樣建立Message物件比直接new更節省空間 msg.obj = bm; // obj欄位可以賦值任何物件,用來攜帶資料 msg.what = 1; // what欄位相當於一個標籤,用來區分出不同的Message,從而進行不同的處理 handler.sendMessage(msg);
-
通過switch語句區分不同的Message
public void handleMessage(android.os.Message msg) { switch (msg.what) { // 如果是1,說明是請求成功的Message case 1: ImageView iv = (ImageView) findViewById(R.id.iv); Bitmap bm = (Bitmap) msg.obj; iv.setImageBitmap(bm); break; case 2: Toast.makeText(MainActivity.this, "請求失敗", 0).show(); break; } }
加入快取圖片的功能
讀取伺服器返回的流裡的資料,把資料寫到本地檔案快取起來
InputStream is = conn.getInputStream(); FileOutputStream fos = new FileOutputStream(file); byte[] b = new byte[1024]; int len = 0; while((len = is.read(b)) != -1){ fos.write(b, 0, len); } fos.close();
讀取快取的資料,並構造成點陣圖物件
Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
每次傳送請求前檢測一下在快取中是否存在同名圖片,如果存在,則讀取快取
Html原始檔檢視器
傳送GET請求
URL url = new URL(path); //獲取連線物件,此時還未建立連線 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //設定連線屬性 conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); // 可以不寫conn.connect(); // 如果不寫conn.connect();,getResponseCode()會先建立連線,然後獲得響應碼 if(conn.getResponseCode() == 200){ }
獲取伺服器返回的流,從流中把html原始碼讀取出來
InputStream is = conn.getInputStream(); byte[] b = new byte[1024]; int len = 0; ByteArrayOutputStream bos = new ByteArrayOutputStream(); while((len = is.read(b)) != -1){ //把讀到的位元組先寫入位元組陣列輸出流中存起來 bos.write(b, 0, len); } //把位元組陣列輸出流中的內容轉換成字串 //Android系統預設使用utf-8編碼 text = new String(bos.toByteArray());
亂碼的處理
亂碼的出現是因為伺服器端和客戶端碼錶不一致所致
text = new String(bos.toByteArray(), "gb2312");// 手動指定碼錶
提交資料
GET方式提交資料
GET方式提交的資料是直接拼接在url的末尾
final String path = "http://192.168.1.104/Web/servlet/CheckLogin?name=" + name + "&pass=" + pass;
傳送GET請求,程式碼和之前一樣
URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setReadTimeout(5000); conn.setConnectTimeout(5000); if(conn.getResponseCode() == 200){ }
瀏覽器在傳送請求攜帶資料時會對資料進行URL編碼,我們寫程式碼時也需要為中文進行URL編碼(這裡使用者名稱name使用了中文)
final String path = "http://192.168.1.104/Web/servlet/CheckLogin?name=" + URLEncoder.encode(name) + "&pass=" + pass;
POST方式提交資料
POST提交資料是用流寫給伺服器的。協議頭中多了兩個屬性:
Content-Type: application/x-www-form-urlencoded,描述提交的資料的mimetype
Content-Length: 32,描述提交的資料的長度
// 給請求頭新增post多出來的兩個屬性 String data = "name=" + URLEncoder.encode(name) + "&pass=" + pass; conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setRequestProperty("Content-Length", data.length() + "");
設定允許開啟POST請求的流
conn.setDoOutput(true);
獲取連線物件的輸出流,往流裡寫要提交給伺服器的資料
OutputStream os = conn.getOutputStream();
os.write(data.getBytes());