运行会议室数据库呼叫后更新UI线程的最佳实践?

时间:2018-01-02 22:00:59

标签: android multithreading android-asynctask

我正在尝试学习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,而是从父片段调用方法。但是,除非声明方法是静态的(因为AsyncTask是静态的),否则我不能调用此方法。我不能将它设置为静态,因为我不能引用我想要更新的TextView(因为它不是静态的[我不能使它静态,因为我得到另一个IDE警告,将任何Android上下文类放在静态字段中是还有内存泄漏])。

我一直在寻找几个小时,但是所有内容都提供了一个未完成的示例代码(没有帮助),或者引用了除Room之外的任何内容,这似乎也似乎无法工作。在Room数据库操作完成后更新UI元素(例如TextView)的最佳做法是什么(不会造成内存泄漏)?

1 个答案:

答案 0 :(得分:1)

你的问题与房间无关。

如果您直接使用SQLiteDatabase,或使用FileInputStream阅读文本文件,或使用HttpUrlConnection发出REST请求或执行任何其他操作,则会遇到同样的问题重要的I / O.特洛伊的海伦面对发射了一千艘船;与UI的后台线程协调是发起一千个博客帖子,书籍,视频,会议演示等的问题。

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

传统解决方案包括:

  • 在保留的片段中使用非static AsyncTask(请参阅this sample app

  • 使用带有普通后台线程的事件总线(请参阅this sample app