Android-在活动之间共享Surfaceview或在切换活动时阻止绘图任务锁定主线程

路易吉尼

我正在开发一个在所有活动中都使用通用标头的应用程序。标头包含一种指示任务完成的自定义进度栏。“进度条”是通过将SurfaceView子类化而实现的,而绘制操作则由inner进行管理ExecutorService

单例自定义AsyncTaskManager发出告诉“进度条”运行特定动画的任务,该自定义AsyncTaskManager包含对自定义SurfaceView和当前活动的引用

AsyncTask单例管理器控件中的某些控件是在自定义“活动”onCreate方法上执行的,因此有时AsyncTaskManager会在实际显示活动之前通知进度条进行动画处理。用户还可能在进度条的绘制Runnable任务完成之前选择切换活动为了更好地解释,这是我切换到一些活动时发生的情况:

  1. oldActivity告诉其ExecutorService取消Future在SurfaceView画布上绘制任务。

  2. 触发newActivity的onCreate并发出AsyncTaskManager单例以启动新的AsyncTask。

  3. AsyncTask在其onPreExecute中指示进度条开始在其画布上绘制。

  4. ExecutorService管理图纸Runnable,进而锁定SurfaceHolder

  5. 当AsyncTask完成时,在其onPostExecute方法中,告诉Surfaceview绘图Runnable根据结果​​绘制其他内容。

我遇到的问题是,SOMETIMES(并非总是-似乎是随机的,但可能与任务线程池有关),在启动新活动时,应用程序跳过了xx帧,而xx显然是随机的(有时它跳过了〜30帧,其他约300次,其他时候应用会获得ANR)。

我已经尝试解决这几天了,但无济于事。

我认为问题可能是以下之一或两者结合的:

  • 绘图线程未及时取消/结束,从而导致SurfaceHolder保持锁定状态,从而阻止Activity在View进入onPause / onResume时控制View,从而导致主线程跳过帧。动画绝不算繁重(几个点在周围移动),但它至少需要持续300毫秒才能正确通知用户。

  • 单例AsyncTaskManager保留对“离开活动”的SurfaceView的引用,以防止前者被破坏,直到释放表面保持器并导致跳帧。

我更倾向于相信第二个问题是让Coreographer生气的原因,因此导致了以下问题:

如何在所有活动之间共享SAME(在同一实例中)surfaceView(或实际上是任何视图),或者如何允许在SurfaceView不等待线程加入/中断的情况下销毁和重新创建SAME的当前实例

到目前为止,SurfaceView在活动之间切换时正在销毁/重新创建,如果新活动开始其生命周期时它的绘制线程停止,我对此将一无所获。

这是自定义AsyncTaskManager,其中包含对SurfaceView的引用

public class AsyncTaskManager { 

    private RefreshLoaderView mLoader;

    //reference to the customactivity holding the surfaceview
    private CustomBaseActivity mActivity;

    private final ConcurrentSkipListSet<RequestedTask> mRequestedTasks;

    private volatile static AsyncTaskManager instance;

    private AsyncTaskManager() {
        mRequestedTasks = new ConcurrentSkipListSet<RequestedTask>(new RequestedTaskComparator());
    }

    public void setCurrentActivity(CustomBaseActivity activity) {
        mActivity = activity;

        if (mLoader != null) {
            mLoader.onDestroy();
        }
        mLoader = (RefreshLoaderView) mActivity.getViewById(R.id.mainLoader);

    }

这是执行AsyncTask(上面的代码片段中的RequestedTask)时发生的情况

    @Override
            protected void onPreExecute() {
                if (mLoader != null) {
                    mLoader.notifyTaskStarted();
                }
            }

            @Override
            protected Integer doInBackground(Void... params) {
                //do the heavy lifting here...
            }

