編輯:關於Android編程
Android 的 Handler 機制(也有人叫消息機制)目的是為了跨線程通信,也就是多線程通信。之所以需要跨線程通信是因為在 Android 中主線程通常只負責 UI 的創建和修改,子線程負責網絡訪問和耗時操作,因此,主線程和子線程需要經常配合使用才能完成整個 Android 功能。
Handler 機制可以近似用圖 1 展示。MainThread 代表主線程,newThread 代表子線程。MainThread 是 Android 系統創建並維護的,創建的時候系統執行了 Looper.prepare();方法,該方法內部創建了 MessageQueue 消息隊列(也叫消息池),該消息隊列是 Message 消息的容器,用於存儲通過 handler發送過來的 Message。MessageQueue 是 Looper 對象的成員變量,Looper 對象通過 ThreadLocal 綁定在MainThread 中。因此我們可以簡單的這麼認為:MainThread 擁有唯一的一個 Looper 對象,該 Looper 對象有用唯一的 MessageQueue 對象,MessageQueue 對象可以存儲多個 Message。
MainThread 中需要程序員手動創建 Handler 對象,並覆寫 Handler 中的 handleMessage(Message msg)方法,該方法將來會在主線程中被調用,在該方法裡一般會寫與 UI 修改相關的代碼。
MainThread 創建好之後,系統自動執行了 Looper.loop();方法,該方法內部開啟了一個“死循環”不斷的去之前創建好的 MessageQueue 中取 Message。如果一有消息進入 MessageQueue,那麼馬上會被
Looper.loop();取出來,取出來之後就會調用之前創建好的 handler 對象的 handleMessage(Message)方法。
newThread 線程是我們程序員自定 new 出來的子線程。在該子線程中處理完我們的“耗時”或者網絡訪問任務後,調用主線程中的 handler 對象的 sendMessage(msg)方法,該方法一被執行,內部將就 msg添加到了主線程中的 MessageQueue 隊列中,這樣就成為了 Looper.loop()的盤中餐了,等待著被消費。這是一個很復雜的過程,但是 Android 顯然已經將這種模式給封裝起來了,就叫 Handler 機制。我們使用時只需要在主線程中創建 Handler,並覆寫 handler 中的handleMessage 方法,然後在子線程中調用 handler 的 sendMessage(msg)方法即可。

