編輯:關於Android編程
Android下訪問網絡資源和一些注意事項 Android下異步消息處理線程技術 Android下異步消息處理線程技術的基本原理 模仿新聞客戶端小案例 GET方式提交數據到服務器 POST方式提交數據到服務器
這裡只說明了最簡單的網絡訪問資源的方式,與服務端交互在後面講述。
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中為我們提供了異步消息處理線程的技術,只要涉及到了Handler
和Message
在Android開發中,常常有這樣的場景,訪問網絡,獲取資源,然後把獲取的資源(圖片、文字)顯示在手機屏幕上。
但是由於網絡訪問是個耗時操作,在Android4.0以後,Google就強制要求不能夠在主線程寫網絡訪問。而在子線程中獲取到需要的資源後,又不能夠在子線程中更新UI界面(比如顯示的文字、圖片等)。
這是異步消息處理線程技術也應運而生。主要涉及到兩個類Handler
和Message
,它們使用的方法超級簡單,但是裡面涉及的原理確實非常的復雜。這裡先簡要的介紹如何使用異步消息處理線程技術,如果在子線程中更新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();
}
}
是不是驚人的相似?
為什麼使用異步消息處理的方式就可以對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請求
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請求也比較簡單,與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();
AIDL的理解:Service中的IBinder還記得我們在MyService中利用new IMyInterface.Stub()向上轉型成了IBinder然後在onBi
BatteryStatsService主要負責電池電量的統計信息,首先我們簡單的看下電量統計服務的啟動過程。 BatteryStatsService啟動過程&n
本文介紹了ListView給每個Item上面的按鈕添加事件,具體如下:1.先看下效果圖:在這裡僅供測試,我把數據都寫死了,根據需要可以自己進行修改,此外實現ListVie
華為榮耀於8月1號下午正式發布了6.6吋大屏手機華為榮耀NOTE8,對於買到新機的朋友是不是有這樣的疑問呢,華為榮耀note8怎麼裝sim卡?華為榮耀not