Activity中startForResult的原理分析

前言:

如果使用androidX支持库中的ComponentActivity,会推荐使用registerForActivityResult的方式。但是对于不支持androidX的项目,或者就是继承自Activity的页面来说,startActivityForResult仍然是唯一的选择。

如果想了解androidX中推荐的方式,可以参考文章:forResult的替代品registerForActivityResult介绍

而本文来讲解一下,正常流程中startActivityForResult的完整实现原理。

本文主要分为5个部分:

第一章,简单介绍下startActivityForResult的使用方式。

然后开始我们讲解原理,我们以简单的Test1Activity跳转Test2Activity为例讲解,主要分为3个部分:

第二章,Test1Activity启动Test2Activity的流程,对应流程图中右上的部分;

第三章,Test2Activity中调用setResult和finish后的流程,对应流程图中左上的部分;

第四章,Test1Activity切换到前台时forResult被调用的流程,对应流程图中下面的部分;

一.使用简介

startActivityForResult的使用方式是很简单的,我们从一个Test1Activity启动另外一个Test2Activity的时候,在调用的时候,使用startActivityForResult替代startActivity方法即可。并且,在Test1Activity中重写onActivityResult方法。相关代码如下:

public class Test1Activity{
    public void onClick(){
        Intent intent = new Intent(this, Test2Activity.class);
        startActivityForResult(intent, 100)
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data)
        Log.e("Test1Activity", "onActivityResult,requestCode:${requestCode},resultCode:${resultCode}")
    }
    
}

Test2Activity中,在finish方调用之前,只要调用setResult,就可以设置返回值。这样等到Test1Activity再次展示到前台的时候,Test1Activity中的onActivityResult方法就会收到来自Test2Activity中传递的数据。

public class Test2Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState)
        setResult(200,new Intent())
    }
}

二.启动流程

启动时,我们使用startActivityForResult的方式启动,然后调用到Instrumentation中的execStartActivity方法,最终通过binder的方法startActivity通知到系统侧。

相关代码如下:

public class Activity{
    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
        ...
        Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);
        ...
    }
}

public class Instrumentation {
    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {
        ...
        int result = ActivityTaskManager.getService().startActivity(whoThread,who.getOpPackageName(), who.getAttributionTag(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()), token,target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
        ...
    }
}

其实这里的调用流程和正常的启动流程是一样的,唯一的区别就是startActivity的传参不一样,正常启动的话,token=null,requestCode=-1。

系统侧收到通知后,构建启动事务类ActivityStarter,然后把resultTo/resultWho/requestCode添加到ActivityStarter中。

public class ActivityTaskManagerService {
    public final int startActivity(IApplicationThread caller, ...) {
        startActivityAsUser(...)
    }
    public final int startActivityAsUser(IApplicationThread caller, ...) {
        ...
        return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
        .setCaller(caller)
        .setResultTo(resultTo)
        .setResultWho(resultWho)
        .setRequestCode(requestCode)
        .execute();
    }
}

 class ActivityStarter {
     int execute() {
         ...
         res = executeRequest(mRequest);
         ...
     }
 
     private int executeRequest(Request request) {
         ...
         final ActivityRecord r = new ActivityRecord.Builder(mService)
            .setCaller(callerApp)
            .setResultTo(resultRecord)
            .setResultWho(resultWho)
            .setRequestCode(requestCode)     
            .build();
         ...
     }       
}

最终,在executeRequest方法中,把resultRecord/resultWho/requestCode绑定到ActivityRecord,至此,Test2Activity中的resultTo指向了Test1Activity。

三.设置返回值

接下来,我们一起看一下,Test2Activity中如何把相关的返回值,设置到系统Test1Activity对应的ActivityRecord上。

Test2Activity中,通过setResult方法设置返回值,最终都会存到Activity的成员变量mResultCode和mResultData中,相关代码如下:

public final void setResult(int resultCode, Intent data) {
    synchronized (this) {
        mResultCode = resultCode;
        mResultData = data;
    }
}

当Test2Activity中调用finish方法时,会把mResultCode和mResultData通过binder方法,通过finishActivity方法通知到系统侧。

private void finish(int finishTask) {
    if (mParent == null) {
        int resultCode;
        Intent resultData;
        synchronized (this) {
            resultCode = mResultCode;
            resultData = mResultData;
        }
        if (false) Log.v(TAG, "Finishing self: token=" + mToken);
        if (resultData != null) {
            resultData.prepareToLeaveProcess(this);
        }
        if (ActivityClient.getInstance().finishActivity(mToken, resultCode, resultData, finishTask)) {
            mFinished = true;
        }
    } else {
        mParent.finishFromChild(this);
    }
    getAutofillClientController().onActivityFinish(mIntent);
}

系统响应的方法是ActivityClientController中的finishActivity方法,系统最终会把resultCode和resultData封装成到ResultInfo对象,记录到其成员变量resultTo的results上。而这里的resultTo就是第一章中,注册的Test1Activity所对应的ActivityRecord。

class ActivityClientController {
    public boolean finishActivity(IBinder token, int resultCode, Intent resultData,
            int finishTask) {
        ...
        r.finishIfPossible(resultCode, resultData, resultGrants,"app-request", true /* oomAdj */);
    }
}

class ActivityRecord {
    ArrayList<ResultInfo> results; 
    
