理論基礎(chǔ)
我們知道,task是用戶為了完成某個(gè)功能而執(zhí)行的一系列操作所形成的一個(gè)Activity序列。
一般情況下,一個(gè)應(yīng)用啟動(dòng)的Activity對(duì)象都處于同一個(gè)task中(task id 相同),也處于同一個(gè)進(jìn)程中(process id 相同)。但我們知道,android的task只是針對(duì)任務(wù)的,也就是說同一個(gè)task中的Activity對(duì)象可以來自不同的進(jìn)程。
問題
當(dāng)然,在大多數(shù)情況下,我們是因?yàn)閼?yīng)用要調(diào)用到其它產(chǎn)品的功能,才會(huì)把一些非本應(yīng)用的Activity調(diào)起來。此時(shí),會(huì)產(chǎn)生同一個(gè)task中的Activity對(duì)象可以來自不同的進(jìn)程的情形。
那么問題來了,在什么樣的情況下,我們需要在同一個(gè)task中(單個(gè)應(yīng)用,不涉及調(diào)用其它應(yīng)用的產(chǎn)品)使用不同的進(jìn)程來啟動(dòng)自己應(yīng)用中的Activity呢?比如下面這種情形:
u0_a888 1248 460 2140048 54060 ffffffff 00000000 S com.test.multiprocesstest
u0_a888 1784 460 2136872 51704 ffffffff 00000000 S com.test.multiprocesstest:process1
u0_a888 1909 460 2136924 52848 ffffffff 00000000 S com.test.multiprocesstest:process2
u0_a888 2012 460 2156528 50864 ffffffff 00000000 S com.test.multiprocesstest:process3
其中每個(gè)進(jìn)程對(duì)應(yīng)一個(gè)不同的Activity。可能的情況有:
- 此應(yīng)用的單個(gè)Activity需要使用相對(duì)比較大的內(nèi)存
- 此應(yīng)用有可能需要處理相對(duì)獨(dú)立的多個(gè)文檔
- 此應(yīng)用的Activity的關(guān)閉動(dòng)作需要非常迅速的完成
比如,一個(gè)應(yīng)用需要同時(shí)打開幾個(gè)非常消耗內(nèi)存的文檔,同時(shí)又需要在這幾個(gè)文檔中來回切換,還需要關(guān)閉文檔不能有遲鈍。
因?yàn)?,?duì)于每個(gè)進(jìn)程最多能使用多少內(nèi)存,每臺(tái)機(jī)器都是不同的,具體可以通過查看:/system/build.prop文件中的dalvik.vm.heapgrowthlimit項(xiàng)即可:
shell@PD1602:/system $ cat build.prop | grep heap
...
dalvik.vm.heapgrowthlimit=256m
...
上述這臺(tái)設(shè)備是256m,單個(gè)process如果使用內(nèi)存超過這個(gè)數(shù)量,就會(huì)拋出OOM異常。
還有,如果關(guān)閉Activity時(shí)需要等待線程結(jié)束,釋放對(duì)象模型等等的操作,可能會(huì)占用一定的時(shí)間,此時(shí)Activity就無法立即銷毀。
解決方案
在以上這種情形下,使用單任務(wù)單進(jìn)程,達(dá)到要求就比較費(fèi)勁。所以,我們可以在一個(gè)應(yīng)用中嘗試使用單任務(wù)多進(jìn)程來解決這個(gè)問題。
具體實(shí)現(xiàn)思路其實(shí)很簡單:
在工程中創(chuàng)建多個(gè)Activity,比如Activity1,Activity2,Activity3等。在Manifest中設(shè)置每個(gè)Activity對(duì)應(yīng)的進(jìn)程名,比如:
<activity
android:name=".Activity1"
android:process=":process1">
</activity>
<activity
android:name=".Activity2"
android:process=":process2">
</activity>
<activity
android:name=".Activity3"
android:process=":process3">
</activity>
這樣每次startActivity的時(shí)候,系統(tǒng)就會(huì)啟動(dòng)指定的進(jìn)程,此Activity創(chuàng)建的所有對(duì)象均運(yùn)行在此進(jìn)程中。我們要做的就是按正常流程逐個(gè)啟動(dòng)Activity,當(dāng)被定義的Activity全部被啟動(dòng)后如果再要啟動(dòng)新的Activity,那就需要先通知Task最底層的Activity對(duì)象finish,并關(guān)閉對(duì)應(yīng)進(jìn)程(可以使用System.exit(0))。同時(shí),關(guān)閉后空出來的Activity可以重新使用。
舉例:
假設(shè)Activity1、Activity2、Activity3都是同一的功能,都用來打開同一種類型的文件。當(dāng)我逐個(gè)啟動(dòng)了Activity1、Activity2、Activity3后,Task中最頂層的是Activity3,最底層的是Activity1。此時(shí),如果我需要再打開一個(gè)文件,那么我可以通知Activity1自行關(guān)閉,而后再創(chuàng)建一個(gè)Activity1壓在Activity3上面,以此類推。

