我正在开发一个在其所有活动中使用通用标头的应用程序。
标题包含一种表示任务完成的自定义进度条。 "进度条"通过继承SurfaceView实现,绘图操作由内部ExecutorService
管理。
告诉"进度条的任务"运行某个动画由Singleton自定义AsyncTaskManager发出,它包含对自定义SurfaceView
和当前活动的引用。
单个管理器控件的一些AsyncTask
是在自定义活动onCreate
方法上执行的,因此有时AsyncTaskManager会在实际显示活动之前通知进度条动画。
在进度条的绘图Runnable
任务完成之前,用户可能还会选择切换活动。
为了更好地解释,当我切换到某些活动时会发生这种情况:
oldActivity告诉ExecutorService
取消它在SurfaceView画布上绘制的Future
任务。
触发newActivity的onCreate并发出AsyncTaskManager 单身人士开始新的AsyncTask。
onPreExecute中的AsyncTask告诉进度条开始在画布上绘图。
ExecutorService
依次管理图纸Runnable
锁定SurfaceHolder
当AsyncTask完成时,在其onPostExecute方法中,
告诉surfaceview drawing Runnable
画一个不同的东西
根据结果。
我遇到的问题是SOMETIMES(并非总是 - 似乎是随机的,但可能与任务线程池有关),在启动新活动时,应用程序跳过帧xx,其中xx显然是随机的(有时它跳过~30帧,其他时间约为300,其他时间应用获得ANR)。
我一直试图解决这个问题已有几天了,但无济于事。
我认为问题可能是以下之一,也可能是两者的结合:
绘图线程不会及时取消/结束,从而导致SurfaceHolder保持锁定状态,从而阻止Activity在视图中执行onPause / onResume时控制View,从而导致主线程跳过帧。动画在计算方面并不重要(几个点移动)但是它需要持续至少300ms以正确通知用户。
单例AsyncTaskManager保存对"离开活动"的SurfaceView的引用,防止前者被销毁,直到释放出曲面所有者并导致跳帧。
我更倾向于认为第二个问题是让Coreographer感到愤怒,因此导致了以下问题:
如何在所有活动之间共享相同的SAME(在同一实例中)surfaceView(或任何视图),或者允许在不等待的情况下销毁和重新创建SurfaceView
的当前实例。线程加入/中断?
就像现在一样,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 ...
这是包含所有其他活动继承的SurfaceView的CustomBaseActivity。
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)
...
答案 0 :(得分:1)
我最终解决了这个问题。同步块中有一个错误:
while (mRunning) {
Canvas c = null;
try {
//mistake was here
c = mHolder.lockCanvas();
synchronized (mHolder) {
if (c != null) {
//do stuff
}
}
}
我在同步块之外得到了画布,因此当需要销毁/重新创建活动时会导致死锁。
在synchronized块中移动c = mHolder.lockCanvas();
解决了这个问题。
最后工作代码如下:
synchronized (mHolder) {
c = mHolder.lockCanvas();
if (c != null) {
switch (mAction) {
//do stuff
}
}
}
非常感谢!