使用静态字段创建 Singleton class 以在 Android 中的活动之间进行通信是否有任何缺点?
Are there any cons of creating Singleton class with static field to communicate between activities in Android?
有3个活动:A->B->C。每个包含一个Button
(打开下一个activity)和一个EditText
.
例如:如果我在 C 中键入一些文本并返回到 A(通过按返回按钮),我如何才能在那里看到相同的文字?
我知道 3 个解决方案:
- LocalBroadcastManager
- 共享首选项
- 使用静态字段创建单例 class,然后在 A 的
onStart
方法中获取此字段 - 此解决方案的缺点?
只需将 class A 的 onResume() 方法设置为调用要保存的 Singleton class 实例(或相同的应用程序 class 的实例)
LocalBroadcastManager
不是可靠的选择。它假定底部 activity 仍然存在,但事实可能并非如此。当您使用 B 时,A 可能会被收集以释放资源。此外,您应该取消注册 LocalBroadcastManager 接收器 onResume()
。所以,没有。
通常要避免带有静态字段的单例。单串文本不用担心,但单例字段很容易被遗忘,并可能导致内存泄漏。如果可能,最好避免这种模式,在您的情况下就是这样。
可能的选项。
如果字段是必须持久化的,例如用户编辑他的用户名,使用 SharedPreferences
或其他存储解决方案(保存到服务器、保存到缓存、保存到设备 SQLite 数据库)。
如果字段是临时的,是activityB的结果,可以用startActivityForResult()
开始activityB,然后把结果传回activity A 到 Intent
.
对于您的问题,最简单的解决方案 - 将您的值存储在应用程序 class 中。任何 activity 都可以访问它和 read/write 值。
缺点是如果不小心将静态引用存储到activity,会导致内存泄漏。
你可以尝试使用EventBus进行横向通信:Activity->Service,Service->Fragment等。
默认情况下,它具有静态实例,而且您可以在 onPause 和 onResume 方法中对其进行 subscribe/unsubscribe。
另一个优点是粘性事件 - 你可以 post 来自服务的事件,它会等到有东西处理它 - Activity 准备好后会收到这个事件 - 在 onResume() 之后。
恕我直言,在您的应用程序中使用单例设计模式总是有缺点。其中一些是(从我的头顶):
- 在其他方面不相关的对象之间的耦合和流经 Singleton 的实例
- "global state" 的出现,这使得调试变得更加困难
- 无法通过 "conventional" 模拟
模拟静态字段和方法
- 在应用程序的任何部分都可以轻松获得对 Singleton 的引用这一事实导致一团糟(人们不再考虑依赖图)
- 单身人士往往会滋生:你介绍一个,然后再介绍一个,然后你会发现自己有 10 个单身人士,这些单身人士将应用程序的状态保存在 "global cloud state"
请注意,您尝试做的事情 违反了 Android 准则 - 如果用户点击 "back" 按钮,那么他应该找到以前的 Activity
或 Fragment
与用户上次看到它时的状态完全相同,没有任何添加(除非您明确不想将其保存在后台堆栈中)。
如果您仍然想这样做,那么我可以建议几个选项:
- 使用
SharedPreferences
并将值存储在那里。获取每个 Activity
中的值并将其显示在 onResume()
中
- 使用
startActivityForResult()
调用以开始新的 Activities
并将值传回结果。请注意,默认情况下按下 "back" 会取消操作,因此您必须重写 onBackPressed()
方法。
- 覆盖
Activity
中的 onBackPressed()
方法,使其启动另一个 Activity
(而不是仅仅弹出返回堆栈)并传递 [=21 中的值=] 你用的。在这种情况下,您可能希望使用 FLAG_ACTIVITY_CLEAR_TOP。
- 使用一些支持 "sticky" 事件的事件总线。当用户输入文本时,您 post 向事件总线发送一个粘性事件。在
Activity
的 onResume()
中,您检查是否存在此类事件,如果存在 - 您更新 UI。
再一次 - 你可以做你想做的事并不意味着它应该做。
有3个活动:A->B->C。每个包含一个Button
(打开下一个activity)和一个EditText
.
例如:如果我在 C 中键入一些文本并返回到 A(通过按返回按钮),我如何才能在那里看到相同的文字?
我知道 3 个解决方案:
- LocalBroadcastManager
- 共享首选项
- 使用静态字段创建单例 class,然后在 A 的
onStart
方法中获取此字段 - 此解决方案的缺点?
只需将 class A 的 onResume() 方法设置为调用要保存的 Singleton class 实例(或相同的应用程序 class 的实例)
LocalBroadcastManager
不是可靠的选择。它假定底部 activity 仍然存在,但事实可能并非如此。当您使用 B 时,A 可能会被收集以释放资源。此外,您应该取消注册 LocalBroadcastManager 接收器onResume()
。所以,没有。通常要避免带有静态字段的单例。单串文本不用担心,但单例字段很容易被遗忘,并可能导致内存泄漏。如果可能,最好避免这种模式,在您的情况下就是这样。
可能的选项。
如果字段是必须持久化的,例如用户编辑他的用户名,使用
SharedPreferences
或其他存储解决方案(保存到服务器、保存到缓存、保存到设备 SQLite 数据库)。如果字段是临时的,是activityB的结果,可以用
startActivityForResult()
开始activityB,然后把结果传回activity A 到Intent
.
对于您的问题,最简单的解决方案 - 将您的值存储在应用程序 class 中。任何 activity 都可以访问它和 read/write 值。 缺点是如果不小心将静态引用存储到activity,会导致内存泄漏。
你可以尝试使用EventBus进行横向通信:Activity->Service,Service->Fragment等。 默认情况下,它具有静态实例,而且您可以在 onPause 和 onResume 方法中对其进行 subscribe/unsubscribe。 另一个优点是粘性事件 - 你可以 post 来自服务的事件,它会等到有东西处理它 - Activity 准备好后会收到这个事件 - 在 onResume() 之后。
恕我直言,在您的应用程序中使用单例设计模式总是有缺点。其中一些是(从我的头顶):
- 在其他方面不相关的对象之间的耦合和流经 Singleton 的实例
- "global state" 的出现,这使得调试变得更加困难
- 无法通过 "conventional" 模拟 模拟静态字段和方法
- 在应用程序的任何部分都可以轻松获得对 Singleton 的引用这一事实导致一团糟(人们不再考虑依赖图)
- 单身人士往往会滋生:你介绍一个,然后再介绍一个,然后你会发现自己有 10 个单身人士,这些单身人士将应用程序的状态保存在 "global cloud state"
请注意,您尝试做的事情 违反了 Android 准则 - 如果用户点击 "back" 按钮,那么他应该找到以前的 Activity
或 Fragment
与用户上次看到它时的状态完全相同,没有任何添加(除非您明确不想将其保存在后台堆栈中)。
如果您仍然想这样做,那么我可以建议几个选项:
- 使用
SharedPreferences
并将值存储在那里。获取每个Activity
中的值并将其显示在onResume()
中
- 使用
startActivityForResult()
调用以开始新的Activities
并将值传回结果。请注意,默认情况下按下 "back" 会取消操作,因此您必须重写onBackPressed()
方法。 - 覆盖
Activity
中的onBackPressed()
方法,使其启动另一个Activity
(而不是仅仅弹出返回堆栈)并传递 [=21 中的值=] 你用的。在这种情况下,您可能希望使用 FLAG_ACTIVITY_CLEAR_TOP。 - 使用一些支持 "sticky" 事件的事件总线。当用户输入文本时,您 post 向事件总线发送一个粘性事件。在
Activity
的onResume()
中,您检查是否存在此类事件,如果存在 - 您更新 UI。
再一次 - 你可以做你想做的事并不意味着它应该做。