巨佬JakeWharton
曾经建议:一个App
只需要一个Activity
.
这说的就是单Activity
多Fragment
模式.使用这种模式有许多好处:
- 首先第一个好处就是流畅,要知道
Activity
属于系统组件,受AMS
管理并且自身是一个God Object
,它的开销是很大的,单Activity
模式可以为我们节省很多资源,还可以避免资源不足时,被前台Activity
覆盖的Activity
被杀掉导致页面数据丢失的情况(因为只有一个Activity
,除非JAVA
堆内存到达系统要杀掉一个程序的临界点,否则系统最不倾向于杀死前台正在运行的Activity
); - 其次就是可以将业务逻辑拆分成更小的模块,并将其组合复用,这在这在大型软件系统中尤为重要(新版
知乎
就使用了单Activity
多Fragment
这种模式),因为我们都知道Activity
的是无法在多个页面中复用的,而此时Fragment
就有了它的勇武之地,它作为轻量级的Activity
,基本可以代理Activity
的工作,并且他是可复用 - 再者,使用
Fragment
可以为程序带来更大的灵活性,我们都知道在Activity
之间传递对象,对象需要序列化,这是因为Activity
作为系统组件,是受AMS
管理的,而AMS
属于系统进程,不在当前程序运行的进程中,启动Activity
时需要暂时离开当前进程去到AMS
的进程中,而AMS
则会将你准备好的数据(也就是Intent
之类的)用来启动Activity
,这也是Fragment
和Activity
之间的区别之一,Activity
属于系统组件,可以在别的进程运行(组件化/多进程方案),而Fragment
只是框架提供给我们的的一个组件,它必须依附于Activity
生存,并且只能在当前进程使用,但这同时也意味这它可以获得更大的灵活性,我们可以给Fragment
传递对象而无需序列化,甚至可以给Fragment
传递View
之类的对象,这都是Activity
不容易做到的.
2.要使用Fragment你必须知道的一些事情
首先要提一点,如果你要学习Fragment
那么你至少得是掌握了Activity
的,如果你还不了解Activity
,笔者建议你先去看一些Activity
相关的文章,再来进阶Fragment
.从下面的文章开始,默认读者已经了解了Activity
的生命周期等相关知识。
Fragment
拥有Activity
所有的生命周期回调函数并且由于自身特点还扩展了一些回调函数,但是这些与Activity
相关的回调函数几乎只与Fragment
依附的Activity
有关,如果不熟悉Fragment
,很容易凭直觉造成误会.例如,一个Fragment
并不会因为在Fragment
回退栈上有其他Fragment
把它盖住,又或者是你使用FragmentTransition
将它hide
而导致他onPause
,onPause
只跟此Fragment
依附的Activity
有关,这在Fragment
的源码中写得清清楚楚.
/**
- Called when the Fragment is no longer resumed. This is generally
- tied to {@link Activity#onPause() Activity.onPause} of the containing
- Activity's lifecycle.*/
@CallSuper
br/>*/
@CallSuper
mCalled = true;
}
那当我们想在Fragment
不显示时做一些事情要怎么办呢?我们有onHiddenChanged
回调,当Fragment
的显示状态通过FragmentTransition
改变时(hide
和show
),就会回调这个函数,参数hidden
将告诉你这个Fragment
现在是被隐藏还是显示着.
/**
- Called when the hidden state (as returned by {@link #isHidden()} of
- the fragment has changed. Fragments start out not hidden; this will
- be called whenever the fragment changes state from that.
- @param hidden True if the fragment is now hidden, false otherwise.
*/
public void onHiddenChanged(boolean hidden) {
}
Fragment
有两种方式生成,一是硬编码到xml
文件中,二是在Java
代码中new
,然后通过FragmentManager#beginTransaction
开启FragmentTransaction
提交来添加Fragment
(下文会介绍).两种方式存在着一定区别.硬编码到xml
的Fragment
无法被FragmentTransition#remove
移除,与Activity
同生共死,所以你要是这么用了,就不用试了,移除不了的,但是在代码中new
出来的是可以被移除的.
硬编码到xml
中:
<fragmentandroid:id="@+id/map_view"
br/>android:id="@+id/map_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
添加Fragment
的第二种方式就是使用FragmentManager#beginTransaction
(代码如下)动态添加,你需要先new
一个Fragment
,然后通过下面Fragment#requireFragmentManager
获取FragmentManager
来使用beginTransaction
添加Fragment
,注意add
方法的第一个参数,你需要给它指定一个id
,也就是Fragment
容器的id
,通常容器是一个没有子View
的FrameLayout
,它决定了Fragment
要在什么位置显示。
需要注意的是FragmentTransaction
并不是立即执行的,而是在当前代码执行完毕后,回到事件循环(也就是你们知道的Looper
)时,才会执行,不过他会保证在下一帧渲染之前得到执行(通过Handler#createAsync
机制),若要在FragmentTransaction
执行时搞事情,你需要使用runOnCommit
,下面的代码中我使用了Java8
的lambda
表达式简写了Runnable
.
若要使用Fragment
回退栈记得addToBackStack
,最后别忘了commit
,这样才会生效,此时commit
函数返回的是BackStackEntry
的id
requireFragmentManager()
.beginTransaction()
.add(id, fragment)
.runOnCommit(()->{/TODO/})
.addToBackStack(null)
.commit();
当然FragmentTransaction
不止可以执行add
操作,同样也可以执行remove
,show
,hide
等操作.
这里插入一个简短的题外话作为上面知识的补充。如何在Android Studio
中启用Java8
?在你模块的build.gradle
中
android{
//省略.....
//加上下面的脚本代码,然后sync你的项目
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
onBackPressed
在哪?我知道第一次使用Fragment
的人肯定都超想问这个问题.众所周知Fragment
本身是没有onBackPressed
的.不是Google
不设计,而是真的没法管理啊!!!,如果一个界面上有三四个地方都有Fragment
存在,一按回退键,谁知道要交给哪个Fragment
处理呢?所以Fragment
是"没有"onBackPressed
的.
在这里我的“没有”打了引号,因为实际上给Fragment
添加类似onBackPressed
的功能的办法是存在的,只是Google
把它设计成交给开发者自行管理了.
要想使用Fragment
的onBackPressed
,你可能需要先升级到AndroidX
.
这里可能有人会问AndroidX
是什么?
简单来讲AndroidX
就是一个与平台解绑的appcompat
(低版本兼容高版本功能)库,也就是说在build.gradle
中不需要再与compileSdkVersion
写成一样,例如之前这样的写法:
compile 'com.android.support:appcompat-v7:24.+'
(注:使用24.+则表明使用 24. 开头的版本的最新版本,若直接使用+号则表明直接使用该库的最新版本。
现在可以写成:
implementation 'androidx.appcompat:appcompat:1.1.0-alpha02'
(注:新的依赖方式implementation
与compile
功能相同,但是implementation
无法在该模块内引用依赖的依赖,但compile
可以,这么做的好处是可以加快编译速度。新的依赖方式api
与compile
完全相同,只是换了名字而已)
在Android Studo
中的Refactor->Migrate to AndroidX
的选点击之后即可将项目迁移到AndroidX
,在确认的时会提示你将项目备份以免迁移失败时丢失原有项目,通常情况下不会迁移失败,只是迁移的过程会花费很多的时间,如果项目很大,迁移时间会很长,这时即使Android Studio
的CPU
利用率为0
也不要关闭, 但是如果发生迁移失败,这时候就需要手动迁移了。
一些使用gradle
依赖的一些第三方库中的某些类可能继承了android.support.v4
包下的Fragment
,但迁移到AndroidX
后appcompat
的Fragment
变成了androidx.fragment.app
包下,原有的代码下会画红线,Android Studio
也会警告你出现错误,但是不用担心,依然可以正常编译,Android Studio
在编译的时候会自动完成基类的替换,但前提是你要确保你项目里的gradle.properties
进行了如下设置。
android.useAndroidX=true
android.enableJetifier=tru
为了消除这些难看的红线,你可以直接将新的Fragment
使用这种方式强制转换成原有的Fragment
。
TextureSupportMapFragment mapFragment = TextureSupportMapFragment
.class.cast(getChildFragmentManager()
.findFragmentById(R.id.map_view));
同理,也可以将旧的Fragment
强制类型转换成新的Fragment
.
Fragment f = Fragment.class.cast(mapFragment);
(注:上面的TextureSupportMapFragment
是一个典型案例,他是高德地图SDK
中的Fragment
,本身已经继承了v4包下的Fragment
,可以用过上面的转换来使他兼容AndroidX
)
差点扯远了,搞定AndroidX
后,我们就可以使用FragmentActivity
的addOnBackPressedCallback
方法为你的Fragment
提供拦截OnBackPressed
的功能了.
public void addOnBackPressedCallback(@NonNull LifecycleOwner owner,
@NonNull OnBackPressedCallback onBackPressedCallback)
OnBackPressedCallback#handleOnBackPressed
需要返回一个boolean
值。如果你在这个回调里拦截了onBackPressed
应该返回true
,说明你自己已经处理了本次返回键按下的操作,这样你的Fragment
就不会被弹出返回栈了。
值得注意的是,这个函数的第一个参数,一个LifecycleOwner
,Activity
和Fragment
都是LifecycleOwner
,用于提供组件的生命周期,这个参数可以帮我们自动管理OnBackPressedCallback
回调,你无需手动将他从Activity
中移除,在LifecycleOwner
的ON_DESTROY
事件来到的时候,他会被自动移除列表,你无需担心内存泄漏,框架会帮你完成这些事情。
/**
- Interface for handling {@link ComponentActivity#onBackPressed()} callbacks without
- strongly coupling that implementation to a subclass of {@link ComponentActivity}.
- @see ComponentActivity#addOnBackPressedCallback(LifecycleOwner, OnBackPressedCallback)
- @see ComponentActivity#removeOnBackPressedCallback(OnBackPressedCallback)
*/
public interface OnBackPressedCallback {
/** - Callback for handling the {@link ComponentActivity#onBackPressed()} event.
- @return True if you handled the {@link ComponentActivity#onBackPressed()} event. No
- further {@link OnBackPressedCallback} instances will be called if you return true.
*/
boolean handleOnBackPressed();
}
我们可以看到Activity
内管理的OnBackPressedCallback
的执行循序与添加时间有关.最后被添加进去的能最先得到执行.
public void addOnBackPressedCallback(@NonNull LifecycleOwner owner,
@NonNull OnBackPressedCallback onBackPressedCallback) {
Lifecycle lifecycle = owner.getLifecycle();
if (lifecycle.getCurrentState() == Lifecycle.State.DESTROYED) {
// Already destroyed, nothing to do
最后
针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
-
Android前沿技术大纲
-
全套体系化高级架构视频
**Android高级架构资料、源码、笔记、视频
。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。**