[Android] Android進程與線程基本知識

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

  很早的時候就想介紹一下Android中的進程和線程,但因爲其餘的事情一直給耽擱了,直到如今才能和你們一塊兒分享下。linux

  1.Android進程基本知識:android

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

    在安裝Android應用程序的時候,Android會爲每一個程序分配一個Linux用戶ID,並設置相應的權限,這樣其它應用程序就不能訪問此應用程序所擁有的數據和資源了。安全

    在 Linux 中,一個用戶ID 識別一個給定用戶;在 Android 上,一個用戶ID 識別一個應用程序。網絡

    應用程序在安裝時被分配用戶 ID,應用程序在設備上的存續期間內,用戶ID 保持不變。app

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

 

  咱們能夠將一些組件運行在其餘進程中,而且能夠爲任意的進程添加線程。組件運行在哪一個進程中是在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正 確的執行了它的生命週期,關閉這個進程對於用戶體驗沒有太大的影響。

  空進程

  未運行任何程序組件。運行這些進程的惟一緣由是做爲一個緩存,縮短下次程序須要從新使用的啓動時間。系統常常停止這些進程,這樣能夠調節程序緩存和系統緩存的平衡。這5種優先級別,其中empty process是爲了下次在啓動該程序而緩存一些數據而保留的進程,這就是爲何當咱們退出程序後,長按Home鍵還能看到該程序進程圖標的緣由。

  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 消息

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

  2.2.2. Handler 處理者

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

  2.2.3. 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中進程和線程的基本知識,我的以爲理解這些知識點很重要,雖然它不能讓咱們在當即可以讓咱們享受到作出一個產品的成就感,可是能夠加強咱們的內力,讓咱們對Android的一些運行機制掌握地更加清楚。

相關文章
相關標籤/搜索