對Andriod子執行緒進行UI更新的理解
問題描述:
在onClick()中的點選事件裡是死迴圈時,點選其他Button按鈕應用閃退。
分析:
當一個程式第一次啟動時,Android會同時啟動一個對應的主執行緒(Main Thread),主執行緒主要負責處理與UI相關的事件,比如按鈕的響應。
在開發Android 應用時必須遵守單執行緒模型的原則,此時的解決方法便是開啟一個子執行緒進行耗時操作的執行。
解決:
當使用了WHILE()迴圈實現點亮星星之後,意識到這是一個耗時的操作,如果讓它一直在主執行緒中執行,會使得其他的事件無法響應(顯示“應用無響應”(ANR)),導致程式退出。
想到將流水燈函式段設為一個子執行緒[new Thread()],可以和主執行緒互不影響的工作。此時主執行緒可以響應其他按鍵的請求,但是流水燈子執行緒仍然工作。
課後經過學習和理解,通過為子執行緒設定一個全域性的同步訊號量(當前只允許一個執行緒進行修改)狀態標誌位[volatile boolean state_flag = true; ]可以控制中斷子執行緒。只要while(state_flag)迴圈條件在其他按鍵事件中被修改為false,子執行緒就可以安全退出了。
同時,應保證執行緒的安全執行。防止應用出現“抱歉,應用已停止執行”的對話方塊,即執行緒由於未捕獲的異常而終止時,呼叫介面Thread.UncaughtExceptionHandler的Thread.getUncaughtExceptionHandler()方法,並將執行緒中斷方法Thread.interrupt()寫入異常處理程式的處理方法 UncaughtException() 。
實驗分析
(一)具體描述
由於上述解決方案沒有通過實驗室環境執行證實,以下是通過UI元件代替LED完成拓展實驗的模擬。
①Handler控制UI更新的方式:
對UI的設定以及修改是要在主執行緒(UI執行緒)之中完成的,這時候才需要用到Handler的Message將子執行緒的結果傳遞給主執行緒,主執行緒進行UI變動。
否則會有報錯:Only the original thread that created a view hierarchy can touch its views
②程式時序圖(Handler工作模式)

③遇到的問題及解決:
i)當我們從流水燈→其他按鍵時,Headler會再執行一次迴圈體,導致結果出錯。

解決方法:
在其他按鈕增加一個判斷,邏輯是:
只要我按下去其他按鈕,Headler將收不到訊息,無法執行迴圈體。
if (state_flag == true) { handler.sendMessage(msg); } ii)java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.Window$Callback android.view.Window.getCallback()' on a null object reference
在使用View的例項化順序應該在setcontentview之後,此時整個View載入完成。
(二)實驗實現
①介面佈局

<LinearLayout <ImageView android:id="@+id/imageView" android:src="@drawable/star_big_off"/> <ImageView android:id="@+id/imageView2"/> <ImageView android:id="@+id/imageView3"/> </LinearLayout> <Button android:id="@+id/button1"/> <Button android:id="@+id/button2"/> <Button android:id="@+id/button3" /> </LinearLayout>
②具體實現
volatile boolean state_flag = true; private Handler handler = new Handler() { public void handleMessage(Message msg) { Log.i("handler message", "star flow"); if (msg.what == 0) { imageView.setImageResource(R.drawable.star_big_on); imaegView3.setImageResource(R.drawable.star_big_off); } if(msg.what == 1){ imageView.setImageResource(R.drawable.star_big_off); imaegView2.setImageResource(R.drawable.star_big_on); ...... } }; Button flow_led = (Button) findViewById(R.id.button3); flow_led.setOnClickListener(new View.OnClickListener() { public Thread thread = new Thread(new Runnable() { public void run() { int num = 0; while(state_flag) { Log.i("thread while()", "button loop"); try { thread.sleep(500); }catch(InterruptedException e) { e.printStackTrace();} Message msg = new Message(); msg.what =num++; num = num % 3; Log.i("thread while()", "button loop: msg.what = " + msg.what); if (state_flag == true) { handler.sendMessage(msg); } } } }); public void onClick(View arg0) { state_flag = true; Log.i("button loop","press down"); thread.start(); } } ); Button turn_on_led = (Button) findViewById(R.id.button1); turn_on_led.setOnClickListener(new View.OnClickListener(){ public void onClick(View arg0) { state_flag = false; Log.i("button 1","press down"); imageView.setImageResource(R.drawable.star_big_on); ...... }} ); Button turn_off_led = (Button) findViewById(R.id.button2); turn_off_led.setOnClickListener(new View.OnClickListener() { public void onClick(View arg0) { state_flag = false; Log.i("button 2","press down"); imageView.setImageResource(R.drawable.star_big_off); ...... }});
參考資料
[1]異常捕獲: https://blog.csdn.net/klxh2009/article/details/77480108
[2]單執行緒模型 https://zhidao.baidu.com/question/812218637696110852 . html
[3]執行緒更新UI: https://blog.csdn.net/lear7/article/details/48497055
[4]訊息處理機制: https://www.cnblogs.com/fuck1/p/5513412.html
[5]View 例項化: https://ask.csdn.net/questions/271338
[6]UML時序圖: https://www.cnblogs.com/samchen2009/p/3315999.html
最後給大家分享一份非常系統和全面的Android進階技術大綱及進階資料,及面試題集
想學習更多Android知識,請加入Android技術開發企鵝交流 7520 16839
進群與大牛們一起討論,還可獲取Android高階架構資料、原始碼、筆記、視訊
包括** 高階UI、Gradle、RxJava、小程式、Hybrid、移動架構、React Native、效能優化等全面的Android高階實踐技術講解效能優化架構思維導圖,和BATJ面試題及答案!**
群裡免費分享給有需要的朋友,希望能夠幫助一些在這個行業發展迷茫的,或者想系統深入提升以及困於瓶頸的朋友,在網上部落格論壇等地方少花些時間找資料,把有限的時間,真正花在學習上,所以我在這免費分享一些架構資料及給大家。希望在這些資料中都有你需要的內容。

