网页唤起 APP中Activity的实现原理
疑问的开端
大家有没有想过一个问题:在浏览器里打开某个网页,网页上有一个按钮点击可以唤起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声明:
<activityandroid:name=".OtherActivity"android:screenOrientation="portrait"><intent-filter><dataandroid: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的处理来自于隐式跳转,所以一切就和上边的流程串了起来。
相关文章:
网页唤起 APP中Activity的实现原理
疑问的开端大家有没有想过一个问题:在浏览器里打开某个网页,网页上有一个按钮点击可以唤起App。这样的效果是怎么实现的呢?浏览器是一个app;为什么一个app可以调起其他app的页面?说到跨app的页面调用,大家是…...
【操作系统】概述
基本特征 1. 并发 并发是指宏观上在一段时间内能同时运行多个程序,而并行则指同一时刻能运行多个指令。 并行需要硬件支持,如多流水线、多核处理器或者分布式计算系统。 操作系统通过引入进程和线程,使得程序能够并发运行 2. 共享 共享…...
Flume三种组件的选择对比
文章目录1.source2.channel3.sink1.source Source: 数据源:通过source组件可以指定让Flume读取哪里的数据,然后将数据传递给后面的 channel Flume内置支持读取很多种数据源,基于文件、基于目录、基于TCP\UDP端口、基于HTTP、Kafka的 等等、当然了&#x…...
响应性基础API
一.什么是proxy和懒代理?什么是proxy?proxy对象是用于定义基本操作的自定义行为(如:属性查找,赋值,枚举,函数调用等等)。什么是懒代理?懒代理:在初始化的时候不会进行全部代理,而是…...
剑指 Offer 25. 合并两个排序的链表
剑指 Offer 25. 合并两个排序的链表 难度:easy\color{Green}{easy}easy 题目描述 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。 示例1: 输入:1->2->4, 1->3->4 输出:1…...
顿悟日记(一)
目录2023年1月顿悟日记:2023年2月24日顿悟日记:2023年2月25日顿悟日记:2023年2月26日顿悟日记:顿悟的经历是如此的奇妙,且让人亢奋的事情。 2023年1月顿悟日记: 1.我是面向对象还是面向过程? …...
前端卷算法系列(二)
前端卷算法系列(二) 回文数 给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。 回文数是指正序(从左向右)和倒序(从右向左)读都是一样…...
网络应用之HTTP响应报文
HTTP响应报文学习目标能够知道HTTP响应报文的结构1. HTTP响应报文分析HTTP 响应报文效果图:响应报文说明:--- 响应行/状态行 --- HTTP/1.1 200 OK # HTTP协议版本 状态码 状态描述 --- 响应头 --- Server: Tengine # 服务器名称 Content-Type: text/html; charsetUTF-8 # 内容类…...
常见的CSS技巧
1.禁止长按图片弹出菜单 img {-webkit-touch-callout: none; // 主要用于禁止长按菜单。主针对webkit内核的浏览器; } /*或者 user-select , 是css3的新属性,用于设置用户是否能够选中文本*/ .img {-webkit-user-select: none;-khtml-user-select: none…...
算法进阶-动态规划
经典例题 大家肯定想用递归做 思路大概就是这样 递归到最后一行就是对应的D(i,j) 然后往上推 但是这样会超时,因为存在大量的重复计算 比如调用第一行MasSum(7)需要调用MaxSum(3)和MaxSum(8) 但是调用第二行MaxSum(3)还要调用3行的MaxSum(8)和3行的MaxSum(1) 第二行…...
python的读写操作
一、使用open函数,可以打开一个已经存在的文件,或着创建一个新文件 语法如下: open(name, mode, encoding) name: 要打开的目标文件的字符串(可以包含文件所在的具体路径) mode: 打开文件模式:只读(r)、写入(w)、追加(a)等 e…...
Mybatis中添加、查询、修改、删除
在Mybatis中添加数据的操作 编写相对应的SQL语句,并完成相关数据的对应关系 编写测试用例 需要提交事务 sqlSession commit() 这里需要注意的是mybatis是默认的是手动提交事务,如果不写的话会进行回滚,添加操作就不会被执行 或者在 如果…...
C++---线性dp---传纸条(每日一道算法2023.2.26)
注意事项: 本题dp思路与 “线性dp–方格取数” 一致,下方思路仅证明为什么使用方格取数的思路是正确的。 题目: 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。 一次素质拓展活动中,班上同学安排坐成…...
浅谈 C/C++ 的输入输出
更好的阅读体验\huge{\color{red}{更好的阅读体验}}更好的阅读体验 文章目录0. 叠甲,过1. 谈谈输入输出缓冲区1.1 基本概念输入输出流标准输入输出流文件输入输出流1.2 输入输出缓冲区什么是输入输出缓冲区?为什么要设置输入输出缓冲区?C/C 的…...
【计算机三级网络技术】 第二篇 中小型系统总体规划与设计
文章目录一、基于网络的信息系统基本结构二、划分网络系统组建工程阶段三、网络需求调研与系统设计原则四、网络用户调查与网络工程需求分析1.网络用户调查2.网络节点的地理位置分布3.应用概要分析4.网络需求详细分析五、网络总体设计基本方法1.网络工程建设总体目标与设计原则…...
Boosting Crowd Counting via Multifaceted Attention之人群密度估计实践
这周闲来无事,看到一篇前不久刚发表的文章,是做密集人群密度估计的,这块我之前虽然也做过,但是主要是基于检测的方式实现的,这里提出来的方法还是比较有意思的,就拿来实践一下。论文在这里,感兴…...
python之面向对象编程
1、面向对象介绍: 世界万物,皆可分类 世界万物,皆为对象 只要是对象,就肯定属于某种类 只要是对象,就肯定有属性 2、 面向对象的几个特性: class类: 一个类即对一类拥有相同属性的对象的…...
常见前端基础面试题(HTML,CSS,JS)(七)
同源策略 浏览器有一个重要的安全策略,称之为同源策略 其中,协议、端口号、域名必须一致,,称之为同源,两个源不同,称之为跨源或跨域 同源策略是指,若页面的源和页面运行过程中加载的源不一致…...
产业链金风控基本逻辑
产业链金风控基本逻辑 产业链金融平台作为一个助贷平台,很大程度上是为银行等金融机构进 行引流,贷款的审批本质上还是依赖金融机构的风控。那么,产业链金融 平台是否还有必要建设自己的风控模型呢?笔者给出的答案是肯定的。 一方面&#x…...
Java高级点的知识
Java 集合框架 该框架必须是高性能的。基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的。 该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性。 对一个集合的扩展和适应…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
