1. 程式人生 > >Android 網路請求登入後更新頁面實現 Handler+HTTP請求詳解

Android 網路請求登入後更新頁面實現 Handler+HTTP請求詳解

為了實現登入功能,我們需要一下幾步:

1、獲取UI資料,並向伺服器傳送請求

2、等待返回資料,解析

3、將返回資料更新到UI執行緒中

為了完成以上幾步,我根據每步的功能提出自己的解決方法,順便整理出對應的知識供大家參考。

Handler

眾所周知,Android程式執行會開啟一個UI執行緒,也就是主執行緒,用於處理UI事件。只有在UI執行緒中才能進行對UI的各種操作,如果在非UI執行緒中直接對介面元素進行操作,會報錯。這是對與獲取網路請求並更新UI頁面這樣的需求來說只能將程式碼寫到UI執行緒中,這樣才能更新UI執行緒。但是對於這種網路請求或者是耗時的工作,由於執行時間的不可確定性,可能會在執行程式碼時阻塞。要是將這樣的程式碼寫到UI主執行緒中,就會造成ANR(application not responding,如果UI執行緒阻塞超過幾秒(現在一般是5秒),使用者就會看到應用無響應的Dialog

)異常,也就是程式無響應,影響客戶體驗。所以子啊Android 4.0之後,Android就不允許在主執行緒中訪問網路,否則會報NetworkOnMainThreadException異常。這時另一個方法是新開一個子執行緒,用於網路訪問,並將獲取的資料傳送給主執行緒。而子執行緒和UI執行緒之間進行通訊的機制就是Handler。

說起Handler,就不得不提Message、MessageQueue以及Looper。

  • Handler:非同步回撥機制。作用就是在子執行緒中傳送資料,通過sendMessage()傳送資料;在UI執行緒中接收資料,通過重寫handlerMessage方法。如果希望Handler正常工作,在當前執行緒中要有一個Looper物件
  • Looper:每個執行緒只能夠有一個Looper,管理MessageQueue,不斷地從中取出Message分發給對應的Handler處理!
  • MessageQueue:訊息佇列,先進先出管理Message,在初始化Looper物件時會建立一個與之關聯的MessageQueue;
  • Message:Handler接收與處理的訊息物件

  通俗一點講:當我們的子執行緒想修改Activity中的UI元件時,我們可以新建一個Handler物件,通過這個物件向主執行緒傳送資訊;而我們傳送的資訊會先到主執行緒的MessageQueue進行等待,由Looper按先入先出順序取出,再根據message物件的what屬性分發給對應的Handler進行處理!

更多關於這四者直接關係,如何呼叫的可以看這篇部落格

反正想要進行網路連線,必須在主執行緒中使用Handler機制,獲取到子執行緒發來的資料。我們在Android定義一個按鈕點選事件,當點選時,向伺服器傳送請求,並用handler更新UI。程式碼如下:

 subBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String uname = unameEt.getText().toString().trim();//獲取使用者名稱,密碼字元資料
                String upwd = upwdEt.getText().toString().trim();
                Handler handler = new Handler() {
                    public void handleMessage(Message msg) {
                        //這裡可以進行對UI的操作
                        }
                };
                String url = "http://192.168.0.1:8080/MyDome/jaxrs/userservice/getuser/" + uname + "," + upwd;
                MyHttpClient hc = new MyHttpClient();
                hc.setUrl(url);
                hc.setHandler(handler);
                hc.start(); 
                hc.interrupt();

            }
        });

subBtn是我定義的登入介面的登入按鈕,可以看出來點選登入按鈕後獲取到EditView輸入的使用者名稱和密碼,並定義了Handler,其中handleMessage(Message msg)方法中的msg就是寫完子執行緒後在子執行緒中handler的sendMessage(Message msg)所傳過來的msg資訊,handleMessage方法中就可以對UI執行緒進行操作了。

之後是我自己定義了一個類,他繼承了Thread,可以看到後面有.start()方法。後面是對物件設定兩個引數,一個是向伺服器傳送的請求地址,一個是handler。下面就來看看MyHttpClient類。

HTTP網路請求