            @Override
            protected void onPostExecute(Integer result) {


                switch (result) {

                    case RESULT_SUCCESS: 

                        if (mLoader != null) {
                            mLoader.notifyTaskSuccess();
                        }
                    break; 
//TELLS THE SURFACE VIEW TO PLAY DIFFERENT ANIMATIONS ACCORDING TO RESULT ...

这是CustomBaseActivity,其中包含所有其他活动都从其继承的SurfaceView。

    public abstract class CustomBaseActivity extends FragmentActivity {

        private volatile RefreshLoaderView mLoader;

//...

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            super.setContentView(R.layout.activity_base);

            mLoaderContainer = (FrameLayout) findViewById(R.id.mainLoaderContainer);
            mLoader = (RefreshLoaderView) findViewById(R.id.mainLoader);

//other uninteresting stuff goin on ...

还有SurfaceView的代码:

    public class RefreshLoaderView extends SurfaceView implements SurfaceHolder.Callback {


        private LoaderThread mLoaderThread;
        private volatile SurfaceHolder mHolder;

        private static final int ANIMATION_TIME = 600;

        private final ExecutorService mExecutor; 

        private Future mExecutingTask;


        public RefreshLoaderView(Context context) {
            super(context);
            ...
            init();

        }


        private void init() {
            mLoaderThread = new LoaderThread();
            ...
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int arg1, int arg2, int arg3) {
...
            mHolder = this.getHolder();
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            //uninteresting stuff here
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            stopThread();

        }

        private void stopThread() {
            mLoaderThread.setRunning(false);
            if (mExecutingTask != null) {
                mExecutingTask.cancel(true);
            }
        }

        private void startThread() {
            if (mLoaderThread == null) {
                mLoaderThread = new LoaderThread();
            }

            mLoaderThread.setRunning(true);

            mExecutingTask = mExecutor.submit(mLoaderThread);
        }

        public void notifyTaskStarted() {
            stopThread();
            startThread();
            mLoaderThread.setAction(LoaderThread.ANIMATION_TASK_STARTED);
        }

        public void notifyTaskFailed() {
            mLoaderThread.setAction(LoaderThread.ANIMATION_TASK_FAILED);
        }

        public void notifyTaskSuccess() {
            mLoaderThread.setAction(LoaderThread.ANIMATION_TASK_SUCCESS);
        }


        private class LoaderThread implements Runnable {

            private volatile boolean  mRunning = false;
            private int mAction;
            private long mStartTime;
            private int mMode;
            public final static int ANIMATION_TASK_STARTED = 0;
            public final static int ANIMATION_TASK_FAILED = 1;
            public final static int ANIMATION_TASK_SUCCESS = 2;

            private final static int MODE_COMPLETING = 0;
            private final static int MODE_ENDING = 1;

            public LoaderThread() {
                mMode = 0;
            }

            public synchronized boolean isRunning() {
                return mRunning;
            }

            public synchronized void setRunning(boolean running) {
                mRunning = running;
                if (running) {
                    mStartTime = System.currentTimeMillis();
                }
            }

            public void setAction(int action) {
                mAction = action;
            }

            @Override
            public void run() {

                if (!mRunning) {

                    return;
                }
                while (mRunning) {
                    Canvas c = null;
                    try {
                        c = mHolder.lockCanvas();
                        synchronized (mHolder) {
                            //switcho quello che devo animare
                            if (c != null) {
                                switch (mAction) {
                                    case ANIMATION_TASK_STARTED:
                                        animationTaskStarted(c);
                                    break;
                                    case ANIMATION_TASK_FAILED:
                                        animationTaskFailed(c, mMode);
                                    break;
                                    case ANIMATION_TASK_SUCCESS:
                                        animationTaskSuccess(c, mMode);
                                    break;
                                }
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        if (c != null) {
                            mHolder.unlockCanvasAndPost(c);
                        }
                    }
                }
            }

            private void animationTaskStarted(Canvas canvas) {
                //do an animation here
            }


            private void animationCloseLoaderCycle(Canvas canvas) {
                //do stuff here ...
                } else {
                    mStartTime = System.currentTimeMillis();
                    mMode = MODE_ENDING;
                }
            }

            private void queryThreadClose() {
                mProgress = 0;
                mMode = MODE_COMPLETING;
                mRunning = false;
            }

            private void animationTaskFailed(Canvas canvas, int mode) {

                switch (mode) {
                    case MODE_COMPLETING:
                        animationCloseLoaderCycle(canvas);
                    break;
                    case MODE_ENDING:
                        if (System.currentTimeMillis() - mStartTime < ANIMATION_TIME) {
                            //notify user task is failed
                        } else {
                            queryThreadClose();
                        }
                    break;
                }
            }

            private void animationTaskSuccess(Canvas canvas, int mode) {

                switch (mode) {
                    case MODE_COMPLETING:
                        animationCloseLoaderCycle(canvas);
                    break;
                    case MODE_ENDING:
                        if (System.currentTimeMillis() - mStartTime < ANIMATION_TIME) {
                            //notify user task is failed
                        } else {
                            queryThreadClose();
                        }
                    break;
                }
            }
        }


        public void onPause() {
            stopThread();
        }

        public void onStop() {
            stopThread();
        }

        public void onDestroy() {
            stopThread();
        }

    }

当Coreographer警告我时使用DDMS时,我跳过帧显示,通常有大约30个线程(守护程序和普通线程)正在运行,其中asynctask,主线程和绘图任务正在等待某些东西。(此外,我该如何检查他们还在等什么?)

在此先感谢您的帮助。

编辑:根据DDMS线程视图,这些是挂起时的主线程调用:

at hava.lang.Object.wait(Native Method)
at java.lang.Thread.parkFor(Thread.java:1205)
at sun.misc.Unsafe.park(Unsafe.java:325)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:157)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:813)
...
路易吉尼

我最终解决了这个问题。同步块中有一个错误:

while (mRunning) {
                    Canvas c = null;
                    try {
          //mistake was here
                        c = mHolder.lockCanvas();
                        synchronized (mHolder) {

                            if (c != null) {
                             //do stuff
                            }
                        }
                    }

我将画布移到同步块之外,从而在需要销毁/重新创建活动时导致死锁。

c = mHolder.lockCanvas();在同步块内部移动解决了这个问题。

最后,工作代码如下:

synchronized (mHolder) {
                        c = mHolder.lockCanvas();
                        if (c != null) {
                            switch (mAction) {
                                //do stuff
                            }
                        }
                    }

不管怎么说,还是要谢谢你!

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

在活动之间切换时,Android App崩溃

来自分类Dev

在活动android之间切换时黑屏黑屏

来自分类Dev

Android在活动之间切换

来自分类Dev

无法在android中的活动之间切换

来自分类Dev

在Android的三个活动之间切换?

来自分类Dev

无法在android中的活动之间切换

来自分类Dev

在Android设计中的活动之间切换

来自分类Dev

在活动之间切换时重置Android中的倒数计时器

来自分类Dev

Android App切换活动

来自分类Dev

在Android中切换活动

来自分类Dev

切换活动Android Studio

来自分类Dev

使用Android L动画在活动之间进行切换

来自分类Dev

是否可以分配热键在Android Studio中的“活动”和“ XML”布局之间切换?

来自分类Dev

android活动之间的缓慢切换。尝试使用进度对话框显示其加载

来自分类Dev

android活动之间的缓慢切换。尝试使用进度对话框显示其加载

来自分类Dev

在Android App中后台运行AsyncTask(Facebook登录)时切换活动

来自分类Dev

切换活动时未显示AdMob插页式广告(Android)

来自分类Dev

Android活动在切换应用程序时崩溃

来自分类Dev

通过滑动Android Java来切换屏幕/活动

来自分类Dev

Android-在单个活动中切换多个布局

来自分类Dev

在Android中切换具有不同布局的活动

来自分类Dev

当我切换活动或关闭应用程序时,Android中的共享首选项不会保存

来自分类Dev

活动之间的Android共享菜单

来自分类Dev

Android:在两个活动之间切换错误-不幸的是,“应用程序名称”已停止

来自分类Dev

Android 在切换到新活动时崩溃

来自分类Dev

每次单击在 Android Studio 中来回切换活动时如何将值增加一

来自分类Dev

Android UI线程在新的活动中被阻止

来自分类Dev

在输入之间切换时,Chrome Android隐藏键盘

来自分类Dev

Android中活动之间的对象共享

Related 相关文章

  1. 1

    在活动之间切换时,Android App崩溃

  2. 2

    在活动android之间切换时黑屏黑屏

  3. 3

    Android在活动之间切换

  4. 4

    无法在android中的活动之间切换

  5. 5

    在Android的三个活动之间切换?

  6. 6

    无法在android中的活动之间切换

  7. 7

    在Android设计中的活动之间切换

  8. 8

    在活动之间切换时重置Android中的倒数计时器

  9. 9

    Android App切换活动

  10. 10

    在Android中切换活动

  11. 11

    切换活动Android Studio

  12. 12

    使用Android L动画在活动之间进行切换

  13. 13

    是否可以分配热键在Android Studio中的“活动”和“ XML”布局之间切换?

  14. 14

    android活动之间的缓慢切换。尝试使用进度对话框显示其加载

  15. 15

    android活动之间的缓慢切换。尝试使用进度对话框显示其加载

  16. 16

    在Android App中后台运行AsyncTask(Facebook登录)时切换活动

  17. 17

    切换活动时未显示AdMob插页式广告(Android)

  18. 18

    Android活动在切换应用程序时崩溃

  19. 19

    通过滑动Android Java来切换屏幕/活动

  20. 20

    Android-在单个活动中切换多个布局

  21. 21

    在Android中切换具有不同布局的活动

  22. 22

    当我切换活动或关闭应用程序时,Android中的共享首选项不会保存

  23. 23

    活动之间的Android共享菜单

  24. 24

    Android:在两个活动之间切换错误-不幸的是,“应用程序名称”已停止

  25. 25

    Android 在切换到新活动时崩溃

  26. 26

    每次单击在 Android Studio 中来回切换活动时如何将值增加一

  27. 27

    Android UI线程在新的活动中被阻止

  28. 28

    在输入之间切换时,Chrome Android隐藏键盘

  29. 29

    Android中活动之间的对象共享

热门标签

归档