
从小开始,我喜欢阅读说明。 我长大了,但仍然令我惊讶的是,成年人对指令的粗心大意:他们中的许多人认为每个人都知道,并且同时使用一两个功能,而其中有很多功能! 你们中有多少人用来保持微波炉的温度? 她几乎在每个人中。
一旦我决定阅读有关Android框架各个类的文档。 我浏览了以下主要类:View,Activity,Fragment,Application,并且我对
Application.registerActivityLifecycleCallbacks()方法和
ActivityLifecycleCallbacks接口非常感兴趣。 在Internet上使用它的示例中,没有什么比记录Activity生命周期更好。 然后我开始自己进行实验,现在进入Yandex.Money,Money在从外部解决与Activity对象的影响相关的所有任务时都积极使用它。
什么是ActivityLifecycleCallbacks?
看一下这个接口,它出现在API 14中的样子:
public interface ActivityLifecycleCallbacks { void onActivityCreated(Activity activity, Bundle savedInstanceState); void onActivityStarted(Activity activity); void onActivityResumed(Activity activity); void onActivityPaused(Activity activity); void onActivityStopped(Activity activity); void onActivitySaveInstanceState(Activity activity, Bundle outState); void onActivityDestroyed(Activity activity); }
从API 29开始,已添加了几个新方法。 public interface ActivityLifecycleCallbacks { default void onActivityPreCreated( @NonNull Activity activity, @Nullable Bundle savedInstanceState) { } void onActivityCreated( @NonNull Activity activity, @Nullable Bundle savedInstanceState); default void onActivityPostCreated( @NonNull Activity activity, @Nullable Bundle savedInstanceState) { } default void onActivityPreStarted(@NonNull Activity activity) { } void onActivityStarted(@NonNull Activity activity); default void onActivityPostStarted(@NonNull Activity activity) { } default void onActivityPreResumed(@NonNull Activity activity) { } void onActivityResumed(@NonNull Activity activity); default void onActivityPostResumed(@NonNull Activity activity) { } default void onActivityPrePaused(@NonNull Activity activity) { } void onActivityPaused(@NonNull Activity activity); default void onActivityPostPaused(@NonNull Activity activity) { } default void onActivityPreStopped(@NonNull Activity activity) { } void onActivityStopped(@NonNull Activity activity); default void onActivityPostStopped(@NonNull Activity activity) { } default void onActivityPreSaveInstanceState( @NonNull Activity activity, @NonNull Bundle outState) { } void onActivitySaveInstanceState( @NonNull Activity activity, @NonNull Bundle outState); default void onActivityPostSaveInstanceState( @NonNull Activity activity, @NonNull Bundle outState) { } default void onActivityPreDestroyed(@NonNull Activity activity) { } void onActivityDestroyed(@NonNull Activity activity); default void onActivityPostDestroyed(@NonNull Activity activity) { } }
也许这个接口很少受到关注,因为它仅出现在Android 4.0 ICS中。 但是徒劳无功,因为它允许您本机执行一件非常有趣的事情:从外部影响所有Activity对象。 但是稍后会更多,首先请仔细研究这些方法。
每种方法都显示与Activity生命周期类似的方法,并且在应用程序中的任何Activity上触发该方法时即会被调用。 也就是说,如果使用MainActivity启动该应用程序,则第一个应用程序将收到对ActivityLifecycleCallback.onActivityCreated(MainActivity,null)的调用。
很好,但是如何运作? 这里没有魔术:活动本身报告他们处于何种状况。 这是来自Activity.onCreate()的一段代码:
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.fragments : null); } mFragments.dispatchCreate(); getApplication().dispatchActivityCreated(this, savedInstanceState); if (mVoiceInteractor != null) {
看来我们自己做了BaseActivity。 只有Android的同事为我们做到了这一点,并且还迫使每个人都必须使用它。 这非常好!
在API 29中,这些方法的工作原理几乎相同,但是在特定方法之前和之后,会诚实地调用它们的Pre和Post副本。 现在可能是由ActivityManager控制的,但这只是我的猜测,因为我没有深入了解源代码。
如何使ActivityLifecycleCallbacks工作?
像所有回调一样,您必须首先注册它们。 我们在Application.onCreate()中注册了所有ActivityLifecycleCallbacks,因此我们可以获得有关所有Activity及其管理能力的信息。
class MyApplication : Application() { override fun onCreate() { super.onCreate() registerActivityLifecycleCallbacks(MyCallbacks()) } }
一个小小的题外话:从API 29开始,ActivityLifecycleCallbacks也可以从Activity内注册。 这将是仅适用于此活动的
本地回调 。
仅此而已。 但您只需在搜索框中输入名称ActivityLifecycleCallbacks即可找到它。 有许多记录“活动”生命周期的示例,但这很有趣吗? 活动有许多公共方法(大约有400种),所有这些都可以用来做很多有趣和有用的事情。
该怎么办?
你要什么 是否要在应用程序的所有Activity中动态更改主题? 请:setTheme()方法是公共的,这意味着您可以从ActivityLifecycleCallback调用它:
class ThemeCallback( @StyleRes val myTheme: Int ) : ActivityLifecycleCallbacks { override fun onActivityCreated( activity: Activity, savedInstanceState: Bundle? ) { activity.setTheme(myTheme) } }
仅在家中重复此技巧连接的库中的某些活动可以使用其自定义主题。 因此,请检查软件包或任何其他可以确定可以安全更改此Activity主题的症状。 例如,我们检查这样的软件包(在Kotlinovsky =中):
class ThemeCallback( @StyleRes val myTheme: Int ) : ActivityLifecycleCallbacks { override fun onActivityCreated( activity: Activity, savedInstanceState: Bundle? ) { val myPackage = "my.cool.application" activity .takeIf { it.javaClass.name.startsWith(myPackage) } ?.setTheme(myTheme) } }
这个例子行不通吗? 您可能忘记了在应用程序或AndroidManifest中的应用程序中注册ThemeCallback。
想要另一个有趣的例子吗? 您可以在应用程序中的任何活动上显示对话框。
class DialogCallback( val dialogFragment: DialogFragment ) : Application.ActivityLifecycleCallbacks { override fun onActivityCreated( activity: Activity, savedInstanceState: Bundle? ) { if (savedInstanceState == null) { val tag = dialogFragment.javaClass.name (activity as? AppCompatActivity) ?.supportFragmentManager ?.also { fragmentManager -> if (fragmentManager.findFragmentByTag(tag) == null) { dialogFragment.show(fragmentManager, tag) } } } } }
仅在家中重复此技巧当然,您不应在每个屏幕上都显示对话-我们的用户不会为此而爱我们。 但是有时在某些特定屏幕上显示类似内容可能会很有用。
这是另一种情况:如果我们需要启动Activity该怎么办呢,这里的一切都很简单:Activity.startActivity()-并将其驱动。 但是,如果我们需要在调用Activity.startActivityForResult()之后等待结果呢? 我有一个食谱:
class StartingActivityCallback : Application.ActivityLifecycleCallbacks { override fun onActivityCreated( activity: Activity, savedInstanceState: Bundle? ) { if (savedInstanceState == null) { (activity as? AppCompatActivity) ?.supportFragmentManager ?.also { fragmentManager -> val startingFragment = findOrCreateFragment(fragmentManager) startingFragment.listener = { resultCode, data ->
在该示例中,我们只是放下Fragment,这将启动Activity并获取结果,然后将其处理委托给我们。 注意:在这里,我们检查我们的Activity是否为AppCompatActivity,这会导致无限循环。 使用其他条件。
让我们使例子复杂化。 在那之前,我们仅使用了Activity中已经存在的那些方法。 如何添加自己的? 假设我们要发送有关打开屏幕的分析。 同时,我们的屏幕具有自己的名称。 如何解决这个问题? 很简单 创建一个可以提供屏幕名称的屏幕界面:
interface Screen { val screenName: String }
现在,我们在所需的Activity中实现它:
class NamedActivity : Activity(), Screen { override val screenName: String = "First screen" }
之后,我们为此类活动设置特殊的ActivityLifecycleCallbacks:
class AnalyticsActivityCallback( val sendAnalytics: (String) -> Unit ) : Application.ActivityLifecycleCallbacks { override fun onActivityCreated( activity: Activity, savedInstanceState: Bundle? ) { if (savedInstanceState == null) { (activity as? Screen)?.screenName?.let(sendAnalytics) } } }
看吗 我们只检查界面,如果实施了,就发送分析数据。
重复进行固定。 如果您需要抛出更多参数怎么办? 扩展接口:
interface ScreenWithParameters : Screen { val parameters: Map<String, String> }
我们实施:
class NamedActivity : Activity(), ScreenWithParameters { override val screenName: String = "First screen" override val parameters: Map<String, String> = mapOf("key" to "value") }
我们运送:
class AnalyticsActivityCallback( val sendAnalytics: (String, Map<String, String>?) -> Unit ) : Application.ActivityLifecycleCallbacks { override fun onActivityCreated( activity: Activity, savedInstanceState: Bundle? ) { if (savedInstanceState == null) { (activity as? Screen)?.screenName?.let { name -> sendAnalytics( name, (activity as? ScreenWithParameters)?.parameters ) } } } }
但这仍然很容易。 所有这些只是将您带入一个非常有趣的主题:本机依赖项注入。 是的,我们有Dagger,Koin,Guice,Kodein等。 但是在小型项目中,它们是多余的。 但是我有一个解决方案...猜猜哪一个?
假设我们有一些这样的工具:
class CoolToolImpl { val extraInfo = "i am dependency" }
使用界面将其关闭,就像成人程序员一样:
interface CoolTool { val extraInfo: String } class CoolToolImpl : CoolTool { override val extraInfo = "i am dependency" }
现在,从ActivityLifecycleCallbacks获得一些街头魔术:我们将创建一个接口来实现此依赖关系,在所需的Activity中实现它,并使用ActivityLifecycleCallbacks找到并实现CoolToolImpl实现。
interface RequireCoolTool { var coolTool: CoolTool } class CoolToolActivity : Activity(), RequireCoolTool { override lateinit var coolTool: CoolTool } class InjectingLifecycleCallbacks : ActivityLifecycleCallbacks { override fun onActivityCreated( activity: Activity, savedInstanceState: Bundle? ) { (activity as? RequireCoolTool)?.coolTool = CoolToolImpl() } }
记住要在您的应用程序中注册InjectingLifecycleCallbacks,运行它,然后它才能工作。
并且不要忘记测试:
@RunWith(AndroidJUnit4::class) class DIActivityTest { @Test fun `should access extraInfo when created`() {
但是在大型项目中,这种方法无法很好地扩展,因此我不会从任何人手中夺走任何DI框架。 更好地结合努力并实现协同增效的地方。 我将向您展示Dagger2的示例。 如果项目中有一些基本的Activity执行类似AndroidInjection.inject(this)的操作,那么该丢掉它了。 而是,请执行以下操作:
- 根据说明,我们在Application中实现DispatchingAndroidInjector;
- 创建一个ActivityLifecycleCallbacks,以在每个Activity上调用DispatchingAndroidInjector.maybeInject();
- 在Application中注册ActivityLifecycleCallbacks。
class MyApplication : Application() { @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any> override fun onCreate() { super.onCreate() DaggerYourApplicationComponent.create().inject(this); registerActivityLifecycleCallbacks( InjectingLifecycleCallbacks( dispatchingAndroidInjector ) ) } } class InjectingLifecycleCallbacks( val dispatchingAndroidInjector: DispatchingAndroidInjector<Any> ) : ActivityLifecycleCallbacks { override fun onActivityCreated( activity: Activity, savedInstanceState: Bundle? ) { dispatchingAndroidInjector.maybeInject(activity) } }
使用其他DI框架也可以达到相同的效果。 尝试在评论中写下发生的事情。
总结一下
ActivityLifecycleCallbacks是一个被低估的功能强大的工具。 请尝试
以下示例之一 ,让它们以与Yandex.Money帮助改善我们的应用程序相同的方式为您的项目提供帮助。