简体   繁体   中英

How to wait for a method with result callback to complete before continuing (Android)?

So I am a noob at Android, and I'm writing a simple app that uses Google Fit to store the users fitness sessions and step count and then retrieve them.

I have two methods, one that fetches all the sessions from a given date range from the cloud, the next method iterates through these and adds up the step count.

Problem is, that although I call the the fetching method first, the result doesn't come back until after I've added the steps up, so step count is always zero.

Here's my code:

private ArrayList<> results;

    @Override
    public ArrayList<IndividualSession> readAllSessions(Date dateFrom, Date dateTo) {

    /* I haven't included the following code in this question just to keep things clean, but here there was
        - The initialisation of the results ArrayList
        - Creating the calendar and date objects
        - Building the session read request
    */

    Fitness.SessionsApi.readSession(mGoogleApiClient, readRequest).setResultCallback(new ResultCallback<SessionReadResult>() {
        @Override
        public void onResult(SessionReadResult sessionReadResult) {
            for (Session session : sessionReadResult.getSessions()) {
                List<DataSet> dataSets = sessionReadResult.getDataSet(session);
                for (DataSet dataSet : dataSets) {
                    for (DataPoint dataPoint : dataSet.getDataPoints()) {
                    // Create new IndividualSession object, add data to it then add it to arraylist
                        IndividualSession individualSessionObject = new IndividualSession();
                        individualSessionObject.setFromDate(new Date(session.getStartTime(TimeUnit.SECONDS)));
                        individualSessionObject.setToDate(new Date(session.getEndTime(TimeUnit.SECONDS)));
                        individualSessionObject.setStepCount(dataPoint.getValue(Field.FIELD_STEPS).asInt());
                        results.add(individualSessionObject);
                    }
                }
            }
            Log.i(TAG, "Number of sessions found while reading:  "+results.size());
        }
    });
    return results;
}



@Override
public int getDaySteps(Date dateTo) {
    int stepCount = 0; // to be returned

    // Sort out the dates
    Calendar calFrom = Calendar.getInstance();
    calFrom.add(Calendar.HOUR, -24);

    // Get the sessions for appropriate date range
    ArrayList results =  readAllSessions(calFrom.getTime(), dateTo);
    Log.i(TAG, "Number of sessions found while trying to get total steps: "+results.size());

    // Iterate through sessions to get count steps
    Iterator<IndividualSession> it = results.iterator();
    while(it.hasNext())
    {
        IndividualSession obj = it.next();
        stepCount += obj.getStepCount();
    }
    return stepCount;
}

This outputs

"Number of sessions found while trying to get total steps: 0"
"Number of sessions found while reading:  8"

There are two solutions to this :

Option 1 : Use a blocking colleciton

  1. Change the ArrayList<> results to an ArrayBlockingQueue<> results .
  2. After the call to the readAllSessions method in the getDaySteps method, call while(results.take()!=null) { //rest of the logic }
  3. You need some kind of mechanistm to exit the while loop in step 2 when all results are read

Option 2 : Use the await method from PendingResult

Looking at the documentation for SessionsAPI class, the readSessions method seems to return a PendingResult :

public abstract PendingResult readSession (GoogleApiClient client, SessionReadRequest request)

Reads data from the user's Google Fit store of the specific type(s) and for the specific session(s) selected from the request parameters.

Looking at the documentation of the await method in PendingResult class :

public abstract R await ()

Blocks until the task is completed. This is not allowed on the UI thread. The returned result object can have an additional failure mode of INTERRUPTED.

This is what you can do. Instead of chaining the entire call to setResultCallBack, first call readSessions :

results = Fitness.SessionsApi.readSession(mGoogleApiClient, readRequest);

And then wait for the results in the getDaySteps method :

SessionReadResults sessionResults = results.await();
for (Session session : sessionReadResult.getSessions()) {
        List<DataSet> dataSets = sessionReadResult.getDataSet(session);
        for (DataSet dataSet : dataSets) {
            for (DataPoint dataPoint : dataSet.getDataPoints()) {
            // Create new IndividualSession object, add data to it then add it to arraylist
               IndividualSession individualSessionObject = new IndividualSession();
               individualSessionObject.setFromDate(new Date(session.getStartTime(TimeUnit.SECONDS)));
               individualSessionObject.setToDate(new Date(session.getEndTime(TimeUnit.SECONDS)));
            individualSessionObject.setStepCount(dataPoint.getValue(Field.FIELD_STEPS).asInt());
                    //use the results
            }
        }
    }

*results must be declared as an instance/class level variable to be accessible in all the methods in the class. The variable result is of type PendingResult<SessionReadResults> . Also, looks like you can do away with the results ArrayList since everything you want can be extracted from the SessionReadResults returned by the await method. One last note, this answer has not been tested with your code because your code sample is not complete.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM