Android 非同步更新UI-線程池-Future-Handler執行個體分析

來源:互聯網
上載者:User

標籤:textview   illegal   move   乾貨   ice   button   creat   use   保留   

 前言: 我們在開發Android過程中,在處理耗時任務和UI互動的過程中,都會將耗時任務放到子線程處理並重新整理. 下面我提出的兩個問題,相信大多數開發人員都會碰到:

1. 資料經常需要讀取更新,並且比較耗時,需要分步重新整理UI.

2. UI介面切換後,如何停止掉子線程裡面正在讀取的資料而不會將舊資料重新整理到新UI介面上.

 目前網上大部分教程主要只是簡單的Handler.postDelayed(), Thread + Handler, Async等方式, 只適用於簡單的一次性重新整理. 或許有人會說我可以採用不斷地new Thread的方式來建立子線程重新整理,然後傳message回去更新UI,但是這樣的不斷地new會有效能消耗大和資料同步的問題.

 關於以上這兩個問題的解決, 我在這裡想要介紹的是使用線程池+Future+handler的配合使用.

 為了更好理解非同步更新UI的原理,這裡先介紹下Thread + Handler + Looper + Message模型, 如1所示:

  圖1 Thread + Handler + Looper + Message模型

  圖1清楚給我們展示出了訊息佇列在Looper裡面的處理方式,這裡有兩個重要的要點: (1)子線程也可以通過Looper管理Message, 但是需要加Looper.prepare() 和Looper.loop()才能實現訊息迴圈; (2)UI主線程無需實現prepare()和loop()因為主線程中已經預設實現了.

  現在開始介紹線程池+Future+handler的一些基本概念和使用demo執行個體.

線程池

  是一種對象池的思想,開闢一塊記憶體空間,裡面存放了眾多(未死亡)的線程,池中線程執行調度由池管理器來處理。當有線程任務時,從池中取一個,執行完成後線程對象歸池,這樣可以避免反覆建立線程對象所帶來的效能開銷,節省了系統的資源.如果在程式中反覆建立和銷毀線程,將會對程式的反應速度造成嚴重影響,有時甚至會Crash掉程式.這裡我們使用簡單的ExecutorService類.

Future

  Future模式可以這樣來描述:我有一個任務,提交給了Future,Future替我完成這個任務。期間我自己可以去做任何想做的事情。一段時間之後,我就便可以從Future那兒取出結果。就相當於下了一張訂貨單,一段時間後可以拿著提訂單來提貨,這期間可以幹別的任何事情。其中Future 介面就是訂貨單,真正處理訂單的是Executor類,它根據Future介面的要求來生產產品。

Handler

  串連子線程和主線程的橋樑,可以通過sendmessage或者post的方式跟主線程通訊.

說了這麼多,如果還有對基本概念不太熟悉的童鞋可以先移步到最後的參考文章裡看下再回來看本文章,此處直接上代碼乾貨.

方法一:利用sendMessage實現

