网页唤起 APP中Activity的实现原理

news2024/9/29 18:09:55

疑问的开端

大家有没有想过一个问题:在浏览器里打开某个网页,网页上有一个按钮点击可以唤起App。

这样的效果是怎么实现的呢?浏览器是一个app;为什么一个app可以调起其他app的页面?

说到跨app的页面调用,大家是不是能够想到一个机制:Activity的隐式调用?

隐式启动原理

当我们有需要调起其他app的页面时,使用的API就是隐式调用。

比如我们有一个app声明了这样的Activity:

<activity android:name=".OtherActivity"
    android:screenOrientation="portrait">
    <intent-filter>
        <action android:name="mdove"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

其他App想启动上边这个Activity如下的调用就好:

val intent = Intent()
intent.action = "mdove"
startActivity(intent)

我们没有主动声明Activity的class,那么系统是怎么为我们找到对应的Activity的呢?其实这里和正常的Activity启动流程是一样的,无非是if / else的实现不同而已。

接下来咱们就回顾一下Activity的启动流程,为了避免陷入细节,这里只展开和大家相对“耳熟能详”的类和调用栈,以串流程为主。

跨进程

首先我们必须明确一点:无论是隐式启动还是显示启动;无论是启动App内Activity还是启动App外的Activity都是跨进程的。比如我们上述的例子,一个App想要启动另一个App的页面,至少涉及3个进程

注意没有root的手机,是看不到系统孵化出来的进程的。也就是我们常见的为什么有些代码打不上断点。

追过startActivity()的同学,应该很熟悉下边这个调用流程,跟进几个方法之后就发现进到了一个叫做ActivityTread的类里边。

ActivityTread这个类有什么特点?有main函数,就是我们的主线程。

很快我们能看到一个比较常见类的调用:Instrumentation:

// Activity.java
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
    mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);
    // 省略
}

注意mInstrumentation#execStartActivity()有一个标黄的入参,它是ActivityThread中的内部类ApplicationThread。

ApplicationThread这个类有什么特点,它实现了IApplicationThread.Stub,也就是aidl的“跨进程调用的客户端回调”。

此外mInstrumentation#execStartActivity()中又会看到一个大名鼎鼎的调用:

public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
    // 省略...
    ActivityManager.getService()
        .startActivity(whoThread, who.getBasePackageName(), intent,
                intent.resolveTypeIfNeeded(who.getContentResolver()),
                token, target != null ? target.mEmbeddedID : null,
                requestCode, 0, null, options);
    return null;
}

我们点击去getService()会看到一个标红的IActivityManager的类。

它并不是一个.java文件,而是aidl文件。

所以ActivityManager.getService()本质返回的是“进程的服务端”接口实例,也就是:

ActivityManagerService

public class ActivityManagerService extends IActivityManager.Stub

所以执行到这就转到了系统进程(system_process进程)。省略一下代码细节,看一下调用栈:

从上述debug截图,看一看到此时已经拿到了我们的目标Activitiy的相关信息。

这里简化一些获取目标类的源码,直接引入结论:

PackageManagerService

这里类相当于解析手机内的所有apk,将其信息构造到内存之中,比如下图这样:

小tips:手机目录中/data/system/packages.xml,可以看到所有apk的path、进程名、权限等信息。

启动新进程

打开目标Activity的前提是:目标Activity的进程启动了。所以第一次想要打开目标Activity,就意味着要启动进程。

启动进程的代码就在启动Activity的方法中:

resumeTopActivityInnerLocked->startProcessLocked。

这里便引入了另一个另一个大名鼎鼎的类:ZygoteInit。这里简单来说会通过ZygoteInit来进行App进程启动的。

ApplicationThread

进程启动后,继续回到目标Activity的启动流程。这里依旧是一系列的system_process进行的转来转去,然后IApplicationThread进入目标进程。

注意看,在这里再次通过IApplicationThread回调到ActivityThread。

class H extends Handler {
    // 省略
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case EXECUTE_TRANSACTION:
                final ClientTransaction transaction = (ClientTransaction) msg.obj;
                mTransactionExecutor.execute(transaction);
                // 省略
                break;
            case RELAUNCH_ACTIVITY:
                handleRelaunchActivityLocally((IBinder) msg.obj);
                break;
        }
        // 省略...
    }
}

// 执行Callback
public void execute(ClientTransaction transaction) {
    final IBinder token = transaction.getActivityToken();
    executeCallbacks(transaction);
}

这里所谓的CallBack的实现是LaunchActivityItem#execute(),对应的实现:

