編輯:關於Android編程
安卓應用只有一個主線程-各個組件都是在這個線程中運行。作為組件的之一的Activity就是在這個線程中更新應用界面的,例如,用戶點擊界面上的一個按鈕,按鈕得到響應,整個過程就是在這個主線程裡。所以這個主線程絕對不可以做耗時的操作。假如在按鈕中做了耗時的操作,那麼當它進行耗時操作的時候,你去點擊界面上的其它按鈕是不會有反應的,就好像程序凍在了那裡。
我們的代碼一旦連續占用這個線程超過一定的時間,系統就會彈出“程序無響應的”提示,這個提示叫做ANR
-Applicatin No Response。
這就好比你在正在做一件事情A,突然另一件事情B來打擾你,你不得不停下手頭的工作來完成,做完了才能繼續之前的工作;這時如果有另外一個人(另一個線程)來幫助你,把事情B全部包攬了,那你就不用分心了。當另一個人把事情B做完後,告訴你一聲就可以了。
啟動一個新的線程,分擔耗時工作的方法是一種異步操作:我讓你幫我做一件事情,布置任務後,我就去做其他的事情了,等你做完了再告訴我結果;
與它對應的是同步操作:我讓你幫我做一件事情,布置任務後,我啥也不做,就等著你做完了告訴我結果;
例如,一個視頻播放應用,獲取視頻信息所需要的時間是個不能確定的事情。如果視頻很少,也許幾十毫秒就能完成,如果視頻很多(比如幾十個),也許就要花二十多秒。
因此,我們可以考慮把獲取視頻信息的操作放到一個單獨的線程thread中進行。啟動一個新線程-工作線程thread-查詢視頻信息,查詢完成後,工作線程再將結果通知到主線程,讓主線程將查詢到結果的結果顯示到界面上。因為界面的更新一定要在主線程中進行,不能在別的線程修改,否則系統後提示運行錯誤。因此我們一定要將查詢的結果發送給主線程,讓主線程處理界面的更新。
這裡就涉及到了線程的創建,工作分配,以及它們之間的配合-信息的傳遞。
一個Activity運行在一個主線程上,它不能進行耗時巨大的操作,也不能執行耗時不確定的操作。因此需要在新的線程中進行這些耗時的操作,這種進行耗時操作的線程稱作工作線程。
創建一個新的線程,
創建一個Runnable
,重寫它的run()
函數,這個函數裡用來進行耗時的操作;
@Override
public void run() {
//耗時的操作
while(xxx)
{
}
}
};
以Runnable
為參數,創建一個Thread
,調用Thread
的start()
方法後,新線程就運行起來了,並執行Runnable
中的run()
函數;
Thread thread = new Thread(runnable);
thread.start();
當run()
函數執行完畢後,新線程就退出;
在線程執行耗時操作的過程中,有時要取消這個線程的操作,
最好的辦法是在run()
函數中增加一個標志位,工作線程會隨時檢查這個標志位是否被設置上,如果設置上了,就讓run()
函數,立即返回,
//設置標志位,為退出線程使用
boolean needStop = false;
......
Runnable runnable = new Runnable() {
@Override
public void run() {
//耗時的操作
while(!needStop)
{
}
}
};
如果主線程要取消這個線程的工作,修改這個標志位就好了,
needStop = true;
Thread可以擁有不同的優先級,從低到高有10級。操作系統根據線程的優先級來進行資源調度,優先為優先級高的線程分配CPU資源。在默認情況下,新創建的線程使用默認的優先級NORM_PRIORITY
。設置優先級的方式很簡單,
Thread thread = new Thread(runnable);
thread.setPriority(Thread.NORM_PRIORITY);
可以為線程取名字,當我們用Android Monitor
工具調試應用的時候,就能看到創建線程時它的名字,方便我們觀察、調試程序,
Thread thread = new Thread(runnable, "新線程的名字");
有時,一個應用會同時啟動多個Thread,在創建它們的時候可以為它們設置ThreadGroup
參數,將它們分成一組,便於對這些線程進行統一的管理。比如,中斷這個組裡所有線程的運行;
ThreadGroup group = new ThreadGroup("線程組");
Thread thread1 = new Thread(runnable, group);
thread1.start();
Thread thread2 = new Thread(runnable, group);
thread2.start();
Thread thread3 = new Thread(runnable, group);
thread3.start();
//中斷3個線程的執行
group.interrupt();
Thread的停止就是指這個線程的退出。線程的退出原因無外乎兩種,
工作線程的工作完成了; 工作線程雖然正在進行耗時工作,但是被取消了,要提前結束;當Runnable
中的run()
函數執行完並返回後,當前的Thread就退出了。
在run()
函數中增加一個標志位,工作線程會隨時檢查這個標志位是否被設置上,如果設置上了,就讓run()
函數,立即返回,
//設置標志位,為退出線程使用
boolean needStop = false;
......
Runnable runnable = new Runnable() {
@Override
public void run() {
//耗時的操作
while(!needStop)
{
}
}
};
如果主線程要取消這個線程的工作,修改這個標志位就好了,
needStop = true;
interrupt()
是線程提供的一個標准的線程退出方法,如果當前的工作線程正被Object.wait
、Thread.join
或Thread.sleep
阻塞,那麼使用thread.interrupt()
之後,正在運行的線程會拋出一個InterruptedException
異常,
Runnable runnable = new Runnable() {
@Override
public void run() {
...
while(!needStop)
{
try {
......
Sleep(1000);
} catch ( InterruptedException e ) {
needStop = true
}
}
}
};
不過interrupt()
方法並不是萬能的,不是所有阻塞情況下都能夠讓線程立即退出。
例如當該線程正在用ServerSocket
的accept()
方法等待連接的時候,即使調用了這個工作線程的interrupt()
方法,該線程還是不會拋出異常的。
它只對Object.wait
、Thread.join
或Thread.sleep
這幾種阻塞有效果。對於網絡讀取數據時代阻塞狀態解除是沒有效果的。
*對於網絡讀取數據時造成的阻塞,我們會在以後相應的章節詳細介紹解決方法。
線程間的同步就是指線程A執行到一個地方的時候,停了下來,等待線程B的執行結果;等線程B執行出結果後,線程A才能繼續執行。
最常見的就是主線程的執行,依賴於工作線程的退出。
主線程啟動了工作線程以後,有時候需要等到工作線程結束以後再進行接下來的操作。
例如一個Activity,在onCreate()
的時候創建了一個工作線程Thread B;後來在用戶的要求下,Activity退出,要被銷毀了;銷毀Activity時,主線程要等到Thread B執行完了才能繼續接著進行剩下的清理工作,那麼Activity可以在它的onDestroy()函數中可以使用join()
方法,等待子線程的結束,
private Thread mTreadB;
@Override
protected void onCreate() {
super.onCreate();
mThreadB = new Thread(runnable);
mThreadB.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
mThreadB.join();
//進行剩下的清理操作
}
join()
方法,會一直處於阻塞狀態,直到線程B退出。
在onDestroy()
中使用join()
有天生的缺點:不能在主線程中進行耗時不可控的操作(例如這裡等待工作線程執行完畢),萬一工作線程的退出花費了很長的時間,那就有問題了。這裡的場景只是用來舉一個例子而已。
利用Object
的wait()
方法實現線程間的同步,需要線程之間共享一個“鎖”-Object
對象。
final Object lock = new Object();
當主線程A執行到一個階段的時候,如果要等待線程B,就把鎖置於等待狀態,
lock.wait()
此時,線程A就出於阻塞的狀態,不能往下執行了。
線程B繼續它的運行,當它執行到一個階段的時候,將鎖設置成放行狀態,
lock.notify();
此時,線程A的阻塞狀態解除,可以繼續往下執行了。
昨天群裡有討論時間軸的項目,沒有接觸過,以為很吊,研究之後才知道表面都是忽悠人的,使用listview就能實現了,也沒有什麼新鮮的東西 廢話少說,直接上圖
1.黑白效果復制代碼 代碼如下:/** * 將彩色圖轉換為黑白圖 * &n
之前有人在知乎提問:“怎麼計算apk的啟動時間?”: 利用Python或者直接用adb命令怎麼計算apk的啟動時間呢?就是計算從點擊圖標到apk完
前幾天,收到了Android Studio 2.2的更新推送,於是迫不及待的更新了一下。不負眾望Android Studio 2.2帶來了很多新的特性,能讓我眼前一亮。A