异步任务核心模型:AsyncTask 基类、回调方法、线程模型

SplitInstallAsyncTask 是 Google Play Core 库提供的抽象基类,专门用于处理模块安装请求的后台执行与状态回调。它遵循经典的 AsyncTask 设计模式,将耗时操作与 UI 更新分离。

AsyncTask 基类定义

SplitInstallAsyncTask 是一个泛型抽象类,继承自 AsyncTask<Void, SplitInstallSessionState, SplitInstallSessionState>。其核心签名如下:

public abstract class SplitInstallAsyncTask
    extends AsyncTask<Void, SplitInstallSessionState, SplitInstallSessionState> {
    protected abstract void onPreExecute();
    protected abstract SplitInstallSessionState doInBackground(Void... params);
    protected abstract void onProgressUpdate(SplitInstallSessionState... values);
    protected abstract void onPostExecute(SplitInstallSessionState result);
    protected abstract void onCancelled(SplitInstallSessionState result);
}

开发者在子类中只需实现 doInBackground() 和 onPostExecute(),其他回调可按需覆盖。

回调方法说明

回调方法线程触发时机典型用途
onPreExecute()主线程execute() 之后立即执行显示加载动画、禁用按钮
doInBackground()后台线程onPreExecute 完成后自动调用执行拆分安装请求、检查状态
onProgressUpdate()主线程每次调用 publishProgress() 时更新进度条、显示下载百分比
onPostExecute()主线程doInBackground 返回后处理安装完成结果、启动模块
onCancelled()主线程任务被取消时清理资源、回滚状态

线程模型执行流程

SplitInstallAsyncTask 采用“单后台线程 + 主线程回调”模型。后台任务在 AsyncTask.SERIAL_EXECUTOR 上顺序执行,确保所有实例排队执行;回调则在主线程中由 Handler 调度。具体流程下图所示:

SplitInstallAsyncTask 线程模型与回调流程 execute() onPreExecute() doInBackground() onProgressUpdate() onPostExecute() onCancelled() 取消 虚线:取消路径

代码示例

下面展示一个最小化的 SplitInstallAsyncTask 实现,用于启动模块安装并更新 UI:

public class InstallModuleTask extends SplitInstallAsyncTask {
    private final Context context;
    private final String moduleName;

    public InstallModuleTask(Context context, String moduleName) {
        this.context = context.getApplicationContext();
        this.moduleName = moduleName;
    }

    @Override protected void onPreExecute() {
        progressBar.setVisibility(View.VISIBLE);
        statusText.setText("正在准备安装…");
    }

    @Override protected SplitInstallSessionState doInBackground(Void... params) {
        SplitInstallManager manager = SplitInstallManagerFactory.create(context);
        SplitInstallRequest request = SplitInstallRequest.newBuilder()
                .addModule(moduleName)
                .build();
        try {
            int sessionId = manager.startInstall(request).getResult();
            // 轮询状态(略)
            return waitForComplete(manager, sessionId);
        } catch (Exception e) {
            return null;
        }
    }

    @Override protected void onProgressUpdate(SplitInstallSessionState... values) {
        SplitInstallSessionState state = values[0];
        statusText.setText("已下载 " + state.bytesDownloaded() + " / " + state.totalBytesToDownload());
        progressBar.setProgress(state.percentProgress());
    }

    @Override protected void onPostExecute(SplitInstallSessionState result) {
        progressBar.setVisibility(View.GONE);
        if (result != null && result.status() == SplitInstallSessionStatus.INSTALLED) {
            statusText.setText("模块安装成功");
        } else {
            statusText.setText("安装失败,错误码: " + (result != null ? result.errorCode() : -1));
        }
    }
}

调试建议

可在每个回调首行添加日志:

Log.d("SplitInstallTask", "onPreExecute 主线程=" + Thread.currentThread().getName());

通过 logcat 过滤 SplitInstallTask 标签即可观察线程跳转与执行顺序。

最佳实践: 始终使用 getApplicationContext() 避免 Activity 泄漏;在 onPostExecute() 中检查 isCancelled() 防止崩溃。
常见错误: 在 doInBackground() 中直接更新 UI(会触发 CalledFromWrongThreadException);忘记调用 publishProgress() 导致进度回调无效;onCancelled() 中未释放资源导致内存泄漏。

性能优化

  • 避免在 doInBackground() 中创建大量对象,减少 GC 压力。
  • 若需并发安装多个模块,考虑为每个任务创建独立实例,利用 THREAD_POOL_EXECUTOR 并行执行。
  • 使用 SoftReference 持有 UI 组件引用,防止任务执行时 Activity 重建导致内存泄漏。