1. 程式人生 > >Android 程序與執行緒基礎知識 及 如何停止程序

Android 程序與執行緒基礎知識 及 如何停止程序

當一個程式第一次啟動的時候,Android會啟動一個LINUX程序和一個主執行緒。預設的情況下,所有該程式的元件都將在該程序和執行緒中執行。

同時,Android會為每個應用程式分配一個單獨的LINUX使用者。Android會盡量保留一個正在執行程序,只在記憶體資源出現不足時,Android會嘗試停止一些程序從而釋放足夠的資源給其他新的程序使用,也能保證使用者正在訪問的當前程序有足夠的資源去及時地響應使用者的事件。Android會根據程序中執行的元件類別以及元件的狀態來判斷該程序的重要性,Android會首先停止那些不重要的程序。

一、程序按照重要性從高到低一共有五個級別:

前臺程序
前臺程序是使用者當前正在使用的程序。只有一些前臺程序可以在任何時候都存在。他們是最後一個被結束的,當記憶體低到根本連他們都不能執行的時候。一般來說,在這種情況下,裝置會進行記憶體排程,中止一些前臺程序來保持對使用者互動的響應。
如果有以下的情形的那麼就是前臺程序:  
這個程序執行著一個正在和使用者互動的Activity(這個Activity的onResume()方法被呼叫)。
這個程序裡有繫結到當前正在和使用者互動的確Activity的一個service。
這個程序裡有一個service物件,這個service物件正在執行一個它的生命週期的回撥函式(onCreate(), onStart(), onDestroy())
這個程序裡有一個正在的onReceive()方法的BroadCastReiver物件。

