Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android基礎筆記

Android基礎筆記

編輯:關於Android編程

Android下訪問網絡資源和一些注意事項 Android下異步消息處理線程技術 Android下異步消息處理線程技術的基本原理 模仿新聞客戶端小案例 GET方式提交數據到服務器 POST方式提交數據到服務器

\

Android下訪問網絡資源和一些注意事項

這裡只說明了最簡單的網絡訪問資源的方式,與服務端交互在後面講述。

Andriod中訪問網絡中資源的基本步驟 ① 通過調用URL.openConnection()獲取一個HttpURLConnection連接對象 ② 設置一些鏈接對象的配置參數,比如:超時時間和請求方式;HttpURLConnection中默認的請求方式是GET ③ 獲取服務端響應碼,並根據響應碼進行操作 ④ 響應成功,拿到響應流conn.getInputStream(),並進行處理。

代碼如下

try {
    URL url = new URL(path);
    // 1. 獲取一個HttpURLConnection鏈接對象,通過調用URL.openConnection()
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();

    // 2. 設置網絡鏈接對象的一些參數
    conn.setRequestMethod("GET");
    conn.setConnectTimeout(5 * 1000);

    // 3. 獲取服務器的響應碼
    int responseCode = conn.getResponseCode();
    if (200 == responseCode) {
        // 響應成功
        // 4. 解析相應結果
        InputStream is = conn.getInputStream();
    }
} catch (Exception e) {
    e.printStackTrace();
}
特別需要注意的幾點 ★在主線程中寫網絡訪問時,會報異常:android.os.NetworkOnMainThreadException 解決辦法:將網絡訪問放置到子線程中。 ★在主線程中修改UI時,會報異常:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 解決辦法:異常報告中說的很明白,只能在主線程(UI線程)中創建View的層級。 Andoird中為我們提供了異步消息處理線程的技術,只要涉及到了HandlerMessage

Android下異步消息處理線程技術

在Android開發中,常常有這樣的場景,訪問網絡,獲取資源,然後把獲取的資源(圖片、文字)顯示在手機屏幕上。

但是由於網絡訪問是個耗時操作,在Android4.0以後,Google就強制要求不能夠在主線程寫網絡訪問。而在子線程中獲取到需要的資源後,又不能夠在子線程中更新UI界面(比如顯示的文字、圖片等)。

這是異步消息處理線程技術也應運而生。主要涉及到兩個類HandlerMessage,它們使用的方法超級簡單,但是裡面涉及的原理確實非常的復雜。這裡先簡要的介紹如何使用異步消息處理線程技術,如果在子線程中更新UI界面。

有以下幾種使用方式

方式一:Handler和Message

① 實例化一個Handler並重寫handlerMessage()方法

private Handler  handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
                   // 處理消息
             };
        };

② 在子線程中獲取或創建消息,並使用handler對象發送。

// 獲取消息的方式有多種
// Message msg = new Message();
// Handler.obtainMessage()
// 但是谷歌的建議是:While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods,
Message msg = Message.obtain();
msg.obj = result;
handler.sendMessage(msg);
方式二:

在子線程中直接調用Activity.runOnUiThread(Runnable action)方法

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        // 更新UI
    }
});
方式三:

在子線程中調用View的post()方法

tv_content.post(new Runnable() {                    
    @Override
    public void run() {
        // 更新UI
        tv_content.setText(result);
    }
});
方式四:Handler的post()方法

① 創建一個Handler成員變量

private Handler handler = new Handler();

② 在子線程中調動post()方法

handler.post(new Runnable() {                       
    @Override
    public void run() {
        // 更新UI
        tv_content.setText(result);
    }
});

雖然方式有很多,但是不管是那種方法在子線程中更新UI,其實背後的原理都是相同的,必須借助異步消息處理的機制來實現。

這是Handler中post的源碼:

 public final boolean post(Runnable r)
  {
      return  sendMessageDelayed(getPostMessage(r), 0);
   }

這是View中post的源碼:

public boolean post(Runnable action) {
       final AttachInfo attachInfo = mAttachInfo;
       if (attachInfo != null) {
           return attachInfo.mHandler.post(action);
       }
       // Assume that post will succeed later
       ViewRootImpl.getRunQueue().post(action);
       return true;
   }

這是Activity中runOnUiThread的源碼

