Debug模式下AsyncTaskRunner执行完毕后应用闪退问题求助
我遇到过类似的Debug专属崩溃问题,结合你的代码和描述,咱们一步步分析:
问题场景回顾
在Debug模式下单步执行AsyncTaskRunner的doInBackground方法时,走到return "done";这一行就会触发应用闪退,Android Studio的Stop按钮也会变灰;但不设置断点时,应用运行完全正常。
相关代码
AsyncTask实现代码
private class AsyncTaskRunner extends AsyncTask<String, String, String> { ProgressDialog progressDialog; @Override protected String doInBackground(String... params) { try { Log.e("SQLMainActivity", "Inicio do try"); Connection con; Statement stmt; ResultSet rs; String driver = "net.sourceforge.jtds.jdbc.Driver"; String conString = "jdbc:jtds:sqlserver://" + GlobalVars.SQLServerInfo.get("server") + ":" + GlobalVars.SQLServerInfo.get("port") + "/" + GlobalVars.SQLServerInfo.get("dbname") + ";instance=" + GlobalVars.SQLServerInfo.get("instance"); Class.forName(driver); Log.e("SQLMainActivity", "Consegui meter a diver como deve ser"); con = DriverManager.getConnection(conString, GlobalVars.SQLServerInfo.get("user"), GlobalVars.SQLServerInfo.get("pass")); String query = "SELECT * FROM Pda_Documentos ORDER BY Ordem"; stmt = con.createStatement(); rs = stmt.executeQuery(query); Log.e("SQLMainActivity", "Consegui executar a query como deve ser"); PDADocumentos tempDoc; // TODO ** Informação ** De lembrar que ao ser adicionados campos aos documentos temos que actualizar esta parte do codigo while (rs.next()) { tempDoc = new PDADocumentos(); tempDoc.setID(rs.getString("Tipo")); tempDoc.setNome(rs.getString("Nome")); GlobalVars.PDADocInfo.put(rs.getString("Tipo"), tempDoc); } stmt.close(); con.close(); Log.e("SQLMainActivity", "Fechei com sucesso a ligação"); SQLConDocuments = true; } catch (Exception e) { e.printStackTrace(); Log.e("SQLConfig", "Não consegui fazer a ligação"); Log.e("SQLConfig", e.toString()); } FillDocumentsToMemory(); return "done"; } @Override protected void onPostExecute(String result) { progressDialog.dismiss(); } @Override protected void onPreExecute() { progressDialog = ProgressDialog.show(MainActivity.this, getBaseContext().getResources().getString(R.string.config_connection_title), getBaseContext().getResources().getString(R.string.config_connection_message)); } @Override protected void onProgressUpdate(String... text) { progressDialog.setMessage("Messagem enviada com sucesso!"); } }
AsyncTask调用逻辑
if (checkFileIfExist("dbHelper.db")) { // Metodo Que irá passar PDA_Documentos para o mapa e também actualizar a base de dados SQL Log.e("AsyncTask","Buscar info principal para andar"); AsyncTaskRunner runner = new AsyncTaskRunner(); runner.execute(); } else { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(R.string.config_sqlitle_message) .setTitle(R.string.alert_titles_warning) .setCancelable(false) .setPositiveButton(R.string.general_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { dialogInterface.cancel(); finishAndRemoveTask(); } }); AlertDialog dialog = builder.create(); dialog.show(); }
可能的原因与解决方案
这种只在Debug断点时出现的崩溃,大概率和线程调度、Debugger监控规则,或者AsyncTask与Activity的生命周期冲突有关,以下是几个优先级较高的排查方向:
1. 检查FillDocumentsToMemory()的线程合法性
你在doInBackground的末尾调用了FillDocumentsToMemory(),而doInBackground是在子线程执行的。如果这个方法里包含任何直接操作UI的代码(比如更新TextView、访问布局控件),在Debug单步执行时,线程调度的延迟会让系统精准检测到这个非法操作,直接触发崩溃;而正常运行时,可能因为线程切换快或者系统检测不及时,侥幸没有崩溃。
解决办法:
- 如果
FillDocumentsToMemory()涉及UI操作:把这部分逻辑移到onPostExecute()中执行,因为onPostExecute是在主线程运行的,完全安全。 - 如果只是数据填充:确保它没有依赖主线程的资源(比如未正确同步的全局变量),必要时给相关操作加同步锁。
2. 修复AsyncTask与Activity的生命周期绑定问题
你的AsyncTaskRunner是Activity的内部类,默认持有Activity的强引用。在Debug单步执行时,Activity可能因为某些原因(比如意外销毁、配置变化)已经被回收,但AsyncTask还在执行,当它走到return时,就会触发空指针或者生命周期冲突,导致应用闪退。
解决办法:
- 给AsyncTask添加Activity的弱引用,避免内存泄漏和生命周期冲突:
private class AsyncTaskRunner extends AsyncTask<String, String, String> { private WeakReference<MainActivity> mActivityRef; ProgressDialog progressDialog; AsyncTaskRunner(MainActivity activity) { mActivityRef = new WeakReference<>(activity); } @Override protected void onPreExecute() { MainActivity activity = mActivityRef.get(); if (activity != null) { progressDialog = ProgressDialog.show(activity, activity.getResources().getString(R.string.config_connection_title), activity.getResources().getString(R.string.config_connection_message)); } } @Override protected void onPostExecute(String result) { MainActivity activity = mActivityRef.get(); if (activity != null && progressDialog != null && progressDialog.isShowing()) { progressDialog.dismiss(); } } // 其余方法保持不变 }
调用时改为:
AsyncTaskRunner runner = new AsyncTaskRunner(MainActivity.this); runner.execute();
- 在Activity的
onDestroy()方法中主动取消AsyncTask:
@Override protected void onDestroy() { super.onDestroy(); if (runner != null && !runner.isCancelled()) { runner.cancel(true); } }
3. 完善数据库资源的释放逻辑
虽然你关闭了stmt和con,但ResultSet没有显式关闭,而且资源释放没有放在finally块中。在Debug单步执行时,资源释放的时机可能和正常运行不同,残留的未关闭资源可能导致线程异常。
解决办法:用finally块统一释放所有数据库资源,确保无论是否发生异常都能正确关闭:
try { // 你的数据库操作代码 } catch (Exception e) { // 异常处理 } finally { try { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (con != null) con.close(); } catch (SQLException e) { e.printStackTrace(); Log.e("SQLMainActivity", "释放数据库资源失败:" + e.toString()); } }
4. 调整Android Studio Debugger的线程监控设置
有时候Debugger的线程自动检测逻辑会和AsyncTask的线程回收产生冲突,导致Debug时闪退。可以尝试调整Debugger设置:
- 打开
Run/Debug Configurations-> 选择你的应用配置 -> 切换到Debugger标签页 -> 取消勾选Auto detect thread frame changes - 在Debug窗口的
Threads面板中,只关注主线程和AsyncTask的线程,减少Debugger的监控负担
总结
因为问题只在Debug断点时出现,所以重点排查子线程中的UI操作和AsyncTask与Activity的生命周期绑定这两个方向,这是这类问题最常见的根源。
内容的提问来源于stack exchange,提问作者Camadas

