EventBus Not on the main thread

2019-08-15 19:05发布

I'm trying to get the positions from my WS and update my markers in my GoogleMap fragment so what I'm doing is:

I've my HomeActivity which contains 2 fragments (2 GoogleMaps where one have TileOverlay).

In my GoogleMap fragment I'm trying to obtain my Markers Positions from OnCameraChangeListener so the markers are added as the user moves on.

I'm using EventBus and Okhttp for an async request!

My GoogleMapFragment:

public class GoogleMapFragment extends FragmentBase {

@Override
public void onResume() {
    mapView.onResume();
    BusHelper.register(this); // Register the EventBus with my helper
    super.onResume();
}

@Override
public void onPause() {
    BusHelper.unregister(this); // Unregister the EventBus with my helper
    super.onPause();
}

public GoogleMap.OnCameraChangeListener getCameraChangeListener() {
    return new GoogleMap.OnCameraChangeListener() {
        @Override
        public void onCameraChange(CameraPosition cameraPosition) {

           //those are corners of visible region
            VisibleRegion visibleRegion = mMap.getProjection().getVisibleRegion();
            LatLng farLeft = visibleRegion.farLeft;
            LatLng farRight = visibleRegion.farRight;
            LatLng nearLeft = visibleRegion.nearLeft;
            LatLng nearRight = visibleRegion.nearRight;

            double minY = nearLeft.latitude;
            double minX = nearLeft.longitude;
            double maxY = farRight.latitude;
            double maxX = farRight.longitude;

            //Send the WS Request to a manager who uses okhttp              
            ApiManager.getPositions(minX, minY, maxX, maxY);
        }
    };
}

//Receive the eventBus event
public void onEvent(GetPositionsEvent event) {
    if (event.positions != null)
        setMarkersByVisibleRegion(event.positions);
}

After this it will do the WS Request in APIManager.

public class IMSApiManager {
private final static Gson gson = new Gson();
private static Positions mPositions;

/**
 * @return Positions
 */
public static void getPositions(double minX, double minY, double maxX, double maxY) {
    String TAG = "getPositions";

    String wSRequest = AppConstants.REQUEST_POSITIONS
            .replace("{vUser}", "CARREPH1")
            .replace("{vMinX}", new BigDecimal(minX).toPlainString())
            .replace("{vMinY}", new BigDecimal(minY).toPlainString())
            .replace("{vMaxX}", new BigDecimal(maxX).toPlainString())
            .replace("{vMaxY}", new BigDecimal(maxY).toPlainString());

    try {
        RestAsyncHttpClient.doGetRequest(wSRequest, new GetPositionsCallback() {
            @Override
            public void onFailure(Request request, IOException e) {
                super.onFailure(request, e);
            }

            @Override
            public void onResponse(Response response) throws IOException {
                super.onResponse(response);
                mPositions = gson.fromJson(response.body().charStream(), Positions.class);
                BusHelper.post(new GetPositionsEvent(mPositions)); //Post the eventBus
            }
        });

    } catch (IOException exp) {
        LogHelper.error(TAG, "Error getting positions", exp);
    }
}

}

I'm familiar with Not on the mainThread error, but in theory this will be possible, if not how i can add markers without doing a new instance of the fragment.

4条回答
\"骚年 ilove
2楼-- · 2019-08-15 19:49

Just use onEventMainThread instead onEvent. It solved my problems too.

// Called in Android UI's main thread
public void onEventMainThread(MessageEvent event) {
    textField.setText(event.message);
}

Why? Because onEvent is called in the same thread, you need run your code on MainThread.

查看更多
爷、活的狠高调
3楼-- · 2019-08-15 19:51

You can call post on eventbus's object (when background thread is successful). The argument of post is an object of any class, so design a custom class as per your requirement which will hold the result from the background thread.

Now on the activity (where UI work needs to be done), simply define :

public void onEventMainThread(final CobbocEvent event)
{  
    if (event.getType() == CobbocEvent.POSITION)
    {  //If background work is success
        if (event.getStatus())
        {
           //Do UI Stuff on main thread
        }
    }
}

CobbocEvent is your custom class for storing background thread's result data.:

public class CobbocEvent{
 public CobbocEvent(int type) {
    this(type, true, null);
}

public CobbocEvent(int type, boolean status) {
    this(type, status, null);
}

public CobbocEvent(int type, boolean status, Object value) {
    TYPE = type;
    STATUS = status;
    VALUE = value;
}
public CobbocEvent(int type, boolean status, int value) {
    TYPE = type;
    STATUS = status;
    VALUE = value;
}
....

I am sure you get the picture :)

查看更多
地球回转人心会变
4楼-- · 2019-08-15 19:54

You would get the error when you try to post an object to the EventBus in background thread. The solution is, if post(...) is called in background thread, let Handler move it to main thread.

Override your BusHelper's post(...) method and do the following.

public class BusHelper extends Bus {

    ...

    private final Handler mHandler = new Handler(Looper.getMainLooper());

    @Override
    public void post(final Object event) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            super.post(event);
        } else {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    BusHelper.super.post(event);
                }
            });
        }
    }

    ...

}

Hope this helps.

查看更多
乱世女痞
5楼-- · 2019-08-15 20:10

Accepted answer only works for versions lower than third, for other versions you should use threadMode = ThreadMode.MAIN (documentation)

@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(MessageEvent event) {
    textField.setText(event.message);
}
查看更多
登录 后发表回答