一、Android 系统架构

Android 系统整体上可以分为五层结构,从上到下依次是:应用层、应用框架层、系统运行库层、硬件抽象层以及 Linux 内核层。下图展示了经典的 Android 架构分层图:

应用层(System Apps)

应用层包含系统自带的应用(如电话、短信、设置等)以及用户安装的第三方应用。这一层主要负责与用户进行交互,通常使用 Java/Kotlin 编写。我们日常开发的 App 就属于这一层。

应用框架层(Java API Framework)

应用框架层为开发者提供了构建应用所需的 API 接口。无论是系统应用还是用户应用,都需要通过调用这一层的 API 来实现功能。应用框架层由 Java 代码编写,因此也常被称为 Java Framework。下面列举了这一层所提供的主要组件:

  • Activity Manager:管理各个应用程序生命周期,以及常用的导航回退功能;

  • Location Manager:提供地理位置和定位功能服务;

  • Window Manager:管理所有开启的窗口程序;

  • Content Provider:使得不同应用程序之间可以共享程序;

系统运行库层(Native)

系统运行库层分为两个部分,分别是 C/C++ 程序库和 Android 运行时库。

  • C/C++:包括 OpenGL、SQLite、Libc 等,为上层框架提供底层能力;

  • Android 运行时库

    • 核心库:提供了 Java 语言核心库;

    • ART 虚拟机:相比传统 JVM,ART 针对移动设备进行了优化。在应用安装时,ART 会执行一次预编译(AOT, Ahead Of Time),将字节码转为本地机器码并存储起来,从而提升运行效率。

硬件抽象层(HAL)

硬件抽象层位于内核与硬件驱动之间,向上层提供统一接口。它的作用是屏蔽底层硬件差异,让上层无需关心硬件细节,就能通过统一 API 调用不同厂商的硬件功能。

Linux 内核层(Linux Kernel)

Android 的核心系统服务基于 Linux 内核,在此基础上添加了部分 Android 专用的驱动。系统的安全性、内存管理、进程管理、网络协议栈和驱动模型等都依赖于该内核。


二、Android 系统启动流程

  1. 启动电源:当电源按下时引导芯片代码从预定义的地方(固化在 ROM)开始执行。加载引导程序 BootLoader 到 RAM 中,然后执行;

  2. BootLoader:引导程序 BootLoader 是在 Android 操作系统开始运行前的一个小程序,它的主要作用是把系统 OS 拉起来并运行;

  3. Linux 内核启动:当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。在内核完成系统设置后,它首先在系统文件中寻找 init.rc 文件,并启动 init 进程;

  4. init 进程:主要用来初始化和启动属性服务,也用来启动 Zygote 进程。

  5. Zygote 进程:创建 Java 虚拟机并为 Java 虚拟机注册 JNI 方法,创建服务器端 Socket,启动 SystemServer 进程;

  6. SystemServer 进程:启动Binder 线程池,和 SystemServiceManager,并且启动各种系统服务;

  7. Launcher 启动:被 SystemServer 进程启动的 AMS 会启动 Launcher,它启动会将已安装应用的图标显示到界面上。


三、App 启动流程

启动一个 App 的过程可以分为两个阶段:先启动应用进程,再启动根 Activity。根 Activity 通常是用户点击应用图标后展示的第一个页面,因此它的启动流程也可以视作应用启动的流程。

下图展示了从桌面点击图标后启动 App 的流程图:

1. Launcher 请求 AMS

当系统启动后,Launcher 会将已安装 App 的快捷图标展示在桌面上。这些图标就是应用根 Activity 的入口。

当用户点击某个 App 图标时,Launcher 会通过 ActivityManager.getService() 获取 AMS 的代理对象,并调用其 startActivity() 方法,请求 AMS 启动对应的根 Activity。

这里的通信底层是通过 AIDL 实现的,也就是说,Launcher 与 AMS 之间是基于 Binder 的跨进程调用。

