Kotlin with Volley

Volley is my go-to networking library in Android and naturally I wanted to bring it with me when moving to Kotlin. Volley and Kotlin both heighten the Android development experience in their own ways. Kotlin in the way it lets you express yourself, and Volley in its easy-to-use networking tools, whether talking with an API or loading an ImageView from a weblink (and much more).

One of the downsides with Volley is its verbose syntax and (as so often in vanilla Java/Android) the excessive use of interfaces and listeners to communicate the networking result to your fragment/activity. Come Kotlin to the rescue!

Ground rules

There’s plenty of “Getting started with Kotlin” content out there so just go and consume it if you need to. This post assumes that you got that part down already.

We will be building mainly three classes

  • BackendVolley — takes care of the request queueing and such
  • ServiceVolley — defines the Volley request(s)
  • APIController — decouples Volley dependency from the project

This will be brought together through a ServiceInterface to facilitate your unit testing/mocking, hence the need of the APIController.

We will be passing a completionHandler to the request object to get rid of interface listeners.

You will need to include the dependency compile 'com.android.volley:volley:1.0.0' in your build.gradle (Module: app) to get access to Volley’s classes. All the code seen below is available in this git repo.

The project

Let’s make a JSON request to an imaginary REST API, using the classes below.

First comes the ServiceInterface which defines how we want our request to look like. In our case we start with a post request.

interface ServiceInterface {
    fun post(path: String, params: JSONObject, completionHandler: (response: JSONObject?) -> Unit)
}

Notice that we pass a completionHandler so that we can handle the result from where we called the request, rather than setting up interfaces for listeners on the network response.

The BackendVolley is a singleton that keeps track of the requests and is defined as below.

class BackendVolley : Application() {
  override fun onCreate() {
      super.onCreate()
      instance = this
  }

  val requestQueue: RequestQueue? = null
      get() {
          if (field == null) {
              return Volley.newRequestQueue(applicationContext)
          }
          return field
      }

  fun <T> addToRequestQueue(request: Request<T>, tag: String) {
      request.tag = if (TextUtils.isEmpty(tag)) TAG else tag
      requestQueue?.add(request)
  }

  fun <T> addToRequestQueue(request: Request<T>) {
      request.tag = TAG
      requestQueue?.add(request)
  }

  fun cancelPendingRequests(tag: Any) {
      if (requestQueue != null) {
          requestQueue!!.cancelAll(tag)
      }
  }

  companion object {
      private val TAG = BackendVolley::class.java.simpleName
      @get:Synchronized var instance: BackendVolley? = null
          private set
  }
}

Notice that BackendVolley inherits from Application, which means you have to include the class in your manifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.varvet.kotlinwithvolley">

    <application
        android:name=".BackendVolley"
        ...
        ...

The APIController complies to the ServiceInterface and is defined as

class APIController constructor(serviceInjection: ServiceInterface): ServiceInterface {
    private val service: ServiceInterface = serviceInjection

    override fun post(path: String, params: JSONObject, completionHandler: (response: JSONObject?) -> Unit) {
        service.post(path, params, completionHandler)
    }
}

APIController breaks the relationship between our “regular” code and our choice of networking library. You can easily inject any other class following the ServiceInterface if you care to change library in the future. And, of course, it allows us to mock the networking api in our tests (not included in this blog post).

ServiceVolley also complies to ServiceInterface and implements the actual Volley request.

class ServiceVolley : ServiceInterface {
    val TAG = ServiceVolley::class.java.simpleName
    val basePath = "https://your/backend/api/"

    override fun post(path: String, params: JSONObject, completionHandler: (response: JSONObject?) -> Unit) {
        val jsonObjReq = object : JsonObjectRequest(Method.POST, basePath + path, params,
                Response.Listener<JSONObject> { response ->
                    Log.d(TAG, "/post request OK! Response: $response")
                    completionHandler(response)
                },
                Response.ErrorListener { error ->
                    VolleyLog.e(TAG, "/post request fail! Error: ${error.message}")
                    completionHandler(null)
                }) {
            @Throws(AuthFailureError::class)
            override fun getHeaders(): Map<String, String> {
                val headers = HashMap<String, String>()
                headers.put("Content-Type", "application/json")
                return headers
            }
        }

        BackendVolley.instance?.addToRequestQueue(jsonObjReq, TAG)
    }
}

Overriding getHeaders() is of course optional, but working with json is standard for me so I kept it as an example.

Notice that we pass the result from the network to our completionHandler, allowing us to move the data handling to where it should actually take place. ServiceVolley is only responsible for handling the requests and returning the result in a JSONObject (in this case), it knows nothing about potential parsing or such — this is left to the callers to handle.

Making the request

Given the above classes, the networking layer is ready to be used. In short the usage might look like

val service = ServiceVolley()
val apiController = APIController(service)

val path = "example_endpoint"
val params = JSONObject()
params.put("email", "foo@email.com")
params.put("password", "barpass")

apiController.post(path, params) { response ->
    // Parse the result
}

As usual in Kotlin, if the last parameter to a function is a function (and you're passing a lambda expression as the corresponding argument), you can specify it outside of parentheses, as we’ve done above — one of the small quirks I love about Kotlin.

And that’s it!

Closing thoughts

You don’t actually need the ServiceInterface and APIController, it’s just my preferred way of doing it.

Volley supports sending the Method you’d like to use (such as Method.POST), so you could just make a request function in the ServiceInterface and pass in the Method as an argument, rather than making an actual post function. Totally up to personal preference.

Any thoughts on this post? I’d love to hear them — leave a comment!