简体   繁体   中英

Best Practices for Updating UI Thread after Running Room DB Calls?

I am trying to learn the Room Persistence database for Android, but am running into some serious conceptual snags. What I want to do is use Room to query a database and return some data. Once that is complete, I want to update the value of a TextView based on that data.

Eventually, the hope is to create a form relating to the DB, where the fields can be pre-filled with the information already in the DB.

After several hours of tinkering, I have come up with the following code that actually works, somewhat:

public class DBTestFragment extends Fragment {

public static final String FRAGMENT_TAG = "DB Test";

private DBTCompanionDatabase mDb;
private TextView mDbDumpText;

private Button mAddDaily;
private Button mRemoveDailies;

// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";

// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;


public DBTestFragment() {
    // Required empty public constructor
}

/**
 * Use this factory method to create a new instance of
 * this fragment using the provided parameters.
 *
 * @param param1 Parameter 1.
 * @param param2 Parameter 2.
 * @return A new instance of fragment DBTestFragment.
 */
// TODO: Rename and change types and number of parameters
public static DBTestFragment newInstance(String param1, String param2) {
    DBTestFragment fragment = new DBTestFragment();
    Bundle args = new Bundle();
    args.putString(ARG_PARAM1, param1);
    args.putString(ARG_PARAM2, param2);
    fragment.setArguments(args);
    return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
        mParam1 = getArguments().getString(ARG_PARAM1);
        mParam2 = getArguments().getString(ARG_PARAM2);
    }

    mDb = DBTCompanionDatabase.getDBTCompanionDatabase(this.getContext());
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View rootView =  inflater.inflate(R.layout.fragment_dbtest, container, false);

    mDbDumpText = rootView.findViewById(R.id.database_test_database_printout);

    mAddDaily = rootView.findViewById(R.id.database_test_add_dummy_daily);
    mAddDaily.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            addDaily();
        }
    });

    mRemoveDailies = rootView.findViewById(R.id.database_test_remove_dummy_dailies);
    /*
    mRemoveDailies.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            removeDailies();
        }
    });
    */

    return rootView;
}

@Override
public void onStart() {
    super.onStart();

    new GetDailyListAsync(mDb, mDbDumpText).execute();
}

private void addDaily() {
    DatabaseInitializer.populateAsync(mDb);

    new GetDailyListAsync(mDb, mDbDumpText).execute();
}

private static class GetDailyListAsync extends AsyncTask<Void, Void, List<Daily>> {

    private final DBTCompanionDatabase mDb;
    private TextView mDumpText;

    GetDailyListAsync(DBTCompanionDatabase db, TextView dumpText) {
        mDb = db;
        mDumpText = dumpText;
    }

    @Override
    protected List<Daily> doInBackground(final Void... params) {
        return mDb.dailyDao().getAll();
    }

    @Override
    protected void onPostExecute(List<Daily> dailies) {
        mDumpText.setText(dailies.toString());
    }
}}

I realize there are several default params and things I have not changed, but I just wanted to get this to work before messing with anything else. Of course though, I am getting a warning in the IDE telling me that the private TextView mDumpText (within my AsyncTask) leaks a context object. However, at least the thing works.

I have tried several different ways to avoid this leak, but nothing works. I tried not passing in the TextView to the AsyncTask, and calling a method from the parent Fragment instead. However, I cannot call this method unless the method is declared to be static (because the AsyncTask is static). I cannot set it to be static, because then I cannot reference the TextView I want to update (because it is not static [and I cannot make it static, because I get another IDE warning that placing any Android context classes in a static field is also a memory leak]).

I have been looking around for hours, but everything either gives an uncompleted example code (which does not help) or is referencing anything other than Room, which also never seems to work. What is the best practice for updating a UI element (such as a TextView) after a Room database operation has completed (without creating a memory leak)?

Your problem has nothing to do with Room.

You would have the same problem if you were working with SQLiteDatabase directly, or reading a text file with FileInputStream , or using HttpUrlConnection to make a REST request, or doing any other significant I/O. Helen of Troy had the face that launched a thousand ships; background thread coordination with the UI is the problem that launched a thousand blog posts, books, videos, conference presentations, and the like.

Modern solutions generally revolve around pushing the asynchronous responsibilities down a layer. In the case of Room, this would be by having it return a LiveData or an RxJava type (eg, Single , Observable ). Then, you do not need an AsyncTask in the first place. See this sample app for using Room with RxJava, for example.

Legacy solutions would include:

  • Using a non- static AsyncTask in a retained fragment (see this sample app )

  • Using an event bus with a plain background thread (see this sample app )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM