Не удается получить доступ к базе данных в основном потоке - Android Room - Использование ThreadPoolExecutor

Я получаю эту известную ошибку "Cannot access database on the main thread since it may potentially lock the UI for a long periods of time" Но, насколько я понимаю, я не обращаюсь к базе данных в основном потоке, так как я выполняю вызов внутри Runnable, выполняемого ThreadPoolExecutor. Что я делаю не так?

В следующем методе я использую runnable для получения данных из сети и сохранения их в локальной базе данных.

private void refresh() {
    executor.execute(() -> {
        if (!dataSource.hasData()) {
            recipeService.getAllRecipes().enqueue(new Callback<List<Recipe>>() {
                @Override
                public void onResponse(Call<List<Recipe>> call, Response<List<Recipe>> response) {
                    dataSource.save(response.body());
                }

                @Override
                public void onFailure(Call<List<Recipe>> call, Throwable t) {
                    throw new RuntimeException(t);
                }
            });
        }

    });
}

Источник данных.сохранить:

@Override
    public void save(List<Recipe> recipes) {
        for (Recipe recipe : recipes) {
            recipeDao.insert(recipe);
            int count = 1;

            for (Ingredient ingredient : recipe.getIngredients()) {
                ingredient.setId(count++);
                ingredient.setRecipeId(recipe.getId());
                ingredientDao.insert(ingredient);
            }

            for (Step step : recipe.getSteps()) {
                step.setRecipeId(recipe.getId());
                stepDao.insert(step);
            }

        }
    }

Исполнитель определяется как:

@Provides
Executor executor() {
    return new ThreadPoolExecutor(4, 8, 60,
            TimeUnit.SECONDS, new LinkedBlockingQueue<>());
}

Ошибка, которую я получаю:

06-18 03:03:38.653 27231-27231/com.github.alexpfx.udacity.nanodegree.android.baking_app E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.github.alexpfx.udacity.nanodegree.android.baking_app, PID: 27231
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long periods of time.
at android.arch.persistence.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:137)
at android.arch.persistence.room.RoomDatabase.beginTransaction(RoomDatabase.java:184)
at com.github.alexpfx.udacity.nanodegree.android.baking_app.data.local.RecipeDao_Impl.insert(RecipeDao_Impl.java:89)
at com.github.alexpfx.udacity.nanodegree.android.baking_app.recipe.RecipeLocalDataSource.save(RecipeLocalDataSource.java:34)
at com.github.alexpfx.udacity.nanodegree.android.baking_app.recipe.RecipesRepositoryImpl$1.onResponse(RecipesRepositoryImpl.java:49)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)

person alexpfx    schedule 18.06.2017    source источник
comment
если вы не уверены, просто вызовите Log.d со значением Thread.currentThread() перед вызовом вашего dataSource.save(response.body());, и вы увидите, что да, он вызывается в потоке пользовательского интерфейса   -  person pskink    schedule 18.06.2017
comment
Я вызываю журнал непосредственно перед условным (hasData?) и перед сохранением, результат такой: Refresh: Thread[pool-2-thread-1,5,main] Refresh: Thread[main,10,main] Что это значит?   -  person alexpfx    schedule 18.06.2017
comment
Thread[main,10,main] - это поток пользовательского интерфейса   -  person pskink    schedule 18.06.2017
comment
Я думал, что тот факт, что он обертывается Runnable, выполняется в другом потоке. Но я попробую то, что вы предлагаете.   -  person alexpfx    schedule 18.06.2017
comment
Да, это работает. Конечно, обратный вызов модификации выполняется в основном потоке. .. Я не понял...   -  person alexpfx    schedule 18.06.2017
comment
runnable есть: executor.execute(() -> {} это лямбда-выражение.   -  person alexpfx    schedule 18.06.2017
comment
ааа, конечно ;-) я пропустил это   -  person pskink    schedule 18.06.2017
comment
используйте этот square.github.io/retrofit/2. x/retrofit/retrofit2/ для настройки собственного исполнителя обратного вызова   -  person pskink    schedule 18.06.2017
comment
Пожалуйста, следуйте тому же ответу на ваш вопрос. Комната в основном потоке   -  person Rizvan    schedule 06.11.2017


Ответы (2)


Вы можете изменить свою реализацию на:

private void refresh() {

        if (!dataSource.hasData()) {
            recipeService.getAllRecipes().enqueue(new Callback<List<Recipe>>() {
                @Override
                public void onResponse(Call<List<Recipe>> call, Response<List<Recipe>> response) {

                executor.execute(() -> {
                    dataSource.save(response.body());
                  });
                }

                @Override
                public void onFailure(Call<List<Recipe>> call, Throwable t) {
                    throw new RuntimeException(t);
                }
            });
        }
}

Функция onResponse всегда вызывается в UIThread.

person Fredy Mederos    schedule 23.10.2017

кажется, что ваш вызов метода callback.onresponce() из потока пользовательского интерфейса

изменение вашего метода onResponse, подобное этому, может помочь

@Override
public void onResponse(Call<List<Recipe>> call, Response<List<Recipe>> response) {
AsyncTask.execute(new Runnable() {
                    @Override
                    public void run() {
                        dataSource.save(response.body());
                    }
                });

            }
person snersesyan    schedule 23.10.2017
comment
Вы можете превратить этот комментарий в полноценный ответ, добавив больше деталей и пример того, как это исправить. - person David; 23.10.2017