我的应用具有带有异步的片段,该片段从数据库中获取记录,并使用listView显示它们。已经运行良好了几年,但是现在在Android 7 IT停滞不前,没有显示记录。但是,离开应用程序(例如进入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立即执行: - 触摸最近的按钮 - 按主键 - 向下滚动并选择设置图标
看来,审计范围的生命周期状态或其父活动的变化正在解除执行Doinbackground。
更新:我已经设法恢复了Android 7的LogCat可见性(使用的SDK工具监视器而不是Eclipse(,因此获取一些调试信息。
实验: -RESTORE UPDATAUDITTASK.EXECUTEONEXECUTOR(asynctask.thread_pool_executor((而不是使用我自己的执行程序( 在调用executeOneXecutor
之前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。禁用较早开始的一个异步。这次,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
因此,看来泳池大小等对任务是否执行没有任何方面?
(澄清。之前,我错误地报道了我已经禁用了较早的任务;而不是禁用了某些在单独线程上运行的任务。(
默认情况下,AsyncTask.execute()
使用AsyncTask.SERIAL_EXECUTOR
,因此单个长期运行的任务将阻止任何其他任务运行。我通过查看棉花糖AsyncTask
来源发现了这一点,并在我的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
阻止串行executor。
这些是在Android 7中改变AsyncTask
的提交。我看不到吸烟枪。
- 调整asynctask#thread_pool_executor设置
- 更多修复问题#22765972:粘合剂交易用尽了地址空间,导致软件包管理器失败
- 修复异步箱以处理Doinbackground中的异常
- 异步终止的异常呼叫oncancelded
我在正在处理的应用程序上遇到了同样的问题。这真的很奇怪,因为相同的代码在其他一些设备上使用相同的版本,配置...但是,解决方法可以帮助您:使用 rx 代替异步。我无法解释主要原因,但它解决了我的阻塞问题。