編輯:關於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); } }
當一個Touch事件發生,系統首先把事件傳遞給當前的Activity,由Activity的dispatchTouchEvent分發事件,源碼如下: public
本文實例講述了Android編程之圖片顏色處理方法。分享給大家供大家參考,具體如下:你想做到跟美圖秀秀一樣可以處理自己的照片,美化自己的照片嗎?其實你也可以自己做一個這樣
廢話少說,直接上圖,有圖有真相。這兩個工具欄全是用布局來實現的。底部工具欄布局代碼:代碼復制代碼 代碼如下: < xmlns:android
搭建環境過程:1. 安裝JDK。注:實質上到該網址上下載好JDK安裝包,安裝後添加一個環境變量:JAVA_HOME,其值為:C:\Program Files\Java\j