【问题标题】:Best Practices for Updating UI Thread after Running Room DB Calls?运行 Room DB 调用后更新 UI 线程的最佳实践?
【发布时间】:2018-01-02 22:00:59
【问题描述】:

我正在尝试学习适用于 Android 的 Room Persistence 数据库,但遇到了一些严重的概念障碍。我想要做的是使用 Room 来查询数据库并返回一些数据。完成后,我想根据该数据更新 TextView 的值。

最终,希望创建一个与数据库相关的表单,其中的字段可以预先填写数据库中已有的信息。

经过几个小时的修修补补,我想出了以下代码,它确实有效:

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

我意识到有几个默认参数和一些我没有改变的东西,但我只是想在弄乱其他任何东西之前让它工作。当然,我在 IDE 中收到警告,告诉我私有 TextView mDumpText(在我的 AsyncTask 中)泄漏了一个上下文对象。但是,至少事情是可行的。

我尝试了几种不同的方法来避免这种泄漏,但没有任何效果。我尝试不将 TextView 传递给 AsyncTask,而是从父 Fragment 调用方法。但是,除非该方法被声明为静态(因为 AsyncTask 是静态的),否则我不能调用此方法。我无法将其设置为静态,因为这样我就无法引用我想要更新的 TextView(因为它不是静态的 [而且我无法将其设为静态,因为我收到另一个 IDE 警告,将任何 Android 上下文类放在静态字段中是也是内存泄漏])。

我已经四处寻找了几个小时,但所有内容要么给出了一个未完成的示例代码(这没有帮助),要么引用了 Room 以外的任何东西,这似乎也从来没有工作过。在 Room 数据库操作完成后(不造成内存泄漏)更新 UI 元素(例如 TextView)的最佳做法是什么?

【问题讨论】:

    标签: android multithreading android-asynctask


    【解决方案1】:

    您的问题与 Room 无关。

    如果您直接使用SQLiteDatabase,或者使用FileInputStream 读取文本文件,或者使用HttpUrlConnection 发出REST 请求,或者执行任何其他重要的I/O,您都会遇到同样的问题。特洛伊的海伦有一张能发射一千艘船的脸;后台线程与 UI 的协调是发布一千篇博客文章、书籍、视频、会议演示等的问题。

    现代解决方案通常围绕着将异步职责下推一层。在 Room 的情况下,这将通过让它返回 LiveData 或 RxJava 类型(例如,SingleObservable)。然后,您首先不需要AsyncTask。例如,请参阅 this sample app 以将 Room 与 RxJava 结合使用。

    传统解决方案包括:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-10
      • 1970-01-01
      • 2010-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-16
      相关资源
      最近更新 更多