Activity中startForResult的原理分析

news2024/9/20 1:07:41

前言:

如果使用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还会收到回调通知吗?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/463066.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虹科教您 | 虹科RELY-TSN-KIT操作指南(3)——基于Linux系统进行TSN协议测试

随着技术的变革和实际生产业务需求的推动&#xff0c;工厂内部互联架构逐渐趋于扁平化&#xff08;IT/OT融合&#xff09;&#xff0c;而TSN则是在这一背景下发展起来的新兴技术&#xff0c;旨在为以太网协议建立“通用”的时间敏感机制&#xff0c;以确保网络数据传输的时间确…

云计算服务安全评估办法

云计算服务安全评估办法 2019-07-22 14:46 来源&#xff1a; 网信办网站【字体&#xff1a;大 中 小】打印 国家互联网信息办公室 国家发展和改革委员会 工业和信息化部 财政部关于发布《云计算服务安全评估办法》的公告 2019年 第2号 为提高党政机关、关键信息基础设施运营者…

鸿蒙系统是什么?鸿蒙与开源鸿蒙的关系?鸿蒙系统的发展历程

鸿蒙OS分布式操作系统简介鸿蒙系统&#xff08;HarmonyOS)&#xff0c;是第一款基于微内核的全场景分布式OS&#xff0c;是华为自主研发的操作系统。现被华为捐献给开放原子基金会管理&#xff0c;为开放原子基金会下的一个项目。 从 系统定位 上来说&#xff0c;HarmonyOS是一…

explain都不懂?搞什么数据库优化,快进来学习了

文章目录 一、 前言二、MySQL EXPLAIN实战三、mysql EXPLAIN输出结果详解3.1 id详解3.2 select_type3.3 table3.4 partitions3.5 type3.6 possible_keys3.7key3.8 key_len3.9 ref3.10 rows3.11 filtered3.12 Extra 一、 前言 EXPLAIN 想必用过mysql的小伙伴都听过&#xff0c;…

PWM 呼吸灯实验

PWM 呼吸灯实验 FPGA实现一个PWM模块&#xff08;硬件&#xff09;来控制灯的亮灭。 实验原理 PWM本质上就是一个输出脉冲的硬件&#xff0c;通过改变一个周期高电平&#xff08;占空比&#xff09;的时间来对其他的硬件进行控制&#xff0c;比如电机。 呼吸灯的实现利用了人…

谈谈如何用开源网关进行 API 管理

需求痛点 1.企业不清楚到底有多少个API&#xff0c;无法形成API资产管理等问题。 2.API在不同集群的生命周期问题。 3.API运行状态监控和告警问题。 4.API请求限流、流量控制以及安全等问题。 功能介绍 Apinto的API管理提供API生命周期控制&#xff1a;可管理所有API&…

Cortex-R52 GIC:Generic Interrupt Controller(一)

ARM Cortex-R52 GIC:Generic Interrupt Controller 1.关于GIC 1.1 GIC Overview ARM的中断控制器被称为GIC(Generic Interrupt Controller)&#xff0c;GIC是支持和管理系统中断的资源的模块。它支持中断优先级、中断路由到CPU或输出端口、中断抢占和中断虚拟化等功能。 中断…

深入浅出Rust核心概念:生命周期

简介 Rust是一种快速、安全、并发的系统级编程语言&#xff0c;它的设计目标是提供一种高效、内存安全的编程方式。而生命周期&#xff08;Lifetime&#xff09;是Rust语言中的一个核心概念&#xff0c;它与内存管理、函数传参和引用操作等方面密切相关。LZ将详细介绍Rust中生…

GitHub 开启 2FA 双重身份验证的方法

为什么要开启 2FA 自2023年3月13日起&#xff0c;我们登录 GitHub 都会看到一个要求 Enable 2FA 的重要提示&#xff0c;具体如下&#xff1a; GitHub users are now required to enable two-factor authentication as an additional security measure. Your activity on Git…

Matplotlib 轴标签和标题

