BrentonGray88 BrentonGray88 - 1 month ago 5
Android Question

Multiple Nested Callbacks from Async Class

Fairly new to Android/Java development and using the Open Source Parseplatform as my backend server. I've created a class to manage a parse object and this object update's its data from an async call to the parse server as per this code.

public class DeviceObject {
private String objectID, deviceName, status;
private ParseGeoPoint location;
int batLevel;

public DeviceObject(){
objectID = null;
deviceName = null;
location = null;
batLevel = 0;
status = null;
}
public void getDeviceLatestData() {
if (objectID != null) {
ParseQuery<ParseObject> query = ParseQuery.getQuery("DeviceData");
query.whereEqualTo("DeviceObjectID", objectID);
query.orderByDescending("createdAt");
query.setLimit(1);
query.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> ParseDeviceList, ParseException e) {
if (e == null) {
if (ParseDeviceList.size() == 0) {
Log.d("debg", "Device not found");
} else {
for (ParseObject ParseDevice : ParseDeviceList) {
status = ParseDevice.getString("Status");
batLevel = ParseDevice.getInt("BatteryLevel");
location = ParseDevice.getParseGeoPoint("Location");
Log.d("debg", "Retrieving: " + deviceName);
Log.d("debg", "Status: " + status + " Battery: " + Integer.toString(batLevel));
}

//callback listener to add marker to map

}
} else {
Log.d("debg", "Error: " + e.getMessage());
}
}
});
}
}


So I create my class object in my Main Activity with the following:

DeviceObject userDevice = new DeviceObject();
userDevice.getDeviceLatestData();


What I can't get my head around is how in my MainActivity I can get notified/callback to continue displaying the information which the userDevice class just got off the parse Server.

I've tried creating an interface and adding a listener as what i've seen suggested however I could not add the listener inside the parse's done function.

The definition of my main activity is, note I need the OnMapReadyCallback as i'm using Google Maps

public class MapMainActivity extends AppCompatActivity implements OnMapReadyCallback {


So in summary i'd like to add something to the main activity so that I can process the data when it has been added to the class from the async call.

Answer

For something like this, I recommend using an event bus. Here is a link to a popular one I've had success with in the past.

Basically, you will have another class involved, which will be your bus. Your activity will register for a specific event (which you will create, subclassing as appropriate). Your async call will tell the event bus to fire off that event, and the bus will then tell all subscribers, including your main activity, that the event fired off. That is when you'd call getDeviceLatestData. Below are simple code snippets you may use, but read the documentation on that bus to fully understand it.

Your event:

 public static class DataReady Event { /* optional properties */ }

Your DeviceObject:

 public class DeviceObject {
   private String objectID, deviceName, status;
   private ParseGeoPoint location;
   int batLevel;

   public DeviceObject(){
      objectID = null;
      deviceName = null;
      location = null;
      batLevel = 0;
      status = null;
   }    
   public void getDeviceLatestData() {
    if (objectID != null) {
        ParseQuery<ParseObject> query = ParseQuery.getQuery("DeviceData");
        query.whereEqualTo("DeviceObjectID", objectID);
        query.orderByDescending("createdAt");
        query.setLimit(1);
        query.findInBackground(new FindCallback<ParseObject>() {
            public void done(List<ParseObject> ParseDeviceList, ParseException e) {
                if (e == null) {
                    if (ParseDeviceList.size() == 0) {
                        Log.d("debg", "Device not found");
                    } else {
                        for (ParseObject ParseDevice : ParseDeviceList) {
                            status = ParseDevice.getString("Status");
                            batLevel = ParseDevice.getInt("BatteryLevel");
                            location = ParseDevice.getParseGeoPoint("Location");
                            Log.d("debg", "Retrieving: " + deviceName);
                            Log.d("debg", "Status: " + status + " Battery: " + Integer.toString(batLevel));
                        }

                        //callback listener to add marker to map
                        EventBus.getDefault().post(new DataReadyEvent());

                    }
                } else {
                    Log.d("debg", "Error: " + e.getMessage());
                }
            }
        });
    }
}

Your MainActivity:

public class MainActivity {

   @Override
   public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
        EventBus.getDefault().unregister(this);
        super.onStop();
    }

    @Subscribe(threadMode = ThreadMode.MAIN) // Seems like you're updating UI, so use the main thread
    public void onDataReady(DataReadyEvent event) {
       /* Do whatever it is you need to do - remember you can add properties to your event and pull them off here if you need to*/
    };
}