1

I want to read files on Google Drive partially (Range request or Partial download). Moreover, I want to do it in series - to read next interval just only after previous interval has been read (with CountDownLatch) and in a separate thread (Android prohibits HTTP-requests in main thread). For such purpose I want to implement ByteChannel method:

@Override
public int read(final ByteBuffer dst) {
    final com.google.api.services.drive.model.File googleFile = mFile.getImpliedFile();
    final int bufferLength = dst.capacity();
    final int[] bytesReceived = {-1};
    final CountDownLatch latch = new CountDownLatch(1);
    new Thread(new Runnable(){
        public void run() {
            byte[] receivedByteArray = getByteArrayGoogle(mService, googleFile, position, bufferLength);
//            byte[] receivedByteArray = getByteArrayApache(googleFile, position, bufferLength);                

            dst.put(receivedByteArray, 0, receivedByteArray.length);
            bytesReceived[0] = receivedByteArray.length;
            position += receivedByteArray.length;
            latch.countDown();
        }
    }).start();
    try {
        latch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return bytesReceived[0];
}

If I use libraries google-api-services-drive-v2-rev151-1.18.0-rc.jar and google-api-client-assembly-1.18.0-rc-1.18.0-rc.zip recommended by Google :

private byte[] getByteArrayGoogle(Drive drive, File file, long position, int byteCount) {
    String downloadUrl = file.getDownloadUrl();
    byte[] receivedByteArray = null;
    if (downloadUrl != null && downloadUrl.length() > 0) {
        try {
            com.google.api.client.http.HttpRequest httpRequestGet = drive.getRequestFactory().buildGetRequest(new GenericUrl(downloadUrl));
            httpRequestGet.getHeaders().setRange("bytes=" + position + "-" + (position + byteCount - 1));
            com.google.api.client.http.HttpResponse response = httpRequestGet.execute();
            InputStream is = response.getContent();
            receivedByteArray = IOUtils.toByteArray(is);
            response.disconnect();
            System.out.println("google-http-client-1.18.0-rc response: [" + position + ", " + (position + receivedByteArray.length - 1) + "]");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return receivedByteArray;
}

I can never read more than six intervals! However, if I use Apache HTTP client:

private byte[] getByteArrayApache(File file, long position, int byteCount) {
    String downloadUrl = file.getDownloadUrl();
    byte[] receivedByteArray = null;
    if (downloadUrl != null && downloadUrl.length() > 0) {
        try {
            org.apache.http.client.methods.HttpGet httpRequestGet = new HttpGet(downloadUrl);
            httpRequestGet.addHeader("Authorization", "Bearer " + GoogleDriveActivity.getAccessToken(mContext));
            httpRequestGet.addHeader("Range", "bytes=" + position + "-" + (position + byteCount - 1));
            org.apache.http.HttpResponse response = new DefaultHttpClient().execute(httpRequestGet);
            InputStream is = response.getEntity().getContent();
            receivedByteArray = IOUtils.toByteArray(is);
            System.out.println("apache-http-client response: [" + position + ", " + (position + receivedByteArray.length - 1) + "]");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return receivedByteArray;
}

I can read all needed intervals.

The problem appears to be in com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential.RequestHandler

public void intercept(HttpRequest request) throws IOException {
  try {
    token = getToken();
    request.getHeaders().setAuthorization("Bearer " + token);
  } catch (GooglePlayServicesAvailabilityException e) {
    throw new GooglePlayServicesAvailabilityIOException(e);
  } catch (UserRecoverableAuthException e) {
    throw new UserRecoverableAuthIOException(e);
  } catch (GoogleAuthException e) {
    throw new GoogleAuthIOException(e);
  }
}

because I can't get token for seventh request and application always freezes in this point under debugging.

token = getToken();     

1. What is the reason of that strange behaviour and how to make more than six requests with Google API?

2. Are there other ways to do partial download with Google API?

P.S. By the way, class RequestHandler has annotation @Beta...

COMMENT: It seems the problem is in Google Play Service proprietary class com.google.android.gms.auth.GoogleAccountCredential used for getting a token:

GoogleAuthUtil.getToken(context, accountName, scope)

As I suspect GoogleAuthUtil.getToken has to use main thread to validate authentication, but main thread is blocked by CountDownLatch in a waiting of request result. Therefore, I have deadlock at this point. First six requests are executed from AsyncTask and they does not block main thread.

isabsent
  • 3,683
  • 3
  • 25
  • 46
  • If you don't get a response on SO, try posting to https://groups.google.com/forum/#!forum/google-api-java-client – pinoyyid Nov 23 '14 at 02:44
  • I don't know if you suspicion about `GoogleAuthUtil.getToken()` is correct, but either way it seems like a bad practice to block your UI thread to wait for the file download to complete. Try refactoring your application to not rely on blocking, and you may get two birds with one stone. – Eric Koleda Dec 02 '14 at 15:12
  • Yes, I agree with you. But I had to lock UI thread to quickly check some features of Google Drive SDK and it was a temporary approach. By the way, it works for Dropbox just fine. The suspicion is correct - all deadlocks disappear after rewriting partial requests by means of AsyncTask. – isabsent Dec 03 '14 at 08:22

0 Answers0