Android上的SurfaceView方法“onTouchEvent(…)”中的ANR

概述在Android上,我已经将SurfaceView子类化,并且结果视图在大多数情况下都能正常工作.但是,大约1%的用户报告了此实现的ANR问题.显然,有一个边缘情况,SurfaceView因某些问题而失败,可能是死锁.不幸的是,我不知道我的onDraw(…)和onTouchEvent(…)的实现有什么问题,或者如何改进代码.

在Android上,我已经将SurfaceVIEw子类化,并且结果视图在大多数情况下都能正常工作.但是,大约1%的用户报告了此实现的ANR问题.

显然,有一个边缘情况,SurfaceVIEw因某些问题而失败,可能是死锁.

不幸的是,我不知道我的onDraw(…)和ontouchEvent(…)的实现有什么问题,或者如何改进代码.你能帮我吗?

"main" prio=5 tID=1 MONITOR| group="main" sCount=1 dsCount=0 obj=0x41920e88 self=0x4190f8d0| sysTID=13407 nice=0 sched=0/0 cgrp=apps handle=1074618708| state=S schedstat=( 50780242971 27570770290 130442 ) utm=4254 stm=824 core=0at com.my.package.util.HandCards.ontouchEvent(Sourcefile:~188)- waiting to lock <0x45b91988> (a androID.vIEw.SurfaceVIEw) held by tID=18 (Thread-14297)at androID.vIEw.VIEw.dispatchtouchEvent(VIEw.java:7837)at androID.vIEw.VIEwGroup.dispatchtransformedtouchEvent(VIEwGroup.java:2216)at androID.vIEw.VIEwGroup.dispatchtouchEvent(VIEwGroup.java:1917)at androID.vIEw.VIEwGroup.dispatchtransformedtouchEvent(VIEwGroup.java:2216)at androID.vIEw.VIEwGroup.dispatchtouchEvent(VIEwGroup.java:1917)at androID.vIEw.VIEwGroup.dispatchtransformedtouchEvent(VIEwGroup.java:2216)at androID.vIEw.VIEwGroup.dispatchtouchEvent(VIEwGroup.java:1917)at androID.vIEw.VIEwGroup.dispatchtransformedtouchEvent(VIEwGroup.java:2216)at androID.vIEw.VIEwGroup.dispatchtouchEvent(VIEwGroup.java:1917)at com.androID.internal.policy.impl.PhoneWindow$DecorVIEw.superdispatchtouchEvent(PhoneWindow.java:2075)at com.androID.internal.policy.impl.PhoneWindow.superdispatchtouchEvent(PhoneWindow.java:1522)at androID.app.Activity.dispatchtouchEvent(Activity.java:2458)at com.androID.internal.policy.impl.PhoneWindow$DecorVIEw.dispatchtouchEvent(PhoneWindow.java:2023)at androID.vIEw.VIEw.dispatchPointerEvent(VIEw.java:8017)at androID.vIEw.VIEwRootImpl$VIEwPostImeinputStage.processpointerEvent(VIEwRootImpl.java:3966)at androID.vIEw.VIEwRootImpl$VIEwPostImeinputStage.onProcess(VIEwRootImpl.java:3845)at androID.vIEw.VIEwRootImpl$inputStage.deliver(VIEwRootImpl.java:3405)at androID.vIEw.VIEwRootImpl$inputStage.onDeliverToNext(VIEwRootImpl.java:3455)at androID.vIEw.VIEwRootImpl$inputStage.forward(VIEwRootImpl.java:3424)at androID.vIEw.VIEwRootImpl$AsyncinputStage.forward(VIEwRootImpl.java:3531)at androID.vIEw.VIEwRootImpl$inputStage.apply(VIEwRootImpl.java:3432)at androID.vIEw.VIEwRootImpl$AsyncinputStage.apply(VIEwRootImpl.java:3588)at androID.vIEw.VIEwRootImpl$inputStage.deliver(VIEwRootImpl.java:3405)at androID.vIEw.VIEwRootImpl$inputStage.onDeliverToNext(VIEwRootImpl.java:3455)at androID.vIEw.VIEwRootImpl$inputStage.forward(VIEwRootImpl.java:3424)at androID.vIEw.VIEwRootImpl$inputStage.apply(VIEwRootImpl.java:3432)at androID.vIEw.VIEwRootImpl$inputStage.deliver(VIEwRootImpl.java:3405)at androID.vIEw.VIEwRootImpl.deliverinputEvent(VIEwRootImpl.java:5554)at androID.vIEw.VIEwRootImpl.doProcessinputEvents(VIEwRootImpl.java:5534)at androID.vIEw.VIEwRootImpl.enqueueinputEvent(VIEwRootImpl.java:5505)at androID.vIEw.VIEwRootImpl$WindowinputEventReceiver.oninputEvent(VIEwRootImpl.java:5634)at androID.vIEw.inputEventReceiver.dispatchinputEvent(inputEventReceiver.java:185)at androID.os.MessageQueue.nativePollOnce(Native Method)at androID.os.MessageQueue.next(MessageQueue.java:138)at androID.os.Looper.loop(Looper.java:196)at androID.app.ActivityThread.main(ActivityThread.java:5135)at java.lang.reflect.Method.invokeNative(Native Method)at java.lang.reflect.Method.invoke(Method.java:515)at com.androID.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:878)at com.androID.internal.os.ZygoteInit.main(ZygoteInit.java:694)at dalvik.system.NativeStart.main(Native Method)..."Thread-14297" prio=5 tID=18 SUSPENDED| group="main" sCount=1 dsCount=0 obj=0x45ba6358 self=0x76036b38| sysTID=21120 nice=0 sched=0/0 cgrp=apps handle=1979936656| state=S schedstat=( 48296386737 3088012659 22649 ) utm=4691 stm=138 core=0#00 pc 00021adc /system/lib/libc.so (__futex_syscall3+8)#01 pc 0000f074 /system/lib/libc.so (__pthread_cond_timeDWait_relative+48)#02 pc 0000f0d4 /system/lib/libc.so (__pthread_cond_timeDWait+64)#03 pc 0005655f /system/lib/libdvm.so#04 pc 00056b21 /system/lib/libdvm.so (dvmChangeStatus(Thread*, ThreadStatus)+34)#05 pc 00050fd7 /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*)+406)#06 pc 00000214 /dev/ashmem/dalvik-jit-code-cache (deleted)at androID.graphics.Canvas.native_drawBitmap(Native Method)at androID.graphics.Canvas.drawBitmap(Canvas.java:1202)at com.my.package.util.HandCards.a(Sourcefile:178)at com.my.package.util.HandCards.onDraw(Sourcefile:136)at com.my.package.util.d.run(Sourcefile:36)

