1. 程式人生 > >Android 在使用介面回撥時呼叫 Thread.Sleep() 引發的思考

Android 在使用介面回撥時呼叫 Thread.Sleep() 引發的思考

寫這篇文章的原因

原本只是想用最簡單最容易理解的方式去介紹在 Android 中如何使用介面回撥機制。剛開始我也覺得介面回撥也很朦朧,只知道是為了處理任務非同步,以及能使程式碼看起來更加容易理解和維護,但是如何去實現還不太清楚。

通過看了其他大神的部落格,終於能用自己的方式去理解介面回撥如何實現,於是想著花十幾分鍾整理出一個小例子,也讓不太理解該機制的人,能夠快速理解。

但是真正寫起來,才發現很多東西都我都沒理解好,所以在這裡將這次的事件做個記錄。

介面回撥簡單理解和實現

介面回撥的使用場景

在 A 類中呼叫 B 類中的方法,在該方法處理完成後需要將結果告知 A 類即可用介面回撥實現

例如在 TestCallBackActivity 中有個按鈕,通過點選這個按鈕可以去做一些事情,比如我想通過點選這個按鈕,讓 Wang.class 這個類裡面的 dowork() 方法去做一些事情,當Wang.class做完了事情之後需要通知 TestCallBackActivity它做完了。

通俗易懂的理解

下面一段是吐槽,可以跳過!!
又或者是今天老闆S給了一個需求給程式設計師A,讓程式設計師去做,不管程式設計師A怎麼做,程式設計師A只要在完成任務後將成品給老闆看就行(一般情況下不可能當場就做出來,需要一定的時間,所以老闆這時候就可以去給其他程式設計師提其他需求或者是去喝杯咖啡??又或者在你旁邊一直等著你做出來,但是誰知道你什麼時候做出來,哈哈哈)

實現

  • 建立CallBack介面,宣告好會出現的結果方法
public interface ICallBack {
    void aBiggerThanb() ;
    void aSmallThanb() ;
}
  • 再建立Wang.class,寫上doWork()方法
public class Wang {
   public void doWork(int a, int b, ICallBack callBack){
       if (a > b){
           callBack.aBiggerThanb();
       }else {
           callBack.aSmallThanb();
       }
   }
}
  • TestCallBackActivity中呼叫,並彈出Toast告訴TestCallBackActivity結果
Button btnCallWangToWork = findViewById(R.id.btn_call_wang_to_work) ;
final Wang wang = new Wang();

btnCallWangToWork.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(CallBackTestActivity.this, "小王正在計算中...", Toast.LENGTH_SHORT).show();

        wang.doWork(10, 50, new ICallBack() {
            @Override
            public void aBiggerThanb() {
                new Timer().schedule(new TimerTask() {
                    @Override
                    public void run() {
                        Looper.prepare();
                        Toast.makeText(CallBackTestActivity.this, "a > b", Toast.LENGTH_SHORT).show();
                        Looper.loop();
                    }
                }, 4 * 1000);
            }

            @Override
            public void aSmallThanb() {
                try {
                    Thread.sleep(500);
                    Toast.makeText(CallBackTestActivity.this, "a < b", Toast.LENGTH_SHORT).show();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
});

一些問題

  • Thread.Sleep() 的含義
    為什麼會講到這個問題呢?因為我在實現的時候,剛開始是讓引數a>b,當點選按鈕的時候先Toast:小王正在計算中...,然後呼叫 Thread.Sleep(5000)休息5s後才Toast 計算返回的結果。想法很完美,但是Toast:小王正在計算中...就是一直不顯示,系統也沒報錯,所以我想到了肯定是Thread.Sleep搞的鬼。

Thread.Sleep的表示的含義我們可以點選進入方法檢視到方法描述:

Causes the currently executing thread to sleep
 (temporarily cease execution) for the specified number 
 of milliseconds, subject to the precision 
 and accuracy of system timers and schedulers.
  The thread does not lose ownership of any monitors

主要是說Sleep方法會讓當前執行緒休眠指定的毫秒數,目標物件服從系統的時間和排程。從該描述中結合 Toast 預設的顯示時長我們可以知道是什麼原因導致Toast不彈出了。

  • Toast 預設的顯示時長
    Android 自帶的兩個 Toast 時長 LENGTH_SHORT (2秒)LENGTH_LONG (3.5秒),上面我們的Thread.Sleep(5000)讓當前執行緒(主執行緒)休眠5s顯然已經超過 Toast 的預設顯示時長。所以當我們讓執行緒休眠完成後第一個Toast 已經顯示完成了,所以當休眠完成後才會看不到 Toast 的文字。那麼我想先彈出 Toast 提示後臺正在計算,然後5s後顯示計算結果,怎麼做呢?

  • 定時任務
    利用定時任務去實現,這裡使用的是 new Timer().schedule(new TimerTask() {...}實現,但是在裡面進行 Toast 是會報錯的Can't create handler inside thread that has not called Looper.prepare(),因為 Toast 的顯示需要Looper 通過Handler去傳送訊息給主執行緒更新UI,但是 Android 系統預設情況下非主執行緒中沒有開啟 Looper,而且 Handler 物件必須繫結 Looper 物件,所以知道原因我們就知道如何去實現了。

  • 如何給開啟 Looper

Looper.prepare();
....doSomething...
Looper.loop();

OK ,手工!