2. AMS 启动应用进程

AMS 收到请求后,会先检查目标应用的进程是否已经存在:

  • 如果进程不存在:AMS 会请求 Zygote 进程启动新的应用进程。Zygote 在 Java 框架层维护着一个 Socket 服务端,专门用于接收 AMS 的请求。当收到创建进程的请求后,Zygote 会通过 fork 自身来创建新的应用进程。通过这种方式创建的新进程可以直接继承 Zygote 已经初始化好的运行环境,比如虚拟机实例和 Binder 线程池,从而提升应用启动效率。

  • 如果进程已存在:AMS 会直接进入下一步,通知该进程去启动目标 Activity。

3. AMS 与 ApplicationThread 通信

AMS 运行在 SystemServer 进程中,它会通过 Binder 通道与应用进程进行通信。当需要启动 Activity 时,AMS 会调用应用进程中的 ApplicationThread

需要注意的是,ApplicationThreadActivityThread 的内部类,负责接收 AMS 下发的各种启动指令(如启动 Activity、启动 Service 等)。

4. ActivityThread 启动 Activity

当 ApplicationThread 收到 AMS 的启动 Activity 请求后,会将任务切换到主线程执行。具体来说,它会向内部的 mH(ActivityThread.H 类)发送消息。

  • H 是继承自 Handler 的类,绑定在主线程的 Looper 上;

  • 由于 ApplicationThread 是一个 Binder 对象,Binder 方法回调运行在 Binder 线程池中,所以需要借助 H 将任务切换回主线程,确保 UI 相关逻辑运行在主线程中。

最终,ActivityThread 会通过反射调用 Activity 的生命周期方法,完成根 Activity 的启动,用户就能看到应用界面。

四、设计模式使用案例

观察者模式

定义一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会自动收到通知并更新。

  • Android 中的 View.setOnClickListener() 就是观察者模式,按钮点击事件发生时,注册的监听器(观察者)会被通知。

装饰器模式

在不改变原有对象的前提下,动态地给对象添加额外的功能。强调包装而不是继承,避免类爆炸。

  • Java IO:BufferedInputStream 装饰了 InputStream,在原始读写功能上增加了缓存,提高效率。

  • Java 集合:Collections.synchronizedList() 方法装饰一个 List,在原有方法的基础上增加了 synchronized 关键字,实现线程安全。

建造者模式

将一个复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表示。解决构造函数参数过多、可选参数复杂的问题。

  • StringBuilder:通过链式调用一步步构建字符串;

  • 安卓中的 AlertDialog.Builder:通过 setTitle(), setMessage(), setPositiveButton() 等方法,最后 create().show()

  • okHttp:OkHttpClientRequest 都是 Builder;

策略模式

定义一系列算法,把它们封装起来,使得它们可以互相替换,客户端可在运行时选择不同的算法。强调行为可替换

  • 排序算法:Java 的 Comparator,可以传入不同的比较策略,达到不同的排序效果。

  • 支付系统:不同支付方式(支付宝、微信、银行卡)可以抽象为不同策略,统一接口 pay(),在运行时根据用户选择切换。

五、MVC、MVP、MVVM

案例需求

我们先通过一个简单的案例来说明问题:根据输入框内容查询用户账号,并展示查询成功或失败的结果。

如果没有使用任何架构设计,那么通常会把所有逻辑都写在 Activity 里,包括:

  • 业务逻辑处理(响应 Button 的点击事件,处理业务逻辑);

  • 获取用户的输入(EditText 中的内容);

  • 查询账号数据:向数据层请求用户信息;

  • 展示结果:根据成功/失败更新 UI。

class NormalActivity : AppCompatActivity() {

    val mTvResult: TextView by lazy { findViewById(R.id.tv_result) }
    val editText: EditText by lazy { findViewById(R.id.et_account) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_normal)

