Android中的多進程、多線程

前面幾篇總結了進程、線程相關的知識。這裏總結下關於Android中的多進程、多線程及其使用。java

這裏總結的Android中的多進程、多線程也是一個基礎,可擴展的不少。linux

 

Android中多進程

常見的幾種使用android

Runtime.getRuntime().exec("xxx")

這個方法,調用程序外的 腳本或命令程序,它會生成一個新的進程去調用 返回一個Process對象。shell

如:windows下,調用記事本。數據庫

Runtime.getRuntime().exec("notepad.exe");

linux下(Android)下,調用系統自己的ps命令後,經過返回的Process對象的輸入流 讀取了調用內容。windows

try {
    String[] commandStr = new String[] {"/bin/sh","-c", "ps -ef"};
    //String commandStr = "/bin/sh -c ps";
    Process process1 = Runtime.getRuntime().exec( commandStr );
    Log.d( TAG, "onCreate: process1=" + process1 );
    byte [] data = new byte[1024];
    int len = 0;
    while( -1 != (len = process1.getInputStream().read(data)) ) {
        String str = new String(data, 0, len , "UTF-8");
        Log.d( TAG, "onCreate: \n" + str );
    }
} catch (IOException e) {
    e.printStackTrace();
}

執行後,經過log,很容易看出,Runtime.getRuntime().exec("xxx")調用就是新建的進程。數組

 

ProcessBuilder("xxx").start()

這個方法一樣能夠調用程序外的 腳本或命令程序。安全

相似Runtime.getRuntime().exec(),如:window下打開計算器。網絡

new ProcessBuilder("calc.exe").start();

 llinux:多線程

try {
    String[] commandStr = new String[] {"/bin/sh","-c", "ps -ef"};
    ProcessBuilder processBuilder = new ProcessBuilder(commandStr);
    processBuilder.redirectErrorStream( true );
    Process process2 = processBuilder.start();
    Log.d( TAG, "onCreate: process2=" + process2 + ";processBuilder.directory="+processBuilder.directory());
    byte [] data = new byte[1024];
    int len = 0;
    while( -1 != (len = process2.getInputStream().read(data)) ) {
        String str = new String(data, 0, len , "UTF-8");
        Log.d( TAG, "onCreate: \n" + str );
    }
} catch (IOException e) {
    e.printStackTrace();
}

注:

Runtime.getRuntime().exec(param)和 ProcessBuilder(param).start()關聯

經過源碼跟蹤,很容易看到Runtime.getRuntime().exec()最終也是調用的ProcessBuilder().start()來實現的。

因此它們不少相似,都是建立一個新的進程來執行程序外的腳本或命令程序,並返回Process實例,經過實例能夠獲取進程信息及控制進程狀態。

傳遞參數有所不一樣,Runtime.getRuntime().exec()能夠是單獨字符串(用空格分隔可執行命令程序和參數,如例子中註釋的那條),也能夠是字符串數組。ProcessBuilder().start()只能是字符串數組或集合,一樣第一個參數是可執行命令程序。

android:process標籤

應用的AndroidManifest.xml的清單文件中,能夠經過設置android:process標籤 實現多進程。

默認,同一應用全部組件運行相同進程中,進程名即包名。

支持

四大組件(Activity,Service,ContentProvider,BroadcastReceive)都支持android:process標籤,組件元素-(<activity>、<service>、<receiver> 和 <provider>),經過此屬性指定組件在哪一個進程中運行。

<application>也支持android:process標籤,它只設置全部組件默認運行進程的默認值。

進程名(屬性值)

若是以冒號(「:」)開頭,建立的 則是應用的私有進程。

如配置android:process=":myprocess1",包名是com.android.test,則實際進程名即com.android.test:myprocess1。

若是不是以冒號而是以小寫字母開頭,則是全局進程,只要有權限便可訪問。不一樣應用的組件能夠共享該進程。

其餘

進程內存限制

以下兩個重要的值,若是超過,則會出現OOM。經過 adb shell getprop | grep xxx(如adb shell getprop | grep dalvik以看到不少配置,不只僅下面兩個 )能夠查詢到相應手機中的配置,不一樣手機多是不一樣的。

dalvik.vm.heapsize ---單個dalvik虛擬機最大內存

dalvik.vm.heapgrowthlimit ---單個進程的最大內存

Android中進程間通訊

進程間通訊總結中提到比較全面,能夠參考下。//TODO

系統中也存在不少包括Activity,Service,Broadcast,ContentProvider都有這樣的實現。

比較經常使用,須要瞭解和掌握的:Bundle(序列化,四大組件經常使用),AIDL,Messenger,ContentProvider,Socket

 

Android中多線程

主線程

當應用啓動後,系統即會爲應用建立一個線程---「main」,即主線程。主線程,也稱爲UI線程,它負責事件的分派,包括繪製事件。

通常全部組件都在主線程中實例化。

當耗時操做(如網絡訪問或數據庫操做)時就會阻塞主線程,會致使事件沒法分派,包括繪製事件,甚至5s ANR。

工做線程

