来自 Fragment.onAttach() 的 mActivity 未保留
mActivity from Fragment.onAttach() not retained
我已经用 RxJava 设置了 Observable/Subscriber。 Observable 是在 MainActivity 中创建的。订阅者是一个名为 MyFragment 的 android.support.v4.app.Fragment
。 Observable 从 RESTful 服务获取数据并将数据存储在设备上的 SQLite 数据库中。这行得通。当它的工作完成时,这需要 4 或 5 秒,这样 MyFragment 已经处理了 onCreate()
、onCreateView()
等,订阅者 MyFragment 将通过其实现的 onNext()
方法(这个有效)然后从 SQLite 数据库中读取数据(这也有效)并且应该填充视图(这不起作用)。
问题是我的class成员mActivity在方法loadDataFromSQLite()
中为空。我想我正在学习的教训是,在片段 class(从 onAttach()
到 onDestroy()
)
中的方法之外,不能用 Fragment
做任何事情
既然如此,我该怎么做呢?
请参阅下面的代码片段:
MyFragment.java
public class MyFragment extends Fragment implements Observer<String> {
private Activity mActivity;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
...
@Override
public void onNext(String string) {
loadDataFromSQLite();
}
public void loadDataFromSQLite() {
<get data from SQLite>
// now want to populate view
// mActivity is null
mTableLayout = (TableLayout) mActivity.findViewById(R.id.fragment_table_layout);
}
}
fragment_table_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/table_wrapper"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="none"
tools:ignore="UselessParent">
<TableLayout
android:id="@+id/fragment_table_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- fill in data in MyFragment.java -->
</TableLayout>
</ScrollView>
</RelativeLayout>
编辑:
我应该添加我也尝试调用 getActivity()
代替 mActivity
,但它 returns null
很明显这一行有问题:
mTableLayout = (TableLayout) mActivity.findViewById(R.id.fragment_table_layout);
这意味着您正在投射不属于该片段的视图,在该片段中。 Fragment 的所有视图管道(包括查找 TableLayout)都应在 Fragment.onCreateView
中完成。稍后您可以在 Fragment 的其他地方使用它们的引用,如下所示:
TableLayout mTableLayout;
@Override
public View onCreateView(LayoutInflater inflater, ...) {
View view = inflater.inflate(R.layout.fragment_layout, container, false);
mTableLayout = (TableLayout) view.findViewById(R.id.fragment_table_layout);
...
return view;
}
另一件事是,您不能仅仅因为您希望它花费更长的时间就配置两个进程中的一个来更新另一个。这是不可靠的,这就是您通过调用 REST API 来更新您不确定是否已创建的片段所做的事情。您可以通过调用 FragmentManager.executePendingTransactions()
强制创建片段,但您仍然需要为 REST 响应到达时片段已经消失的情况做好准备。
同样重要的是要注意,保留对 Activity 的引用是一个坏主意。您应该始终在片段中使用 getActivity
。如果在任何时候它 returns null 那么它意味着 Activity 片段锚定到尚未创建或已经销毁。
您遇到问题是因为您假设 onNext
将在 onAttach
之后调用,但正如您所注意到的,情况不一定如此。我会说你有两个选择:
订阅 onAttach
中的可观察对象,而不是之前,这样您就可以确保 onNext
将在之后调用;
in onNext
检查 activity 是否为空,如果是,保存数据以备后用。然后在 onAttach
中检查您是否有数据,如果有,将其可视化。这样你就可以通过在片段初始化的同时加载数据来节省一些时间,但是你必须有一个方法,它会在 if (getActivity != null && mData != null) { ... }
.
[ 之类的内部调用两次。 =31=]
所以,第一个更优雅,但第二个可能更高效。
PS。显然,所有 findViewById 都应该在 onCreateView()
.
中完成
我已经用 RxJava 设置了 Observable/Subscriber。 Observable 是在 MainActivity 中创建的。订阅者是一个名为 MyFragment 的 android.support.v4.app.Fragment
。 Observable 从 RESTful 服务获取数据并将数据存储在设备上的 SQLite 数据库中。这行得通。当它的工作完成时,这需要 4 或 5 秒,这样 MyFragment 已经处理了 onCreate()
、onCreateView()
等,订阅者 MyFragment 将通过其实现的 onNext()
方法(这个有效)然后从 SQLite 数据库中读取数据(这也有效)并且应该填充视图(这不起作用)。
问题是我的class成员mActivity在方法loadDataFromSQLite()
中为空。我想我正在学习的教训是,在片段 class(从 onAttach()
到 onDestroy()
)
Fragment
做任何事情
既然如此,我该怎么做呢?
请参阅下面的代码片段:
MyFragment.java
public class MyFragment extends Fragment implements Observer<String> {
private Activity mActivity;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
...
@Override
public void onNext(String string) {
loadDataFromSQLite();
}
public void loadDataFromSQLite() {
<get data from SQLite>
// now want to populate view
// mActivity is null
mTableLayout = (TableLayout) mActivity.findViewById(R.id.fragment_table_layout);
}
}
fragment_table_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/table_wrapper"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="none"
tools:ignore="UselessParent">
<TableLayout
android:id="@+id/fragment_table_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- fill in data in MyFragment.java -->
</TableLayout>
</ScrollView>
</RelativeLayout>
编辑:
我应该添加我也尝试调用 getActivity()
代替 mActivity
,但它 returns null
很明显这一行有问题:
mTableLayout = (TableLayout) mActivity.findViewById(R.id.fragment_table_layout);
这意味着您正在投射不属于该片段的视图,在该片段中。 Fragment 的所有视图管道(包括查找 TableLayout)都应在 Fragment.onCreateView
中完成。稍后您可以在 Fragment 的其他地方使用它们的引用,如下所示:
TableLayout mTableLayout;
@Override
public View onCreateView(LayoutInflater inflater, ...) {
View view = inflater.inflate(R.layout.fragment_layout, container, false);
mTableLayout = (TableLayout) view.findViewById(R.id.fragment_table_layout);
...
return view;
}
另一件事是,您不能仅仅因为您希望它花费更长的时间就配置两个进程中的一个来更新另一个。这是不可靠的,这就是您通过调用 REST API 来更新您不确定是否已创建的片段所做的事情。您可以通过调用 FragmentManager.executePendingTransactions()
强制创建片段,但您仍然需要为 REST 响应到达时片段已经消失的情况做好准备。
同样重要的是要注意,保留对 Activity 的引用是一个坏主意。您应该始终在片段中使用 getActivity
。如果在任何时候它 returns null 那么它意味着 Activity 片段锚定到尚未创建或已经销毁。
您遇到问题是因为您假设 onNext
将在 onAttach
之后调用,但正如您所注意到的,情况不一定如此。我会说你有两个选择:
订阅
onAttach
中的可观察对象,而不是之前,这样您就可以确保onNext
将在之后调用;in
[ 之类的内部调用两次。 =31=]onNext
检查 activity 是否为空,如果是,保存数据以备后用。然后在onAttach
中检查您是否有数据,如果有,将其可视化。这样你就可以通过在片段初始化的同时加载数据来节省一些时间,但是你必须有一个方法,它会在if (getActivity != null && mData != null) { ... }
.
所以,第一个更优雅,但第二个可能更高效。
PS。显然,所有 findViewById 都应该在 onCreateView()
.