ActivityLifecycleCallbacks-公共API中的盲点



从小开始,我喜欢阅读说明。 我长大了,但仍然令我惊讶的是,成年人对指令的粗心大意:他们中的许多人认为每个人都知道,并且同时使用一两个功能,而其中有很多功能! 你们中有多少人用来保持微波炉的温度? 她几乎在每个人中。

一旦我决定阅读有关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 -> // handle response here } // start Activity inside StartingFragment } } } private fun findOrCreateFragment( fragmentManager: FragmentManager ): StartingFragment { val tag = StartingFragment::class.java.name return fragmentManager .findFragmentByTag(tag) as StartingFragment? ?: StartingFragment().apply { fragmentManager .beginTransaction() .add(this, tag) .commit() } } } 

在该示例中,我们只是放下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`() { // prepare val mockTool: CoolTool = mock() val application = getApplicationContext<android.app.Application>() application.registerActivityLifecycleCallbacks( object : Application.ActivityLifecycleCallbacks { override fun onActivityCreated( activity: Activity, savedInstanceState: Bundle? ) { (activity as? RequireCoolTool)?.coolTool = mockTool } }) // invoke launch<DIActivity>(Intent(application, DIActivity::class.java)) // assert verify(mockTool).extraInfo } } 

但是在大型项目中,这种方法无法很好地扩展,因此我不会从任何人手中夺走任何DI框架。 更好地结合努力并实现协同增效的地方。 我将向您展示Dagger2的示例。 如果项目中有一些基本的Activity执行类似AndroidInjection.inject(this)的操作,那么该丢掉它了。 而是,请执行以下操作:
  1. 根据说明,我们在Application中实现DispatchingAndroidInjector;
  2. 创建一个ActivityLifecycleCallbacks,以在每个Activity上调用DispatchingAndroidInjector.maybeInject();
  3. 在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帮助改善我们的应用程序相同的方式为您的项目提供帮助。

Source: https://habr.com/ru/post/zh-CN482476/


All Articles