Android登录页调用Retrofit异步任务未等待完成的问题问询
问题分析
你的核心问题在于AsyncTask的execute()方法是异步非阻塞的——调用它之后,主线程会立刻继续执行后续代码,不会等待doInBackground里的API请求完成。所以你在checkIfAuthenticated里,刚启动runAuthenticationCheck就去读取SharedPreferences并判断跳转,这时候API请求可能还没结束,token的清理操作(如果失效的话)还没执行,导致逻辑完全不符合预期。
你的解决方案:使用.get()阻塞主线程
你提到的给execute()加上.get()的方法是可行的,它会强制当前线程(这里是主线程)等待AsyncTask完成所有流程(包括doInBackground和onPostExecute)之后再继续执行。修改后的代码如下:
public void runAuthenticationCheck (String mobile_token, String Access_token) { checkAuthTask = new checkAuthenticationTask(mobile_token, Access_token); try { checkAuthTask.execute((Void) null).get(); } catch (ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } }
重要提醒
不过要特别注意:.get()会阻塞主线程,如果你的API请求耗时较长(比如网络环境差的时候),很容易触发ANR(应用无响应)错误。所以建议在调用这个方法前,给用户显示一个加载提示(比如ProgressDialog或者自定义的加载弹窗),让用户知道APP正在处理中,避免误以为APP卡死。
更优的异步方案:使用回调接口(推荐)
如果不想阻塞主线程,更优雅且符合Android开发规范的方式是给AuthenticationGlobal添加回调接口,在AsyncTask的onPostExecute里通知LoginActivity执行后续逻辑。这样既不会阻塞主线程,又能严格保证逻辑执行顺序。
具体实现步骤如下:
- 定义一个回调接口:
public interface AuthCheckCallback { void onAuthCheckComplete(boolean isAuthenticated); }
- 修改
runAuthenticationCheck方法,接收回调参数:
public void runAuthenticationCheck (String mobile_token, String Access_token, AuthCheckCallback callback) { checkAuthTask = new checkAuthenticationTask(mobile_token, Access_token, callback); checkAuthTask.execute((Void) null); }
- 修改
checkAuthenticationTask,持有回调并在onPostExecute触发:
public class checkAuthenticationTask extends AsyncTask<Void, Void, Boolean> { private GetDataService service; private String mobile_token; private String access_token; private AuthCheckCallback callback; // 新增回调引用 checkAuthenticationTask(String Access_token, String Mobile_token, AuthCheckCallback callback) { mobile_token = Mobile_token; access_token = Access_token; this.callback = callback; service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class); } @Override protected Boolean doInBackground(Void... params) { boolean isAuthenticated = true; try { Call<CheckAuthenticationResponse> call = service.checkAuthentication(access_token,mobile_token); Response<CheckAuthenticationResponse> response = call.execute(); if (response.code() != 200){ clearAuthentication(); Log.e("AuthKey Global","Expired0"); isAuthenticated = false; } } catch (IOException ea) { clearAuthentication(); Log.e("AuthKey Global","Expired1"); Log.e("AuthenticationResponseError Global","Network Went Wrong"); ea.printStackTrace(); isAuthenticated = false; } return isAuthenticated; } @Override protected void onPostExecute(final Boolean isAuthenticated) { // 触发回调,通知LoginActivity结果 if (callback != null) { callback.onAuthCheckComplete(isAuthenticated); } if (isAuthenticated) { Log.e("AuthKey Global","Done"); } else { clearAuthentication(); Log.e("AuthKey Global","Expired2"); } } // 其他原有方法保持不变 }
- 在LoginActivity的
checkIfAuthenticated里使用回调:
private void checkIfAuthenticated(){ SharedPreferences reader_auth = getSharedPreferences(getString(R.string.auth_preferences), MODE_PRIVATE); String auth_key = reader_auth.getString(getString(R.string.auth_access_key),null); String mobile_token = reader_auth.getString(getString(R.string.auth_mobile_token),null); if (auth_key != null) { authGlobal = new AuthenticationGlobal(this); authGlobal.runAuthenticationCheck(auth_key,mobile_token, new AuthCheckCallback() { @Override public void onAuthCheckComplete(boolean isAuthenticated) { if (isAuthenticated) { Log.d("Auth Key Check 1", auth_key); MoveToDashboardActivity(); } else { // token失效,留在登录页或者提示用户重新登录 Log.d("Auth Key Check", "Token expired or invalid"); } } }); } }
这种方式既保持了异步执行的优势(不阻塞主线程,避免ANR),又能严格控制逻辑顺序,是Android处理异步任务的标准实践。
内容的提问来源于stack exchange,提问作者J_LDAP