    int finishIfPossible(int resultCode, Intent resultData,NeededUriGrants resultGrants, String reason, boolean oomAdj) {
        ...
        finishActivityResults(resultCode, resultData, resultGrants);
        ...
    }
    
    private void finishActivityResults(int resultCode, Intent resultData,NeededUriGrants resultGrants) {
        if (resultTo != null) {
            resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData);
        }else{
            ...
        }
    }
    
    void addResultLocked(ActivityRecord from, String resultWho,int requestCode, int resultCode, Intent resultData) {
        ActivityResult r = new ActivityResult(from, resultWho,
                requestCode, resultCode, resultData);
        if (results == null) {
            results = new ArrayList<ResultInfo>();
        }
        results.add(r);
    }
}

四.回调流程

然后等到Activity切换到前台的时候,最终都会调用到resumeTopActivity方法。这时候会判断其results是否为空,如果不为空,则说明设置过回调的内容。

则构建事务的时候,添加ActivityResultItem类型非生命周期事务,和生命周期ResumeActivityItem事务,最后通知到应用一侧。

final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,boolean deferPause) {
    ArrayList<ResultInfo> a = next.results;
    if (a != null) {
        final int size = a.size();
        if (!next.finishing && size > 0) {
            if (DEBUG_RESULTS) {
                Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a);
            }
            transaction.addCallback(ActivityResultItem.obtain(a));
        }
    }
    ...
    mAtmService.getLifecycleManager().scheduleTransaction(transaction);   
}

应用侧事务接收到后,分别处理非生命周期事务ResumeActivityItem和生命周期事务ActivityResultItem。

public class TransactionExecutor {
    ...
    public void execute(ClientTransaction transaction) {
        //处理非生命周期事务
        executeCallbacks(transaction);
        //处理生命周期事务
        executeLifecycleState(transaction);
    }
}

public class ActivityResultItem{
    public void execute(ClientTransactionHandler client, ActivityClientRecord r,PendingTransactionActions pendingActions) {
        client.handleSendResult(r, mResultInfoList, "ACTIVITY_RESULT");
    }
}

public class ActivityThread extends ClientTransactionHandler{
    public void handleSendResult(ActivityClientRecord r, List<ResultInfo> results, String reason) {
        ...
        deliverResults(r, results, reason);
        ...
    }
    
    private void deliverResults(ActivityClientRecord r, List<ResultInfo> results, String reason) {
        final int N = results.size();
        for (int i=0; i<N; i++) {
            ResultInfo ri = results.get(i);
            r.activity.dispatchActivityResult(ri.mResultWho, ri.mRequestCode, ri.mResultCode, ri.mData, reason);
        }
    }
}

public class Activity {
    void dispatchActivityResult(String who, int requestCode, int resultCode, Intent data, String reason) {
        onActivityResult(requestCode, resultCode, data);
    }
}

在执行ActivityResultItem的事务时,会执行其execute方法,最终会通知到ActivityThread的handleSendResult方法去处理回调通知事件,然后转交给deliverResults处理。而在这个方法中,会遍历所有的ResultInfo对象,然后把最终的resultCode和data传递给所对应的Activity,从而至此,Test1Activity的onActivityResult方法就会收到回调通知。

五.扩展性问题

问题1.T1跳转T2时,如果T2不是通过finish的方法结束,而是通过其它的方式被desotry,会怎样?举个例子,T1设置为singleTask,T1通过forResult的方式跳转T2,T2中再跳转T1,这时,系统会remove掉最上层的T2。此时,T1中的onActivityForResult还会收到回调通知吗?

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

失落夏天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值