本文介紹Android平臺中進程與線程的基本知識。

1.Android進程基本知識:

我們先來了解下Android中的進程基本知識。

 

當一個程序第一次啓動的時候,Android會啓動一個LINUX進程和一個主線程。默認的情況下,所有該程序的組件都將在該進程和線程中運行。 同時,Android會爲每個應用程序分配一個單獨的LINUX用戶。Android會盡量保留一個正在運行進程,只在內存資源出現不足時,Android會嘗試停止一些進程從而釋放足夠的資源給其他新的進程使用, 也能保證用戶正在訪問的當前進程有足夠的資源去及時地響應用戶的事件。

我們可以將一些組件運行在其他進程中,並且可以爲任意的進程添加線程。組件運行在哪個進程中是在manifest文件裏設置的,其中<Activity>,<Service>,<receiver>和<provider>都有一個process屬性來指定該組件運行在哪個進程之中。我們可以設置這個屬性,使得每個組件運行在它們自己的進程中,或是幾個組件共同享用一個進程,或是不共同享用。<application>元素也有一個process屬性,用來指定所有的組件的默認屬性。 

 

Android中的所有組件都在指定的進程中的主線程中實例化的,對組件的系統調用也是由主線程發出的。每個實例不會建立新的線程。對系統調用進行響應的方法——例如負責執行用戶動作的View.onKeyDown()和組件的生命週期函數——都是運行在這個主線程中的。這意味着當系統調用這個組件時,這個組件不能長時間的阻塞主線程。例如進行網絡操作時或是更新UI時,如果運行時間較長,就不能直接在主線程中運行,因爲這樣會阻塞這個進程中其他的組件,我們可以將這樣的組件分配到新建的線程中或是其他的線程中運行。

 

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 對進程的重要性評級的時候,選取它最高的級別。例如,如果一個進程含有一個service和一個可視activity,進程將被歸入一個可視進程而不是service進程。

 

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

 

2. 單線程模型

  線程在代碼是使用標準的java Thread對象來建立,那麼在Android系統中提供了一系列方便的類來管理線程——Looper用來在一個線程中執行消息循環,Handler用來處理消息,HandlerThread創建帶有消息循環的線程。具體可以看下面的詳細介紹。

 

當一個程序第一次啓動時,Android會同時啓動一個對應的主線程(Main Thread),主線程主要負責處理與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組件進行信息交換。下面將對它們進行分別介紹:

 

2.2.1. Message Message消息

理解爲線程間交流的信息,處理數據後臺線程需要更新UI,則發送Message內含一些數據給UI線程。

 

2.2.2. Handler Handler處理者

是Message的主要處理者,負責Message的發送,Message內容的執行處理。後臺線程就是通過傳進來的Handler對象引用來sendMessage(Message)。而使用Handler,需要implement 該類的 handleMessage(Message) 方法,它是處理這些Message的操作內容,例如Update UI。通常需要子類化Handler來實現handleMessage方法。

 

2.2.3. Message Queue Message Queue消息隊列

用來存放通過Handler發佈的消息,按照先進先出執行。 每個message queue都會有一個對應的Handler。Handler會向message queue通過兩種方法發送消息:sendMessage或post。這兩種消息都會插在message queue隊尾並按先進先出執行。但通過這兩種方法發送的消息執行的方式略有不同:通過sendMessage發送的是一個message對象,會被Handler的handleMessage()函數處理;而通過post方法發送的是一個runnable對象,則會自己執行。

 

2.2.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,以便回收!

 

3.Android另外提供了一個工具類:AsyncTask。

它使得UI thread的使用變得異常簡單。它使創建需要與用戶界面交互的長時間運行的任務變得更簡單,不需要藉助線程和Handler即可實現。

 

1) 子類化AsyncTask

 

2) 實現AsyncTask中定義的下面一個或幾個方法

 

onPreExecute() 開始執行前的準備工作;

 

doInBackground(Params...) 開始執行後臺處理,可以調用publishProgress方法來更新實時的任務進度;

 

onProgressUpdate(Progress...) 在publishProgress方法被調用後,UI thread將調用這個方法從而在界面上展示任務的進展情況,例如通過一個進度條進行展示。

 

onPostExecute(Result) 執行完成後的操作,傳送結果給UI 線程。

 

這4個方法都不能手動調用。而且除了doInBackground(Params...)方法,其餘3個方法都是被UI線程所調用的,所以要求:

 