可見程序
可見程序不包含前臺的元件但是會在螢幕上顯示一個可見的程序是的重要程度很高,除非前臺程序需要獲取它的資源,不然不會被中止。
如果有如下的一種情形就是可見程序: 
這個程序中含有一個不位於前臺的Activity,但是仍然對使用者是可見的(這個Activity的onPause()方法被呼叫,這是很可能發生的,例如,如果前臺Activity是一個對話方塊的話,就會允許在它後面看到前一個Activity。
這個程序裡有一個繫結到一個可見的Activity的Service。

服務程序
執行著一個通過startService()方法啟動的service,這個service不屬於上面提到的2種更高重要性的。service所在的程序雖然對使用者不是直接可見的,但是他們執行了使用者非常關注的任務(比如播放mp3,從網路下載資料)。只要前臺程序和可見程序有足夠的記憶體,系統不會回收他們。

後臺程序
執行著一個對使用者不可見的activity(呼叫過 onStop() 方法).這些程序對使用者體驗沒有直接的影響,可以在服務程序、可見程序、前臺程序需要記憶體的時候回收。通常,系統中會有很多不可見程序在執行,他們被儲存在LRU (least recently used) 列表中,以便記憶體不足的時候被第一時間回收。如果一個activity正 確的執行了它的生命週期,關閉這個程序對於使用者體驗沒有太大的影響。

空程序
未執行任何程式元件。執行這些程序的唯一原因是作為一個快取,縮短下次程式需要重新使用的啟動時間。系統經常中止這些程序,這樣可以調節程式快取和系統快取的平衡。

Android 對程序的重要性評級的時候,選取它最高的級別。另外,當被另外的一個程序依賴的時候,某個程序的級別可能會增高。一個為其他程序服務的程序永遠不會比被服 務的程序重要級低。因為服務程序比後臺activity程序重要級高,因此一個要進行耗時工作的activity最好啟動一個service來做這個工作,而不是開啟一個子程序――特別是這個操作需要的時間比activity存在的時間還要長的時候。例如,在後臺播放音樂,向網上上傳攝像頭拍到的圖片,使用service可以使程序最少獲取到“服務程序”級別的重要級,而不用考慮activity目前是什麼狀態。broadcast receivers做費時的工作的時候,也應該啟用一個服務而不是開一個執行緒。

二. 單執行緒模型
當一個程式第一次啟動時,Android會同時啟動一個對應的主執行緒(MainThread),主執行緒主要負責處理與UI相關的事件,如使用者的按鍵事件,使用者接觸螢幕的事件以及螢幕繪圖事件,並把相關的事件分發到對應的元件進行處 理。所以主執行緒通常又被叫做UI執行緒。在開發Android應用時必須遵守單執行緒模型的原則: Android UI操作並不是執行緒安全的並且這些操作必須在UI執行緒中執行。

2.1 子執行緒更新UI
Android的UI是單執行緒(Single-threaded)的。為了避免拖住GUI,一些較費時的物件應該交給獨立的執行緒去執行。如果幕後的執行緒來執行UI物件,Android就會發出錯誤訊息
CalledFromWrongThreadException。以後遇到這樣的異常丟擲時就要知道怎麼回事了!

2.2 Message Queue
在單執行緒模型下,為了解決類似的問題,Android設計了一個Message Queue(訊息佇列), 執行緒間可以通過該Message Queue並結合Handler和Looper元件進行資訊交換。下面將對它們進行分別介紹:
1. Message
Message訊息,理解為執行緒間交流的資訊,處理資料後臺執行緒需要更新UI,則傳送Message內含一些資料給UI執行緒。
2. Handler
Handler處理者,是Message的主要處理者,負責Message的傳送,Message內容的執行處理。後臺執行緒就是通過傳進來的 Handler物件引用來sendMessage(Message)。而使用Handler,需要implement 該類的 handleMessage(Message)方法,它是處理這些Message的操作內容,例如Update UI。通常需要子類化Handler來實現handleMessage方法。
3. Message Queue
Message Queue訊息佇列,用來存放通過Handler釋出的訊息,按照先進先出執行。
每個message queue都會有一個對應的Handler。Handler會向message queue通過兩種方法傳送訊息:sendMessage或post。這兩種訊息都會插在message queue隊尾並按先進先出執行。但通過這兩種方法傳送的訊息執行的方式略有不同:通過sendMessage傳送的是一個message物件,會被 Handler的handleMessage()函式處理;而通過post方法傳送的是一個runnable物件,則會自己執行。
4. Looper
Looper是每條執行緒裡的Message Queue的管家。Android沒有Global的Message Queue,而Android會自動替主執行緒(UI執行緒)建立Message Queue,但在子執行緒裡並沒有建立Message Queue。所以呼叫Looper.getMainLooper()得到的主執行緒的Looper不為NULL,但呼叫Looper.myLooper() 得到當前執行緒的Looper就有可能為NULL。

對於子執行緒使用Looper,API Doc提供了正確的使用方法:

package com.test;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

class LooperThread extends Thread { 
    public Handler mHandler; 

    public void run() { 
        Looper.prepare(); //建立本執行緒的Looper並建立一個MessageQueue
        mHandler = new Handler() { 
            public void handleMessage(Message msg) { 
            // process incoming messages here 
            } 
        }; 
        Looper.loop(); //開始執行Looper,監聽Message Queue 
    }     
}

這個Message機制的大概流程:
1. 在Looper.loop()方法執行開始後,迴圈地按照接收順序取出Message Queue裡面的非NULL的Message。
2. 一開始Message Queue裡面的Message都是NULL的。當Handler.sendMessage(Message)到Message Queue,該函式裡面設定了那個Message物件的target屬性是當前的Handler物件。隨後Looper取出了那個Message,則呼叫該Message的target指向的Hander的dispatchMessage函式對Message進行處理。
在dispatchMessage方法裡,如何處理Message則由使用者指定,三個判斷,優先順序從高到低:
    1) Message裡面的Callback,一個實現了Runnable介面的物件,其中run函式做處理工作;
    2) Handler裡面的mCallback指向的一個實現了Callback介面的物件,由其handleMessage進行處理;
    3) 處理訊息Handler物件對應的類繼承並實現了其中handleMessage函式,通過這個實現的handleMessage函式處理訊息。
    由此可見,我們實現的handleMessage方法是優先順序最低的!
3. Handler處理完該Message (update UI) 後,Looper則設定該Message為NULL,以便回收!

在網上有很多講述主執行緒和其他子執行緒如何互動,傳送資訊,最終誰來執行處理資訊之類的文章,個人理解最簡單的方法——判斷Handler物件裡面的Looper物件是屬於哪條執行緒的,則由該執行緒來執行!
1. 當Handler物件的建構函式的引數為空,則為當前所線上程的Looper;
2. Looper.getMainLooper()得到的是主執行緒的Looper物件,Looper.myLooper()得到的是當前執行緒的Looper物件。

三.Android中如何結束程序


3.1 Android 結束程序,關閉程式的方法 即採用下面這個類
Void android.app.ActivityManager.restartPackage(String packageName)
public void restartPackage (String packageName)
Since: API Level 3
Have the system perform a force stop of everything associated with the given application package. All processes that share its uid will be killed, all services it has running stopped, all activities removed, etc. In addition, a ACTION_PACKAGE_RESTARTED broadcast will be sent, so that any of its registered alarms can be stopped, notifications removed, etc. You must hold the permission RESTART_PACKAGES to be able to call this method. Parameters packageNameThe name of the package to be stopped.
使用這個類的具體原始碼
final ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);   
am.restartPackage(getPackageName()); 
不要忘記了在配置檔案中設定許可權:
<uses-permission android:name="android.permission.RESTART_PACKAGES"></uses-permission> 

