Google Cloud Pub Sub on Android with MVVM

Matheus Hofstede
3 min readApr 30, 2020

--

As a demand for a project of my work I had to study Pub Sub architecture to implement an app with real time data. I’ve used as a example a internal project that was already running Pub Sub. Because there is not much content on the web on how to use Pub Sub on Android, I’ve decided to explain how I did it.

Prologue

First of all you need to sign up for a free Google Cloud Platform account and enroll to a free trial at https://cloud.google.com/free

Now follow this video tutorial up to step 2 to create everything you need. In the end you should have 4 things:
- gcloud project name (mine is wired-record-275613)
- topic name (mine is topic)
- subscription name (mine is subscription)
- the .json file with the credentials

On Android

Let’s start with the gradle file (app), put these dependencies that we will use in the project.

implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.google.android.material:material:1.1.0'

implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha01'

implementation 'com.github.blocoio:faker:1.2.8'

implementation ('com.google.cloud:google-cloud-pubsub:1.105.0') {
exclude group: 'org.apache.httpcomponents'
}
implementation 'io.grpc:grpc-okhttp:1.28.0'

You will notice that you will get an error when you build the project, something about META-INF and Dex, so put this under buildTypes

packagingOptions {
exclude 'META-INF/INDEX.LIST'
}

And inside defaultConfig

multiDexEnabled true

Then rename the .json file downloaded from Cloud Platform to credentials.json and move it to res/raw/

Also don’t forget to give internet permissions to the app

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

I’ve used MVVM architecture with Repository in this project having “Depend on an abstraction and not an implementation”, the Dependency inversion principle mindset. So the ViewModel will depend on an interface (abstraction), and it doesn’t matter where the data comes from. This makes testing and future changes much easier, in case I start using REST, for example.

Project structure

Here are the implementation of the main Pub Sub features, subscribe(), publish() and receiveMessage()

override fun subscribe(subscription: String) {
GlobalScope.launch(Dispatchers.IO) {
try {
val subscriptionName = ProjectSubscriptionName.of(projectName, subscription)

subscriber = Subscriber.newBuilder(subscriptionName, this@GoogleCloudPubSubDataSource)
.setCredentialsProvider { credentials }
.build()
subscriber?.startAsync()?.awaitRunning()
subscriber?.awaitTerminated()
} catch (t: Throwable) {
Log.i("PUBSUB", t.message ?: "Something went wrong")
}
}
}
override fun publish(topicName: String, message: String) {
var publisher: Publisher? = null
try {
val projectTopicName = ProjectTopicName.of(projectName, topicName);
publisher = Publisher.newBuilder(projectTopicName).setCredentialsProvider(
FixedCredentialsProvider.create(credentials)).build()
val data = ByteString.copyFromUtf8(message)
val pubsubMessage = PubsubMessage.newBuilder().setData(data).build()
val messageIdFuture: ApiFuture<String> = publisher.publish(pubsubMessage)

ApiFutures.addCallback(
messageIdFuture,
object : ApiFutureCallback<String> {
override fun onSuccess(messageId: String) {
println("published with message id: $messageId")
}
override fun onFailure(t: Throwable) {
println("failed to publish: $t")
}
},
MoreExecutors.directExecutor()
)
} finally {
if (publisher != null) {
publisher.shutdown();
publisher.awaitTermination(1, TimeUnit.MINUTES);
}
}
}
override fun receiveMessage(message: PubsubMessage, consumer: AckReplyConsumer) {
Log.i("PUBSUB", message.data.toStringUtf8())
consumer.ack()
pubSubMessages.add(gson.fromJson(message.data.toStringUtf8(), Message::class.java))
viewModel.updateCodeArrayList(pubSubMessages)
}

I’m not going to paste the code here, Github himself does it very well, I just wanted to leave an explanation of what’s going on behind the scenes.
When I click on the FAB, I call the ViewModel’s sendMessage() method with a random name and message. ViewModel calls sendMessage() that was injected from MainActivity and it publishes the message. Now everyone who subscribed to this topic subscription will receive the message, and as the app itself has subscribed in MainActivity, it receives the message, updates ViewModel’s LiveData and populates RecyclerView.

Do not worry on things running on the main thread and the adapter being recreated every time a new data arrives, what I really wanted to show is Pub Sub on Android with a fully reactive architecture.

Useful things

Complete code:
https://github.com/hofstede-matheus/PubSubAndroidExample
A nice playlist from Google explaining Pub Sub using Cloud Plataform
https://www.youtube.com/playlist?list=PLIivdWyY5sqKwVLe4BLJ-vlh9r9zCdOse
A nice vide explaining the concept behind Pub Sub:
https://www.youtube.com/watch?v=O1PgqUqZKTA

--

--