Fixing ModelPreload cancelling existing load

When a model preload call was made while the loader task is running
(eg: on enabling/disabling icon theme, Launcher reloads and then
launcher preview start a model-preload), it would cancel the original
loader and then start a new loader with empty callbacks. So the
model indeed get loaded, but the original callbacks never got notified
of it.

> Instead we only start preload if an existing task is not running.
> Also when preloading, we use existing callbacks, instead of using
  empty callbacks

Bug: 193851085
Bug: 195155924
Test: Verified repro steps
Change-Id: I0a96310be8489756f364aa2a12e4345e1418733d
This commit is contained in:
Sunny Goyal 2021-08-06 11:52:10 -07:00
parent 464fc41df7
commit 57d4f748b8
3 changed files with 22 additions and 105 deletions

View File

@ -72,6 +72,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@ -376,7 +377,13 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
loaderResults.bindWidgets();
return true;
} else {
startLoaderForResults(loaderResults);
stopLoader();
mLoaderTask = new LoaderTask(
mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
// Always post the loader task, instead of running directly
// (even on same thread) so that we exit any nested synchronized blocks
MODEL_EXECUTOR.post(mLoaderTask);
}
}
}
@ -399,25 +406,17 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
}
}
public void startLoaderForResults(LoaderResults results) {
/**
* Loads the model if not loaded
* @param callback called with the data model upon successful load or null on model thread.
*/
public void loadAsync(Consumer<BgDataModel> callback) {
synchronized (mLock) {
stopLoader();
mLoaderTask = new LoaderTask(
mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, results);
// Always post the loader task, instead of running directly (even on same thread) so
// that we exit any nested synchronized blocks
MODEL_EXECUTOR.post(mLoaderTask);
}
}
public void startLoaderForResultsIfNotLoaded(LoaderResults results) {
synchronized (mLock) {
if (!isModelLoaded()) {
Log.d(TAG, "Workspace not loaded, loading now");
startLoaderForResults(results);
if (!mModelLoaded && !mIsLoaderTaskRunning) {
startLoader();
}
}
MODEL_EXECUTOR.post(() -> callback.accept(isModelLoaded() ? mBgDataModel : null));
}
@Override

View File

@ -49,7 +49,6 @@ import com.android.launcher3.model.GridSizeMigrationTask;
import com.android.launcher3.model.GridSizeMigrationTaskV2;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.ModelDelegate;
import com.android.launcher3.model.ModelPreload;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Themes;
@ -174,18 +173,13 @@ public class PreviewSurfaceRenderer {
}
}.run();
} else {
new ModelPreload() {
@Override
public void onComplete(boolean isSuccess) {
if (isSuccess) {
MAIN_EXECUTOR.execute(() ->
renderView(inflationContext, getBgDataModel(), null));
} else {
Log.e(TAG, "Model loading failed");
}
LauncherAppState.getInstance(inflationContext).getModel().loadAsync(dataModel -> {
if (dataModel != null) {
MAIN_EXECUTOR.execute(() -> renderView(inflationContext, dataModel, null));
} else {
Log.e(TAG, "Model loading failed");
}
}.start(inflationContext);
});
}
}

View File

@ -1,76 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.model;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.Context;
import android.util.Log;
import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.model.BgDataModel.Callbacks;
import java.util.concurrent.Executor;
/**
* Utility class to preload LauncherModel
*/
public class ModelPreload implements ModelUpdateTask {
private static final String TAG = "ModelPreload";
private LauncherAppState mApp;
private LauncherModel mModel;
private BgDataModel mBgDataModel;
private AllAppsList mAllAppsList;
@Override
public final void init(LauncherAppState app, LauncherModel model, BgDataModel dataModel,
AllAppsList allAppsList, Executor uiExecutor) {
mApp = app;
mModel = model;
mBgDataModel = dataModel;
mAllAppsList = allAppsList;
}
@Override
public final void run() {
mModel.startLoaderForResultsIfNotLoaded(
new LoaderResults(mApp, mBgDataModel, mAllAppsList, new Callbacks[0]));
MODEL_EXECUTOR.post(() -> {
Log.d(TAG, "Preload completed : " + mModel.isModelLoaded());
onComplete(mModel.isModelLoaded());
});
}
public BgDataModel getBgDataModel() {
return mBgDataModel;
}
/**
* Called when the task is complete
*/
@WorkerThread
public void onComplete(boolean isSuccess) { }
public void start(Context context) {
LauncherAppState.getInstance(context).getModel().enqueueModelUpdateTask(this);
}
}