如何将复杂的、不可序列化的对象传递给 android 个片段
How to pass complex, non serializable object to android fragments
你好 Android 开发人员,
我想知道你们如何将复杂的不可序列化(和不可打包)对象传递给片段。 (例如 Listener、Api 客户端、...)
让我解释一下我的用例:
用例
我正在构建一个 Android 应用程序,它由一个 "host" activity 和 3 个片段组成。
目前我正在使用片段上的自定义构造函数传递对象(我知道这是不好的做法)。
片段构造函数如下所示:
/**
* Do not remove ever or you'll face RuntimeException
*/
public FirstFragment() {
}
public FirstFragment(Session session,
ApiClient apiClient,
FirebaseAnalytics firebaseAnalytics) {
mSession = session;
mApiClient = apiClient;
mFirebaseAnalytics = firebaseAnalytics;
}
我在主机中使用它们 activity 就像这样
private FirstFragment getFirstFragment() {
if (mFirstFragment == null) {
mFirstFragment = new FirstFragment(mSession, mApiClient, mFirebaseAnalytics);
}
return mHomeFragment;
}
[...]
private void loadFragment(Fragment fragment, String tag) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_container, fragment, tag);
transaction.commit();
}
[...]
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case FIRST_FRAGMENT_RES_ID:
toolbar.setTitle(R.string.first_fragment_title);
loadFragment(getFirstFragment(), "first_fragment");
return true;
[...]
}
return false;
}
};
此解决方案几乎在所有时间都运行良好。但有时(我不知道确切的时间)默认构造函数被调用,因此所有本地成员都是空的。
可能的解决方案
为了解决这个问题,我正在考虑以下解决方案:
单身,到处都是单身
我传递的大多数对象都是单例,因此我可以在片段的默认构造函数中访问它们:
public FirstFragment() {
mSession = Session.getInstance(getContext());
mApiClient = ApiClient.getInstance(getContext());
mFirebaseAnalytics = FirebaseAnalytics.getInstance(getContext());
}
问题
但是,如果我需要传递回调或其他东西,上述解决方案将不起作用。那怎么能这样呢?
使用父对象访问对象activity
我认为这是最丑陋的解决方案之一,因为它将 Fragments 耦合到父级 activity。思路是这样的
public FirstFragment() {
mSession = Session.getInstance(getContext());
mApiClient = ApiClient.getInstance(getContext());
mFirebaseAnalytics = FirebaseAnalytics.getInstance(getContext());
mListener = (Listener) getActivity(); // <- will works because parent activity implement the interface
}
使用广播和接收器
想法是在任何地方都传递单例并使用广播和接收器而不是监听器。
你们是如何处理这种情况的?
提前致谢!
您可能想研究依赖注入(使用像 Dagger 或其他工具这样的工具),尤其是对于像 Api 客户端这样的对象。 Post 设置,您只需定义一次,即可构建 Api 客户端实例。以后你几乎可以在任何地方用一行语句使用它。该实例保证在片段实例化时可用。延伸阅读:https://dagger.dev/tutorial/
根据您的用例,使用 ViewModel
并将您的对象存储在那里可能更容易。您的 ViewModel 将在您的片段和主机之间共享
activity。
参见 https://developer.android.com/topic/libraries/architecture/viewmodel
您是否考虑过使用 "Shared" ViewModel?
本质上,ViewModel
的子class(class 旨在以生命周期意识的方式存储和管理与活动和活动相关的 UI 相关数据片段)可以像下面这样创建,
class SharedViewModel : ViewModel()
在此 class 中,您可以拥有具有正确状态的自定义对象
接下来,在您的第一个片段中,您可以获得此 SharedView 模型的句柄,如下所示,
class MasterFragment : Fragment() {
private lateinit var model: SharedViewModel
并使用下面的代码获取它的句柄,
model = activity?.run {
ViewModelProviders.of(this)[SharedViewModel::class.java]
}
您可以在 SharedViewModel
中编写自己的 logic/method/flow 来操纵任何自定义对象的状态。
完成所有这些后,在您的第二个片段中,您可以创建 SharedViewModel
的句柄,类似于上面的代码并使用 SharedViewModel
对象,您可以检索 "modified" 自定义来自同一 SharedViewModel
的对象
几个月过去了,我现在想出了一个不同的解决方案。
为UI相关数据
对于 UI 相关的东西,我现在使用 androidx livedata
对于复杂的不可序列化数据
我的用例是将复杂的 object 传递给片段,例如管理器、parent activity(通过侦听器)等...我采用的方法是通过从 parent activity.
手动注入这些数据
首先要做的是从片段构造函数中删除 objects 并改用默认构造函数,这样我就不会遇到任何实例化错误。
然后我在片段 类 上创建了一个 inject() 方法,如下所示:
public void inject(BillingManager billingManager, Listener listener) {
mBillingManager = billingManager;
mListener = listener;
}
每个片段都有自己的注入方法宽度 object 应作为参数注入。
在 parent activity 中,我重写了 onAttachFragment() 方法来处理片段附加过程:
@Override
public void onAttachFragment(@NonNull Fragment fragment) {
super.onAttachFragment(fragment);
if (fragment.getClass().equals(FirstFragment.class)) {
((FirstFragment) fragment).inject(mBillingManager, this);
} else if (fragment.getClass().equals(HomeFragment.class)) {
((HomeFragment) fragment).inject(this);
}
}
简单,现在一切正常。
你好 Android 开发人员,
我想知道你们如何将复杂的不可序列化(和不可打包)对象传递给片段。 (例如 Listener、Api 客户端、...)
让我解释一下我的用例:
用例
我正在构建一个 Android 应用程序,它由一个 "host" activity 和 3 个片段组成。 目前我正在使用片段上的自定义构造函数传递对象(我知道这是不好的做法)。
片段构造函数如下所示:
/**
* Do not remove ever or you'll face RuntimeException
*/
public FirstFragment() {
}
public FirstFragment(Session session,
ApiClient apiClient,
FirebaseAnalytics firebaseAnalytics) {
mSession = session;
mApiClient = apiClient;
mFirebaseAnalytics = firebaseAnalytics;
}
我在主机中使用它们 activity 就像这样
private FirstFragment getFirstFragment() {
if (mFirstFragment == null) {
mFirstFragment = new FirstFragment(mSession, mApiClient, mFirebaseAnalytics);
}
return mHomeFragment;
}
[...]
private void loadFragment(Fragment fragment, String tag) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_container, fragment, tag);
transaction.commit();
}
[...]
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case FIRST_FRAGMENT_RES_ID:
toolbar.setTitle(R.string.first_fragment_title);
loadFragment(getFirstFragment(), "first_fragment");
return true;
[...]
}
return false;
}
};
此解决方案几乎在所有时间都运行良好。但有时(我不知道确切的时间)默认构造函数被调用,因此所有本地成员都是空的。
可能的解决方案
为了解决这个问题,我正在考虑以下解决方案:
单身,到处都是单身
我传递的大多数对象都是单例,因此我可以在片段的默认构造函数中访问它们:
public FirstFragment() {
mSession = Session.getInstance(getContext());
mApiClient = ApiClient.getInstance(getContext());
mFirebaseAnalytics = FirebaseAnalytics.getInstance(getContext());
}
问题
但是,如果我需要传递回调或其他东西,上述解决方案将不起作用。那怎么能这样呢?
使用父对象访问对象activity
我认为这是最丑陋的解决方案之一,因为它将 Fragments 耦合到父级 activity。思路是这样的
public FirstFragment() {
mSession = Session.getInstance(getContext());
mApiClient = ApiClient.getInstance(getContext());
mFirebaseAnalytics = FirebaseAnalytics.getInstance(getContext());
mListener = (Listener) getActivity(); // <- will works because parent activity implement the interface
}
使用广播和接收器
想法是在任何地方都传递单例并使用广播和接收器而不是监听器。
你们是如何处理这种情况的?
提前致谢!
您可能想研究依赖注入(使用像 Dagger 或其他工具这样的工具),尤其是对于像 Api 客户端这样的对象。 Post 设置,您只需定义一次,即可构建 Api 客户端实例。以后你几乎可以在任何地方用一行语句使用它。该实例保证在片段实例化时可用。延伸阅读:https://dagger.dev/tutorial/
根据您的用例,使用 ViewModel
并将您的对象存储在那里可能更容易。您的 ViewModel 将在您的片段和主机之间共享
activity。
参见 https://developer.android.com/topic/libraries/architecture/viewmodel
您是否考虑过使用 "Shared" ViewModel?
本质上,ViewModel
的子class(class 旨在以生命周期意识的方式存储和管理与活动和活动相关的 UI 相关数据片段)可以像下面这样创建,
class SharedViewModel : ViewModel()
在此 class 中,您可以拥有具有正确状态的自定义对象
接下来,在您的第一个片段中,您可以获得此 SharedView 模型的句柄,如下所示,
class MasterFragment : Fragment() {
private lateinit var model: SharedViewModel
并使用下面的代码获取它的句柄,
model = activity?.run {
ViewModelProviders.of(this)[SharedViewModel::class.java]
}
您可以在 SharedViewModel
中编写自己的 logic/method/flow 来操纵任何自定义对象的状态。
完成所有这些后,在您的第二个片段中,您可以创建 SharedViewModel
的句柄,类似于上面的代码并使用 SharedViewModel
对象,您可以检索 "modified" 自定义来自同一 SharedViewModel
几个月过去了,我现在想出了一个不同的解决方案。
为UI相关数据
对于 UI 相关的东西,我现在使用 androidx livedata
对于复杂的不可序列化数据
我的用例是将复杂的 object 传递给片段,例如管理器、parent activity(通过侦听器)等...我采用的方法是通过从 parent activity.
手动注入这些数据首先要做的是从片段构造函数中删除 objects 并改用默认构造函数,这样我就不会遇到任何实例化错误。
然后我在片段 类 上创建了一个 inject() 方法,如下所示:
public void inject(BillingManager billingManager, Listener listener) {
mBillingManager = billingManager;
mListener = listener;
}
每个片段都有自己的注入方法宽度 object 应作为参数注入。
在 parent activity 中,我重写了 onAttachFragment() 方法来处理片段附加过程:
@Override
public void onAttachFragment(@NonNull Fragment fragment) {
super.onAttachFragment(fragment);
if (fragment.getClass().equals(FirstFragment.class)) {
((FirstFragment) fragment).inject(mBillingManager, this);
} else if (fragment.getClass().equals(HomeFragment.class)) {
((HomeFragment) fragment).inject(this);
}
}
简单,现在一切正常。