我的应用程序有一个带有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立即执行: - 触摸“最近”按钮 - 按Home键 - 向下滚动并选择“设置”图标
看起来,AuditFragment或其父Activity的生命周期状态发生了变化,解除了doInBackground的执行。
更新:我已经设法恢复Android 7的LogCat可见性(使用sdk工具Monitor而不是Eclipse),因此获得一些调试信息。
实验: - 恢复updateAuditTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)(而不是使用我自己的执行程序) 在调用executeOnExecutor之前将THREAD_POOL_EXECUTOR属性输出到LogCat
Android 7.失速,即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
因此看起来池大小等与任务是否执行无关?
(澄清。早些时候,我错误地报告说我已经尝试过禁用早期任务;我已经禁用了一些在不同线程上运行的任务。)
答案 0 :(得分:0)
默认情况下,AsyncTask.execute()
使用AsyncTask.SERIAL_EXECUTOR
,因此单个长时间运行的任务将阻止任何其他任务运行。我通过查看Marshmallow AsyncTask
source并在我的6.0.1设备上对其进行了验证来发现了这一点。
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(final Void... voids) {
Log.d("DERP", "bad task starting");
try {
Thread.sleep(5000);
}
catch(InterruptedException e) {
// ignore
}
finally {
Log.d("DERP", "bad task exiting");
}
return null;
}
}.execute();
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(final Void... voids) {
Log.d("DERP", "nice task running");
return null;
};
}.execute();
输出:
06-30 15:43:23.777 8761-8792/com.chalcodes.rogueasynctask D/DERP: bad task starting
06-30 15:43:28.777 8761-8792/com.chalcodes.rogueasynctask D/DERP: bad task exiting
06-30 15:43:28.779 8761-9084/com.chalcodes.rogueasynctask D/DERP: nice task running
我建议使用executeOnExecutor(...)
并提供自己的执行人。如果您无法控制的两个任务相互干扰,您可以使用此hack来更改默认执行程序。 AsyncTask
有一个公共静态setDefaultExecutor
方法,但它标记为@hide
,因此您必须通过反射访问它。
try {
final Method method = AsyncTask.class.getMethod("setDefaultExecutor", Executor.class);
method.invoke(null, executor);
} catch(Exception e) {
Log.e("AsyncTask", "error setting default executor", e);
}
但这并不能解释Android 6和7之间的变化。也许与AsyncTask
无关的内容发生了变化,导致一个AsyncTask
阻止了串行执行程序。
这些是在Android 7中更改AsyncTask
的提交。我没有看到吸烟枪。
答案 1 :(得分:-1)
我在应用程序上遇到了同样的问题。这真的很奇怪,因为相同的代码正在其他一些设备上运行,具有相同的版本,配置...... 但是,解决方法可以帮助您:使用 Rx 而不是AsyncTask。我无法解释主要原因,但它解决了我的阻塞问题。