Question

I'm creating a game that uses the Sphero robot ball. When the game starts up/resumes, it checks for a paired or connected ball. If bluetooth is off, or if there is no ball paired, it hides the connection window. If there is a ball paired, but not connected, it goes to this function.

// If the user clicked a Sphero and it failed to connect, this event will be fired
@Override
public void onRobotConnectionFailed(Robot robot) {
    Log.d("activity", "onRobotConnectionFailed");
    removeConnectionView();
}

It crashes in the following method.

private void removeConnectionView() {
    mFrameLayout.removeView(mSpheroConnectionView);
    mSpheroConnectionView = null;
}

This function works on each other possible outcome. This is the error.

Thread [<14> Thread-2606] (Suspended (exception ViewRootImpl$CalledFromWrongThreadException))   
<VM does not provide monitor information>   
ViewRootImpl.checkThread() line: 5031   
ViewRootImpl.invalidateChildInParent(int[], Rect) line: 998 
FrameLayout(ViewGroup).invalidateChild(View, Rect) line: 4358   
ImageView(View).invalidate(boolean) line: 10565 
ImageView(View).invalidate() line: 10520    
ImageView.invalidateDrawable(Drawable) line: 202    
XDrawable(Drawable).invalidateSelf() line: 382  
XDrawable(Drawable).setVisible(boolean, boolean) line: 578  
ImageView.onDetachedFromWindow() line: 1196 
ImageView(View).dispatchDetachedFromWindow() line: 12136    
FrameLayout(ViewGroup).dispatchDetachedFromWindow() line: 2824  
RelativeLayout(ViewGroup).dispatchDetachedFromWindow() line: 2824   
SpheroConnectionView$SpheroItemView(ViewGroup).dispatchDetachedFromWindow() line: 2824  
SpheroConnectionView$SpheroListView(ViewGroup).dispatchDetachedFromWindow() line: 2824  
SpheroConnectionView(ViewGroup).dispatchDetachedFromWindow() line: 2824 
FrameLayout(ViewGroup).removeViewInternal(int, View) line: 3943 
FrameLayout(ViewGroup).removeViewInternal(View) line: 3918  
FrameLayout(ViewGroup).removeView(View) line: 3850  
discgroove.removeConnectionView() line: 233 
discgroove.access$4(discgroove) line: 232   
discgroove$2.onRobotConnectionFailed(Robot) line: 191   
SpheroConnectionView$4.onRobotConnectionFailed(Robot) line: 157 
RobotProvider.update(Observable, Object) line: 570  
Robot(Observable).notifyObservers(Object) line: 138 
Robot.setConnected(boolean) line: 300   
DeviceConnection$4.run() line: 378  
Was it helpful?

Solution

The removeConnectionView method is being called from a thread that isn't the main Looper thread (or, the "UI Thread"). View can only be modified from the main looper thread.

In order to fix this, instantiate a Handler sometime on the main Looper thread (such as when the view or Activity is created), and use the Hander#post(Runnable) method to call your method, or use the View#post(Runnable) method on your FrameLayout.

Example of the first kind:

Put this in the onCreate method of the Activity, which always runs in the main Looper thread.

mHandler = new Handler();

Then, when you want to run that method:

    handler.post(new Runnable() {
        @Override
        public void run() {

            mFrameLayout.removeView(mSpheroConnectionView);
            mSpheroConnectionView = null;
        }
    });

Example of the second kind:

    mFrameLayout.post(new Runnable() {
        @Override
        public void run() {
            mFrameLayout.removeView(mSpheroConnectionView);
            mSpheroConnectionView = null;
        }
    })

OTHER TIPS

You can only do UI related things from the main thread. Try creating a Handler using the main looper and posting runnables to that handler.

Ex.

private Handler mHandler = new Handler(Looper.getMainLooper());
.
.
.
mHandler.post(new Runnable() {
        public void run() {
            removeConnectionView();
        }
    });
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top