我们可以使用 xlabel() 和 ylabel() 方法来设置 x 轴和 y 轴的标签。 实例 import numpy as np import matplotlib.pyplot as pltx np.array([1, 2, 3, 4]) y np.array([1, 4, 9, 16]) plt.plot(x, y)plt.xlabel("x - label") plt.ylabel("y - label")…

Java BIO

1.Java BIO(Blocking IO:同步并阻塞式IO)编程 1.1.基本介绍 1>.Java BIO就是传统的java io编程,其相关的类和接口在"java.io"包下; 2>.BIO(Blocking I/O): 同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处…

深入分析,Redis为什么这么快?

我们都知道Redis很快&#xff0c;它QPS可达10万&#xff08;每秒请求数&#xff09; Redis为什么这么快&#xff1f; 基于内存实现高效的数据结构合理的数据编码合理的线程模型虚拟内存机制 基于内存实现 我们都知道内存读写是比磁盘读写快很多的。Redis是基于内存存储实现的…

电磁兼容原理、方法及设计的科普好文

什么是电磁兼容 电磁兼容性&#xff08;EMC&#xff09;是指设备或系统在其电磁环境中符合要求运行并不对其环境中的任何设备产生无法忍受的电磁干扰的能力。因此&#xff0c;EMC包括两个方面的要求&#xff1a;一方面是指设备在正常运行过程中对所在环境产生的电磁干扰不能超…

操作系统之调度

目录 什么是调度 进程调度的时机、切换、过程与方式 调度器/调度程序 调度算法 先来先服务算法 短作业优先算法 高响应比优先算法 时间片轮转算法 优先级调度算法 多级反馈队列调度算法 什么是调度 调度的三个层次 高级调度 中级调度 低级调度 总结如下&#xff1a; …

利用docker部署深度学习环境摆脱操作系统版本限制与cuda版本限制

利用docker部署深度学习环境摆脱操作系统版本限制与cuda版本限制 文章背景描述&#xff1a; 近期公司想给客户部署OCR文本识别项目&#xff0c;项目用到了tensorflow1.13&#xff0c;可支持该框架版本的cuda得低于10.2&#xff0c;但是客户要求的操作系统版本是Ubuntu22.04&…

学成在线笔记+踩坑(9)——课程发布,xxl-job+消息SDK实现分布式事务、页面静态化、Hystrix熔断降级

导航&#xff1a; 【黑马Java笔记踩坑汇总】JavaSEJavaWebSSMSpringBoot瑞吉外卖SpringCloud黑马旅游谷粒商城学成在线牛客面试题_java黑马笔记 目录 1 业务流程&#xff0c;入库缓存ESMinIO存静态化页面 2 分布式事务技术方案 2.1 回顾本地事务和分布式事务 2.2 什么是CA…

Nginx下载和使用

nginx: downloadhttp://nginx.org/en/download.html下载成功后打开 \nginx-x.xx.x\conf\nginx.conf 文件 #user nobody; worker_processes 1;#error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info;#pid logs/nginx.pid…

国内top5正规好用纸黄金交易软件最新排名(2023评测版)

随着互联网技术的不断发展&#xff0c;网上投资理财变得越来越流行。而随着互联网理财产品的日益增多&#xff0c;越来越多的投资者开始选择纸黄金交易软件进行交易。然而&#xff0c;对于初入此行的投资者而言&#xff0c;如何选择合适的纸黄金交易软件显得尤为重要。 首先&…

PostgreSQL的数据类型有哪些?

数据类型分类 分类名称 说明 与其他数据库的对比 布尔类型PG支持SQL标准的boolean数据类型与MySQL中的bool、boolean类型相同&#xff0c;占用1字节存储空间数值类型整数类型有2字节的smallint、4字节的int、8字节的bigint&#xff1b;精确类型的小数有numeric&#xff1b;非精…

C语言CRC-8 ITU格式校验函数

C语言CRC-8 ITU格式校验函数 CRC校验基于前处理和后处理的不同&#xff0c;由不同的协会标准推出了一些不同格式的版本。这里介绍CRC-8 ITU格式的校验函数。 CRC-8 ITU格式特征 标准CRC-8的校验函数参考&#xff1a; C语言标准CRC-8校验函数 CRC-8 ITU格式和标准CRC-8校验算…