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 ,手工!