Blockstack Android Tips

When developing the Envelop Android app using the Blockstack Android library, I ran into some important gotchas. I wanted to share them for whoever starts on the same path.

Starting out

The library is still pre-release, but it’s built upon the more stable blockstack.js library. That means is also not a native library. It uses the j2v8 engine to interpret and interact with the javascript library.

The two main consequences are:

  • Your app will weight around 20Mb more due to the blockstack library
  • You need to be careful when you initialize a BlockstackSession. It takes a while for it to be ready to use.


The library threading is configured by an Executor. It defines 3 threads, the main thread, the background thread and the V8 thread that runs the javascript library.

By default, the V8 thread uses the main thread. But for a production app, that means blocking the main thread on every operation, something definitely not recommended. To avoid this, you should define a new Executor and set it on the BlockstackSession. Here’s an example:

class BlockstackExecutor(
  private val context: Context
) : Executor {

  private val handlerThread = HandlerThread("BlockstackV8").apply { start() }
  private val handler = Handler(handlerThread.looper)

  override fun onV8Thread(function: () -> Unit) { {

  override fun onMainThread(function: (ctx: Context) -> Unit) {
    GlobalScope.launch(Dispatchers.Main) {

  override fun onNetworkThread(function: suspend () -> Unit) {
    GlobalScope.launch(Dispatchers.IO) {

But now you need to make sure you’re calling the blockstack operations for the thread you just created. If, for example, you’re using RxJava, that means building a custom Scheduler for that thread, and using it in subscribeOn:

val blockstackScheduler = Schedulers.from {   

For example, here’s how we wrap a deleteFile operation with RxJava:

fun deleteFile(fileName: String) =
    .create { emitter ->
      try {
        blockstack.deleteFile(fileName, DeleteFileOptions()) {
          if (it.hasErrors) {
          } else {
      } catch (t: Throwable) {

Network Errors

In the current library version (0.4.3), there’s a bug that hides network errors. They become indistinguishable from requests that are taking too long. There’s a pending PR to fix it. But until then, we’re using our own fork.


In the examples given, BlockstackSession is initialized with an Activity Context. But for almost all Blockstack operations, the Application Context. This is useful for taking that logic out of the Activity classes and properly architecture your code.

But the login call redirectUserToSignIn requires a BlockstackSession with an Activity Context. That’s because it opens a WebView from the inside. If you still want to use the Application Context for all the other calls, just make sure the different BlockstackSession instances share the same SessionStore. That way, all BlockstackSession instances read and store the session data from the same location.

APK signing

This one is not directly related with Blockstack, but something to keep an eye on. During login, users first sign in on Blockstack inside a WebView. Then the authentication response comes back to the Android app through an app link. For that process to be seamless, the app link should be automatically verified.

Google Play now encourages delegating the APK signing process to them. But that means that the sha256_cert_fingerprints that ends up in the server assetlinks.json file will not be the one you would expect. Your upload key signing gets removed from the APK. You need to fetch the correct fingerprint from your Google Play Dashboard under Release management > App signing.

And that’s what I’ve got. Hope I didn’t scare you too much, it’s not that bad actually. Just some details that will likely be ironed out in the future. Any other issue, you can reach out on the library’s Github. I’ll be keeping an eye on there too.

Curious about what a Blockstack app is like? Try out our Envelop Android app, and look into our app’s source code.