public void execute(ClientTransactionHandler client, IBinder token,
        PendingTransactionActions pendingActions) {
    ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
            mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
            mPendingResults, mPendingNewIntents, mIsForward,
            mProfilerInfo, client);
    client.handleLaunchActivity(r, pendingActions, null);
}

此时就转到了ActivityThread#handleLaunchActivity(),也就转到了咱们日常的生命周期里边,调用栈如下:

上述截图的调用链中暗含了Activity实例化的过程(反射):

public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className, @Nullable Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

    return (Activity) cl.loadClass(className).newInstance();

}

浏览器启动原理

Helo站内的回流页就是一个标准的,浏览器唤起另一个App的实例。

交互流程

html标签有一个属性href,比如:<a href="...">。

我们常见的一种用法:<a href="``https://www.baidu.com``">。也就是点击之后跳转到百度。

因为这个是前端的标签,依托于浏览器及其内核的实现,跳转到一个网页似乎很“顺其自然”(不然叫什么浏览器)。

当然这里和android交互的流程基本一致:用隐式调用的方式,声明需要启动的Activity;然后<a href="">传入对应的协议(scheme)即可。比如:

前端页面:

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<a href="mdove1://haha"> 启动OtherActivity </a>
</body>

android声明:

<activity
    android:name=".OtherActivity"
    android:screenOrientation="portrait">
    <intent-filter>
        <data
            android:host="haha"
            android:scheme="mdove1" />
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.BROWSABLE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

推理实现

浏览器能够加载scheme,可以理解为是浏览器内核做了封装。那么想要让android也能支持对scheme的解析,难道是由浏览器内核做处理吗?

很明显不可能,做了一套移动端的操作系统,然后让浏览器过来实现,是不是有点杀人诛心。

所以大概率能猜测出来,应该是手机中的浏览器app做的处理。我们就基于这个猜想去看一看浏览器.apk的实现。

浏览器实现

基于上边说的/data/system/packages.xml文件,我们可以pull出来浏览器的.apk。

然后jadx反编译一下Browser.apk中WebView相关的源码:

我们可以发现对href的处理来自于隐式跳转,所以一切就和上边的流程串了起来。

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

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

相关文章

python之面向对象编程

1、面向对象介绍&#xff1a; 世界万物&#xff0c;皆可分类 世界万物&#xff0c;皆为对象 只要是对象&#xff0c;就肯定属于某种类 只要是对象&#xff0c;就肯定有属性 2、 面向对象的几个特性&#xff1a; class类&#xff1a; 一个类即对一类拥有相同属性的对象的…

STM32笔记

目录 1.1. 预备阶段 1.2. 单片机介绍 2. 初识STM32 2.1. STM32 1.1. 预备阶段 1.2. 单片机介绍 1.2.1. 单片机是什么 单片微型计算机(Single Chip Microcomputer)简称为单片机&#xff08;Microcontrollers&#xff09;&#xff0c;也称为微控制单元(Microcontroller Uni…

Python对遥感影像重采样,以及重采样方法

首先&#xff0c;本次重采样使用的是GDAL方法完成 参考了以下博客&#xff0c;并根据自己的需要进行了删改以及原理的探究&#xff1a; 重采样&#xff1a;栅格影像重采样 我使用了下该代码&#xff0c;发现是可行的&#xff0c;但是仍然存在一定的问题&#xff0c;即他的采样…

【java web篇】Maven的基本使用以及IDEA 配置Maven

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…

Redis高级-主从复制相关操作

2.1 主从复制简介 2.1.1 高可用 首先我们要理解互联网应用因为其独有的特性我们演化出的三高架构 高并发 应用要提供某一业务要能支持很多客户端同时访问的能力&#xff0c;我们称为并发&#xff0c;高并发意思就很明确了 高性能 性能带给我们最直观的感受就是&#xff1a;速…

西北工业大学大学物理(I)下期末考试2021-2022选填解析

11 告诉你n2了&#xff0c;那么l0或者1&#xff0c;后续限制类推。2 几乎每年都出。散射波波长的偏移只与散射角有关。3 产生激光的条件。先认识到激光就是受激幅射光放大。受激辐射是产生激光的必要条件&#xff0c;粒子数偏转是产生激光的必要条件&#xff0c;谐振腔也需要。…

速来~与 Werner Vogels 博士一起探索敏捷性与创新速度一起提升的秘方

Amazon Web Services 的现代应用程序创新一直是 Amazon 公司坚持追求的核心目标。约20年前&#xff0c;我们经历了一次彻底的转型&#xff0c;旨在建立起“发明、发布、再发明、再发布、重新开始、洗牌、再重复”的快速迭代流程。正是此番探索&#xff0c;彻底改变了我们构建应…

