mActivity from Fragment.onAttach() not retained

2019-08-21 06:21发布

I have setup a Observable/Subscriber with RxJava. The Observable is created in MainActivity. The Subscriber is a android.support.v4.app.Fragmentcalled 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

2条回答
ら.Afraid
2楼-- · 2019-08-21 07:02

Clearly this line is a problem:

mTableLayout = (TableLayout) mActivity.findViewById(R.id.fragment_table_layout);

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:

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; 
}

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.

查看更多
Lonely孤独者°
3楼-- · 2019-08-21 07:05

You have a problem because you're presuming that onNext will be called after onAttach, but as you've noticed, that's not necessarily the case. I would say you have two options:

  1. subscribe to the observable in the onAttach and not before, that way you're making sure that the onNext will be called after;

  2. in onNext check if the activity is null and if it is, save the data for later. then in onAttach 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 like if (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().

查看更多
登录 后发表回答