OsmSharp / ui

The UI components.
http://osmsharp.com/
GNU General Public License v2.0
138 stars 91 forks source link

Infrequent App Shut-Downs on Android #188

Open muhkuh opened 9 years ago

muhkuh commented 9 years ago

Hello,

i get an infrequent error which is shutting down my android app using OsmSharp, while zooming the map.

[InputEventReceiver] Exception dispatching input event.
[AndroidRuntime] Shutting down VM
[AndroidRuntime] FATAL EXCEPTION: main
[AndroidRuntime] Process: de.draeger.servicelocator, PID: 2829
[AndroidRuntime] java.lang.NullPointerException
[AndroidRuntime]    at android.view.ViewGroup.resetCancelNextUpFlag(ViewGroup.java:2011)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1957)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
[AndroidRuntime]    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
[AndroidRuntime]    at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2068)
[AndroidRuntime]    at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1515)
[AndroidRuntime]    at android.app.Activity.dispatchTouchEvent(Activity.java:2458)
[AndroidRuntime]    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2016)
[AndroidRuntime]    at android.view.View.dispatchPointerEvent(View.java:7886)
[AndroidRuntime]    at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:3947)
[AndroidRuntime]    at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3826)
[AndroidRuntime]    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3392)
[AndroidRuntime]    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3442)
[AndroidRuntime]    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3411)
[AndroidRuntime]    at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3518)
[AndroidRuntime]    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3419)
[AndroidRuntime]    at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3575)
[AndroidRuntime]    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3392)
[AndroidRuntime]    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3442)
[AndroidRuntime]    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3411)
[AndroidRuntime]    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3419)
[AndroidRuntime]    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3392)
[AndroidRuntime]    at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5532)
[AndroidRuntime]    at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5512)
[AndroidRuntime]    at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5483)
[AndroidRuntime]    at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5612)
[AndroidRuntime]    at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
[AndroidRuntime]    at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
[AndroidRuntime]    at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:176)
[AndroidRuntime]    at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:5585)
[AndroidRuntime]    at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:5631)
[AndroidRuntime]    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
[AndroidRuntime]    at android.view.Choreographer.doCallbacks(Choreographer.java:574)
[AndroidRuntime]    at android.view.Choreographer.doFrame(Choreographer.java:542)
[AndroidRuntime]    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
[AndroidRuntime]    at android.os.Handler.handleCallback(Handler.java:733)
[AndroidRuntime]    at android.os.Handler.dispatchMessage(Handler.java:95)
[AndroidRuntime]    at android.os.Looper.loop(Looper.java:136)
[AndroidRuntime]    at android.app.ActivityThread.main(ActivityThread.java:5001)
[AndroidRuntime]    at java.lang.reflect.Method.invokeNative(Native Method)
[AndroidRuntime]    at java.lang.reflect.Method.invoke(Method.java:515)
[AndroidRuntime]    at com.android.internal.os.Z

It's a known android kernel issue: https://code.google.com/p/android/issues/detail?id=64476 this are other links to the topic: https://github.com/avalax/FitBuddy/issues/37

I've tried to troubleshoot it at RouteTrackerAnimator.cs in the function MapViewMapTouched with try and catch, but without success.

xivk commented 9 years ago

Any fast way to reproduce this? Probably not, but hoping....

muhkuh commented 9 years ago

Thanks for the reply. I fixed it myself yesterday, but didnt had the time to post it yet.

Informations for reproducing Devices Nexus 5 - Android 4.4.4 Samsung Galaxy S4 - Android 4.2

To reproduce the bug, zoom in and zoom out all the time and then it will happen. Maybe you have to release the touch for a short duration before zomming in the opposite direction.

Now to my changes: The reason was the following line in the function MapView.NotifyMapChangeToControl :

this.RemoveView(mapControl.BaseView);

I also increased the performance of the repositioning of the markers a bit and fixed the bug:

MapView.cs

internal void NotifyMapChangeToControl(double pixelsWidth, double pixelsHeight, View2D view, IProjection projection, MapControl mapControl)
{
    if (mapControl != null &&
        mapControl.Handle != IntPtr.Zero)
    {
        //this.RemoveView(mapControl.BaseView);
        if (mapControl.SetLayout (pixelsWidth, pixelsHeight, view, projection)) {

            //this.AddView(mapControl.BaseView, mapControl.BaseView.LayoutParameters);
            UpdateViewLayout (mapControl.BaseView, mapControl.LayoutParams);
        }
    }
}

MapControl.cs

abstract MapControl added:

public abstract FrameLayout.LayoutParams LayoutParams {
    get;
}

MapControl:

added attribute

protected FrameLayout.LayoutParams _layoutParams;

added initialisation in constructors

_layoutParams = this.View.LayoutParameters as FrameLayout.LayoutParams;

added public getter

public override FrameLayout.LayoutParams LayoutParams
{
    get
    {
        return _layoutParams;
    }
}

modified SetLayout

internal override bool SetLayout(double pixelsWidth, double pixelsHeight, View2D view, IProjection projection)
{
    if (this.Location != null)
    { // only set layout if there is a location set.

        var projected = projection.ToPixel(this.Location);
        double leftMargin, topMargin;

        var fromMatrix = view.CreateToViewPort(pixelsWidth, pixelsHeight);
        fromMatrix.Apply(projected[0], projected[1], out leftMargin, out topMargin);

        int width = _view.Width;
        leftMargin = leftMargin - width / 2.0;

        /*switch (_alignment)
        {
            case MapControlAlignmentType.Center:
                topMargin = topMargin - (this.View.LayoutParameters as FrameLayout.LayoutParams).Height / 2.0;
                break;
            case MapControlAlignmentType.CenterTop:
                break;
            case MapControlAlignmentType.CenterBottom:
                topMargin = topMargin - (this.View.LayoutParameters as FrameLayout.LayoutParams).Height;
                break;
        }*/

        _layoutParams.SetMargins((int)leftMargin, (int)topMargin, 0, 0);
    }
    return true;
}

I dont need the alignment so i commented it out for performance, but i think with using the class attribute _layoutParams it is faster.

Maybe you can use these informations.

ChristopheOosterlynck commented 9 years ago

I'm experiencing the same bug.

@xivk A quick way to reproduce: have a MapView with 1 or more markers and perform a zoom gesture. The first finger to touch the screen to perform the zoom gesture should touch one of the markers.

JoeCooper commented 9 years ago

"have a MapView with 1 or more markers and perform a zoom gesture. The first finger to touch the screen to perform the zoom gesture should touch one of the markers."

I can reproduce it reliably this way.

xivk commented 9 years ago

Sorry guys, still had no time to have a look.

Anyone try the fix proposed by @muhkuh ?

JoeCooper commented 9 years ago

Hi. I've tried the fix in question. It works. In light of the changes I sent in, however, you need to make sure that _layoutParams are assigned to in SetSize. Like so:

protected void SetSize(int width, int height)
{
    _view.SetMinimumWidth(width);
    _view.SetMinimumHeight(height);

    if (_view.LayoutParameters != null) {
        _view.LayoutParameters.Width = width;
        _view.LayoutParameters.Height = height;
    } else {
        var layoutParams = new FrameLayout.LayoutParams (width, height);
        layoutParams.LeftMargin = -1;
        layoutParams.TopMargin = -1;
        layoutParams.Gravity = GravityFlags.Top | GravityFlags.Left;
        _view.LayoutParameters = layoutParams;
    }

    _layoutParams = this.View.LayoutParameters as FrameLayout.LayoutParams;

    if (Host != null) {
        Host.NotifyControlChange (this);
    }
}

(Just before the host notify control change call.)