Room会立即插入通过UI生成的实体,但是将asynctask发送的实体延迟到生成asynctask的(远端)末尾:接收到的实体对象可用并显示在UI上,但是没有数据库的任何ID,从而妨碍了其他任何对象操作依赖id。 仅当正确停止生成异步任务时,才会发生插入操作:为什么?以及如何解决这个问题?
我们使用asynctask监视套接字,并将一些事件(作为Room实体)发送回应用程序存储库(如android体系结构组件所预期)。这个异步任务基本上在后台连续运行(定期设置一些睡眠),并且仅在应用程序使用结束前停止一会(如果操作正确)。到目前为止,对于我们而言,与短暂的asynctask的原始概念有很大不同。 我很清楚我们可以改进设计,但这是另一个主题/问题/时间空洞;-)。
插入是通过专用的asynctask进行的,其中数据库中条目的返回ID会影响回到刚插入的实体(请参见下面的代码)。这被记录下来,并且UI中的实体“立即”保存,它们取回ID,一切都很好。由asynctask生成的实体,以及它们等待其“父”任务停止并随后全部插入。
首先,实体是在asynctask内部生成的,并通过进度消息发送。然后,对象的构造移到asynctask之外,并且处于UI事件构造的相同级别,但行为相同。 这些事件是一些long(时间戳)和几个字符串。
从生成的asynctask都从这里开始:
@Override
protected void onProgressUpdate(OnProgressObject... values) {
OnProgressObject onProgressObject = values[0];
if (onProgressObject instanceof OnProgressEvent) {
eventRecipient.sendAutoEvent(((OnProgressEvent) onProgressObject).autoEvent);
}
}
eventRecipient是EventsRepository:
public void sendAutoEvent(AutoEvent autoEvent) {
Log.d(LOG_TAG, "got an autoevent to treat...");
EventModel newEvent = EventModel.fromCub(
autoEvent.cubTimeStamp,
autoEvent.description,
autoEvent.eventType
);
addEvent(newEvent);
}
public void addEvent(EventModel event) {
new insertEventAsyncTask(event).execute(event);
// other operations using flawlessly the "event"...
}
private class insertEventAsyncTask extends AsyncTask<EventModel, Void, Long> {
private EventModel eventModel;
public insertEventAsyncTask(EventModel eventModel) {
this.eventModel = eventModel;
}
@Override
protected Long doInBackground(EventModel... eventModels) {
// inserting the event "only"
return eventDao.insert(eventModels[0]);
}
@Override
protected void onPostExecute(Long eventId) {
super.onPostExecute(eventId);
// inserting all the medias associated to this event
// only one media is expected this way though.
eventModel.id = eventId;
Log.d(LOG_TAG, "event inserted in DB, got id : " + eventId);
}
}
答案 0 :(得分:1)
我很清楚我们可以改进设计,但这是另一个主题/问题/时间空洞
由于我怀疑这是造成您当前问题的原因,所以也许您不应该忽略此问题。
我对您的问题的解释是:您有一个外部AsyncTask
(在第一个代码清单中显示的是带有onPublishProgress()
方法的那个)。您正在使用execute()
执行该操作。在外部AsyncTask
的内部,有一个内部AsyncTask
(来自存储库的一个)。您正在使用execute()
执行该操作。而且,您的抱怨是内部AsyncTask
直到外部AsyncTask
完成后才运行。
如果是这样,您的问题是execute()
是单线程的,并且要通过无限期运行AsyncTask
来占用该线程。在外部AsyncTask
完成其背景工作并从doInBackground()
返回之前,内部AsyncTask
被阻止。
“我们可以继续使用黑客吗?”解决方案是继续使用AsyncTask
,但切换到executeOnExecutor()
而不是execute()
,提供要使用的线程池。 AsyncTask.THREAD_POOL_EXECUTOR
将是候选人。
“好,我们可以收拾一下吗?”解决方案是用简单的AsyncTask
对象或直接使用某些多线程线程池(请参见Thread
)替换两个Executors
实例。 AsyncTask
已过时,但在一定程度上有用,仅在完成后台工作({{1之后,需要在主应用程序线程(onPostExecute()
)上进行工作时使用它}})。后台工作完成后,您的doInBackground()
的实现都不需要在主应用程序线程上进行工作,因此您都不需要AsyncTask
。因此,例如,当您使用存储库内部的线程池进行DAO调用时,永远运行的线程可能是AsyncTask
。
(“嘿,我们可以在使用我们的体系结构组件的同时在线程上实现现代化吗?”解决方案是与Thread
一起切换到RxJava或Kotlin协程—这是更多的东西工作,但它们在手动线程管理方面各有千秋)
答案 1 :(得分:0)
基本上,它是用AsyncTask documentation编写的:所有异步任务均在唯一的后台线程上串行执行。
即使没有嵌套的asynctask,我的代码也用几乎永无休止的任务来阻塞该线程,将所有数据库操作延迟到完成为止(或应用程序崩溃,因此会丢失一些数据)。
(CommonsWare)[https://stackoverflow.com/a/56925864/9138818]列出了其他替代方案,这是我解决该问题所遵循的步骤。
主要困难是通过与主线程关联的 Handler 重定向在UI线程(onPreExecute,onProgressUpdate,onPostExecute)上执行的代码。
第一步是获取处理程序的引用:
// Inside Runnable task's constructor :
// get the handler of the main thread (UI), needed for sending back data.
this.uiHandler = new Handler(Looper.getMainLooper());
然后,重构“ doInBackground”以适合可运行的主方法签名:
// previously "public String doInBackground()"
// returned value handled through publishProgress.
@Override
public void run() {
// recommended by Android Thread documentation
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// code previously in doInBackground
现在,将onProgressUpdate中的代码(在doInBackground方法内部由publishProgress调用)移至发布在UI线程处理程序上的Runnable中:
// asynctask method onProgressUpdate was renamed publishProgress =>
// doInBackground's body is almost untouched.
private void publishProgress(final OnProgressObject... values) {
uiHandler.post(new Runnable() {
@Override
public void run() {
// move here code previously in the AsyncTask's publishProgress()
}
});
}
最后,我不得不通过使用Thread.interrupted
而不是isCancelled
并通过在线程之前创建Runnable任务来更改任务的创建,运行和停止方式:
public void startCUBMonitoring() {
if (autoEventThread == null) {
Log.d(LOG_TAG, "startCUBMonitoring");
addUIEvent("CUB MONITORING STARTED", "CUB_connexion");
SessionRepository sessionRepository =
ElabsheetApplication.getInstance().getSessionRepository();
// Creation of the task
AutoEventTask autoEventTask = new AutoEventTask(
this,
sessionRepository,
sessionRepository.getCUBConfig()
);
autoEventThread = new Thread(autoEventTask);
autoEventThread.start();
}
}
public void stopCUBMonitoring() {
if (autoEventThread != null) {
Log.d(LOG_TAG, "stopCUBMonitoring");
addUIEvent("CUB MONITORING STOPPED", "CUB_connexion");
autoEventThread.interrupt();
autoEventThread = null;
}
}
希望它可以帮助...