#Android# Single Activity Multiple Fragments Architecture

#Android# Single Activity Multiple Fragments Architecture

前言

在 Android 开发中,正常且推荐的 App 页面架构应该为「包含多个 Activity,Activity 即页面」,这样做的好处在于系统内存告急时可以回收处于后台的 Activity,保证更多的资源给前台的页面和任务。但是我们知道,启动新 Activity 是在 ActivityManagerService 中运行(非 UI 主线程),它需要执行一系列耗时的操作,我们能明显地意识到「页面切换」这个动作。

而知乎使用的是另外一种解决方案「单个 / 少量 Activity,多个 Fragment,Fragment 即页面」。它消耗更少的资源,能更快地响应页面间切换和交互。但是它也有些短处,在层次深的页面进行现场保存和还原会消耗更多的资源和时间。所以它适合在页面层级结构不深的应用或场合中应用。

你可以在 Kotgo Lastest Release 中下载 Sample Apk 进行尝试:sample-release.apk(可能需要 fan墙



一些数据

对比 Kotgo 的 0.8.x 和 0.9.x 两个版本(分别使用 Activity 和 Fragment 来构建页面),我使用 Log 来纪录它们切换页面的用时:(单位:毫秒)

  • 使用 Activity 切换页面:
  • 使用 fragment 切换页面:


如何设计

要使用 Fragment 来取代 Activity 的职能还需要做一系列的工作,我们需要把一些 Activity 中常用的功能搬运到 Fragment 中,并且我们还需要另外维护一份 Fragment 页面的栈队。要知道 FragmentManager 在进行 Fragment 现场还原的时候只恢复内部的事务栈队(Transaction Stack),并不会恢复每个 Fragment 的显示状态,所以会产生 Fragment 重叠的问题。

而需要从 Activity 中「移植」到 Fragment 的一些必要功能包括:

  • onBackPressed() 的处理
  • onReslut() 的处理

而且为了模仿 startActivityForResult(),我们还需要实现 pushForReslut() 这一函数,可以将一个能产生返回值的 Fragment 加入栈顶。

上面说的这一大堆功能,都在 FragmentActivity.kt 中实现了,它在内部维护了多个列表和栈来解决一系列问题以及实现 FragmentManager 未提供的一些功能。下面来看看我们的 Fragment 应该设计成怎样。

abstract class BaseFragment: WithLifecycleFragment() {
    var requestInfo: FragmentActivity.RequestInfo? = null
    val fragAct: FragmentActivity?
        get() = activity as FragmentActivity?

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if(savedInstanceState != null) {
            requestInfo = savedInstanceState.getParcelable("__requestInfo")
        }
    }

    override fun onSaveInstanceState(outState: Bundle?) {
        if(requestInfo != null)
            outState?.putParcelable("__requestInfo", requestInfo!!)

        super.onSaveInstanceState(outState)
    }

    open fun onBackPressed(): Boolean {
        return false
    }

    open fun onResult(requestCode: Int, resultCode: Int, data: Intent?) { }

    protected fun setResult(resultCode: Int, data: Intent? = null) {
        requestInfo?.apply {
            this.resultCode = resultCode
            this.resultData = data
        }
    }
}

然后我们在 HostActivity/FragmentActivity 中提供对栈进行操作的一些函数,用来跳转到其他 Fragment 或者 Activity 上(因篇幅问题,详细实现可查阅源码):

  • push():将 Fragment 加入栈顶并显示
  • pushForResult():将 Fragment 加入栈顶并显示,并捕获返回值
  • pop():将栈顶的 Fragment 弹出并隐藏
  • get():获取栈中指定 Tag 的 Fragment
  • getFragmentTopInStack():获取栈顶中的 Fragment
  • startActivityForResult():启动另外一个 Activity,并捕获返回值给指定 Fragment


结尾

Single Activity Multiple Fragments 确实是一种不错的架构页面的新方案。它提供了一种新的思路来组织页面,所有页面的 View 保存在碎片中,然后在单个 Activity 中快速切换,思考下,其实这是一种退化(或者说底层化)的思想,用 Fragment 组织 View 分页面,然后全部 View 一碌脑全在单个 Activity 上初始化/显示/隐藏/绘制。

所以说更底层化的思想带来了更快的页面切换与交互。

编辑于 2016-05-01