Android 7

时间:2017-06-27 16:43:50

标签: android android-asynctask

我的应用程序有一个带有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

  1. Android 7.失速,即doInBackground不执行。

            THREAD_POOL_EXECUTOR.getCorePoolSize ()  : 4
            THREAD_POOL_EXECUTOR.getMaximumPoolSize(): 17
            THREAD_POOL_EXECUTOR.getPoolSize()       : 4
            THREAD_POOL_EXECUTOR.getActiveCount()    : 4
    
  2. Android 6.不会停止,即doInBackground会执行。

            THREAD_POOL_EXECUTOR.getCorePoolSize ()  : 5
            THREAD_POOL_EXECUTOR.getMaximumPoolSize(): 9
            THREAD_POOL_EXECUTOR.getPoolSize()       : 5
            THREAD_POOL_EXECUTOR.getActiveCount()    : 5
    
  3. 我对此感到困惑:在每种情况下,当前活动的核心池大小线程都不少; 新任务在Android6中运行,但在Android7中不运行。

    实验2。 禁用一个先前启动的AsyncTask。这次,THREAD_POOL_EXECUTOR属性与以前保持相同,但任务不会停止。

    1. Android 7.不会停止,即doInBackground会执行。

              THREAD_POOL_EXECUTOR.getCorePoolSize ()  : 4
              THREAD_POOL_EXECUTOR.getMaximumPoolSize(): 17
              THREAD_POOL_EXECUTOR.getPoolSize()       : 4
              THREAD_POOL_EXECUTOR.getActiveCount()    : 4
      
    2. 因此看起来池大小等与任务是否执行无关?

      (澄清。早些时候,我错误地报告说我已经尝试过禁用早期任务;我已经禁用了一些在不同线程上运行的任务。)

2 个答案:

答案 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。我无法解释主要原因,但它解决了我的阻塞问题。