如何在AsyncTask执行完毕后返回值且不阻塞UI线程?
解决AsyncTask结合OkHttp上传文件的非阻塞回调问题
我完全懂你的需求——不想用get()把AsyncTask变成同步阻塞UI线程,而是通过接口委托来灵活处理上传过程中的状态和结果,这才是Android异步操作的正确打开方式,能有效避免ANR,还能让UI逻辑和网络操作解耦。
先给你完善后的完整代码实现:
// 首先定义回调接口,用来传递上传进度和结果 public interface TaskInterface<T, P> { // 上传成功时回调结果 void onTaskSuccess(T result); // 上传失败时回调错误信息 void onTaskFailed(String errorMsg); // 上传进度更新时回调 void onProgressUpdate(P progress); } // 完善后的UploadFile AsyncTask static class UploadFile extends AsyncTask<Void, Float, String> { private TaskInterface<String, Float> delegate; private String url; private File file; private MediaType fileType; // 构造函数,传入必要参数和回调委托 public UploadFile(String url, File file, MediaType fileType, TaskInterface<String, Float> delegate) { this.url = url; this.file = file; this.fileType = fileType; this.delegate = delegate; } @Override protected String doInBackground(Void... voids) { try { // 自定义RequestBody来监听上传进度 RequestBody requestBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("file", file.getName(), new RequestBody() { @Override public MediaType contentType() { return fileType; } @Override public long contentLength() throws IOException { return file.length(); } @Override public void writeTo(BufferedSink sink) throws IOException { long fileLength = file.length(); byte[] buffer = new byte[4096]; FileInputStream in = new FileInputStream(file); long uploaded = 0; try { int read; while ((read = in.read(buffer)) != -1) { // 计算进度并发布 uploaded += read; float progress = (float) uploaded / fileLength; publishProgress(progress); sink.write(buffer, 0, read); sink.flush(); } } finally { in.close(); } } }) .build(); Request request = new Request.Builder() .url(url) .post(requestBody) .build(); OkHttpClient client = new OkHttpClient(); Response response = client.newCall(request).execute(); if (response.isSuccessful() && response.body() != null) { return response.body().string(); } else { return "上传失败:" + response.code(); } } catch (IOException e) { e.printStackTrace(); return "上传异常:" + e.getMessage(); } } @Override protected void onProgressUpdate(Float... values) { super.onProgressUpdate(values); // 把进度回调给UI线程 if (delegate != null) { delegate.onProgressUpdate(values[0]); } } @Override protected void onPostExecute(String result) { super.onPostExecute(result); // 根据结果触发成功或失败回调 if (delegate != null) { if (result.startsWith("上传失败") || result.startsWith("上传异常")) { delegate.onTaskFailed(result); } else { delegate.onTaskSuccess(result); } } } }
下面是在UI线程中的调用示例(比如Activity里):
File uploadFile = new File("/sdcard/test.jpg"); MediaType imageType = MediaType.parse("image/jpeg"); new UploadFile("https://your-upload-url.com", uploadFile, imageType, new TaskInterface<String, Float>() { @Override public void onTaskSuccess(String result) { // UI线程处理成功结果,比如更新TextView runOnUiThread(() -> { tvResult.setText("上传成功:" + result); }); } @Override public void onTaskFailed(String errorMsg) { // UI线程处理失败情况 runOnUiThread(() -> { tvResult.setText("上传失败:" + errorMsg); }); } @Override public void onProgressUpdate(Float progress) { // 更新进度条,比如设置进度值 runOnUiThread(() -> { progressBar.setProgress((int) (progress * 100)); }); } }).execute();
最后给你划几个关键要点:
- 回调接口解耦:
TaskInterface把上传的状态(进度、成功、失败)传递给调用方,AsyncTask只负责网络操作,不用关心UI怎么处理 - 彻底避免UI阻塞:全程没有使用
get()方法,所有UI更新都通过AsyncTask的生命周期方法或runOnUiThread回到主线程,保证线程安全 - OkHttp进度监听:通过自定义
RequestBody的writeTo方法,实时计算上传进度并通过publishProgress通知主线程 - 清晰的参数传递:通过构造函数传入上传所需的URL、文件、媒体类型和回调委托,代码逻辑更直观
内容的提问来源于stack exchange,提问作者ryuk
相关产品推荐
相关产品推荐