HandCards.ontouchEvent(Sourcefile:~188)的位置是:

synchronized (mRenderThread.getSurfaceHolder()) {

而HandCards.a(Sourcefile:178)是:

canvas.drawBitmap(drawCardBitmap, null, mDrawingRect, mGraphicsPaint);

SurfaceVIEw子类的完整代码是:

public class HandCards extends SurfaceVIEw implements SurfaceHolder.Callback {    /** Opacity of the shadow layer that hIDes other cards when one card is highlighted and covers all cards when it's another player's turn (where 0 is transparent and 255 is opaque) */    private static final int SHADOW_Alpha = 150;    private static SparseArray<Bitmap> mCardCache = new SparseArray<Bitmap>(); // cache array for own card bitmaps    private HandThread mRenderThread;    private volatile List<Card> mCards;    private volatile int mCardCount;    private volatile int mScreenWIDth;    private volatile int mScreenHeight;    private volatile int mCarDWIDth;    private volatile int mCardHeight;    private volatile int mHighlightedCard = -1;    private CardClickCallback mCardClickCallback;    private volatile int mblattID = 1;    private volatile int mCurrentCardSpacing;    private final Paint mGraphicsPaint;    private final Paint mShadowPaint;    private final Rect mDrawingRect;    private volatile int mtouchEventAction;    private volatile int mtouchEventCard;    private Bitmap drawCardBitmap;    private volatile int mOnDrawX1;    private final BitmapFactory.Options mBitmapOptions;    private volatile boolean mIsActive = true;    private final int[] mCardSelection = new int[GameState.MAX_SWAP_CARDS];    /** Indicates that the card vIEw is currently used for choosing some cards to create a selection */    private volatile boolean mIsChooseMode;    /** Holds the index of the selected card that will be replaced next if all selection slots are full */    private volatile int mNextReplaceposition;    /** Used only locally in drawCard() but is declared here to save repeated allocations */    private volatile int mCardOffsetY;    private volatile int mrequiredSelectionCount;    public HandCards(Context activityContext, AttributeSet attributeSet) {        super(activityContext, attributeSet);        getHolder().addCallback(this);        setFocusable(true); // touch events should be processed by this class        mCards = new ArrayList<Card>();        mGraphicsPaint = new Paint();        mGraphicsPaint.setAntiAlias(true);        mGraphicsPaint.setFilterBitmap(true);        mShadowPaint = new Paint();        mShadowPaint.setARGB(SHADOW_Alpha, 20, 20, 20);        mShadowPaint.setAntiAlias(true);        mBitmapOptions = new BitmapFactory.Options();        mBitmapOptions.ininputShareable = true;        mBitmapOptions.inPurgeable = true;        mDrawingRect = new Rect();    }    public Card getCard(int location) throws Exception {        if (mCards != null) {            synchronized (mCards) {                return mCards.get(location); // card may not be found (throw exception then)            }        }        return null;    }    public static Bitmap cardCacheGet(int key) {        synchronized (mCardCache) {            return mCardCache.get(key);        }    }    public static voID cardCachePut(int key, Bitmap object) {        synchronized (mCardCache) {            mCardCache.put(key, object);        }    }    public int[] getSelectedCards() {        return mCardSelection;    }    public voID setActive(boolean active) {        if (mCardSelection != null) {            for (int i = 0; i < GameState.MAX_SWAP_CARDS; i++) { // loop through all slots for selected cards                mCardSelection[i] = -1; // unset the slot so that it is empty by default            }        }        mIsActive = active;    }    public boolean isActive() {        return mIsActive;    }    public voID setChooseMode(boolean active, int swapCardCount) {        mNextReplaceposition = 0;        mIsChooseMode = active;        mrequiredSelectionCount = swapCardCount;    }    public boolean isChooseMode() {        return mIsChooseMode;    }    public voID stopThread() {        if (mRenderThread != null) {            mRenderThread.setRunning(false);        }    }    @OverrIDe    public voID onDraw(Canvas canvas) {        if (canvas != null) {            synchronized (mCards) {                mCardCount = mCards.size();                canvas.drawcolor(color.BLACK);                if (mCardCount > 0) {                    mCurrentCardSpacing = Math.min(mScreenWIDth/mCardCount, mCarDWIDth);                    for (int c = 0; c < mCardCount; c++) {                        if (c != mHighlightedCard || !isActive()) {                            try {                                drawCard(canvas, mCards.get(c).getDrawableID(mblattID), false, c*mCurrentCardSpacing, c*mCurrentCardSpacing+mCarDWIDth, c);                            }                            catch (Exception e) { }                        }                    }                    if (mHighlightedCard > -1 && isActive()) {                        mOnDrawX1 = Math.min(mHighlightedCard*mCurrentCardSpacing, mScreenWIDth-mCarDWIDth);                        try {                            drawCard(canvas, mCards.get(mHighlightedCard).getDrawableID(mblattID), true, mOnDrawX1, mOnDrawX1+mCarDWIDth, mHighlightedCard);                        }                        catch (Exception e) { }                    }                    else if (!isActive()) {                        drawCard(canvas, 0, true, 0, mScreenWIDth, 0);                    }                }            }        }    }    private voID drawCard(Canvas canvas, int resourceID, boolean highlighted, int xleft, int xRight, int cardposition) {        if (canvas != null) {            try {                if (highlighted) {                    canvas.drawRect(0, 0, mScreenWIDth, mScreenHeight, mShadowPaint);                }                if (resourceID != 0) {                    drawCardBitmap = cardCacheGet(resourceID);                    if (drawCardBitmap == null) {                        drawCardBitmap = BitmapFactory.decodeResource(getResources(), resourceID, mBitmapOptions);                        cardCachePut(resourceID, drawCardBitmap);                    }                    mCardOffsetY = 0; // by default draw all cards right at the bottom (without highlighting by position)                    if (mCardSelection != null) {                        for (int i = 0; i < GameState.MAX_SWAP_CARDS; i++) { // loop through all slots for selected cards                            if (mCardSelection[i] == cardposition) { // if current card has been selected (in that slot)                                mCardOffsetY = mScreenHeight*1/4; // lift the card by one quarter to highlight it                                break; // card has already been detected to be selected so stop here                            }                        }                    }                    mDrawingRect.set(xleft, mCardOffsetY, xRight, mCardHeight+mCardOffsetY);                    canvas.drawBitmap(drawCardBitmap, null, mDrawingRect, mGraphicsPaint);                }            }            catch (Exception e) { }        }    }    @OverrIDe    public boolean ontouchEvent(MotionEvent event) {        if (mRenderThread == null) { return false; }        synchronized (mRenderThread.getSurfaceHolder()) { // synchronized so that there are no concurrent accesses            mtouchEventAction = event.getAction();            if (isActive()) {                if (mtouchEventAction == MotionEvent.ACTION_DOWN || mtouchEventAction == MotionEvent.ACTION_MOVE) {                    if (event.getY() >= 0 && event.getY() < mScreenHeight) {                        mtouchEventCard = (int) event.getX()/mCurrentCardSpacing;                        if (mtouchEventCard > -1 && mtouchEventCard < mCardCount) {                            mHighlightedCard = mtouchEventCard;                        }                        else {                            mHighlightedCard = -1;                        }                    }                    else {                        mHighlightedCard = -1;                    }                }                else if (mtouchEventAction == MotionEvent.ACTION_UP) {                    if (mCardClickCallback != null && mHighlightedCard > -1 && mHighlightedCard < mCardCount) {                        if (isChooseMode()) { // card has been chosen as a swap card                            int freeSelectionIndex = -1; // remember the index of a free selection slot (default = none available)                            for (int i = 0; i < mrequiredSelectionCount; i++) { // loop through all allowed slots for selected cards                                if (mCardSelection[i] == mHighlightedCard) { // if this card has already been selected                                    mCardSelection[i] = -1; // unselect the card                                    freeSelectionIndex = -2; // mark that there is no need to select a new card                                    break; // slot of current card has already been found so stop here                                }                                else if (mCardSelection[i] == -1 && freeSelectionIndex == -1) { // if slot is still available and no free slot has been found yet                                    freeSelectionIndex = i; // remember the index of this free slot                                }                            }                            if (freeSelectionIndex > -2) { // if a new card is to be placed in the selection array                                if (freeSelectionIndex >= 0) { // if a free slot was available                                    mCardSelection[freeSelectionIndex] = mHighlightedCard; // just place the card there                                }                                else { // if no free slot was available anymore                                    mCardSelection[mNextReplaceposition] = mHighlightedCard; // replace another card in one of the slots                                    mNextReplaceposition = (mNextReplaceposition+1) % mrequiredSelectionCount; // advance the cursor that points to the slot which will be replaced next                                }                            }                        }                        else { // card has been selected to be played on the table                            try {                                mCardClickCallback.chooseCard(mCards.get(mHighlightedCard));                            }                            catch (Exception e) {                                // index was out of mCards' bounds (just ignore this, user may tap on card again)                            }                        }                    }                    mHighlightedCard = -1;                }            }            else {                try {                    mCardClickCallback.resyncManually();                }                catch (Exception e) { }            }        }        return true;    }    @OverrIDe    public voID surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { }    public voID setCards(List<Card> currentCards) {        synchronized (mCards) {            mCards.clear();            mCards.addAll(currentCards);        }    }    @OverrIDe    public voID surfaceCreated(SurfaceHolder arg0) {        mScreenWIDth = getWIDth();        mScreenHeight = getHeight();        mCardHeight = mScreenHeight;        mCarDWIDth = mCardHeight*99/150;        mCurrentCardSpacing = mCarDWIDth;        mRenderThread = new HandThread(getHolder(), this);        mRenderThread.setRunning(true);        mRenderThread.start();    }    @OverrIDe    public voID surfaceDestroyed(SurfaceHolder holder) {        boolean retry = true;        mRenderThread.setRunning(false); // stop thread        while (retry) { // wait for thread to close            try {                mRenderThread.join();                retry = false;            }            catch (InterruptedException e) { }        }    }    public synchronized voID setCardClickCallback(CardClickCallback callback) {        mCardClickCallback = callback;    }    public voID setblattID(int blattID) {        mblattID = blattID;    }}

然后,还有渲染线程:

public class HandThread extends Thread {    private final SurfaceHolder mSurfaceHolder;    private final HandCards mSurface;    private volatile boolean mRunning = false;    public HandThread(SurfaceHolder surfaceHolder, HandCards surface) {        mSurfaceHolder = surfaceHolder;        mSurface = surface;    }    public SurfaceHolder getSurfaceHolder() {        return mSurfaceHolder;    }    public voID setRunning(boolean run) {        mRunning = run;    }    @OverrIDe    public voID run() {        Canvas c;        while (mRunning) {            c = null;            try {                c = mSurfaceHolder.lockCanvas(null);                synchronized (mSurfaceHolder) {                    if (c != null) {                        mSurface.onDraw(c);                    }                }            }            finally { // when exception is thrown above we may not leave the surface in an inconsistent state                if (c != null) {                    try {                        mSurfaceHolder.unlockCanvasAndPost(c);                    }                    catch (Exception e) { }                }            }        }    }}

解决方法:

ANR正在发生,因为你的ontouchEvent()方法正在由tID = 18持有的锁上进行同步,这是一个只知道为Thread-14297的未命名线程.

许多人都遵循一种模式,在这种模式下,当他们锁定SurfaceVIEw画布时,他们还会锁定SurfaceHolder对象.在具有公共可见性的对象上进行同步是一个坏主意,甚至更糟糕的想法是在与GUI框架共享的对象上进行同步,因此很难过这种模式持续存在. (但我离题了.)

您正在绘制一个重写的onDraw()方法,如果您从渲染器线程绘制则没有意义 – 视图层次结构使用onDraw()方法并且将从UI线程调用,但是这里显然是从别处打来的.你应该把它叫做别的东西,也许只是myDraw(). (但我离题了.)

Thread-14297处于“挂起”状态,这意味着它正在执行但在捕获堆栈跟踪时停止.由于最顶层的帧是本机方法 – 它不会被VM暂停 – 它可能正在进入或退出帧.线程的系统和用户时间(以“utm =”和“stm =”值为单位显示)相当低,表明它没有做过多的cpu工作.当然,除非你的渲染线程是一次性的,在这种情况下,它相当繁忙(可能还没有完成).

好消息是你似乎没有陷入僵局.渲染线程运行缓慢.或者,也许,你有一个未能退出的循环(尽管从发布的代码中没有一个明显).在一个速度较慢的设备上,系统上有许多其他活动,以及一个大的mCards列表,它可能会因cpu而缺乏响应并且无法快速响应.假设您在抓取Canvas时遵循常见模式并锁定SurfaceHolder,则ontouchEvent()将在绘制的整个持续时间内锁定UI线程. logcat中的ANR摘要通常列出最近的线程活动级别;如果您有权访问该信息,则该信息可以告诉您渲染线程的繁忙程度.

并非所有的ANR都是致命的.如果应用程序永久无响应,则与用户点击“等待”时清除的临时ANR非常不同.你知道这是哪种吗?

你需要:

>重新评估您的数据同步.使用较短的窗口和可能的读写锁来传递数据.在java.util.concurrent中查找.长时间停止UI线程是不好的.
>确定渲染似乎花费很长时间的原因,以及它是慢速运行还是永久旋转.

总结

以上是内存溢出为你收集整理的Android上的SurfaceView方法“onTouchEvent(…)”中的ANR全部内容,希望文章能够帮你解决Android上的SurfaceView方法“onTouchEvent(…)”中的ANR所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1110848.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-29
下一篇2022-05-29

发表评论

登录后才能评论

评论列表(0条)

    保存