        /**
         * 业务逻辑代码
         */
        findViewById<Button>(R.id.btn_get_account).setOnClickListener {
            getAccountData(getUserInput(), object : MCallback{
                override fun onSuccess(account: Account) {
                    showSuccessPage(account)
                }

                override fun onFailed() {
                    showFailedPage()
                }

            })
        }
    }

    // 获取用户输入
    fun getUserInput(): String {
        return editText.text.toString()
    }

    // 展示成功页面
    fun showSuccessPage(account: Account) {
        val text = "用户账号:${account.name},用户等级:${account.level}"
        mTvResult.text = text
    }

    // 展示失败页面
    fun showFailedPage() {
        mTvResult.text = "获取数据失败"
    }

    // 查询用户数据
    fun getAccountData(accountName: String, mCallback: MCallback) {
        if (Random.nextBoolean()) {
            val account = Account(accountName, 100)
            mCallback.onSuccess(account)
        } else {
            mCallback.onFailed()
        }
    }
}

从这个例子可以看到:当不使用任何架构时,Activity 会变得非常臃肿。它既要关心界面渲染,又要操心数据获取和业务逻辑,所有功能都耦合在一个类中。随着需求增加,代码会越来越复杂,维护成本也会越来越高。

因此,我们需要引入 MVC、MVP、MVVM 等分层架构,将不同的职责拆分到不同模块

  • UI 层只负责界面展示;

  • 逻辑层负责业务处理;

  • 数据层负责数据获取。

这种分层方式能够有效降低耦合度,让代码更加清晰、易维护。

MVC 模式

  • Controller:Activity、Fragment;

  • View:layout、View 控件;

  • Model:数据处理(网络请求,SQL查询等)。

MVC 架构的流转过程是:用户点击某个 View 控件,事件传递到 Activity 中响应点击事件,通知 Model 层进行数据处理,Model 层处理完毕后通知 View 更新 UI。

如果使用 MVC 实现需求,各个模块需要处理的内容如下:

  • Activity(Controller 层):

    • 业务逻辑处理

    • 获取用户输入

    • 展示成功/失败界面

  • Model 层:查询账号数据

  • View 层:layout(布局)

Controller 层(Activity)代码如下:

class MVCActivity : AppCompatActivity() {

    val textView: TextView by lazy { findViewById(R.id.tv_result) }
    val editText: EditText by lazy { findViewById(R.id.et_account) }

    val mvcModel = MVCModel()   // Activity 持有 Model 层的引用

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_normal)

        // 业务逻辑代码
        findViewById<Button>(R.id.btn_get_account).setOnClickListener {
            // 通知 Model 层处理数据,通过回调通知 View 层
            mvcModel.getAccountData(getUserInput(), object : MCallback {
                override fun onSuccess(account: Account) {
                    showSuccessPage(account)
                }

                override fun onFailed() {
                    showFailedPage()
                }
            })
        }
    }

    // 获取用户输入
    fun getUserInput(): String {
        return editText.text.toString()
    }

    // 展示成功页面
    fun showSuccessPage(account: Account) {
        val text = "用户账号:${account.name},用户等级:${account.level}"
        textView.text = text
    }

    // 展示失败页面
    fun showFailedPage() {
        textView.text = "获取数据失败"
    }
}

Model 层代码如下:

// Model 层
class MVCModel {
    // 查询用户数据
    fun getAccountData(accountName: String, mCallback: MCallback) {
        if (Random.nextBoolean()) {
            val account = Account(accountName, 100)
            mCallback.onSuccess(account)
        } else {
            mCallback.onFailed()
        }
    }
}

通过代码可以发现,Controller 层(Activity)通过持有 Model 层的引用实现和 Model 层的通信,Model 层通过回调通知 View 层更新 UI 界面。

