- What's new?
- Setup
- Basics
- Asynchronous
- Config
- Request Cancelation
- Void Return Types
- JSON Deserialization
- Remote Methods Parameters
- Unauthenticated Requests
- Batch
- RxJava
See this presentation for what's new in the Liferay Android SDK 2.0.
Add the Liferay Android SDK as a dependency to your project:
repositories {
jcenter()
}
dependencies {
compile group: 'com.liferay.mobile', name: 'liferay-mobile-sdk', version: "{look.at.github's.releases.section.for.latest.version.number}"
}
This library can be used in any Java project, even non-Android projects. There's no dependency on Android.
-
Create an interface to access some portal's remote API:
import com.liferay.mobile.sdk.Call; import com.liferay.mobile.sdk.annotation.Param; import com.liferay.mobile.sdk.annotation.Path; import org.json.JSONArray; @Path("/group") public interface GroupService { @Path("/get-user-sites-groups") Call<JSONArray> getUserSitesGroups(); }
-
We provide all existing portal services in another package. To access them, you must also add this dependency to your project:
dependencies { compile group: 'com.liferay.mobile', name: 'liferay-mobile-sdk-services', version: '1.0' }
This dependency contains prebuilt interfaces for both Liferay Portal 6.2 and 7.0.
Alternatively, you can also use the SDK Builder to generate these interfaces for your custom API. It's usually simpler, however, for you to use the prebuilt ones as described above.
-
Call the remote API:
import com.liferay.mobile.sdk.ServiceBuilder; import com.liferay.mobile.sdk.Call; import com.liferay.mobile.sdk.Config; import com.liferay.mobile.sdk.auth.BasicAuthentication; import org.json.JSONArray; GroupService service = ServiceBuilder.build(GroupService.class); Call<JSONArray> call = service.getUserSitesGroups(); Config config = new Config.Builder("http://10.0.2.2:8080") .auth(new BasicAuthentication("test@liferay.com", "test")) .build(); JSONArray groups = call.execute(config);
Android doesn't allow synchronous HTTP requests; you can only make them from different threads.
The SDK can help you make asynchronous HTTP requests. Instead of calling call.execute(...)
, call call.async(...)
with a Callback
implementation as follows:
call.async(config, new Callback<JSONArray>() {
@Override
public void onFailure(Exception exception) {
// Implement exception handling code
}
@Override
public void onSuccess(JSONArray result) {
// Called after request has finished successfully
}
});
If an exception occurs during the request, the onFailure
method is called. The exception can be a connection exception (e.g., a request timeout) or a ServerException
. A ServerException
occurs when something goes wrong on the server side.
The onSuccess
and onFailure
methods are called on the main UI thread after the request finishes.
The Config
object lets you configure the following request properties: server URL, timeout (in milliseconds), HTTP headers, and an Authentication
instance for handling authentication.
All Authentication
implementations are available here. The following authentication mechanisms are supported: basic, digest, and cookie authentication.
Config
objects are immutable and you can only change them by cloning an instance first:
Config cloned = config.newBuilder().timeout(1000).build();
It's possible to set a global Config
object. This way you don't need to pass a Config
instance every time you call call.execute()
or call.async(...)
.
You can set a global Config
instance like this:
Config.global(config);
It can be useful to cancel ongoing requests. For example, the user may want to cancel a file upload. To cancel a request, hold the Call
instance and call:
Call.cancel(call);
Some portal remote methods are void
. In this case, change the return type to Call<Response>
:
@Path("/disable-staging")
Call<Response> disableStaging(@Param(name = "groupId") long groupId);
Use the response's statusCode()
method to check whether the request succeeded.
Return types aren't limited to JSONObject
or JSONArray
. You can write your own POJO and return it. For example, you can write a Site
class and use it as a return type:
public class Site {
String friendlyURL;
long groupId;
}
@Path("/get-user-sites-groups")
Observable<List<Site>> getUserSitesGroups();
The SDK uses the Gson library for serializing and deserializing objects. If you want to have a custom way of deserializing JSON, you can register a new type adapter:
public class SiteDeserializer implements JsonDeserializer<Site> {
@Override
public Site deserialize(
JsonElement json, Type type, JsonDeserializationContext context)
throws JsonParseException {
// Write your custom deserialization logic
}
}
JSONParser.registerTypeAdapter(Site.class, new SiteDeserializer());
It's also possible to create a Config
instance that has no authentication information. This makes the request to the portal as Guest
:
Config config = new Config.Builder("http://10.0.2.2:8080").build();
Most portal remote methods, however, don't accept unauthenticated remote calls. In most cases, these return an Authentication access required
exception message.
Unauthenticated remote calls will only work if the remote portal method or your plugin has the @AccessControlled
annotation just before the method:
import com.liferay.portal.security.ac.AccessControlled;
public class FooServiceImpl extends FooServiceBaseImpl {
@AccessControlled(guestAccessEnabled = true)
public void bar() { ... }
The SDK allows sending requests using batch processing, which can be much more efficient. For example, suppose you want to delete ten blog entries in a site’s Blogs portlet at the same time. Instead of making a request for each deletion, you can create a batch of calls and send them all together:
import com.liferay.mobile.sdk.Batch;
// Synchronous
Response response = Batch.execute(config, call1, call2, call3);
String json = response.bodyAsString();
// Asynchronous
Batch.async(config, new Callback<Response>() {
@Override
public void onFailure(Exception exception) {
}
@Override
public void onSuccess(Response response) {
String json = response.bodyAsString();
}
}, call1, call2, call3);;
The SDK supports RxJava. To use RxJava, change the return type from Call
to Observable
:
@Path("/get-user-sites-groups")
Observable<List<Site>> getUserSitesGroups();
Observable<List<Site>> observable = service.getUserSitesGroups();
observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Action1<List<Site>>() {
@Override
public void call(List<Site> sites) {
}
},
new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
}
}
);
This will use the global config object (Config.global()
). If you want to use a specific Config
instance, cast Observable
to CallObservable
and set the config object:
CallObservable<List<Site>> observable = (CallObservable<List<Site>>)service.getUserSitesGroups();
observable.config(config);