理论基础
我们知道,task是用户为了完成某个功能而执行的一系列操作所形成的一个Activity序列。
一般情况下,一个应用启动的Activity对象都处于同一个task中(task id 相同),也处于同一个进程中(process id 相同)。但我们知道,android的task只是针对任务的,也就是说同一个task中的Activity对象可以来自不同的进程。
问题
当然,在大多数情况下,我们是因为应用要调用到其它产品的功能,才会把一些非本应用的Activity调起来。此时,会产生同一个task中的Activity对象可以来自不同的进程的情形。
那么问题来了,在什么样的情况下,我们需要在同一个task中(单个应用,不涉及调用其它应用的产品)使用不同的进程来启动自己应用中的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
其中每个进程对应一个不同的Activity。可能的情况有:
- 此应用的单个Activity需要使用相对比较大的内存
- 此应用有可能需要处理相对独立的多个文档
- 此应用的Activity的关闭动作需要非常迅速的完成
比如,一个应用需要同时打开几个非常消耗内存的文档,同时又需要在这几个文档中来回切换,还需要关闭文档不能有迟钝。
因为,对于每个进程最多能使用多少内存,每台机器都是不同的,具体可以通过查看:/system/build.prop文件中的dalvik.vm.heapgrowthlimit项即可:
shell@PD1602:/system $ cat build.prop | grep heap
...
dalvik.vm.heapgrowthlimit=256m
...
上述这台设备是256m,单个process如果使用内存超过这个数量,就会抛出OOM异常。
还有,如果关闭Activity时需要等待线程结束,释放对象模型等等的操作,可能会占用一定的时间,此时Activity就无法立即销毁。
解决方案
在以上这种情形下,使用单任务单进程,达到要求就比较费劲。所以,我们可以在一个应用中尝试使用单任务多进程来解决这个问题。
具体实现思路其实很简单:
在工程中创建多个Activity,比如Activity1,Activity2,Activity3等。在Manifest中设置每个Activity对应的进程名,比如:
这样每次startActivity的时候,系统就会启动指定的进程,此Activity创建的所有对象均运行在此进程中。我们要做的就是按正常流程逐个启动Activity,当被定义的Activity全部被启动后如果再要启动新的Activity,那就需要先通知Task最底层的Activity对象finish,并关闭对应进程(可以使用System.exit(0))。同时,关闭后空出来的Activity可以重新使用。
举例:
假设Activity1、Activity2、Activity3都是同一的功能,都用来打开同一种类型的文件。当我逐个启动了Activity1、Activity2、Activity3后,Task中最顶层的是Activity3,最底层的是Activity1。此时,如果我需要再打开一个文件,那么我可以通知Activity1自行关闭,而后再创建一个Activity1压在Activity3上面,以此类推。
由于这些Activity处于不同的进程中,所以无法使用Application来维护管理它们,所以在这里我选择了AIDL作为进程间通讯的工具。
关于AIDL双向通讯的具体使用,可参考其它文档,此处只给出简单的示例代码:
定义的管理器AIDL以及用于回调的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);
//得到需要被启动的 activity 的 code(比如:1、2、3)
int getActivityCodeTobeStarted();
//注册(反注册)回调
boolean registerCallback(IActivityManagerCallback cb, int code);
boolean unregisterCallback(IActivityManagerCallback cb);
}
管理器Manifest定义:
管理器部分代码:
public class ActivityManagerService extends Service
{
public static final int ACTIVITY_SIZE = 3;
private LinkedList mActivityCodeList = new LinkedList<>();
private RemoteCallbackList 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();
//通知对应的 activity 关闭
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;
//服务回调
private IActivityManagerCallback mManagerCallback = new IActivityManagerCallback.Stub()
{
@Override
public void managerFinish() throws RemoteException
{
System.out.println("managerFinish Activity" + mActivityCode);
//管理器要求关闭此 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);
//与服务连接后,需要注册回调
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);
}
}
异常处理
假设某个Activity自身发生崩溃,而我们的Service却无法得知,则会导致重新使用此Activity时发生ANR。所以我们希望Activity在发生崩溃时能通知Service或者Service能够以某种方式来检测Activity是否发生崩溃。
方法1:
在启动Activity时,调用AIDL方法,给Service传入一个IBinder对象,然后监听这个binder的死亡回调,例如:
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()
{
//说明对应code的Activity对象已经崩溃,需要处理...
}
}
此时,Service的
putActivity(int activityCode);
方法就要改成
putActivity(Client client);
方法2:
方法1在某种程度上可以让Service得知Activity崩溃的情况,而很多ANR的异常时无法立即通知Service的。此时,我们可以通过调用AIDL的方法,故意让Activity去做一些UI线程上的操作,如果出现异常,则认为Activity已经崩溃。
多任务(Task)
多任务和单任务区别不大,只是在StartActivity的时候添加两个Flags:
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
注意:使用这种方式,manifest中必须设置 android:launchMode="standard" 属性值(默认就是这个属性)
使用这种做法,每启动一个任务,就会在近期使用列表中添加一个记录,用户可以在这些记录间切换使用(ps:此方法不一定适用每个android版本,亲测7.0可用)
后台Activity切换到前台
单任务多进程
Class> cls;
//比如:cls = Activity1.class;
Intent intent = new Intent(context, cls);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
多任务多进程
ActivityManager manager = (ActivityManager) getBaseContext().getSystemService(Context.ACTIVITY_SERVICE);
manager.moveTaskToFront(taskID, ActivityManager.MOVE_TASK_WITH_HOME);
//taskID是Activity所在task的id