3.2 android.os.Process.killProcess(pid)
只能終止本程式的程序,無法終止其它的
public static final void killProcess (int pid)
Kill the process with the given PID. Note that, though this API allows us to request to kill any process based on its PID, the kernel will still impose standard restrictions on which PIDs you are actually able to kill. Typically this means only the process running the caller's packages/application and any additional processes created by that app; packages sharing a common UID will also be able to kill each other's processes.
具體程式碼如下:
Process.killProcess(Process.myPid());
public void finish ()
Call this when your activity is done and should be closed. The ActivityResult is propagated back to whoever launched you via onActivityResult().
這是結束當前activity的方法。 要主動的結束一個活動Activity,這裡需要注意finish是結束掉一個Activity,而不是一個程序。這個方法最後會呼叫Activity的生命週期函式onDestroy方法,結束當前的Activity,從任務棧中彈出當前的Activity,啟用下一個Activity。當然其他的finish系列方法,我們不在這裡做詳細討論。

3.3 System.exit(int code)
例如: System.exit(0);
該方法只能用於結束當前程序自身,在程式遇到異常,無法正常執行時,可以 通過這個方法強制退出。 
需要注意的是: android.os.Process.killProcess(pid) 和 System.exit(int code)會導致程序非正常退出,程序退出時不會去執行Activity的onPause、onStop和onDestroy方法,那麼程序很有可能錯過了儲存資料的機會。因此,這兩個方法最好使用在出現異常的時候!大家需要注意其使用方法。

3.4 在android2.2版本之後則不能再使用restartPackage()方法,而應該使用killBackgroundProcesses()方法
manager.killBackgroundProcesses(getPackageName());
使用示例程式碼如下:
ActivityManager manager = (ActivityManager)getSystemService(ACTIVITY_SERVICE);   
manager.killBackgroundProcesses(getPackageName());
//需要在xml中加入許可權宣告    
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>  
另外,在android2.2以後,如果服務在ondestroy里加上了start自己,用kill backgroudprocess通常無法結束自己。

3.5還有一種最新發現的方法,利用反射呼叫forceStopPackage來結束程序
Method forceStopPackage = am.getClass().getDeclaredMethod("forceStopPackage", String.class);  
forceStopPackage.setAccessible(true);  
forceStopPackage.invoke(am, yourpkgname);  
配置檔案中需要新增定義:
android:sharedUserId="android.uid.system"   
另外需要再在配置檔案新增許可權: 
<uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"></uses-permission>   
並且採用系統platform簽名 因為需要用FORCE_STOP_PACKAGES許可權,該許可權只賦予系統簽名級程式 即可實現強制停止指定程式  

3.6 還有一種方法 利用linux的kill -9命令

3.7 退出到主螢幕(是對當前程序的一種處理) 
這個方法,也是退出當前程序的一個方法。如果我們在程序中建立了很多的Activity, 但是又不想關閉時去退出不在任務棧頂的Activity ,那麼就可以直接使用這個方法了。 
功能:當按下返回鍵時,就返回到主螢幕,並帶有引數 FLAG_ACTIVITY_CLEAR_TOP , 會清理掉當前的活動。 

以下是按下返回鍵同時不重複時,返回到主螢幕的示例:

package com.test.android; 
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
/**
 * Andriod按返回鍵返回到主螢幕示例
 * @Description: Andriod按返回鍵返回到主螢幕示例
 * @FileName: MyTestProjectActivity.java 
 * @Package com.test.android 
 * @Author Hanyonglu
 * @Date 2012-4-11 上午11:57:31 
 * @Version V1.0
 */
public class MyTestProjectActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
    @Override 
    public boolean onKeyDown(int keyCode, KeyEvent event) { 
        // event.getRepeatCount():按下返回鍵,同時沒有重複
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
            Intent home = new Intent(Intent.ACTION_MAIN);   
            home.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);   
            home.addCategory(Intent.CATEGORY_HOME);   
            startActivity(home);
        } 
        return super.onKeyDown(keyCode, event);   
    } 
}

這種方法其實並沒有將程序完全地退出,只是將該程式進入到了後臺執行,以便下次更快的啟動,所以想要程式進入到後臺執行可以考慮採用這種方式。關於這點呢,大家瞭解就可以了。

以上就是關於Android中程序和執行緒的基本知識,個人覺得理解這些知識點很重要,雖然它不能讓我們在立即能夠讓我們享受到做出一個產品的成就感,但是可以增強我們的內力,讓我們對Android的一些執行機制掌握地更加清楚。

原文傳送門:http://www.cnblogs.com/hanyonglu/archive/2012/04/12/2443262.html