由于這些Activity處于不同的進(jìn)程中,所以無法使用Application來維護(hù)管理它們,所以在這里我選擇了AIDL作為進(jìn)程間通訊的工具。
關(guān)于AIDL雙向通訊的具體使用,可參考其它文檔,此處只給出簡單的示例代碼:
定義的管理器AIDL以及用于回調(diào)的AIDL:
// IActivityManagerCallback.aidl
package com.test.multiprocesstest;
interface IActivityManagerCallback {
void managerFinish();
}
// IActivityManager.aidl
package com.test.multiprocesstest;
import com.test.multiprocesstest.IActivityManagerCallback;
interface IActivityManager {
//activity code 放入管理器
void putActivity(int activityCode);
//得到需要被啟動(dòng)的 activity 的 code(比如:1、2、3)
int getActivityCodeTobeStarted();
//注冊(cè)(反注冊(cè))回調(diào)
boolean registerCallback(IActivityManagerCallback cb, int code);
boolean unregisterCallback(IActivityManagerCallback cb);
}
管理器Manifest定義:
<service
android:name=".ActivityManagerService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.multiprocess.aidl.activitymanager"/>
</intent-filter>
</service>
管理器部分代碼:
public class ActivityManagerService extends Service
{
public static final int ACTIVITY_SIZE = 3;
private LinkedList<Integer> mActivityCodeList = new LinkedList<>();
private RemoteCallbackList<IActivityManagerCallback> mCallBack = new RemoteCallbackList<>();
@Override
public IBinder onBind(Intent intent)
{
return new ManagerBinder();
}
class ManagerBinder extends IActivityManager.Stub
{
@Override
public void putActivity(int activityCode) throws RemoteException
{
mActivityCodeList.add(activityCode);
}
@Override
public int getActivityCodeTobeStarted() throws RemoteException
{
if (mActivityCodeList.size() < ACTIVITY_SIZE)
{
return mActivityCodeList.size() + 1;
}
//移出最底層 activity 的 code
int rmCode = mActivityCodeList.removeFirst();
//通知對(duì)應(yīng)的 activity 關(guān)閉
int len = mCallBack.beginBroadcast();
for (int i = 0; i < len; i++)
{
if ((Integer)mCallBack.getBroadcastCookie(i) == rmCode)
{
mCallBack.getBroadcastItem(i).managerFinish();
break;
}
}
mCallBack.finishBroadcast();
return rmCode;
}
@Override
public boolean registerCallback(IActivityManagerCallback cb, int code) throws RemoteException
{
return mCallBack.register(cb, code); //code is cookie
}
@Override
public boolean unregisterCallback(IActivityManagerCallback cb) throws RemoteException
{
return mCallBack.unregister(cb);
}
}
}
Activity1的代碼(Activity2、Activity3都是繼承于Activity1)
package com.test.multiprocesstest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class Activity1 extends AppCompatActivity
{
protected int mActivityCode = 1;
private Button mBtnGetMainActivity;
private Context mContext;
private Handler mHandler = new Handler();
private boolean mIsBind = false;
private boolean mIsRegisterCallback = false;
private IActivityManager mManagerService = null;
//服務(wù)回調(diào)
private IActivityManagerCallback mManagerCallback = new IActivityManagerCallback.Stub()
{
@Override
public void managerFinish() throws RemoteException
{
System.out.println("managerFinish Activity" + mActivityCode);
//管理器要求關(guān)閉此 activity
mHandler.post(new Runnable()
{
@Override
public void run()
{
finish();
}
});
}
};
private ServiceConnection mConnection = new ServiceConnection()
{
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
mManagerService = IActivityManager.Stub.asInterface(service);
//與服務(wù)連接后,需要注冊(cè)回調(diào)
if (!mIsRegisterCallback)
{
try
{
mIsRegisterCallback = mManagerService
.registerCallback(mManagerCallback, mActivityCode);
}
catch (RemoteException e)
{
e.printStackTrace();
}
}
try
{
mManagerService.putActivity(mActivityCode);
}
catch (RemoteException e)
{
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name)
{
mManagerService = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_01);
mContext = this;
System.out.println("pid = " + android.os.Process.myPid() + ", task id = " + getTaskId());
mBtnGetMainActivity = (Button) findViewById(R.id.get_main_activity);
mBtnGetMainActivity.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Intent intent = new Intent(mContext, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
}
});
Intent intent = new Intent();
intent.setAction("com.multiprocess.aidl.activitymanager"); //manifest.xml中定義
intent.setPackage("com.test.multiprocesstest");
if (!mIsBind)
{
mIsBind = bindService(intent, mConnection, BIND_AUTO_CREATE);
}
TextView text = (TextView) findViewById(R.id.text);
text.setText("Activity" + Integer.toString(mActivityCode));
}
@Override
protected void onDestroy()
{
if (mIsRegisterCallback)
{
try
{
mIsRegisterCallback = mManagerService.unregisterCallback(mManagerCallback);
}
catch (RemoteException e)
{
e.printStackTrace();
}
}
if (mIsBind)
{
unbindService(mConnection);
}
super.onDestroy();
System.exit(0);
}
}
異常處理
假設(shè)某個(gè)Activity自身發(fā)生崩潰,而我們的Service卻無法得知,則會(huì)導(dǎo)致重新使用此Activity時(shí)發(fā)生ANR。所以我們希望Activity在發(fā)生崩潰時(shí)能通知Service或者Service能夠以某種方式來檢測(cè)Activity是否發(fā)生崩潰。
方法1:
在啟動(dòng)Activity時(shí),調(diào)用AIDL方法,給Service傳入一個(gè)IBinder對(duì)象,然后監(jiān)聽這個(gè)binder的死亡回調(diào),例如:
private class Client implements IBinder.DeathRecipient
{
private IBinder binder;
private int code;
public Client(IBinder binder, int activityCode)
{
this.binder = binder;
this.code = activityCode;
try
{
this.binder.linkToDeath(this, 0);
}
catch (RemoteException e)
{
e.printStackTrace();
}
}
@Override
public void binderDied()
{
//說明對(duì)應(yīng)code的Activity對(duì)象已經(jīng)崩潰,需要處理...
}
}
此時(shí),Service的
putActivity(int activityCode);
方法就要改成
putActivity(Client client);
方法2:
方法1在某種程度上可以讓Service得知Activity崩潰的情況,而很多ANR的異常時(shí)無法立即通知Service的。此時(shí),我們可以通過調(diào)用AIDL的方法,故意讓Activity去做一些UI線程上的操作,如果出現(xiàn)異常,則認(rèn)為Activity已經(jīng)崩潰。
多任務(wù)(Task)
多任務(wù)和單任務(wù)區(qū)別不大,只是在StartActivity的時(shí)候添加兩個(gè)Flags:
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
注意:使用這種方式,manifest中必須設(shè)置 android:launchMode="standard" 屬性值(默認(rèn)就是這個(gè)屬性)
使用這種做法,每啟動(dòng)一個(gè)任務(wù),就會(huì)在近期使用列表中添加一個(gè)記錄,用戶可以在這些記錄間切換使用(ps:此方法不一定適用每個(gè)android版本,親測(cè)7.0可用)
后臺(tái)Activity切換到前臺(tái)
單任務(wù)多進(jìn)程
Class<?> cls;
//比如:cls = Activity1.class;
Intent intent = new Intent(context, cls);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
多任務(wù)多進(jìn)程
ActivityManager manager = (ActivityManager) getBaseContext().getSystemService(Context.ACTIVITY_SERVICE);
manager.moveTaskToFront(taskID, ActivityManager.MOVE_TASK_WITH_HOME);
//taskID是Activity所在task的id