圖1 Handler原理圖
網頁源碼查看器:
activity_layout.xml:
工具類將字節流轉化為字符串 StreamUtls.java:
public class StreamUtils {
/**
* 將字節流轉化為字符串,使用android 默認編碼
*
* @author ZhangSeachal
* @date 2016年8月6日下午4:20:43
* @version 1.0
* @param inputStream
* @return
* @throws IOException
*/
public static String inputStream2String(InputStream inputStream)
throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len = -1;
byte[] buffer = new byte[1024];
while ((len = inputStream.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
inputStream.close();
return new String(baos.toByteArray());
}
}
MainActivity.java
/**
* 網絡源碼查看器
*
* @author ZhangSeachal
* @date 2016年8月5日 下午10:07:34
* @version 1.0
* @since
*/
public class MainActivity extends Activity {
private TextView tv_content;
private EditText et_url;
/** 創建一個Handler對象, 覆寫類體、方法體 */
private Handler handler = new Handler() {
/**
* 覆寫handleMessage方法,在該方法中完成我們想做的工作, 該方法是在主線程中 被 調用的,因此可以再這裡面修改UI。
*/
public void handleMessage(Message msg) {
// 判斷Message 的類型,根據msg的what屬性去獲取期類型
switch (msg.what) {
// 如果成功
case RESULT_OK:
// 從msg的obj屬性中獲取數據,然後顯示在TextView 上。
tv_content.setText(msg.obj.toString());
break;
// 如果失敗
case RESULT_CANCELED:
// 彈 吐司,給用戶提示
Toast.makeText(MainActivity.this, "訪問網頁失敗", Toast.LENGTH_LONG)
.show();
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化控件
et_url = (EditText) findViewById(R.id.et_url);
tv_content = (TextView) findViewById(R.id.tv_content);
}
/**
* 加載 網頁源碼
*
* @author ZhangSeachal
* @date 2016年8月5日下午10:29:12
* @version 1.0
* @param view
*/
public void load(View view) {
// 獲取用戶輸入的數據
final String path = et_url.getText().toString().trim();
/*
* 網絡訪問必須在子線程中進行
*/
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
// 1.創建一個 URl對象,需要傳入url
URL url = new URL(path);
/*
* 2.使用url對象打開一個HttpURLConnection,
* 由於其返回的是HttpURLConnection的父類,
*/
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
/*
* 3.配置connection 連接參數
*/
// 設置聯網超時時長,單位毫秒
connection.setConnectTimeout(5000);
/*
* 設置數據讀取超時 注意: 不是指讀取數據總耗時超時, 而是能夠讀取到數據流等待時長
*/
connection.setReadTimeout(5000);
/**
* 設置請求方式,默認是GET,但是為了增加代碼易讀性, 建議顯示只是為GET
*/
connection.setRequestMethod("GET");
// 4. 開始連接網絡
connection.connect();
// 5.以字節 輸入流 的形式獲取服務端發來的數據
InputStream inputStream = connection.getInputStream();
// 6.將字節流轉化為字符串 (使用自定義的StreamUtils工具類)
final String data = StreamUtils
.inputStream2String(inputStream);
/*
* 7.將獲取的數據封裝到Message對象,然後發送給handler
*/
Message msg = new Message();
/*
* 給Message 對象 的what屬性設置一個int類型的值。 因為消息可能會有多個,因此為了區分這些不同的消息。
* 需要給消息設置What屬性. RESULT_OK 是Activity的常量值為-1,
* 當然也可以自定義一個int類型的值。
*/
msg.what = RESULT_OK;
// msg.what = RESULT_CANCELED;
/**
* 給Message隊形的obj屬性設置一個object類型的屬性。 該值正是我們需要在
* Meaage對象上綁定的數據,這裡綁定的 從網絡上獲取到的網頁編碼字符串。
*/
msg.obj = data;
/*
* 給主線程發送消息。 發送後,系統會調用handler對象的handlerMessage(Message) 方法。
* 該方法正是 我們自己實現的,而且該方法是在主線程中執行的。 從而就實現了從子線程中
* 訪問網絡數據(耗時操作),然後交給主線程, 讓主線程修改UI(修改UI只能在主線程中做)。
*/
handler.sendMessage(msg);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
Log.d("tag", "遇到異常" + e, e);
/**
* 如果遇到異常,最好讓主線程也知道子線程遇到異常了。 因此使用handler 發動一個空消息,
* 所謂的空消息是指,該消息沒有obj值, 只有一個what屬性。 這列的RESULT_CANCELED
* 就是一個int型的常量, 當然我們可以自定義,這裡只不過是直接使用了Activity類的 一個常量而已。
* 該消息發送後,系統依然會調用handler對象 的handlerMessage(Message)方法。
*/
handler.sendEmptyMessage(RESULT_CANCELED);
}
}
}).start();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Android事件分發機制---------ViewGroup
當一個Touch事件發生,系統首先把事件傳遞給當前的Activity,由Activity的dispatchTouchEvent分發事件,源碼如下: public
Android編程之圖片顏色處理方法
本文實例講述了Android編程之圖片顏色處理方法。分享給大家供大家參考,具體如下:你想做到跟美圖秀秀一樣可以處理自己的照片,美化自己的照片嗎?其實你也可以自己做一個這樣
Android頂部工具欄和底部工具欄的簡單實現代碼
廢話少說,直接上圖,有圖有真相。這兩個工具欄全是用布局來實現的。底部工具欄布局代碼:代碼復制代碼 代碼如下: < xmlns:android
網頁打包安卓APP流程
搭建環境過程:1. 安裝JDK。注:實質上到該網址上下載好JDK安裝包,安裝後添加一個環境變量:JAVA_HOME,其值為:C:\Program Files\Java\j