public class MyActivity extends Activity {     private final String TAG = DemoExecutorService;     @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        initFindView();        setListener();    }     private TextView mTitle;    private Button mBtn;    private void initFindView() {        mTitle = (TextView) findViewById(R.id.title);        mBtn = (Button) findViewById(R.id.btn);    }     private void setListener() {        mBtn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                TestCallBack testCallBack = new TestCallBack();                testCallBack.loadToHandler();            }        });    }     private class TestCallBack {        public TestCallBack() {            Log.d(TAG, #####TestCallBack===Constructor);        }         public void loadToHandler() {            Handler myHandler = new Handler(getMainLooper()) {                @Override                public void handleMessage(Message msg) {                    Log.d(TAG, #######receive the msg?? what =  + msg.what);                    int num = msg.what;                    switch(num){                        case 1:                            mTitle.setText(######Yeah, we receive the first msg 1);                            break;                        case 2:                            mTitle.setText(######Yeah, we receive the second msg 2);                            break;                        default:                            break;                    }                }            };            testExecutorHandler(myHandler);        }    }     private final ExecutorService mExecutor  = Executors.newSingleThreadExecutor();    Future<!--?--> mTask;    boolean mSendMsg;     public void testExecutorHandler(final Handler handler) {        Log.d(TAG, ########testExecutorHandler, mTask =  + mTask);        if(mTask != null) {            // 通過取消mTask,來實現之前排隊但未啟動並執行submit的task的目的,通過標誌位不讓其發msg給UI主線程更新.            mTask.cancel(false);            Log.d(TAG, ########mTask.isCannel? ===  + mTask.isCancelled());            mSendMsg = false;        }        Runnable r = new Runnable() {            @Override            public void run() {                mSendMsg = true;                try {                    Log.d(TAG, ###step 1####start to sleep 6s.);                    Thread.sleep(6000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                Message msg;                Log.d(TAG, #######1111 mSendMsg ===  + mSendMsg);                if(mSendMsg) {                    msg = handler.obtainMessage();                    msg.what = 1;                    handler.sendMessage(msg);                } else {                    return ;                }                Log.d(TAG, ####step 2####start to sleep 4s.);                try {                    Thread.sleep(4000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                // 若沒有重新obtainMessage的話,就會出現以下錯誤,因為已經被回收, 所以報錯. 需要重新 obtainMessage().//                E/AndroidRuntime( 1606): java.lang.IllegalStateException: The specified message queue synchronization  barrier token has not been posted or has already been removed.                Log.d(TAG, #######22222 mSendMsg ===  + mSendMsg);                if(mSendMsg) {                    msg = handler.obtainMessage();                    msg.what = 2;                    handler.sendMessage(msg);                } else {                    return ;                }            }        };//        mExecutor.submit(r);      // 若只是這樣子就不會進入Future任務裡面,那樣每一個submit提交的都會被依次執行.        mTask = mExecutor.submit(r);    } }

結果和列印如2和3所示:

圖4用多次點擊來類比UI不停調用重新整理的情況,背景執行任務會只是保留當前task和最後一次提交的task,中間的task都被Futurecancel掉了.而且當前舊的task也會受到標誌位的控制,不會將更新內容sendMessage出來,從而不會影響最後一次UI的重新整理.

方法二:利用runnable實現更新:

由於部分方法跟上面一樣,所以要看完整代碼可以在下面下載,以下只是核心代碼.

public void loadToRunnable() {           Runnable runable = new Runnable(){               @Override               public void run() {                   Log.d(TAG, #########Ok..1111 let update callback1...);                   mTitle.setText(####Yeah, Refresh in runnable, callback1);               }           };            Runnable runable2 = new Runnable() {               @Override               public void run() {                   Log.d(TAG, ######Ok, let update callback2, ...);                   mTitle.setText(####Callback2 update success!!!);               }           };            testExecutorRunnable(runable, runable2, getMainLooper());       }   }
public void testExecutorRunnable(final Runnable callback,                             final Runnable callback2, final Looper looper) {        Log.d(TAG, #####testExecutor##mTask..#### =  + mTask);        if(mTask != null) {www.2cto.com            mTask.cancel(false);        // true 表示強製取消,會發生異常; false表示等待任務結束後取消.            mSendMsg = false;        }         Runnable r = new Runnable(){            @Override            public void run() {                mSendMsg = true;                try {                    Log.d(TAG, #####Step 1####Async run after submit...###sleep 6s);                    Thread.sleep(6000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                if(callback != null && mSendMsg){//                    Handler handler = new Handler();      // Not use it, should use the Looper.                    Handler handler = new Handler(looper);                    handler.post(callback);                }                 try {                    Log.d(TAG, #####Step 2####Async run after submit...###sleep 4s);                    Thread.sleep(4000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                if(callback2 != null && mSendMsg) {                    Handler handler = new Handler(looper);                    handler.post(callback2);                }            }        };         mTask = mExecutor.submit(r);    }

 

Android 非同步更新UI-線程池-Future-Handler執行個體分析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.