我正在尝试学习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)的最佳做法是什么(不会造成内存泄漏)?
答案 0 :(得分:1)
你的问题与房间无关。
如果您直接使用SQLiteDatabase
,或使用FileInputStream
阅读文本文件,或使用HttpUrlConnection
发出REST请求或执行任何其他操作,则会遇到同样的问题重要的I / O.特洛伊的海伦面对发射了一千艘船;与UI的后台线程协调是发起一千个博客帖子,书籍,视频,会议演示等的问题。
现代解决方案通常围绕将异步职责推向一层。在Room的情况下,这将是返回LiveData
或RxJava类型(例如,Single
,Observable
)。然后,您首先不需要AsyncTask
。例如,请参阅this sample app以了解使用带有RxJava的Room。
传统解决方案包括:
在保留的片段中使用非static
AsyncTask
(请参阅this sample app)
使用带有普通后台线程的事件总线(请参阅this sample app)