开源 _ Scene:Android 开源页面导航和组合框架

  1. 栈管理弱,Intent+LaunchMode 的设计,使得开发者在使用的时候要么极容易出错,要么用 Hack 做对了但是动画过度黑屏;
  2. Activity 性能差,普通的空白页面切换也得 60、70ms 耗时(基于三星 S9 设备测试);
  3. 因为销毁恢复的强制要求:
  • 导致的 Activity 动画能力非常弱,无法直接拿到前后两个页面的 View 也就无法简单的实现复杂的交互动画;
  • SharedElement 动画能力弱,动画的瞬间不得不来回传递上下两个 Activity 各种控件的 Bitmap;
  • Android 9 之前 Activity 每次启动新的 Activity,都需要上个页面执行完 onSaveInstance,这一步影响了页面打开的速度;
  1. Activity 依赖 Manifest 给 Android 动态化增加了难度,需要对系统的 Instrumentation ActivityThread 进行各种 Hack ;
  2. 依赖注入很难,因为创建 Activity 对象的流程在 Android 8 之前是没有 API 暴露给外部处理的;
  3. 因为 Window 的机制导致做悬浮窗播放也是问题,导致实现窗口播放必须依赖了一个危险的悬浮窗权限;
  4. 共享元素动画在某些版本的 Framework 层有 NPE,无法解决。

java.lang.NullPointerException(android.app.EnterTransitionCoordinator);

页面组合对比 Fragment

  1. 各种奇怪的崩溃,就算不用 Fragment,但是用了 AppCompatActivity 还是会在 onBackPressed 里面触发崩溃;

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

对于这种情况,西瓜直接在自己的 Activity 基类对 super.onBackPressed() 进行了try catch。

  1. add/remove/hide/show 操作不是立刻执行,就算 commitNow 执行了 Fragment 的状态,也不能保证他的 Child Fragment 状态刷新到最新。在执行了 getChildFragmentManager().executePendingTransactions() 后,开发者会误以为 Child Fragment 都已经切到最新的 Parent Fragment 状态,其实并没有;
  2. Fragment 有两套 Lifecycle,View Lifecycle 和 Fragment 实例 Lifecycle;
  3. Fragment show/hide 方法不会触发生命周期回调,调用了 hide 不会触发 onPause/onStop,只是修改了 View 的可见性;
  4. Fragment 动画能力有限,只能使用资源文件,而且页面导航无法保证 Z 轴正确;
  5. 就算 Fragment 已经被销毁,但是 View.OnClickListener onClick 回调依然继续触发,导致回调内部不得不补大量的判空逻辑;

if (getActivity() == null) {
return;
}

  1. 导航功能非常弱,除了打开和关闭,没有更加高级的栈管理,导航的回调连顺序都保证不了,有可能一次导航触发多次回调;
  2. 原生 Fragment 和 Support Fragment 的生命周期并不完全相同;
  3. 同时支持 add/remove/hide/show+addToBackStack 使得 Fragment 的代码极度混乱。

Scene 框架

功能特点

Scene 提供页面导航页面组合两大功能,特点如下:

  1. 基于 View 实现,非常轻量;
  2. 只有一个 Lifecycle,View 销毁,那么 Scene 也会销毁,不会出现 Fragment 有两套 Lifecycle 的问题;
  3. 导航栈管理非常灵活,不会出现页面切换黑屏问题;
  4. 无论是导航操作还是组合操作,通常都是直接执行,不需要区分 commit 和 commitNow;
  5. 不强制要求状态保存,甚至可以把状态保存控制在页面级别,增强组件通讯的能力;
  6. 有完整的共享元素动画支持;
  7. 页面导航和页面组合功能可以独立使用。

基本概念

Scene 框架有3种基本组件:Scene、NavigationScene、GroupScene。

用处
Scene所有 Scene 的基类,带生命周期和 View 支持的组件
NavigationScene支持页面导航
GroupScene支持将任何 Scene 组合

Scene

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

NavigationScene

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

GroupScene

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Scene 使用

简单使用

