android单任务(多任务)多进程研究

理论基础

我们知道,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

你可能感兴趣的:(android单任务(多任务)多进程研究)