Google Drive integration using current activity

2019-04-19 06:40发布

问题:

Is it possible to integrate with Google Drive without creating ones own activity, instead just using the current activity for the application without polluting it with Google Drive related code?

I have a background "service" (not an Android service - just a UI-agnostic class) that is responsible for loading some data from Google Drive. As a service, it has no real business being an activity. However, the samples for Drive integration have an override of onActivityResult to handle the situation where authentication is required. I'm not sure how I would get a hold of this information if my service is not implemented as an activity.

Assuming I can get a reference to the current Activity from my "service", is there some way I can implement Google Drive integration in a completely self-contained manner?

回答1:

I've done something similar from an app that has an Activity and a SyncService. You can actually see a lot of the functionality in the demo here. This demo does not use a service, but the idea remains the same.

1/ Create a singleton class (GDAA in this case) with an init() method that accepts your activity context. This init() will instantiate a local static GoogleApiClient that stays alive as long as you don't need to switch accounts (explained later).
init() is called with an activity context at the start or when there is a need to switch to another user account (different GooDrive for different user). Activity passes in it's context and supplies onConnFail(), onConnOK() callbacks.
For sake of completeness, there should be also a provision for onConnectionSuspended() in case the connection is interrupted (not a WIFI/CELL connection drop, but GooPlaySvcs suspention).

  • The onConnFail() callback passes control to GooPlaySvcs for authentication/authorization whose result comes back in onActivityResult(). Connect again, wash, rinse, repeat...
  • The onConnOK() callback tells your activity that everything is ready.

As I pointed out, you will have to call the init() again if you handle multiple GooDrive accounts in order to create a new GoogleApiClient for a new account. Just follow the 'REQ_ACCPICK' trail here. You will also have to manage your accounts as you can see in the account manager 'AM' class here. Most of this dancing can be seen in the MainActivity.
For completeness, I should also mention that you can leave account management to GooPlaySvcs by omitting the setAccountName(email) in the GoogleApiClient.Builder() and use clearDefaultAccountAndReconnect() to reset account picking. But your app will not know who the current user is. Yet another account management can be handled by means of the PlusApi (or whatever it is called, never used it). But I digress.

2/ When the initialization is done and the private static GoogleApiClient mGAC is non-null and connected, GDAA static methods can be referenced from anywhere in your app, including a service. The methods will certainly fail if the GoogleApiClient is not alive or not connected.

In the demo mentioned here, all GDAA calls are of the 'await()' (sync) flavor. It is not a problem if they are called from a non-UI thread like a service. They can easily be turned into async versions like in this example:

DriveFile df = ...;
// sync version
DriveContentsResult rslt = df.open(mGAC, DriveFile.MODE_READ_ONLY, null).await();
if ((rslt != null) && rslt.getStatus().isSuccess()) {
  DriveContents cont = rslt.getDriveContents();
  InputStream is = cont.getInputStream();
  cont.discard(mGAC);    // or cont.commit();  they are equiv if READONLY
}
// async version
df.open(mGAC, DriveFile.MODE_READ_ONLY, null).setResultCallback(
  new ResultCallback<DriveContentsResult>() {
  @Override
  public void onResult(DriveContentsResult rslt) {
    if ((rslt != null) && rslt.getStatus().isSuccess()) {
      DriveContents cont = rslt.getDriveContents();
      InputStream is = cont.getInputStream();
      cont.discard(mGAC);    // or cont.commit();  they are equiv if READONLY
    }
  }
});

The flavor depends on you app's needs (spaghetti code with consumed return values vs. async processing).

It should be mentioned, that this demo uses the GDAA version of the Api. There is a REST Api as well that can be handled the same way (sync flavor only). An alternate demo that uses exactly the same logic and methods is available here. You can even combine the two into one singleton class with both
com.google.api.services.drive.Drive and com.google.android.gms.common.api.GoogleApiClient present.

It is dangerous to mix the GDAA and REST Api though, since you'll certainly run into timing issues caused by the fact that GDAA synchronizes on it's own schedule, whereas REST is under your control.

Hope it helped, Good Luck



回答2:

The onActivityResult code path is only used when there is an authentication failure requiring user intervention. Thus by definition, it cannot simply be handled "in the background" by your service.

In the case of background service, I think one appropriate way to handle authentication failure is to show a notification saying to the effect of "You need to authenticate to continue". You can attach an Intent to the notification so it launches your own "ResolveAuthActivity" when the user taps on the notification. This ResolveAuthActivity simply tries to connect to the API, handles the failure in onConnectionFailed, and then launches the resolution Intent. Once the failure is resolved, your ResolveAuthActivity can poke your service to try to connect to the API again.



回答3:

I think you can achieve the effect you want using the google drive REST api calls https://developers.google.com/drive/v2/reference/

Here's how you can proceed. Since you get the context you can do the api calls to drive. For the authentication since google follows OAuth 2.0 you can use these api calls to achieve authentication https://developers.google.com/identity/