我正在开发一个在所有活动中都使用通用标头的应用程序。标头包含一种指示任务完成的自定义进度栏。“进度条”是通过将SurfaceView子类化而实现的,而绘制操作则由inner进行管理ExecutorService
。
单例自定义AsyncTaskManager发出告诉“进度条”运行特定动画的任务,该自定义AsyncTaskManager包含对自定义SurfaceView
和当前活动的引用。
AsyncTask
单例管理器控件中的某些控件是在自定义“活动”onCreate
方法上执行的,因此有时AsyncTaskManager会在实际显示活动之前通知进度条进行动画处理。用户还可能在进度条的绘制Runnable
任务完成之前选择切换活动。为了更好地解释,这是我切换到一些活动时发生的情况:
oldActivity告诉其ExecutorService
取消Future
在SurfaceView画布上绘制的任务。
触发newActivity的onCreate并发出AsyncTaskManager单例以启动新的AsyncTask。
AsyncTask在其onPreExecute中指示进度条开始在其画布上绘制。
该ExecutorService
管理图纸Runnable
,进而锁定SurfaceHolder
当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] 删除。
我来说两句