SS-*_*alt 5 java sqlite android background android-asynctask
我有这个待办事项列表应用程序,其中“任务”可以包含自己的“子任务”列表。问题是,每当这些“任务”通过拖放重新排列时,有时会给我带来奇怪的行为,例如单击它们以显示子列表时崩溃。
这是拖放监听器的代码:
private void attachItemTouchHelperToAdapter() {
ItemTouchHelper.SimpleCallback touchCallback =
new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
final int fromPosition = viewHolder.getAdapterPosition();
final int toPosition = target.getAdapterPosition();
rearrangeTasks(fromPosition, toPosition);
// Call background thread to update database.
new UpdateDatabaseTask(mTaskList).execute();
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
Task taskToDelete = mTaskAdapter.mTasks.get(position);
TaskLab.get(getActivity()).deleteTask(taskToDelete);
updateUI(position);
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(touchCallback);
itemTouchHelper.attachToRecyclerView(mTaskRecyclerView);
}
Run Code Online (Sandbox Code Playgroud)
重新排列 onMove 中调用的列表的代码:
private void rearrangeTasks(int fromPosition, int toPosition) {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(mTaskList, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(mTaskList, i, i - 1);
}
}
mTaskAdapter.notifyItemMoved(fromPosition, toPosition);
}
Run Code Online (Sandbox Code Playgroud)
AsyncTask调用它来更新 sqlite 数据库:
private class UpdateDatabaseTask extends AsyncTask<Void, Void, Void> {
private List<Task> mTaskList;
public UpdateDatabaseTask(List<Task> taskList) {
mTaskList = taskList;
}
@Override
protected Void doInBackground(Void... voids) {
TaskLab.get(getActivity()).updateDatabase(mTaskList);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
}
}
Run Code Online (Sandbox Code Playgroud)
调用数据库辅助函数以使用重新排列的列表更新整个表:
public void updateDatabase(List<Task> tasks) {
mDatabase.delete(TaskTable.NAME, null, null);
for (Task task : tasks) {
addTask(task);
}
}
Run Code Online (Sandbox Code Playgroud)
这里的问题是,有时,当快速拖放“任务”时,AsyncTask 更新数据库的速度不够快,因此当我尝试在更新完成之前对其执行功能时,会出现奇怪的行为。
RecyclerView在拖放时更新 SQLite 数据库的更好方法是什么?
这个问题太宽泛,无法回答,因为您可以考虑多种解决方案来解决您的问题。我现在能想到两个,在这里我将对它们进行简要描述。
解决方案#1
您可能会考虑直接调用更新表的数据库方法,而不通过AsyncTask. 向数据库表注册一个内容观察器,以便每次更新表时,内容观察器都会RecyclerView随之更新,而无需notifyDataSetChanged()调用。示例实现应如下所示。
像这样声明数据库表的 URI。
public static final Uri DB_TABLE_TASKS_URI = Uri
.parse("sqlite://" + Constants.ApplicationPackage + "/" + DB_TABLE_TASKS);
Run Code Online (Sandbox Code Playgroud)
现在,在您的updateDatabase函数中,您需要调用此通知函数,以在您的任务表发生更改或更新时通知监听此内容的观察者。
public static void updateDatabase(List<Task> taskList) {
// .. Your other implementation goes here
context.getContentResolver().notifyChange(DBConstants.DB_TABLE_TASKS_URI, null);
}
Run Code Online (Sandbox Code Playgroud)
现在使用 来LoaderCallbacks显示 .sql 文件中 sqlite 表中的任务RecyclerView。
public class TasksActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
private final TAKS_QUERY_LOADER = 0;
protected void onCreate(Bundle savedInstanceState) {
// Other code.
// Get your cursor loader initialized here.
getLoaderManager().initLoader(TASKS_QUERY_LOADER, null, this).forceLoad();
}
}
Run Code Online (Sandbox Code Playgroud)
现在您需要LoaderCallbacks在您的TasksActivity. 一个示例实现应该如下所示。
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new SQLiteCursorLoader(getActivity()) {
@Override
public Cursor loadInBackground() {
Cursor cursor;
cursor = TasksLab.getTasks();
// Register the cursor with the content observer
this.registerContentObserver(cursor, DBConstants.DB_TABLE_TASKS_URI);
return cursor;
}
};
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Set the cursor in the adapter of your RecyclerView here.
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
// Do not forget to destroy the loader when you are destroying the view.
@Override
public void onDestroy() {
getLoaderManager().destroyLoader(TASKS_QUERY_LOADER);
super.onDestroyView();
}
Run Code Online (Sandbox Code Playgroud)
就是这个。现在,您可以立即在 sqlite 表中更新您的任务,并立即获得反映RecyclerView。我强烈建议您一次更新一个任务。updateDatabase正如我所看到的,您正在使用在此处传递整个任务列表的函数来更新数据库表。我建议仅传递被拖动或更新的任务 ID,以便表立即更新。
解决方案#2
此解决方案比前一个解决方案更简单。这个想法是在刷新列表或销毁视图时更新 SQLite 数据库。
当任务被拖动到某个位置(即更新其位置)或删除时,您需要将轨道保留ArrayList在TasksActivity. 不要立即更新数据库中的 sqlite 表,或使用AsyncTask. 保存此任务以供稍后在您的onDestroy或onPause方法中销毁视图时使用。RecyclerView在此过程中,每次Activity恢复(即在功能中)时,您都需要刷新onResume。这是伪实现。
@Override
public boolean onMove(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
final int fromPosition = viewHolder.getAdapterPosition();
final int toPosition = target.getAdapterPosition();
rearrangeTasks(fromPosition, toPosition);
// Do not call background thread to update database.
// new UpdateDatabaseTask(mTaskList).execute();
// Keep a local ArrayList and keep the track of dragging the item.
updateLocalArrayListWhichIsBindToRecyclerView(mTaskList);
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
Task taskToDelete = mTaskAdapter.mTasks.get(position);
// TaskLab.get(getActivity()).deleteTask(taskToDelete);
updateLocalArrayListWhichIsBindedToRecyclerView(taskToDelete);
updateUI(position);
}
Run Code Online (Sandbox Code Playgroud)
现在,当您离开时,TaskActivity您需要最终将更改保存在数据库中。
@Override
public void onDestroy() {
updateDatabaseFromTheList();
super.onDestroyView();
}
@Override
public void onPause() {
updateDatabaseFromTheList();
super.onDestroyView();
}
Run Code Online (Sandbox Code Playgroud)
并每次从函数内的数据库加载任务onResume。
@Override
public void onResume() {
super.onResume();
updateRecyclerViewFromDatabase();
}
Run Code Online (Sandbox Code Playgroud)
更新
基于上述评论中解决方案 #1 所需的澄清。
“我建议仅传递被拖动或更新的任务 ID,以便表立即更新。”。我到底该怎么做?
我不确定您现在如何更新任务表。不过,我可以想到一些我自己的实现。只需传递您想要更新其在任务表中的位置的任务即可。因此伪实现可能如下所示。
public class Task {
public int taskId;
public int taskPosition;
}
public void updateDatabase(Task taskToBeUpdated, int newPosition) {
// You need two update queries.
// Update task_table set task_position += task_position + 1 where task_position >= newPosition;
// Update task_table set task_position = newPosition where taskId = taskTobeUpdated.taskId
}
Run Code Online (Sandbox Code Playgroud)
希望有帮助。
| 归档时间: |
|
| 查看次数: |
923 次 |
| 最近记录: |