【发布时间】:2017-11-30 19:50:52
【问题描述】:
我的应用程序有一个带有 AsyncTask 的 Fragment,它从数据库中获取记录并使用 ListView 显示它们。几年来一直运行良好,但现在在 Android 7 上它停滞不前,没有显示任何记录。但是,在离开应用程序(例如进入 Android 设置)然后返回时,会显示记录。 调试显示最初执行的是onPreExecute,但是直到离开应用程序的那一刻才执行doInBackground。
谁能提出 Android 7 中的哪些变化可以解释这一点?
// 1. ==== Fragment containing AsyncTask ====
public class AuditFragment extends ListFragment implements OnClickListener
{
// Using beep for debugging until I can get LogCat in Eclipse restored for Android 7
public static void beep ( final int times )
{
...
}
private class UpdateAuditTask extends AsyncTask<Void, RecordEntry, SQLException>
{
@Override
protected SQLException doInBackground ( Void... parameters )
{
beep ( 5 ); // Debug, in the absence of LogCat
...
}
@Override
protected void onProgressUpdate ( RecordEntry... values )
{
L.logMethodCall ( (Object[]) values );
if ( values.length == 1 )
{
auditListAdapter.add ( values[0] );
auditListAdapter.notifyDataSetChanged ();
}
}
@Override
protected void onPreExecute ()
{
L.logMethodCall ();
beep ( 2 ); // Debug, in the absence of LogCat
auditListAdapter.clear ();
}
@Override
protected void onPostExecute ( SQLException result )
{
L.logMethodCall ( result );
...
}
}
private void updateAuditList ()
{
L.logMethodCall ();
beep (1); // Debug, in the absence of LogCat
new UpdateAuditTask ().execute ();
auditListAdapter.notifyDataSetChanged ();
}
public AuditFragment()
{
}
@Override
public void onClick ( View view )
{
...
}
@Override
public View onCreateView ( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState )
{
...
}
@Override
public void onStart ()
{
L.logMethodCall ();
super.onStart ();
getListView ().setAdapter ( auditListAdapter );
updateFragmentGui ();
}
@Override
public void onResume ()
{
L.logMethodCall ();
super.onResume ();
...
}
private void updateFragmentGui ()
{
L.logMethodCall ();
...
}
private class AuditListAdapter extends ArrayAdapter<RecordEntry>
{
...
}
}
// 2. ==== Activity which executes Fragment ====
public class AuditActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
L.logMethodCall(savedInstanceState);
setContentView(R.layout.audit);
// Add "static" fragments
if (savedInstanceState == null) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
AuditFragment audit = new AuditFragment();
ft.add(R.id.kstation_audit_audit_frag, audit);
ft.commit();
}
}
@Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.donothing, R.anim.collapse_righttoleft);
}
}
// 3. ==== Method in main Activity ====
public void showAudit() {
Intent intent = new Intent(C.getActivity(), AuditActivity.class);
C.getActivity().startActivity(intent);
}
我正在三星 SM-T580 上进行测试。
当 onPreExecute 运行后应用程序“停止”时,以下任何操作都会导致 doInBackground 立即执行: - 触摸“最近”按钮
- 按主页键
- 向下滚动并选择设置图标
看起来 AuditFragment 或其父 Activity 的生命周期状态的变化正在解除对 doInBackground 执行的阻塞。
更新:我已设法恢复 Android 7 的 LogCat 可见性(使用 sdk 工具 Monitor 而不是 Eclipse),因此获得了一些调试信息。
实验: - 恢复 updateAuditTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) (而不是使用我自己的执行器) 并在调用 executeOnExecutor 之前将 THREAD_POOL_EXECUTOR 属性输出到 LogCat
-
Android 7. Stalls,即 doInBackground 不执行。
THREAD_POOL_EXECUTOR.getCorePoolSize () : 4 THREAD_POOL_EXECUTOR.getMaximumPoolSize(): 17 THREAD_POOL_EXECUTOR.getPoolSize() : 4 THREAD_POOL_EXECUTOR.getActiveCount() : 4 -
Android 6. 不会停止,即 doInBackground 会执行。
THREAD_POOL_EXECUTOR.getCorePoolSize () : 5 THREAD_POOL_EXECUTOR.getMaximumPoolSize(): 9 THREAD_POOL_EXECUTOR.getPoolSize() : 5 THREAD_POOL_EXECUTOR.getActiveCount() : 5
我对此感到困惑:在每种情况下,当前活动的线程都不少于核心池大小; 新任务在 Android6 中运行,但在 Android7 中不运行。
实验 2。 禁用一个较早开始的 AsyncTask。这一次,THREAD_POOL_EXECUTOR 属性和之前一样,但是任务没有停止。
-
Android 7. 不会停止,即 doInBackground 会执行。
THREAD_POOL_EXECUTOR.getCorePoolSize () : 4 THREAD_POOL_EXECUTOR.getMaximumPoolSize(): 17 THREAD_POOL_EXECUTOR.getPoolSize() : 4 THREAD_POOL_EXECUTOR.getActiveCount() : 4
所以看起来池大小等与任务是否执行无关?
(澄清。早些时候,我错误地报告说我在禁用早期任务的情况下尝试过它;相反,我禁用了一些在单独线程上运行的任务。)
【问题讨论】:
-
发布您的代码。我有一种预感,问题出在您的
Activity,而不是AsyncTask本身。 -
您是否正在运行任何其他异步任务?
-
是的,还有其他几个 AsyncTask 正在运行。但是,昨天我尝试了以下更改但没有成功: // 27/06/17 这应该是一个普遍的改进 //new UpdateAuditTask ().execute (); UpdateAuditTask updateAuditTask = new UpdateAuditTask(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) updateAuditTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);否则 updateAuditTask.execute((Void[])null);
-
用
AsyncTask.THREAD_POOL_EXECUTOR调用executeOnExecutor只是在它本来可以运行的同一个执行器上运行它。尝试在您提供的另一个执行器上运行它。 -
您的建议已经奏效。我根据developer.android.com/training/multiple-threads/… 中的详细信息创建了一个 ThreadPoolExecutor。现在稍微整理一下。谢谢。