异步任务核心模型: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 实现,用于启动模块安装并更新 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 重建导致内存泄漏。