Chapter 7 of the tutorial uses a native Android Mobile App to consume content from AEM Content Services.
This tutorial uses a simple native Android Mobile App to consume and display Event content exposed by AEM Content Services.
The use of Android is largely unimportant, and the consuming mobile app could be written in any framework for any mobile platform, for example iOS.
Android is used for tutorial due to the ability to run an Android Emulator on Windows, macOs and Linux, its popularity, and that it can be written as Java, a language well understand by AEM developers.
The tutorial’s Android Mobile App is not intended to instruct how to build Android Mobile apps or convey Android development best practices, but rather to illustrate how AEM Content Services can be consumed from a Mobile Application.
If AEM Publish is not being run on http://localhost:4503 the host and port can be updated in the Mobile App’s Settings to point to the property AEM Publish host/port.
This section highlights the Android Mobile App code that most interacts and depends on AEM Content Services and it’s JSON output.
Upon load, the Mobile App makes HTTP GET
to /content/wknd-mobile/en/api/events.model.json
which is the AEM Content Services end-point configured to provide the content to drive the Mobile App.
Because the Editable Template of the Events API (/content/wknd-mobile/en/api/events.model.json
) is locked, the Mobile App can be coded to look for specific information in specific locations in the JSON response.
HTTP GET
request to the AEM Publish at /content/wknd-mobile/en/api/events.model.json
to collect the content to populate the Mobile App’s UI.The following is a distillation of the code in the Mobile App’s MainActivity
responsible for invoking AEM Content Services to collect the content that drives the Mobile App experience.
protected void onCreate(Bundle savedInstanceState) {
...
// Create the custom objects that will map the JSON -> POJO -> View elements
final List<ViewBinder> viewBinders = new ArrayList<>();
viewBinders.add(new LogoViewBinder(this, getAemHost(), (ImageView) findViewById(R.id.logo)));
viewBinders.add(new TagLineViewBinder(this, (TextView) findViewById(R.id.tagLine)));
viewBinders.add(new EventsViewBinder(this, getAemHost(), (RecyclerView) findViewById(R.id.eventsList)));
...
initApp(viewBinders);
}
private void initApp(final List<ViewBinder> viewBinders) {
final String aemUrl = getAemUrl(); // -> http://localhost:4503/content/wknd-mobile/en/api/events.mobile.json
JsonObjectRequest request = new JsonObjectRequest(aemUrl, null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
for (final ViewBinder viewBinder : viewBinders) {
viewBinder.bind(response);
}
}
},
...
);
}
onCreate(..)
is the initialization hook for the Mobile App, and registers the 3 custom ViewBinders
responsible for parsing the JSON and binding the values to the View
elements.
initApp(...)
is then called which makes the HTTP GET request to the AEM Content Services end-point on AEM Publish to collect the content. Upon receiving a valid JSON Response, the JSON response is passed to each ViewBinder
which is responsible for parsing the JSON and binding it to the mobile View
elements.
Next we’ll look at the LogoViewBinder
, which is simple, but highlights several important considerations.
public class LogoViewBinder implements ViewBinder {
...
public void bind(JSONObject jsonResponse) throws JSONException, IOException {
final JSONObject components = jsonResponse.getJSONObject(":items").getJSONObject("root").getJSONObject(":items");
if (components.has("image")) {
final Image image = objectMapper.readValue(components.getJSONObject("image").toString(), Image.class);
final String imageUrl = aemHost + image.getSrc();
Glide.with(context).load(imageUrl).into(logo);
} else {
Log.d("WKND", "Missing Logo");
}
}
}
The first line of bind(...)
navigates down the JSON Response via the keys :items → root → :items which represents the AEM Layout Container the components were added to.
From here a check is made for a key named image, which represents the Image component (again, it is important this node name → JSON key is stable). If this object exists, it read and mapped to the custom Image POJO via the Jackson ObjectMapper
library. The Image POJO is explored below.
Finally, the logo’s src
is loaded into the Android ImageView using the Glide helper library.
Notice that we must provide the AEM schema, host and port (via aemHost
) to the AEM Publish instance as AEM Content Services will only provide the JCR path (ie. /content/dam/wknd-mobile/images/wknd-logo.png
) to the referenced content.
While optional, the use of the Jackson ObjectMapper or similar capabilities provided by other libraries like Gson, helps map complex JSON structures to Java POJOs without the tedium of dealing directly with the native JSON objects themselves. In this simple case we map the src
key from the image
JSON object, to the src
attribute in the Image POJO directly via the @JSONProperty
annotation.
package com.adobe.aem.guides.wknd.mobile.android.models;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Image {
@JsonProperty(value = "src")
private String src;
public String getSrc() {
return src;
}
}
The Event POJO, which requires selecting many more data points from the JSON object, benefits from this technique more than the simple Image, which all we want is the src
.
Now that you have an understanding of how AEM Content Services can drive native Mobile experience, use what you’ve learned to perform the following steps and see your changes reflected in the Mobile App.
After each step, pull-to-refresh the Mobile App and verify the update to the mobile experience.
You’ve completed with the AEM Headless tutorial!
To learn more about AEM Content Services and AEM as a Headless CMS, visit Adobe’s other documentation and enablement materials: