1、Handler介紹
在Android開發中,我們常會使用單獨的線程來完成某些操作,比如用一個線程來完成從網絡上下的圖片,然后顯示在一個ImageView上,在多線程操作時,Android中必須保證以下兩點:
(1)不要阻塞UI線程
(2)不要再UI線程之外訪問Android UI工具包
有了以上兩點的限制,我們在程序之間的消息如何進行傳遞呢?
用Handler,消息的處理者。
public class MainActivity extends Activity {private TextView tv;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv = (TextView) findViewById(R.id.tv);}private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case 100:tv.setText("下載完成");break;}}};public void downloadClick(View view) {//使用線程模擬下載操作new Thread(new Runnable() {@Overridepublic void run() {while (true) {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}break;}handler.sendEmptyMessage(100);}}).start();} }
?
2、Handler常用API
使用handler可以完成以下兩點工作:
(1)消息調度和在將來的某個時間點執行一個Runnable
(2)多個任務加入到一個隊列中執行
Handler相關方法:
//發送一個空消息,即obj為空,標記為100handler.sendEmptyMessage(100);//獲取一個消息對象,返回一個Msg對象Message msg = handler.obtainMessage();msg.what = 100;msg.obj = "要存的信息";//任意類型handler.sendMessage(msg);//發送消息//在制定時間后發送消息handler.sendEmptyMessageAtTime(200, System.currentTimeMillis() + 3000);//延遲2s后發送消息handler.sendEmptyMessageDelayed(300, 2000);
?
3、Handler內部實現原理
Handler實現機制:
(1)Message對象,表示要傳遞的一個消息
(2)MessageQueue對象,存放消息對象的消息隊列,先進先出原則
(3)Looper對象負責管理當前線程的消息隊列(MessageQueue)
(4)Handler對象負責把消息push到消息隊列中,以及接收Looper從消息隊列中取出的消息
?
Android啟動程序時會在UI線程創建一個MessageQueue。
/*** Handler機制* 1、Message 消息對象,內部使用鏈表數據結構實現一個消息池,用于重復利用,避免大量創建消息對象,造成內存浪費* 2、Handler 消息處理者,通過該對象把消息存入消息隊列,并最后通過HandlerMessage方法處理消息* 3、MessageQueue 消息隊列,用于存儲Message對象的數據結構,先進先出* 4、Looper 消息隊列的處理者,用于循環檢查消息隊列,從消息隊列中一個一個的取出消息對象,傳入HandlerMessage方法*/
?
4、Handler內存泄露問題分析
內存泄漏:當activity退出后,handler依然還占用activity的引用,導致activity沒有真正退出,依然占用內存。解決方法如下:
/*** Handler的內存泄露問題* 1、定義一個內部類時,會默認擁有外部類對象的引用,所以建議使用內部類時,最好定義為一個靜態內部類* 2、引用的強弱,強引用->軟引用 ->弱引用*/public class HandlerMemoryActivity extends Activity {private MyHandler handler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_handler_memory);//使用Handler延遲執行一個Runnable(10分鐘)handler.postDelayed(new Runnable() {@Overridepublic void run() {System.out.println("!!!!!!run");}}, 1000 * 60 * 10);//關閉當前的Activity finish();}private static class MyHandler extends Handler {WeakReference<HandlerMemoryActivity> weakReference;public MyHandler(HandlerMemoryActivity activity) {weakReference = new WeakReference<HandlerMemoryActivity>(activity);}@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);HandlerMemoryActivity activity = weakReference.get();if (activity != null) {//做處理 }}} }
?
5、AsyncTask
除了使用Handler實現線程間的通信外,Android提供了一個工具類:AsyncTask,他使創建需要與用戶界面交互的長時間運行的任務變得簡單,相對來說AsyncTask更清涼已寫,適用與簡單的異步處理,不需要借助線程和Handler即可實現。
AsyncTask是抽象類,AsyncTask定義了三種泛型類型:Params,Progress和Result
Params啟動任務執行的輸入參數,比如,Http請求的URL;
Progress后臺任務執行的百分比
Result后臺執行任務的最中返回結果,比如String
AsyncTask的執行步驟:
AsyncTask的執行分為四個步驟,每一步對應一個回調方法,我們需要的就是實現這些方法。
(1)首先定義一個類繼承AsyncTask
(2)實現AsyncTask中定義的下面一個或幾個方法
四個執行步驟分別為:
(1)onPreExecute():被UI Thread調用,該方法用來做已寫準備工作,如在界面上顯示一個進度條
(2)doInBackground(Params..):將在onPreExcute之后執行,運行在后臺的線程中。負責執行耗時操作。可以調用publishProgress方法來更新實時任務進度
(3)onProgressUpdate(Progress..):在publishProgress方法被調用后,UI Thread將調用該方法在界面上展示任務的進展情況
(4)onPostExcute(Result):在doInBackground執行完成后,onPostExcute(Result)方法將被UI Thread調用,后臺的計算結果將通過該方法傳遞到UI Thread。
AsyncTask準則:
(1)AsyncTask的實例必須在UI Thread中創建。
(2)excute方法必須在UI Thread中調用
(3)不要手動調用onPreExecute、onPostExecute、doInBackground和onProgressUpdate這借個方法
(4)改Task只能被執行一次,否則多次調用時會出現異常
(5)AsyncTask不能餓完全取代線程,在一些邏輯較為復雜或者后臺反復執行的邏輯可能就需要線程來實現了
public class MainActivity extends Activity {private TextView tv;private ProgressBar progressBar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv = (TextView) findViewById(R.id.tv);progressBar = (ProgressBar) findViewById(R.id.progressBar);}public void downloadClick(View view) {new MyAsyncTask(MainActivity.this).execute("http://a.hiphotos.baidu.com/image/pic/item/d50735fae6cd7b8926b326c20c2442a7d8330e97.jpg");}/*** 通過AsyncTask實現一個異步任務*/private static class MyAsyncTask extends AsyncTask<String, Integer, Integer> {private MainActivity activity;public MyAsyncTask(MainActivity activity) {this.activity = activity;}//執行任務之前觸發的事件,可以在該方法中做一些初始化動作,例如顯示一個dialog//這個是在主線程中 @Overrideprotected void onPreExecute() {super.onPreExecute();activity.progressBar.setProgress(0);}//在子線程中//執行后臺任務的方法 @Overrideprotected Integer doInBackground(String... params) {String s = params[0];try {URL url = new URL(s);HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();//獲取文件的大小int size = urlConnection.getContentLength();//0是一個標記,表示需要更新的最大進度值,1表示更新當下下載的進度值publishProgress(0, size);byte[] bytes = new byte[100];int len = -1;FileInputStream in = (FileInputStream) urlConnection.getInputStream();FileOutputStream out = new FileOutputStream("/sdcard/" + System.currentTimeMillis() + ".jpg");while ((len = in.read(bytes)) != -1) {out.write(bytes, 0, len);//更新進度publishProgress(1, len);out.flush();}out.close();in.close();} catch (Exception e) {e.printStackTrace();}return 200;}//更新進度 @Overrideprotected void onProgressUpdate(Integer... values) {super.onProgressUpdate(values);switch (values[0]) {case 0:activity.progressBar.setMax(values[1]);break;case 1:activity.progressBar.incrementProgressBy(values[1]);break;}}@Overrideprotected void onPostExecute(Integer integer) {super.onPostExecute(integer);if (integer == 200) {activity.tv.setText("下載完成");}}} }
?