AsyncTask阻塞UI线程求助:同类问题无法解决
嘿,我猜你遇到的问题应该是在ParkLoader这个AsyncTask里处理权限请求时,权限回调根本没触发吧?毕竟好多人都踩过这个坑——AsyncTask本身不是Activity或者Fragment,就算你让它实现了ActivityCompat.OnRequestPermissionsResultCallback接口,也接不到系统的权限回调通知。我给你两个靠谱的解决办法:
方案1:把权限检查移到MainActivity里(最推荐)
权限回调只能由Activity或Fragment来处理,所以最稳妥的方式是先在MainActivity里搞定权限,拿到权限后再启动ParkLoader:
// 在MainActivity里定义权限相关常量 private static final int REQUEST_PARK_PERMISSION = 101; // 替换成你需要的权限,比如定位权限 private String[] neededPermissions = {Manifest.permission.ACCESS_FINE_LOCATION}; // 调用ParkLoader前先检查权限 private void initParkLoader() { // 检查是否已有权限 if (ActivityCompat.checkSelfPermission(this, neededPermissions[0]) == PackageManager.PERMISSION_GRANTED) { // 权限已拿到,直接启动任务 new ParkLoader(this, parks -> showList(parks)).execute(); } else { // 没有权限,发起请求 ActivityCompat.requestPermissions(this, neededPermissions, REQUEST_PARK_PERMISSION); } } // 重写MainActivity的权限回调方法 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_PARK_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 用户同意了权限,启动ParkLoader new ParkLoader(this, parks -> showList(parks)).execute(); } else { // 用户拒绝了权限,给个提示或者处理后续逻辑 Toast.makeText(this, "需要权限才能加载园区数据哦", Toast.LENGTH_SHORT).show(); } } }
这样就把权限逻辑放到了能正确接收回调的MainActivity里,从根源上解决了问题。
方案2:让ParkLoader通过Activity处理权限(适合必须在AsyncTask里触发请求的场景)
如果你因为某些原因必须在ParkLoader里触发权限请求,可以通过持有Activity的弱引用,让Activity帮你处理回调,之后再通知ParkLoader继续执行:
首先修改ParkLoader的代码:
public class ParkLoader extends AsyncTask<Void, Void, ArrayList<Park>> { // 用WeakReference避免内存泄漏 private WeakReference<MainActivity> activityRef; private OnLoadCompleted loadCallback; private boolean permissionGranted = false; public static final int REQUEST_PARK_PERMISSION = 101; // 替换成你的目标权限 private String[] neededPermissions = {Manifest.permission.ACCESS_FINE_LOCATION}; public ParkLoader(MainActivity activity, OnLoadCompleted callback) { this.activityRef = new WeakReference<>(activity); this.loadCallback = callback; } @Override protected void onPreExecute() { super.onPreExecute(); MainActivity activity = activityRef.get(); if (activity == null) return; // 检查权限 if (ActivityCompat.checkSelfPermission(activity, neededPermissions[0]) != PackageManager.PERMISSION_GRANTED) { // 通过Activity发起权限请求 ActivityCompat.requestPermissions(activity, neededPermissions, REQUEST_PARK_PERMISSION); // 暂时取消任务,等权限回调后再重启 cancel(false); } else { permissionGranted = true; } } @Override protected ArrayList<Park> doInBackground(Void... voids) { if (!permissionGranted || isCancelled()) { return null; } // 这里写你的数据加载逻辑,返回Park列表 return loadParkDataFromSource(); } @Override protected void onPostExecute(ArrayList<Park> parks) { super.onPostExecute(parks); if (loadCallback != null && parks != null) { loadCallback.onLoadCompleted(parks); } } // 给MainActivity调用的方法,权限通过后重启任务 public void restartAfterPermissionOk() { if (!isCancelled()) { permissionGranted = true; execute(); } } // 你的回调接口 public interface OnLoadCompleted { void onLoadCompleted(ArrayList<Park> parks); } // 模拟数据加载方法 private ArrayList<Park> loadParkDataFromSource() { // 这里替换成你的实际逻辑 return new ArrayList<>(); } }
然后在MainActivity里处理回调并重启任务:
private ParkLoader parkLoader; // 初始化并启动ParkLoader private void startParkLoader() { parkLoader = new ParkLoader(this, parks -> showList(parks)); parkLoader.execute(); } // 权限回调 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == ParkLoader.REQUEST_PARK_PERMISSION && parkLoader != null) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 权限通过,重启ParkLoader parkLoader.restartAfterPermissionOk(); } } }
⚠️ 这里用WeakReference持有Activity非常重要,不然容易造成内存泄漏,一定要记得在使用前检查Activity是否为空。
小提醒
另外,我注意到你的ParkLoader继承的是AsyncTask<Void, Void, Void>,但你实际需要返回ArrayList<Park>给回调,建议把泛型改成AsyncTask<Void, Void, ArrayList<Park>>,这样doInBackground可以直接返回数据,逻辑会更清晰规范哦~
内容的提问来源于stack exchange,提问作者Jan