1) AsyncTask的實例必須在UI thread中創建;

 

2) AsyncTask.execute方法必須在UI thread中調用;

 

同時要注意:該task只能被執行一次,否則多次調用時將會出現異常。

 

在使用過程中,發現AsyncTask的構造函數的參數設置需要看明白:

AsyncTask<Params, Progress, Result> Params對應doInBackground(Params...)的參數類型。

而new AsyncTask().execute(Params... params),就是傳進來的Params數據,你可以execute(data)來傳送一個數據,或者execute(data1, data2, data3)這樣多個數據。

Progress對應onProgressUpdate(Progress...)的參數類型;

Result對應onPostExecute(Result)的參數類型。 當以上的參數類型都不需要指明某個時,則使用Void,注意不是void。不明白的可以參考上面的例子,或者API Doc裏面的例子。

 

下面是關於AsyncTask的使用示例:

 

複製代碼
((Button) findViewById(R.id.load_AsyncTask)).setOnClickListener(new View.OnClickListener(){ 
 
    @Override 
    public void onClick(View view) { 
        data = null
        data = new ArrayList<String>(); 
 
        adapter = null
 
        //顯示ProgressDialog放到AsyncTask.onPreExecute()裏 
        
//showDialog(PROGRESS_DIALOG); 
        new ProgressTask().execute(data); 
    } 
}); 
 
private class ProgressTask extends AsyncTask<ArrayList<String>, Void, Integer> { 
 
/* 該方法將在執行實際的後臺操作前被UI thread調用。可以在該方法中做一些準備工作,如在界面上顯示一個進度條。*/ 
@Override 
protected void onPreExecute() { 
    // 先顯示ProgressDialog
    showDialog(PROGRESS_DIALOG); 

 
/* 執行那些很耗時的後臺計算工作。可以調用publishProgress方法來更新實時的任務進度。 */ 
@Override 
protected Integer doInBackground(ArrayList<String>... datas) { 
    ArrayList<String> data = datas[0]; 
    for (int i=0; i<8; i++) { 
        data.add("ListItem"); 
    } 
    return STATE_FINISH; 

 
/* 在doInBackground 執行完成後,onPostExecute 方法將被UI thread調用, 
 * 後臺的計算結果將通過該方法傳遞到UI thread. 
 
*/ 
@Override 
protected void onPostExecute(Integer result) { 
    int state = result.intValue(); 
    switch(state){ 
    case STATE_FINISH: 
        dismissDialog(PROGRESS_DIALOG); 
        Toast.makeText(getApplicationContext(), 
                "加載完成!", 
                Toast.LENGTH_LONG) 
             .show(); 
 
        adapter = new ArrayAdapter<String>(getApplicationContext(), 
                android.R.layout.simple_list_item_1, 
                data ); 
                 
        setListAdapter(adapter); 
 
        break
         
    case STATE_ERROR: 
       dismissDialog(PROGRESS_DIALOG); 
       Toast.makeText(getApplicationContext(), 
               "處理過程發生錯誤!", 
               Toast.LENGTH_LONG) 
            .show();
 
       adapter = new ArrayAdapter<String>(getApplicationContext(), 
               android.R.layout.simple_list_item_1, 
               data );
 
          setListAdapter(adapter);
 
          break;
 
   default:
 
   } 
}
複製代碼

 

以上是從網絡獲取數據,加載到ListView中示例。

 

4.Android中如何結束進程 

4.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 packageName The 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> 

 

結束進程還有

4.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());

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系列方法,我們不在這裏做詳細討論。

 

4.3 System.exit(int code)

例如: System.exit(0);

該方法只能用於結束當前進程自身,在程序遇到異常,無法正常執行時,可以 通過這個方法強制退出。 

 

需要注意的是: android.os.Process.killProcess(pid) 和 System.exit(int code)會導致進程非正常退出,進程退出時不會去執行Activity的onPause、onStop和onDestroy方法,那麼進程很有可能錯過了保存數據的機會。因此,這兩個方法最好使用在出現異常的時候!大家需要注意其使用方法。

 

4.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通常無法結束自己。

 

4.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權限,該權限只賦予系統簽名級程序 即可實現強制停止指定程序  

 

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

 

4.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中進程和線程的基本知識,個人覺得理解這些知識點很重要,雖然它不能讓我們在立即能夠讓我們享受到做出一個產品的成就感

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章