I have setup a Observable/Subscriber with RxJava. The Observable is created in MainActivity. The Subscriber is a android.support.v4.app.Fragment
called MyFragment. The Observable gets data from RESTful service and stores the data in a SQLite db on the device. This works. When its work is done, which takes 4 or 5 seconds such that MyFragment has already processed onCreate()
, onCreateView()
etc., the subscriber, MyFragment, is notified via its implemented onNext()
method (this works) and is then reads data from the SQLite db (this also works) and is the supposed to populate a view (this does not work).
The problem is that my class member mActivity is null in the method loadDataFromSQLite()
. The lesson I think I am learning is that nothing with a Fragment
can be done outside of methods in the Fragment class (from onAttach()
to onDestroy()
)
That being the case, how do I do this?
See below for code snippets:
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>
Edit:
I should add the I also tried calling getActivity()
in place of mActivity
, but it returns null
Clearly this line is a problem:
This means you are casting a view that doesn't belong to that Fragment, in that Fragment. All view plumbing of a Fragment (including finding that TableLayout) should be done in
Fragment.onCreateView
. Later you can use their references elsewhere in the Fragment, like this:Another thing is that you cannot configure one of two processes to update the other one simply because you expect it to take longer. This is unreliable and this is what you do by calling a REST API to update a fragment that you're not sure has been created. You can force the creation of a Fragment by invoking
FragmentManager.executePendingTransactions()
but you still need to prepare for the case when the fragment is already gone when REST response arrives.It is also important to note that keeping a reference to an Activity is a bad idea. You should always use
getActivity
in a Fragment. If at any point it returns null then it means the Activity the Fragment is anchored to is not yet created or already destroyed.You have a problem because you're presuming that
onNext
will be called afteronAttach
, but as you've noticed, that's not necessarily the case. I would say you have two options:subscribe to the observable in the
onAttach
and not before, that way you're making sure that theonNext
will be called after;in
onNext
check if the activity is null and if it is, save the data for later. then inonAttach
check if you have the data and if yes, visualize it. that way you will be able to save some time by loading the data concurrently with initialization of the fragment, but you will have to have a method that will be called twice with inside something likeif (getActivity != null && mData != null) { ... }
.So, the first one is more elegant, but the second might be more efficient.
PS. obviously, all findViewById should be done in
onCreateView()
.