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.