MVC 架构在日常的代码中使用还是比较多的,它的优缺点如下:

  • 优点:一定程度上实现了 Model 层与 View 层的分离,降低了代码的耦合性;

  • 缺点:在 Android 中,Controller 层与 View 层难以完全解耦,随着项目复杂度的提升,Controller(Activity)将会越来越臃肿。

对于 MVC 模式,有的模型图如下图所示,因为 View 和 Controller 难以完全分离。

MVP 模式

MVP 即 Model-View-Presenter 模型。

  • View:视图层(Activity);

  • Model:数据处理(网络请求,SQL查询等);

  • Presenter:中间层,负责业务逻辑处理,以及和 View/Model 层的通信。

如果使用 MVP 实现需求,各个模块需要处理的内容如下:

  • View 层(Activity):

    • 获取用户输入

    • 展示成功/失败界面

  • Model 层:查询账号数据

  • Presenter 层:业务逻辑处理,同时负责和 View/Model 层的通信

在 View 层首先创建对应的接口,定义需要实现的功能(获取用户输入、展示界面等):

interface IMvpView {
    fun getUserInput(): String
    fun showSuccessPage(account: Account)
    fun showFailedPage()
}

Model 层不变,还是处理数据的逻辑:

class MvpModel {
    // 查询用户数据
    fun getAccountData(accountName: String, mCallback: MCallback) {
        if (Random.nextBoolean()) {
            val account = Account(accountName, 100)
            mCallback.onSuccess(account)
        } else {
            mCallback.onFailed()
        }
    }
}

Presenter 层同时持有 View 层和 Model 层的引用,以及对业务逻辑的处理:

class MvpPresenter(
    val view: IMvpView // View 层的实例通过构造参数传递
) {
    // Model 层的实例直接创建对象
    val model: MvpModel = MvpModel()

    // 业务逻辑处理
    fun getData(accountName: String){
        // 通过 Model 层处理数据
        // 通过 View 层更新 UI
        model.getAccountData(accountName, object : MCallback {
            override fun onSuccess(account: Account) {
                view.showSuccessPage(account)
            }

            override fun onFailed() {
                view.showFailedPage()
            }
        })
    }
}

让 Activity 实现该 IMvpView 接口,以提供 View 层的功能。在按钮点击后通知 Presenter 层处理业务逻辑。

class MVPActivity : AppCompatActivity(), IMvpView{
    val textView: TextView by lazy { findViewById(R.id.tv_result) }
    val editText: EditText by lazy { findViewById(R.id.et_account) }

    val presenter = MvpPresenter(this)  // 持有 Presenter 引用

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_normal)

        // 点击按钮时通知 Presenter 进行具体的业务逻辑处理
        findViewById<Button>(R.id.btn_get_account).setOnClickListener {
            presenter.getData(getUserInput())
        }
    }

    // 获取用户输入
    override fun getUserInput(): String {
        return editText.text.toString()
    }

    // 展示成功页面
    override fun showSuccessPage(account: Account) {
        val text = "用户账号:${account.name},用户等级:${account.level}"
        textView.text = text
    }

    // 展示失败页面
    override fun showFailedPage() {
        textView.text = "获取数据失败"
    }
}

MVP 与 MVC 的差别

  • Model 不再于 View 直接进行通信,而是通过中间层 Presenter 来实现;

  • Activity 的功能被简化,不再充当控制器,主要负责 View 层面的工作。

MVP 的优缺点

  • 优点:解决了 MVC 中 Controller 与 View 过度耦合的缺点,职责划分明显,更加易于维护;

  • 缺点:接口数量多(Presenter、Model 层也可以定义接口),随着项目复杂度的升高,Presenter 层会越来越臃肿。

使用 MVP 的建议

  1. 接口规范化,封装父类接口以减少接口的使用量;

  2. 使用第三方插件自动生成 MVP 代码;

  3. 对应一些简单的界面,可以选择不使用框架;

  4. 根据项目的复杂程度,部分模块可以选择不使用接口。

MVVM 模式

什么是 MVVM 模型?