Android系統提供一下3種通訊介面:

  • 標準Java介面:java.net;
  • Apache介面:org.apache.http;
  • Android網路介面:android.net,http

其中使用最多的是Apache介面,下面就是以這個介面為例

Apache的核心功能是HttpClient,和網路有關的功能幾乎都是要用到HttpClient來實現。

HttpClient client = new DefaultHttpClient();

如果想從伺服器檢索資訊,則需要使用HttpGet類的構造器,例如下面的程式碼:

HttpGet request = new HttpGet(“輸入伺服器網址”);

然後用HttpClient類的execute()方法中的HttpGet物件來檢索HttpResponse物件,例如程式碼

HttpResponse response = client.execute(request);

更多請看:

MyHttpClient

public class MyHttpClientUtils extends Thread {
    private String url = "";
    private Handler handler = null;


    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Handler getHandler() {
        return handler;
    }

    public void setHandler(Handler handler) {
        this.handler = handler;
    }

    @Override
    public void run() {
        HttpGet request = new HttpGet(url);
        HttpResponse response = null;
        Message message = new Message();
        Bundle bundle = new Bundle();
        message.setData(bundle);
        
        ArrayList<Map<String,String>> data = new ArrayList<Map<String, String>>();
        try{
            HttpClient client = new DefaultHttpClient();
            response = client.execute(request);
            if(response.getStatusLine().getStatusCode() == 200){//200伺服器返回正常
                String resultStr = EntityUtils.toString(response.getEntity(),"UTF-8");//將獲取的資料放到Entity中
                Map<String,String> jsonMap = new HashMap<String,String>();
                JSONObject jsonObject = new JSONObject(resultStr);//
                Iterator<String> keys = jsonObject.keys();//獲得key獲得物件第一個isRename
                if (keys.hasNext()){
                    String str = jsonObject.getString(keys.next());//獲得login後面的數
                    JSONObject temp = new JSONObject(str);//str = {"uid":"6","uname":"a","upwd":"a"}
                    Iterator<String> tempKeys = temp.keys();
                    while(tempKeys.hasNext()){
                         String key = tempKeys.next();
                         jsonMap.put(key,temp.getString(key));
                         }
                        data.add(jsonMap);

                    }
                }
              bundle.putSerializable("res",data);
              handler.sendMessage(message);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
Map<String,String> jsonMap = new HashMap<String,String>();

設定網路超時版本:

@Override
    public void run() {
        //獲取地址
        Log.d(TAG,"最大空間:" + Runtime.getRuntime().maxMemory() );
        HttpGet request = new HttpGet(url);
        HttpResponse response = null;
        Message message = new Message();
        Bundle bundle = new Bundle();
        message.setData(bundle);

        Log.d(TAG,"最大空間:" + Runtime.getRuntime().maxMemory() );
        ArrayList<Map<String,String>> data = new ArrayList<Map<String, String>>();
        try{
            //設定網路超時
            int REQUEST_TIMEOUT = 10*1000;//設定請求超時10秒鐘
            int SO_TIMEOUT = 10*1000;  //設定等待資料超時時間10秒鐘
            BasicHttpParams httpParams = new BasicHttpParams();
            HttpConnectionParams.setConnectionTimeout(httpParams, REQUEST_TIMEOUT);
            HttpConnectionParams.setSoTimeout(httpParams, SO_TIMEOUT);
            HttpClient client = new DefaultHttpClient(httpParams);
            response = client.execute(request);
            if(response.getStatusLine().getStatusCode() == 200){
                String resultStr = EntityUtils.toString(response.getEntity(),"UTF-8");//漢子要轉字符集
                System.out.print(resultStr);
                //"{\"isReName\":[{\"state\":\"1\"}]}"
                /*
                * {"reUser":
                * [{"uid":"1","uname":"w","upwd":"w"},
                * {"uid":"2","uname":"1","upwd":"1"},
                * {"uid":"3","uname":"qssss","upwd":"qasd"},
                * {"uid":"4","uname":"qq","upwd":"qq"},
                * {"uid":"5","uname":"e","upwd":"e"},
                * {"uid":"6","uname":"a","upwd":"a"},
                * {"uid":"7","uname":"d","upwd":"d"}]
                * }
                * */
                JSONObject jsonObject = new JSONObject(resultStr);//
                Iterator<String> keys = jsonObject.keys();//獲得key獲得物件第一個isRename....keys會有很多
                if (keys.hasNext()){//有第一個key
                    String str = jsonObject.getString(keys.next());//座標移到第一個key上,keys。next()獲取key值。獲取第一個key的值

                    if(str.indexOf("[") == 0){
                        JSONArray jsonArray = new JSONArray(str);//[{"uid":"6","uname":"a","upwd":"a"},{"uid":"6","uname":"a","upwd":"a"}]
                        for(int i = 0;i < jsonArray.length() ; i++){
                            Map<String,String> jsonMap = new HashMap<String, String>();
                            JSONObject temp = new JSONObject(jsonArray.get(i).toString());//獲得values裡的key值
                            Iterator<String> tempKeys = temp.keys();
                            while(tempKeys.hasNext()){
                                String key = tempKeys.next();
                                jsonMap.put(key,temp.getString(key));//獲得數組裡面的values值
                            }
                            data.add(jsonMap);
                        }
                    }else{
                        //{"reUser":{"uid":"6","uname":"a","upwd":"a"}}
                        Map<String,String> jsonMap = new HashMap<String,String>();
                        JSONObject temp = new JSONObject(str);//str = {"uid":"6","uname":"a","upwd":"a"}
                        Iterator<String> tempKeys = temp.keys();
                        while(tempKeys.hasNext()){
                            String key = tempKeys.next();
                            jsonMap.put(key,temp.getString(key));
                        }
                        data.add(jsonMap);
                    }



                }
              // System.out.println("sssss ----" + data.get(0).get("state").toString());
              //  System.out.println(" state------- " + data.get("state").toString());
                bundle.putSerializable("res",data);
                handler.sendMessage(message);
                //result有值
            }
        }catch (Exception ex){
            Map<String,String> info = new HashMap<String, String>();
            info.put("connectInfo","null");
            data.add(info);

            bundle.putSerializable("res",data);
            handler.sendMessage(message);

            Log.d(TAG,"連線超時");
        }
    }

類中定義兩個屬性一個url一個handler用於獲取資訊。整個類繼承Thread,在run()方法中進行網路訪問
Message message = new Message();
Bundle bundle = new Bundle();
message.setData(bundle);

定義Message用於handler的傳輸,使用Bundle來放置鍵值對值,使用Message的setData()方法來關聯到bundle

String resultStr = EntityUtils.toString(response.getEntity(),"UTF-8");//漢子要轉字符集
獲取resulStr就是網路Get請求後返回來的值,我寫的伺服器是返回Json字串。根據resulStr來解析Json字串就可以得到返回資訊。返回的字串樣式是

{"reUser":{"uid":"1","uname":"w","upwd":"w"}}

JSONObject jsonObject = new JSONObject(resultStr);//將字串變為json物件
                Iterator<String> keys = jsonObject.keys();//獲得這個物件中的所有key,這裡只有一個key就是reUser
                if (keys.hasNext()){//是否存在key
                    String str = jsonObject.getString(keys.next());//獲得第一個key的值,這裡就是{"uid":"1","uname":"w","upwd":"w"}
                    JSONObject temp = new JSONObject(str);//str ={"uid":"1","uname":"w","upwd":"w"}。再將他轉為json物件
                  Map<String,String> jsonMap = new HashMap<String,String>();
                 Iterator<String> tempKeys = temp.keys();//獲取所有key,這裡是uid uname
while(tempKeys.hasNext()){ String key = tempKeys.next(); jsonMap.put(key,temp.getString(key));     } data.add(jsonMap);//將資料放入Bundle中 } }
bundle.putSerializable("res",data);
              handler.sendMessage(message);

最後利用bundle將資料轉Serializable序列化,通過handle.sendMessage將資料傳到UI執行緒中

在Handler的handleMessage方法中使用
ArrayList<Map<String, String>> data = (ArrayList<Map<String, String>>) msg.getData().getSerializable("res");
進行msg的解析