基礎篇-在非UI線程中更新UI元素,基礎篇ui
個人原創,轉載請註明出處: http://blog.csdn.net/supluo/article/details/
先瞭解兩個概念
1、UI:User Interface的縮寫,使用者介面的意思。你可以不恰當的理解為我們能夠看到的,操作的東西;在Android中什麼才稱為UI呢,可以簡單的理解為View及其子類等元素。這是一個不夠正確的概念,只是對新手做一個簡單的拋磚引玉。
2、ANR:Application Not Responding,意思是程式沒有響應。
在如下情況下,Android會報出ANR錯誤:
– 主線程 (“事件處理線程” / “UI線程”) 在5秒內沒有響應輸入事件
– BroadcastReceiver 沒有在10秒內完成返回
因此耗時的操作通常會放在其他線程中去執行,以防導致程式卡頓甚至出現ANR錯誤,線程執行完成之後再操作UI元素。
下面舉例導致卡頓的例子,在一個按鈕中執行以下代碼:
proDialog.Show();//顯示進度對話方塊 System.Threading.Thread.Sleep(10 * 1000);//線程休眠10秒,類比耗時操作,觸發ANR HandleResult("資料更新成功:" + new Random().Next(10));更新其他UI元素上面的代碼就會阻塞主UI線程,其實對話方塊也不會顯示出來,調用對話方塊show方法後,對話方塊並不會立即出現,待讀者自行討論,因此上面的效果特別糟糕,只會顯示按鈕被按下的效果,十秒之後更新UI,對話方塊不會顯示出來,因此正確的做法是顯示一個對話方塊之後,將耗時的操作放在其他線程中去執行,執行完成之後再更新ui,但是我們並不能在其他線程中直接更新UI元素,因此這篇文章的主題就是講解如何在非UI線程中更新UI元素。
下面進入正題
在正常開發過程中,我們通常需要將程式中遇到的比較耗時的操作新開其他線程去執行,以防阻塞主UI線程導致ANR錯誤,但是在實際開發過程中可能會遇到如下兩個異常資訊
1、Only the original thread that created a view hierarchy can touch its views;
2、Can't create handler inside thread that has not called Looper.prepare();
第一種在非UI線程中更新UI元素就會導致這種錯誤,第二種是在非UI線程中視圖顯示某些UI提示資訊;其實可以笼統的歸納為都是在非UI線程中更新UI導致的。我們先看下面兩個錯誤的例子:
一、非UI線程更新UI元素,(在一個按鈕事件中執行以下操作,事件綁定代碼就不貼了)
new Thread(() => { try { mText.Text = "資料更新成功:" + new Random().Next(10); } catch (Exception ex) { } }).Start();
執行結果拋出android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.即上面說的第一種錯誤資訊出現的情況。
二、非UI線程進行UI提示。(在一個按鈕事件中執行以下操作,事件綁定代碼就不貼了)
new Thread(() => { try { proDialog.Show();//proDialog是一個dialog } catch (Exception ex) { } }).Start();執行結果拋出java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare(),即上面說的第二種錯誤資訊出現的情況。
解決辦法:
這種錯誤對於才接觸這行的開發人員來講,經常出現這樣的問題,其實這種問題也相當簡單比較容易解決,但是最主要的是找到一個最為合適的解決辦法,下面說幾種比較簡單的解決方案。
1、委託式
將非UI元素中執行UI操作的部分委託給主UI線程執行,見以下代碼:
proDialog.Show(); new Thread(() => { System.Threading.Thread.Sleep(10 * 1000); this.RunOnUiThread(() => //this 指代的是Activity對象,RunOnUiThread 是Activity的一個成員方法 { HandleResult("資料更新成功:" + new Random().Next(10)); }); }).Start();
2、View.Post形式
使用UI元素的Post或者PostDelay方法
官方文檔注釋:Causes the Runnable to be added to the message queue. The runnable will be run on the user interface thread.
Parameters: action The Runnable that will be executed.
Returns:Returns true if the Runnable was successfully placed in to the message queue. Returns false on failure, usually because the looper processing the message queue is exiting.
樣本見以下代碼:
proDialog.Show(); new Thread(() => { try { System.Threading.Thread.Sleep(10 * 1000); mText.Post(() => { HandleResult("資料更新成功:" + new Random().Next(10)); }); } catch (Exception ex) { this.RunOnUiThread( () => { Toast.MakeText(this, "線程執行過程中出現了錯誤!", ToastLength.Long).Show(); } ); } }).Start();這種方法包括PostDelay方法其實跟第一種方法是極其類似的,底層實現的本質是一樣。
3、Handler形式
1)、初始化一個Handler對象
Handler mHandler;//定義變數
mHandler = new Handler(HandleMessage);//初始化,此處可以使用匿名函數等方式
//方法定義,此方法中主要是更新UI操作
private void HandleMessage(Message msg) { switch (msg.What) { case 1: HandleResult("資料更新成功:" + new Random().Next(10)); break; } }
2)、在恰當的時候,使用handler發送訊息進行UI更新
proDialog.Show(); new Thread(() => { System.Threading.Thread.Sleep(10 * 1000); mHandler.SendEmptyMessage(1); }).Start();Handler的具體使用可以查閱相關資料再進一步瞭解。
上面的方法大多都是使用了Handler來進行訊息的處理,下面給出一張handler的工作圖,
4、AsyncTask形式
AsyncTask,是android提供的輕量級的非同步類,可以直接繼承AsyncTask,在類中實現非同步作業,並提供介面反饋當前非同步執行的程度(可以通過介面實現UI進度更新),最後反饋執行的結果給UI主線程.
這個代碼有點多,可以自行百度查閱,有相當多的不錯的文章可以借閱。
提醒一點,在Xamarin.Android中,如果傳遞給AsyncTask的執行參數是字串,則需要使用Java.Lang.String進行封裝,早些的版本我測試是需要這樣做的,最近的版本還沒來得及進行測試,稍後我補全這一部分,或者有知道的也可以交流下。
以上就是比較常用的方式。
Demo
個人搞了個部落格App,平時上個廁所,睡覺前等隨便看兩篇文章,總能有些收穫,希望大家支援!http://blog.csdn.net/supluo/article/details/43489475