public final void runOnUiThread(Runnable action) {
       if (Thread.currentThread() != mUiThread) {
           mHandler.post(action);
       } else {
           action.run();
       }
   }

是不是驚人的相似?

Android下異步消息處理線程技術的基本原理

為什麼使用異步消息處理的方式就可以對UI進行操作了呢?

這是由於Handler總是依附於創建時所在的線程,比如我們的Handler是在主線程中創建的,而在子線程中又無法直接對UI進行操作,於是我們就通過一系列的發送消息、入隊、出隊等環節,最後調用到了Handler的handleMessage()方法中,這時的handleMessage()方法已經是在主線程中運行的,因而我們當然可以在這裡進行UI操作了。

整個異步消息處理流程的示意圖如下圖所示:
\

模仿新聞客戶端小案例

寫一個模仿網易新聞客戶端的小案例,只要目的是把之前的知識全部串聯起來。

效果圖如下:

\

用到的知識點有: ① RelativeLayout相對布局 ② 請求網絡資源(HttpUrlConnection) ③ XmlPullParser解析XML數據 (得到Bean集合) ④ ListView展示數據 (相當於View視圖層) ⑤ BaseAdapter適配器(相當於Controller控制器層) ⑥ 異步消息處理線程技術(Handler、Message) 開發步驟: ① 確定好客戶端與服務端交換數據的格式為XML ② 確定好服務端資源是否能夠訪問到 ③ 畫主界面UI ④ 畫Item條目UI ⑤ 請求網絡,獲取到結果流 ⑥ 解析XML,返回要被展示的數據集合 ⑦ 寫ListView的適配濃ky"/kf/ware/vc/" target="_blank" class="keylink">vcgouAg0uyyvc/7z6K0psDtz9+zzLy8yvWjrM6qTGlzdFZpZXfJ6NbDysrF5Mb3DQo8cD48c3Ryb25nPqLZIMi3tqi6w7/Nu6e2y9Prt/7O8bbLvbu7u8r9vt21xLjxyr3OqlhNTDwvc3Ryb25nPjxiciAvPg0KyrnTw+SvwMDG98i3yM/XytS0yse38bTm1No8L3A+DQo8cHJlIGNsYXNzPQ=="brush:java;"> 國家發改委副主任朱之鑫【關鍵詞:霧霾天】歐V推行辦法正在研究中朱之鑫說,霧霾天氣有三方面成因:一是中國高耗能產業和高污染產業較多,如水泥、制造業、鋼鐵等行業,在華北地區分布較多;二是和大規模建設有關,工地上未覆蓋苫布造成揚塵;三是汽車尾氣增加了細微顆粒物排放。民盟中央提交的一份關於大氣污染治理的提案 http://192.168.1.100:8080/img/a.jpg 1 163

② 確定好服務端資源是否能夠訪問到

\

③ 畫主界面UI
很簡單裡面只有一個ListView



    
    

④ 畫Item條目UI




    

    

    


    

⑤ 請求網絡,獲取到結果流

/**
* 請求數據
 */
