##DISCLAIMER
'With my brains and your looks, we could go places' - Frank Chambers, The postman rings always twice
LibPostman (Rings Twice) for Android is a library intended to make the asynchronous interaction with a remote server easier, without having to deal with all the well known problems related to asynctasks bound to activities.
It uses scribe java library under the hood for basic http calls and for oauth 1.0 and 2.0 authentication. The minSdkVersion
declared in the lib's manifest is 8 since scribe itself uses HttpUrlConnection
which received a significant amount of bugfixes in ApiLevel 8,
###QUESTIONS? Did you check the faqs?
PostmanLib's basic architecture is barely inspired by Virgil Dobjanschi's talk at Google IO 2010, and it was initially developed after the experience I had interacting with a rest service during the development of MyDearSanta app. After having it in progress for more than a year I now decided (XMas 2012) to make it working.
###Setup PostmanLib must be added as a library project. Since it uses fragment dialog to open a webview, it depends on compatibility-v4 library in order to support pre HC devices. It uses also Java Scribe library.
####Maven PostManLib is available from MavenCentral. Just add
<dependency>
<groupId>com.fedepaolapps.postman</groupId>
<artifactId>library</artifactId>
<type>apklib</type>
<version>1.0.3</version>
</dependency>
to your pom.xml
dependencies section. You may incur in errors like:
java.lang.IllegalArgumentException: already added: Landroid/support/v4/widget/CursorAdapter$1;
which means that some other dependency already includes a compatibiltiy library. In this case just exclude it from the dependency:
<dependency>
<groupId>com.whiterabbit.postman</groupId>
<artifactId>library</artifactId>
<type>apklib</type>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>android.support</groupId>
<artifactId>compatibility-v4</artifactId>
</exclusion>
</exclusions>
</dependency>
####Eclipse / IntelliJ If you are using Eclipse or intelliJ, just create a library project using the library folder as source. You will also need to add scribe jar and support library.
####Changes in your app's manifest The services used by the library must be declared into the application's manifest:
<service
android:enabled="true"
android:name="com.whiterabbit.postman.InteractionService" />
You can declare up to 4 services. The number of services you define in the manifest describes the concurrency level used by the library. At least one service must be declared
<service
android:enabled="true"
android:name="com.whiterabbit.postman.InteractionService1" />
<service
android:enabled="true"
android:name="com.whiterabbit.postman.InteractionService2" />
<service
android:enabled="true"
android:name="com.whiterabbit.postman.InteractionService3" />
If you need to perform wakeful scheduled requests, you need to add
<service
android:enabled="true"
android:name="com.whiterabbit.postman.WakefulInteractionService" />
as well.
##ServerInteractionHelper It's a singleton class to be used to send asynchronous commands. Allows also to register listener to be notified of execution results / failures. Any sent command is associated to a requestid which can be used to check if the given request is still pending and is passed back in result notifications.
###ServerCommand A server command is an implementation of the command pattern. The execute() method will be executed asynchronously and it is guaranteed to be executed in a background thread.
###RestServerCommand RestServerCommand is a specialization of ServerCommand. It's intended to be used to perform rest operations against a given url. It uses Java Scribe library under the hood, and it can be associated to an authenticated OAuthService to perform oauth authorized requests. To perform its actions must be initialized with one or more RestStrategy objects that represent the actions to be performed in background
####Executing a RestServerRequest To make a request you need to implement a RestServerRequestObject. The most obvious method to implement are getUrl and getVerb .
Since a RestServerRequest executes a scribe oauth request under the hood (even if not authenticated), you need to implement addParamsToRequest in order to enrich the request. For example, in order to add a "status" parameter in a call:
request.addBodyParameter("status", "this is sparta! *");
or
request.addQuerystringParameter("status", "this is sparta!");
in case of a query string parameter.
####Sending a server command: A RestServerRequest might be used to initialize a RestServerCommand to be sent. There is also an helper method to make the things easier:
ServerInteractionHelper.getInstance().sendRestAction(this,"MyRequestId", request1, request2);
####Handling a response The
void onHttpResult(Response result, int statusCode, RequestExecutor executor, Context context);
of all the RestServerAction s you passed will be called with the result of the call.
The statusCode parameter is the status code returned by the http call. In case it is 200 (success), a response can be parsed. The Response parameter is a scribe Response object. You can get the stringified result using
result.getBody()
in case of a text result, such as json.
Otherwise, a stream can be fetched using:
result.getStream()
Other callbacks are available to get notified of errors.
####Chained requests In many cases (such as facebook graph api), the rest api may require to use a request's result as a parameter of a newer request. This might be tricky using the callbacks given by the ServerInteractionHelper. To handlethese special cases, the executor parameter of the onHttpResult call. Its executeRequest method allows to execute new requests in the same thread (or service) that is handling the current request.
Check FacebookExample for more details.
####Scheduled requests Despite PostmanLib does not natively provide a way to schedule requests, it provides a facility to retrieve a PendingIntent that will execute the given request once triggered. Calling
helper.getActionToSchedule(context, "MyRequestID", myRequest);
will result in a pending intent that can be passed to any alarmmanager. This provides a higher flexibility because the whole scheduling management is left to the user of the library. For a simple example check AlarmManagerSample class.
####Wakeful scheduled requests Wakeful requests need to be performed while keeping the wakelock for the length of the operation. This can be achieved using a wakeful alarm manager that triggers a broadcast receiver. In the broadcast receiver postmanlib can be used to execute wakeful requests. In short, bind an alarm receiver to a wakeful alarm and perform:
helper.sendWakefulRequest(context, "RequestID", request);
Wakeful requests rely on Commonsware WakefulIntentService pattern (more details here) Check the WakefulAlarmManagerExample class from samples for more details.
##OAuth Authentication
Another feature provided by ServerInteractionHelper is to asynchronously authorize a Scribe OAuth service, displaying a webview to the user for authorization. Once the OAuthService is registered, it can be used to sign RestServer commands.
Requesting an authorization is quite simple. A storable builder, which is a simple wrapper to scribe's ServiceBuilder, must be created and registered against the OAuthHelper.
StorableServiceBuilder builder = new StorableServiceBuilder("Twitter")
.provider(TwitterApi.class)
.apiKey("YOURAPIKEY")
.apiSecret("YOURAPISECRET")
.callback("http://YOUR_CALLBACK_URL", "CALLBACK_TOKEN_PARAMETER");
OAuthHelper o = OAuthHelper.getInstance();
o.registerOAuthService(builder, this);
In case the service is not yet authorized, the authorization process must be started:
if(!o.isAlreadyAuthenticated("Twitter", this)){
o.authenticate(this, "Twitter");
}
The callback url is the one that will be called after the first step of the authorization has been completed. It will be intercepted by the webview, so it should not care. However, some services (such as facebook) will check the domain it belongs to so be sure it's the same you put into the api console.
The callback token parameter is the parameter of the callback that contains the token to be used in order to proceed with the authentication. It generally is "auth_verifier" but in case of facebook it was "code" .
PostManLib library will provide a dialog to the user containing the webview to authorize the application.
Once the service is registered, all it takes to sign a RestServerRequest is to return the name of the signer in getOauthSigner
public String getOAuthSigner() {
return "Twitter";
}
Using the same name used to register the service.
-
In order to make the authentication possible you need to have a callback url. In twitter you need to explicitly set up a callback url into the api dashboard. If it's not the case, a redirect will not occour and OAuth Fragment will not be able to intercept the verifer token from the redirect.
-
Adding call parameters is straight. You however need to rely on Scribe documentation in order to check how to add them
-
The common pattern I suggest to use with postman lib is not to return all the data to the activity, but update your data model inside the onHttpResult method and only update the ui when the activity gets notified of the end of the request
-
ServerCommand and RestServerAction implement Parcelable interface. This is because the command must be passed to the intent service through an intent. As per the official android doc, in addition to the Parcelable methods, every command implementation must have a static CREATOR field, such as
public static final Parcelable.Creator CREATOR = ...
-
In case of multiple rest requests, a failure will be notified whenever one of the requests fails, even if the previous ones are executed successfully.
-
Since plain authentication is just an header property, I preferred not to give a straight handler to it. Implementing addParamsToRequest and calling: request.addHeader("Authorization", "Basic "+Base64.encode(username+":"+password));
should work. I don't have a proper server to test it against.
Copyright (C) 2013 Federico Paolinelli
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
- Localization of error messages
- callback url personalization. Not sure yet that oauth__verifer is a standard
- Recycling commands using a pool