这里介绍简单的上手,更多用法见 Github 仓库的示例。

接入

添加依赖:

dependencies {
implementation ‘com.bytedance.scene:scene: l a t e s t v e r s i o n ′ i m p l e m e n t a t i o n ′ c o m . b y t e d a n c e . s c e n e : s c e n e − u i : latest_version' implementation 'com.bytedance.scene:scene-ui: latestversionimplementationcom.bytedance.scene:sceneui:latest_version’
implementation ‘com.bytedance.scene:scene-shared-element-animation:$latest_version’

// Kotlin
implementation ‘com.bytedance.scene:scene-ktx:$latest_version’
}

创建首页:

class MainScene : AppCompatScene() {
override fun onCreateContentView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View? {
return View(requireSceneContext())
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setTitle(“Main”)
toolbar?.navigationIcon = null
}
}

创建 Activity:

class MainActivity : SceneActivity() {
override fun getHomeSceneClass(): Class {
return MainScene::class.java
}

override fun supportRestore(): Boolean {
return false
}
}

添加到 Manifest.xml,注意把输入法模式也改了:






运行就可以了。

这是新应用想全部使用 Scene 写的方式。如果是老应用重构迁移,或者只想用页面组合替代 Fragment,导航依旧用 Activity 的做法,可以见 Github 的 Demo。

导航

打开新页面:

requireNavigationScene().push(TargetScene::class.java)

返回:

requireNavigationScene().pop()

打开页面拿结果:

requireNavigationScene().push(TargetScene::class.java, null,
PushOptions.Builder().setPushResultCallback { result ->
}
}.build())

设置结果:

requireNavigationScene().setResult(this@TargetScene, YOUR_RESULT)

组合

组合的 API 类似 Fragment,继承 GroupScene,然后可以操作任意 Scene 添加到自己的 View 布局内:

void add(@IdRes int viewId, @NonNull Scene childScene, @NonNull String tag);
void remove(@NonNull Scene childScene);
void show(@NonNull Scene childScene);
void hide(@NonNull Scene childScene);
@Nullable
T findSceneByTag(@NonNull String tag);

示例:

class SecondScene : AppCompatScene() {
private val mId: Int by lazy { View.generateViewId() }

override fun onCreateContentView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View? {
val frameLayout = FrameLayout(requireSceneContext())
frameLayout.id = mId
return frameLayout
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setTitle(“Second”)
add(mId, ChildScene(), “TAG”)
}
}
class ChildScene : Scene() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View {
val view = View(requireSceneContext())
view.setBackgroundColor(Color.GREEN)
return view
}
}

通讯

Scene 支持 ViewModel,可以通过 by activityViewModels,by viewModels 拿到托管到 Activity 或者自己的 ViewModel:

class ViewModelSceneSamples : GroupScene() {
private val viewModel: SampleViewModel by activityViewModels()

示例:

class ViewModelSceneSamples : GroupScene() {
private val viewModel: SampleViewModel by activityViewModels()
private lateinit var textView: TextView

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel.counter.observe(this, Observer { t -> textView.text = “” + t })

add(R.id.child, ViewModelSceneSamplesChild(), “Child”)
}
}

学习宝典

对我们开发者来说,一定要打好基础,随时准备战斗。不论寒冬是否到来,都要把自己的技术做精做深。虽然目前移动端的招聘量确实变少了,但中高端的职位还是很多的,这说明行业只是变得成熟规范起来了。竞争越激烈,产品质量与留存就变得更加重要,我们进入了技术赋能业务的时代。

不论遇到什么困难,都不应该成为我们放弃的理由!

很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习,对此我针对Android程序员,我这边给大家整理了一套学习宝典!包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【算法合集】

【延伸Android必备知识点】

【Android部分高级架构视频学习资源】
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
1715263517364)]

【算法合集】

[外链图片转存中…(img-Wd7lOijx-1715263517364)]

【延伸Android必备知识点】

[外链图片转存中…(img-hWu70sDr-1715263517365)]

【Android部分高级架构视频学习资源】
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值