private void initData() {

    // 子線程
    new Thread(new Runnable() {

        @Override
        public void run() {
            // 訪問網絡
            try {
                // 獲取鏈接對象
                URL url = new URL(dataPath);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();

                // 配置鏈接對象
                conn.setReadTimeout(5 * 1000);
                conn.setRequestMethod("GET");

                // 獲取響應碼
                int responseCode = conn.getResponseCode();
                if (responseCode == 200) {

                    // 獲取結果流
                    InputStream is = conn.getInputStream();
                    // 獲取到集合
                    newsBeanList = parserXML(is);
                    // 發送消息表明數據獲取成功
                    handler.sendEmptyMessage(SUCCESS);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }).start();
}

⑥ 解析XML,返回要被展示的數據集合

/**
 * 解析XML數據,並將數據封裝到實體bean中
 * 
 * @param is
 * @return
 * @throws Exception
 */
private List parserXML(InputStream is) throws Exception {

    // 獲取到解析器
    XmlPullParser parser = Xml.newPullParser();

    // 設置解析器的參數
    parser.setInput(is, "UTF-8");

    // 獲取到事件
    int eventType = parser.getEventType();

    // 定義
    List newsBeanList = null;
    NewsBean newsBean = null;

    // 循環處理事件,並封裝成為實體bean
    while (eventType != XmlPullParser.END_DOCUMENT) {

        switch (eventType) {
        case XmlPullParser.START_TAG:
            if ("channel".equals(parser.getName())) {
                // 初始化集合
                newsBeanList = new ArrayList();
            } else if ("item".equals(parser.getName())) {
                // 初始化bean
                newsBean = new NewsBean();
            } else if ("title".equals(parser.getName())) {
                newsBean.setTitle(parser.nextText());
            } else if ("description".equals(parser.getName())) {
                newsBean.setDescription(parser.nextText());
            } else if ("image".equals(parser.getName())) {
                newsBean.setImage(parser.nextText());
            } else if ("type".equals(parser.getName())) {
                newsBean.setType(parser.nextText());
            } else if ("comment".equals(parser.getName())) {
                newsBean.setComment(parser.nextText());
            }
            break;
        case XmlPullParser.END_TAG:
            if ("item".equals(parser.getName())) {
                // 將bean添加到集合中
                newsBeanList.add(newsBean);
            } else if ("channel".equals(parser.getName())) {
                // 返回集合
                return newsBeanList;
            }

            break;

        default:
            break;
        }

        // 循環事件
        eventType = parser.next();
    }

    return null;
}

⑦ 寫ListView的適配器,在設置圖片時,使用到了SmartImageView開源項目

class MyAdapter extends BaseAdapter {

    @Override
    public int getCount() {
        return newsBeanList.size();
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = null;
        if (convertView == null) {
            view = View.inflate(getApplicationContext(), R.layout.item, null);
        } else {
            view = convertView;
        }

        SmartImageView iv_icon = (SmartImageView) view.findViewById(R.id.iv_icon);
        TextView tv_title = (TextView) view.findViewById(R.id.tv_title);
        TextView tv_content = (TextView) view.findViewById(R.id.tv_content);
        TextView tv_type = (TextView) view.findViewById(R.id.tv_type);

        // 獲取集合中的數據,顯示到控件上
        NewsBean bean = newsBeanList.get(position);
        tv_title.setText(bean.getTitle());
        tv_content.setText(bean.getDescription());
        tv_type.setText(bean.getType());
        iv_icon.setImageUrl(bean.getImage().replace("192.168.1.100", localhost));

        return view;
    }

}

⑧ 異步消息處理線程技術,為ListView設置適配器

private Handler handler = new Handler() {
    public void handleMessage(android.os.Message msg) {
        if (msg.what == SUCCESS) {
            // 為ListView設置適配器
            lv.setAdapter(new MyAdapter());
        }
    };
};

GET方式提交數據到服務器

簡單的get請求

String username = et_username.getText().toString().trim();
String password = et_password.getText().toString().trim();

final String path = "http://192.168.1.101:8080/web/LoginServlet?username=" + username + "&password=" + password;

// 子線程
new Thread(new Runnable() {

    @Override
    public void run() {
        // 訪問網絡
        try {
            // 獲取鏈接對象
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            // 配置鏈接對象
            conn.setReadTimeout(5 * 1000);
            conn.setRequestMethod("GET");

            // 獲取響應碼
            int responseCode = conn.getResponseCode();
            if (responseCode == 200) {

                // 獲取結果流
                InputStream is = conn.getInputStream();
                String result = streamToString(is);
                showToast(result);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}).start();

POST方式提交數據到服務器

post請求也比較簡單,與get方式相比,也僅有小部分不同。

final String username = et_username.getText().toString().trim();
final String password = et_password.getText().toString().trim();

final String path = "http://192.168.1.101:8080/web/LoginServlet";

// 子線程
new Thread(new Runnable() {

    @Override
    public void run() {
        // 訪問網絡
        try {
            // 獲取鏈接對象
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            // 配置鏈接對象
            conn.setReadTimeout(5 * 1000);
            conn.setRequestMethod("POST");

            // ☆ 不同之處:設置POST請求的頭信息
            String data = "username=" + username + "&password=" + password;
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            conn.setRequestProperty("Content-Length", data.length() + "");

            // ☆ 不同之處:發送數據
            conn.setDoOutput(true);
            conn.getOutputStream().write(data.getBytes());

            // 獲取響應碼
            int responseCode = conn.getResponseCode();
            if (responseCode == 200) {

                // 獲取結果流
                InputStream is = conn.getInputStream();
                String result = streamToString(is);
                showToast(result);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}).start();
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved