1. 程式人生 > >Android應用開發-網路程式設計(一)

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());