现代安卓开发之 Jetpack Compose、Xposed Hook 与 Kotlin Multiplatform
AI Summary
emm上来堆三个技术名词太劝退,每个部分开始之前我都会写一段简短的介绍,用很通俗的语言讲解下这是个什么东西。
写这篇文章起源于我最近悲惨的生活和精神状态,因此适当摸摸鱼写写有趣的东西,尤其是写kotlin真的很爽,最近是真的写了很多
Jetpack Compose
Jetpack Compose 是 Google 推出的用于构建原生 Android 界面的现代化声明式 UI 工具包。它从传统的命令式 UI(手动查找并更新 View)转变为声明式 UI,说人话就是你现在只需要描述你的UI在不同状态下是什么样子的,至于渲染和更新放心交给Compose就好。另外他完全基于 Kotlin 构建,充分利用了 Kotlin 的语言特性(如 Lambda、DSL、协程等),如果你会Kotlin,上手这个是相当舒服的。
前情提要(bushi):
声明式
声明式UI首先是在web前端领域广泛应用的,Jetpack Compose受到启发,将它作为安卓平台的全新UI构建工具,它简化了原命令式UI复杂的操作过程find set等等
函数式
首先它是函数式的,函数是一等公民,而这些描述UI的函数被称为Composable
函数,其具有@Composable
注解,他们不是返回特定的对象,而是类似“发出UI”,它们之间相互调用可以组成复杂的UI结构
状态驱动
这里就和 React 什么的很像了,当State
对象改变时,Compose 会智能地“重组”(Recomposition)受影响的 Composable 函数,更新 UI。其中mutableStateOf()
创建一个可观察的可变状态持有者,而remember { ... }
用于在重组过程中“记住”状态或对象,避免每次重组都重新创建。
注:remember
本身是为了在多次重组之间保持实例不变,避免重复创建开销。与 mutableStateOf
结合使用 (remember { mutableStateOf(...) }
) 才是创建既能被记住又能触发重组的状态的标准方式。
当然,类似于 React/Vue 的 diff,Compose 的优化也是相当好的,当 Composable 函数依赖的状态发生变化时,Compose 运行时会自动重新调用该函数及其可能受影响的子函数,以更新 UI。Compose 会进行智能优化,只重组必要的部分。
副作用和单向数据流
副作用(Side Effects): 我们都知道不会影响状态的叫做纯函数,而改变状态的是副作用函数,实际开发中,还需要处理副作用(如网络请求、数据库操作、启动协程等)。Compose 提供了 LaunchedEffect
, SideEffect
, DisposableEffect
等 API 来安全地处理这些与 Composable 生命周期相关的副作用。
单向数据流 (Unidirectional Data Flow - UDF): Compose 强烈推荐遵循 UDF 模式:状态向下流动(父组件传给子组件),事件向上传递(子组件通过回调通知父组件)。这有助于构建更可预测、更易于维护的 UI。
浅浅上手
在之前摸鱼的时候我写了个小小简陋的app:传送门
我们就以这个为例来快速上手 Jetpack Compose
我们以这个卡片组件为例,如你所见,在Android Studio中,工作区分为两部分,代码和Compose Preview,你可以通过添加@Preview
注解来启动对应组件的预览,当然也可以添加主题来查看对应主题的效果,比如默认的Material3
kotlin
我们重心还是回到上面的Composable函数上:
它从外向内分别是Box Row Column,类似三层flex div,modifier是样式修饰符,类比css,而里面也是由一个个封装好的Composable函数嵌套而成的(其实就是调用函数,组合UI)
我们可以根据传入的参数描述不同的UI,进而渲染不同的元素,例如:
kotlin
当涉及到副作用时,我们可以以简单的网络加载为例
kotlin
注:这里我手动创建了 ViewModel Factory 来传递 Application
Context,但在实际项目中,通常会使用 Hilt 或 Koin 等依赖注入库来简化这个过程。别感觉复杂,koin很简单的。
当然,想要深入使用还是得先浅浅学学Android中Activity的生命周期,这样才能会用ViewModel和各个Effect,不过这里只是简单介绍啦,它的思想和前端框架还是蛮像的。
Xposed Hooks
这个如果是搞机大佬肯定不会陌生,它是 一个运行于 Android 系统底层的框架,允许用户和开发者在不修改应用程序 APK 文件的情况下,实时地修改(“Hook”)系统和应用程序的行为,当然现在用的更多的还是LSPosed(老**框架)
Hook意为钩子,可以理解为钩住对应的函数,拦截下来,进而完成你想要的操作,它在应用程序启动时加载 Xposed Bridge,从而获得在 Zygote 进程中 Hook 任意 Java 方法的能力。
emm还是一个摸鱼时候随便写的小项目:传送门
作者自评:我看你小子没少摸鱼
核心实践
我们首先要确定一个模块入口类,让他继承IXposedHookLoadPackage
,这样我们就有了修改程序的能力。
在这个重载方法里,我们可以针对加载模块做一些自定义,比如因为我后续需要广播信息,这里就获取了一下context。
然后hook的方法就是这样,提供具体的类,方法名,随后我们可以自定义自己的前钩子函数和后钩子函数。 为了便于调试,我们可以添加一些日志。
kotlin
这里有一个param,我们可以通过它的args(Arr)和result(Object)来拿到所hook方法的参数和结果,整体效果大概这样:
kotlin
完整简要流程
当然,为了大家复现,我也贴了一段由AI生成的创建模块的讲解
开发流程:
-
环境准备:
- IDE: Android Studio (最新稳定版推荐)。
- 构建系统: Gradle (Android Studio 内置)。
- 编程语言: Java 或 Kotlin (Kotlin 因其简洁性和现代特性更受欢迎)。
- LSPosed API: 需要在项目中引入 LSPosed 提供的 API 库。
- 测试设备:
- 一台已 Root 的 Android 设备或模拟器。
- 已安装 Magisk (用于 Zygisk) 或 Riru (较旧)。
- 已安装 LSPosed Manager (通过 Magisk Manager 或 Riru 安装 LSPosed Zygisk/Riru 版本,然后安装管理器 APK)。
-
创建 Android 项目:
- 在 Android Studio 中,选择 "File" -> "New" -> "New Project..."。
- 选择一个模板,通常 "Empty Activity" 或 "No Activity" 都可以,因为模块本身不一定需要界面 (除非你想提供配置界面)。
- 配置项目名称、包名、保存位置、语言 (Java/Kotlin) 和最低 SDK 版本。最低 SDK 通常可以设置得较低 (如 API 21 或更高),但要确保你的代码兼容。
-
配置项目依赖和 Manifest:
-
添加 LSPosed API 依赖:
- 打开项目级别的
build.gradle
或build.gradle.kts
文件 (通常是app
模块下的那个)。 - 在
dependencies
代码块中添加 LSPosed API 依赖。关键: 使用compileOnly
或provided
作用域,因为这个 API 在运行时由 LSPosed 框架提供,不需要打包进你的模块 APK 中。
Gradle
kotlin-
注意: API 版本
82
是 Xposed 原始 API 的最后一个正式版本,LSPosed 完全兼容它,通常使用这个即可。提示:这里别忘加源
kotlin
- 打开项目级别的
-
配置
AndroidManifest.xml
:- 打开
app/src/main/AndroidManifest.xml
。 - 在
<application>
标签内添加以下<meta-data>
标签,以声明这是一个 LSPosed 模块:
XML
xml - 打开
-
-
分析目标应用和寻找 Hook 点:
- 确定目标: 你想修改哪个应用或系统功能的行为?
- 反编译: 使用反编译工具 (如 JADX-GUI) 打开目标应用的 APK 文件。
- 代码分析: 浏览反编译后的 Java 代码,找到你想要修改的功能对应的类和方法。记下完整的类名、方法名、参数类型和返回类型。
- 注意事项:
- 目标应用可能经过混淆 (ProGuard/R8),类名和方法名可能变成无意义的字母 (如
a.b.c
)。这会增加定位难度,并且 Hook 点可能在应用更新后失效。 - 优先寻找逻辑清晰、不易变动的方法进行 Hook。
- 注意方法的访问修饰符 (public, private, protected, package-private)。
- 目标应用可能经过混淆 (ProGuard/R8),类名和方法名可能变成无意义的字母 (如
-
编写 Hook 代码:
-
创建 Hook 入口类: 创建一个新的 Java 或 Kotlin 类,实现
de.robv.android.xposed.IXposedHookLoadPackage
接口。这个接口只有一个方法需要实现:handleLoadPackage
。 -
实现
handleLoadPackage
方法:这个方法会在每个应用加载时被 LSPosed 调用。
-
过滤目标应用: 在方法内部,首先检查
LoadPackageParam
(通常命名为lpparam
) 的packageName
字段,判断当前加载的是否是你的目标应用。如果不是,直接return
。 -
执行 Hook: 如果是目标应用,使用
de.robv.android.xposed.XposedHelpers
类提供的静态方法来查找并 Hook 目标方法。最常用的是findAndHookMethod
。 -
XC_MethodHook
回调:findAndHookMethod
的最后一个参数是一个XC_MethodHook
的匿名内部类 (或 Lambda 表达式) 实例。你需要重写它的beforeHookedMethod
和/或afterHookedMethod
方法。beforeHookedMethod(MethodHookParam param)
: 在原始方法执行 之前 调用。你可以读取/修改方法的输入参数 (param.args
),或者直接阻止原始方法执行并设置返回值/抛出异常 (param.setResult()
,param.setThrowable()
)。afterHookedMethod(MethodHookParam param)
: 在原始方法执行 之后 调用。你可以读取/修改方法的返回值 (param.getResult()
,param.setResult()
),或者读取原始方法的执行结果和参数,然后执行其他操作。
-
-
Kotlin Multiplatform
Kotlin Multiplatform (以前称为 Kotlin Multiplatform Mobile 或 KMM,现在范围更广) 是 JetBrains 提供的一种技术,允许开发者使用 Kotlin 编写代码,并将其共享到多个平台,如 Android, iOS, Web (JS), Desktop (JVM, Native), Server (JVM) 等。
简单来说就是逻辑代码一次编写处处运行,尽管现在 Compose Multiplatform 正在探索跨平台 UI,但是目前还不是production ready。kmp还主要是共享业务逻辑、数据层、网络请求、数据模型等非 UI 代码。
它大概是这样的:
-
Common Code (
commonMain
): 编写平台无关的 Kotlin 代码。 -
Platform-Specific Code (
androidMain
,iosMain
,jsMain
等): 编写需要调用特定平台 API 的代码。 -
expect
/actual
机制: 在commonMain
中声明预期的功能 (expect
class/function/property),然后在每个平台源集 (androidMain
,iosMain
等) 中提供具体的实现 (actual
class/function/property)。kotlin
其实就是能共用的就共用,平台有关的就通过expect/
actual在各个平台分别实现。
~~当然这个我也摸鱼...~~额,是有的,只不过这个想认真写写,写的差不多再开源。
emm这个怎么说,学习曲线有点陡,因为得各个平台开发比较熟悉,整体是:
-
编写 Common Logic: 在
commonMain
中用纯 Kotlin 编写大部分共享逻辑非常顺畅,尤其是利用 Kotlin Coroutines 处理异步、Serialization 处理 JSON 等,共用的网络库,拦截管道和错误处理,共用的viewstate什么的真的非常爽 -
处理平台差异 (
expect
/actual
): 对于需要平台 API 的功能(如文件系统访问、特定硬件交互、日期格式化等),使用expect
/actual
模式。这部分需要同时理解 Kotlin 和目标平台的 API。当平台差异很大时,actual
实现可能会变得复杂,这个就得每个平台都得会了。
还有,kotlin和swift互操作是真的难受
这里先不贴代码了,因为我感觉自己写的不是很行啊@_@
简要说说
好了,这次的“摸鱼心得”就先到这里。(误)
从 Jetpack Compose 带来的 UI 编写新范式,到 Xposed (LSPosed) 赋予我们的“魔改”能力,再到 Kotlin Multiplatform 对“一次编写,多端运行”的探索,不难看出安卓(以及更广阔的移动开发领域)正经历着快速的演进。
这三者或许代表了现代安卓开发的不同切面:面向未来的 UI 构建、深入底层的系统定制、以及跨平台代码复用的探索。它们都基于 Kotlin 的强大能力。对我来说,折腾这些新技术,也确是“悲惨生活”中的一点短暂休息了。
讲的不深,简单说说,希望这篇小文章让你对这些技术产生兴趣,那就下次摸鱼再见吧!
《现代安卓开发之 Jetpack Compose、Xposed Hook 与 Kotlin Multiplatform》采用知识共享署名 4.0 国际许可协议
转载请注明出处并遵循 CC BY 许可协议条款