OAK相机如何将yoloV8模型转换成blob格式?

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 ▌前言 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是助手…

Paddle配置

目录&#xff1a; 1.激活环境 2.版本选择 突发情况&#xff1a;ModuleNotFoundError: No module named paddle 检验是否安装成功 1.激活环境 Anaconda&#xff1a; conda remove -n paddle --all conda activate paddle 2.版本选择 打开链接&#xff1a;https://www.pa…

银行数字化转型导师坚鹏:BLM银行数字化转型战略课程大纲

BLM银行数字化转型战略——以BLM模型为核心&#xff0c;实现知行果合一 课程背景&#xff1a; 很多银行存在以下问题&#xff1a; 不知道如何系统地制定银行数字化转型战略&#xff1f; 不清楚其它银行数字化转型战略是如何制定的&#xff1f; 不知道其它银行数字化转型战略…

【离线数仓-8-数据仓库开发DWD层设计要点-工具域互动域流量域用户域相关事实表】

离线数仓-8-数据仓库开发DWD层-工具域&互动域&流量域&用户域相关事实表离线数仓-8-数据仓库开发DWD层设计要点-工具域&互动域&流量域&用户域相关事实表一、工具域相关事实表1.工具域优惠券领取事务事实表&使用&#xff08;下单&#xff09;事务事实…

基于nodejs+vue的果蔬商城在线销售系统vscode

水果蔬菜在线销售借助于当今盛行互联网技术&#xff0c;为消费者和供应商提供了一个更加方便的交易平台&#xff0c;使消费者足不出户就可以选购所需商品&#xff0c;省下许多时间和精力。商家通过该销售系统可以快速了解市场行情&#xff0c;更好地适应市场需求&#xff0c;扩…

【华为OD机试模拟题】用 C++ 实现 - 找到它(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明找到它题目输入输出示例一输入输出示例二输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD …

工具篇(四)基于WPS的数据处理和分析

作者的话 大家好&#xff0c;我是一名练习时长两年半的数据分析师&#xff0c;今天想和大家分享一下我在使用WPS进行数据操作时的经验。 在日常工作中&#xff0c;数据处理是我们最常用到的功能之一。而在处理数据时&#xff0c;一个强大的工具是至关重要的。我个人非常喜欢使…

Clickhouse学习:MergeTree

MergeTree一、MergeTree逻辑存储结构二、MergeTree物理存储结构三、总结一、MergeTree逻辑存储结构 如上图所示,在排序键(CountrID、Date)上做索引,数据会按照这两个字段先后排序ClickHouse是稀疏索引,每隔8192行做一个索引,如(a,1),(a,2),比如想查a,要读取[0,3)之间的内容,稀疏…

2023年三月份图形化四级打卡试题

活动时间 从2023年3月1日至3月21日&#xff0c;每天一道编程题。 本次打卡的规则如下&#xff1a; 小朋友每天利用10~15分钟做一道编程题&#xff0c;遇到问题就来群内讨论&#xff0c;我来给大家答疑。 小朋友做完题目后&#xff0c;截图到朋友圈打卡并把打卡的截图发到活动群…

网络原理之初识

目录 一. 网络互连 1. 局域网 2. 广域网 二. 网络通信基础 1. IP 地址 2. 端口号 3. 网络协议 4. 协议分层 5. TCP/IP 五层网络模型 &#xff08;简述&#xff09; 6. 网络数据传输的基本流程 一. 网络互连 随着时代的发展&#xff0c;越来越需要计算机之间互相通信&am…

【华为OD机试模拟题】用 C++ 实现 - IPv4 地址转换成整数(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明IPv4 地址转换成整数题目输入输出示例一输入输出说明示例一输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,

使用Phpstorm进行项目管理

对于项目管理我们还是使用传统的终端命令行&#xff08;命令行很重要是基础中的基础&#xff09;么 &#xff1f; 不现在我们要通过工具提高我们的效率&#xff0c;作为一名合格的程序猿下班提交代码是我们的基操&#xff01;&#xff01;&#xff01;&#xff01;但是经过一天…

ctf pwn基础-4

今天是学pwn的第四天&#xff0c;去接触了pwn的整数溢出。 目录 基础 实例讲解 实例讲解2 基础 关于整数溢出&#xff0c;这里以int为例&#xff0c;因为我php之前搞的比较多&#xff0c;以为这个int也是想php一样是64&#xff0c;最大值是9开头的那个&#xff0c;闹了不少笑…