MVVM 是 Model-View-ViewModel 的简写,MVVM 在 MVP 的基础上实现类数据视图的绑定(DataBinding),当数据变化时,视图会自动更新;反之,当视图发生变化时,数据也会自动更新。

使用 MVVM 模式实现需求,各层的功能划分如下:

  • View 层(Activity、layout 布局文件):获取用户输入、展示成功/失败界面;

  • ViewModel 层:业务逻辑处理、数据更新(通过 DataBinding);

  • Model 层:查询账号数据。

View 层主要包括 layout 文件和 Activity,layout 文件需要设置为 databinding 布局,定义 ViewModel 层的对象,在布局中使用里面的数据。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="viewModel"
            type="top.tonydon.interview.mvvm.mvvm.MvvmViewModel" />
    </data>

    <LinearLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".normal.NormalActivity">

        <EditText
            android:id="@+id/et_account"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            android:text="@={viewModel.userInput}"
            android:hint="请输入要查询的账号" />

        <Button
            android:id="@+id/btn_get_account"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="50dp"
            android:text="获取账号信息" />

        <TextView
            android:id="@+id/tv_result"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="50dp"
            android:text="@{viewModel.result}"
            android:hint="账号信息未获取" />

    </LinearLayout>
</layout>

然后是 Activity,需要使用 DataBindingUtil 设置布局,完成 ViewModel 的创建、变量初始化、点击事件通知 ViewModel 层处理。

class MvvmActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 通过 DataBindingUtil 设置 ContentView,返回一个 ActivityMvvmBinding 实例(系统自动生成)
        val binding: ActivityMvvmBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_mvvm)

        // 创建 ViewModel 对象绑定到 layout 中
        val viewModel =  MvvmViewModel(application)
        binding.viewModel = viewModel

        binding.btnGetAccount.setOnClickListener {
            viewModel.getData()
        }
    }
}

ViewModel 层一般持有 Model 层的引用和 DataBinding,实现向双方的通信。通过 ObservableField 等手段,更新变量值时可以自动更新 UI 界面。

// 一般需要传入 Application 对象,方便在 ViewModel 中使用 Application
class MvvmViewModel(
    val application: Application
) {
    var userInput = ObservableField("") // 和 View 层双向绑定
    var result = ObservableField("")    // 和 View 层单向绑定
    val model = MvvmModel() // 持有 Model 层引用

    fun getData() {
        model.getAccountData(userInput.get()!!, object : MCallback {
            override fun onSuccess(account: Account) {
                val text = "用户账号:${account.name},用户等级:${account.level}"
                result.set(text)
            }

            override fun onFailed() {
                result.set("获取数据失败")    // 设置时自动更新 UI
            }
        })
    }
}

Model 层与其他模式相同,进行数据处理即可:

class MvvmModel {
    // 查询用户数据
    fun getAccountData(accountName: String, mCallback: MCallback) {
        if (Random.nextBoolean()) {
            val account = Account(accountName, 100)
            mCallback.onSuccess(account)
        } else {
            mCallback.onFailed()
        }
    }
}

注:MVVM 模式在 Android 中想要使用的完美需要搭配 LiveData + ViewModel 实现,上述代码只是作为了解 MVVM 模式的案例,并不是最佳实践。

MVVM 与 MVP 的区别

  • 减少了接口数量;

  • 告别了繁琐的 findViewById() 操作;

MVVM 的优缺点

  • 优点:实现了数据和视图的双向绑定,极大的简化代码;

  • 缺点:bug 难以调试,并且 DataBinding 目前现在还存在一些编译问题。

三种模式的对比

  • MVC:学习简单,但是解耦不够彻底;

  • MVP:解耦更加彻底,学习起来也相对比较简单,但是代码相对比较繁琐;

  • MVVM:代码逻辑非常简洁,但是学习成本较大。


六、APK 打包流程和反编译

内存优化、卡顿优化