【问题标题】:Why android room inserts an object at the end of the asynctask generating it (the object)?为什么 android room 在生成它的 asynctask 末尾插入一个对象(对象)?
【发布时间】:2019-07-07 20:04:52
【问题描述】:

总结

Room 会立即插入通过 UI 生成的实体,但会将由 asynctask 发送的实体延迟到生成 asynctask 的(远)端:接收到的实体对象可用并显示在 UI 上,但没有来自数据库的任何 id,妨碍任何其他操作依赖于 id。 插入操作只有在生成的 asynctask 正确停止时才会发生:为什么?以及如何解决这个问题?

更多上下文

生成异步任务

我们使用异步任务来监控套接字并将一些事件(作为 Room 实体)发送回应用程序存储库(如 android 架构组件所期望的那样)。此异步任务基本上在后台连续运行(定期设置一些睡眠),并且仅在应用程序使用结束前停止一段时间(如果操作正确)。到目前为止,它还没有对我们造成任何问题,因为它偏离了最初的短期异步任务概念。 我很清楚我们可以改进设计,但这是另一个主题/问题/时间空洞;-)。

房间插入操作

插入是通过一个专用的异步任务进行的,其中数据库中条目的返回 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);
        }

    }

【问题讨论】:

  • 我已回滚您的编辑。请不要在您的问题标题中添加“已解决”或类似内容。请不要在您的问题中添加解决方案,这就是答案的用途。如果您认为它在接受的答案之外有用,您可以发布自己的答案。
  • 好的,感谢您的建议,一旦完全验证,我将添加采用的解决方案:-)。

标签: java android android-asynctask android-room


【解决方案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 已过时,但在有用的范围内,仅在完成后台工作 (doInBackground()) 后需要在主应用程序线程 (onPostExecute()) 上工作时使用它。在后台工作完成后,您的任何AsyncTask 实现都不需要在主应用程序线程上工作,因此您不需要为它们中的任何一个提供AsyncTask。因此,例如,您的永久运行线程可能是 Thread,而您使用存储库内的线程池进行 DAO 调用。

(“嘿,我们能否在线程方面变得更现代,以配合我们对架构组件的使用?”解决方案是切换到 RxJava 或 Kotlin 协程,并结合 LiveData — 这需要更多的工作,但相对于手动线程管理,它们各有各的优点)

【讨论】:

  • 其实asynctasks不是嵌套的,事件生成的任务是在repository之外的,但确实是repository发起的。此外,当此生成器运行时,其他 UI 事件插入通过相同的 insertEventAsyncTask 工作。感谢您的回答并提供所有这些设计线索:-)。
【解决方案2】:

为什么?

基本上,它是写在AsyncTask documentation 中的:所有异步任务在唯一的后台线程上串行执行

我的代码,即使没有嵌套 asynctask,也会用一个几乎永无止境的任务阻塞这个线程,延迟所有数据库操作直到它完成(或应用程序崩溃,因此丢失一些数据)。

快速解决方案:将 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”被重构以适应 Runnable 主方法签名:

// 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;
    }
}

希望能帮到你……

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-12
    • 2014-08-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多