保證應用界面的響應能力,關鍵是不能阻塞界面線程。若是執行的操做不能即時完成,則應確保它們在單獨的線程中運行。這個單獨的線程即工做線程。

注意:

1.不要阻塞主線程,即耗時操做不要放在主線程中。

2.只有主線程能夠更新UI,其餘全部線程都沒法更新UI。

從其餘線程進入主線

因爲第二點,系統提供了從其餘線程進入主線程的幾種方法:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

例如

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            // a potentially time consuming task
            final Bitmap bitmap =
                    processBitMap("image.png");
            imageView.post(new Runnable() {
                public void run() {
                    imageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

 

可是,隨着操做日趨複雜,這類代碼也會變得複雜且難以維護。

解決://TODO

1.若經過工做線程完成複雜交互,考慮在工做線程中使用Handler處理來自主線程的消息

2.擴展AsyncTask類。AsyncTask經過異步通訊和消息傳遞,將工做線程中的結果傳遞到主線程,更新相關UI操做。

線程安全

先看下面的例子,onCreate中開啓了10個線程,調用plusOne方法,對num進行加1處理。

private static final String TAG = "ProcessThread";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate( savedInstanceState );
    for (int i = 0; i < 5; i++) {
        new Thread_thread().start();
        new Thread( runnable ).start();
    }
}

int num = 0;
//繼承Thread類
private class Thread_thread extends Thread {
    @Override
    public void run() {
        plusOne();
    }
}

//實現Runnable接口
private Runnable runnable = new Runnable() {
    @Override
    public void run() {
        plusOne();
    }
};

private void plusOne() {
    num++;
    Log.d( TAG, "num=" + num );
}

 打印的log以下:

2020-05-28 10:58:12.731 9531-9567/com.flx.process_thread D/ProcessThread: num=1
2020-05-28 10:58:12.741 9531-9568/com.flx.process_thread D/ProcessThread: num=3
2020-05-28 10:58:12.741 9531-9570/com.flx.process_thread D/ProcessThread: num=3
2020-05-28 10:58:12.744 9531-9569/com.flx.process_thread D/ProcessThread: num=4
2020-05-28 10:58:12.761 9531-9571/com.flx.process_thread D/ProcessThread: num=5
2020-05-28 10:58:12.762 9531-9572/com.flx.process_thread D/ProcessThread: num=6
2020-05-28 10:58:12.787 9531-9574/com.flx.process_thread D/ProcessThread: num=7
2020-05-28 10:58:12.791 9531-9573/com.flx.process_thread D/ProcessThread: num=8
2020-05-28 10:58:12.801 9531-9575/com.flx.process_thread D/ProcessThread: num=9
2020-05-28 10:58:12.802 9531-9576/com.flx.process_thread D/ProcessThread: num=10

 從log看到num值存在重複。像上述的狀況,多是兩個線程同時操做了num,操做時num都是同樣的。這種狀況就是線程不安全的。

線程安全就是,多個線程訪問同一個對象時,若是不用考慮這些線程在運行時環境下的調度和交替執行,也不須要進行額外的同步,或者在調用方進行任何其餘操做調用這個對象的行爲均可以得到正確的結果,那麼這個對象就是線程安全的。

上述例子中,分別使用了建立線程經常使用的兩種方法:

繼承Thread類 實現Runnable接口

實現線程安全經常使用方法

synchronized關鍵字

以下,應該都很熟悉,不作解釋。

private synchronized void plusOne() {
    num++;
    Log.d( TAG, "num=" + num );
}

 Lock鎖

ReentrantLock是Lock的一個子類。

以下示例,通常使用,unlock放在finally中,執行方法體放在try中。

private final ReentrantLock lock = new ReentrantLock();
private void plusOne() {
    lock.lock();
    try {
        num++;
        Log.d( TAG, "num=" + num );
    }finally {
        lock.unlock();
    }
}

 

注:

volatile關鍵字

volatile保證了不一樣線程對某個變量進行操做時的可見性,即一個線程修改了某個變量的值,這新值對其餘線程來講是當即可見的。

volatile不能保證原子性,所以不能保證線程安全

經常使用進程線程信息獲取

//當前進程ID,用戶ID
Log.d( TAG, "onCreate: currPID=" + android.os.Process.myPid()
  + ";currUID=" + android.os.Process.myUid() );
//當前線程ID。下面兩種方法獲取的值是不同的。 
//第一個是系統級的,系統分配管理;第二個是java級的,Thread管理的。因爲java跨平臺
Log.d( TAG, "onCreate: currTid=" + android.os.Process.myTid()
  + ";currTid2=" + Thread.currentThread().getId() );
//主線程ID
Log.d( TAG, "onCreate: mainTid=" + Looper.getMainLooper().getThread().getId() );

打印狀況:

2020-05-28 10:58:12.703 9531-9531/com.flx.process_thread D/ProcessThread: onCreate: currPID=9531;currUID=10133
2020-05-28 10:58:12.704 9531-9531/com.flx.process_thread D/ProcessThread: onCreate: currTid=9531;currTid2=2
2020-05-28 10:58:12.704 9531-9531/com.flx.process_thread D/ProcessThread: onCreate: mainTid=2
相關文章
相關標籤/搜索