Quickly update App content without reinstall

adbandroid.png?w=2924

How does it work?

Have you ever wondered what the quickest and easiest way to apply a change in your App immediately is? Many people have the first thought on their mind is to use the instant run installation from Android studio. That would be one possible solution.

But what if I ask you, can you do that without any installation and directly apply a new change to your project? You may say, how could that be possible? Yes, it’s possible to adopt changes without any installation. How to do? Let me show you. You can see the demo video below. I use the command-line tool to update the content of a TextView in the App quickly.

out Command line demo

Let’s see how to do it. First of all, create a debugger broadcast receiver -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Debugger(
private val context: Context,
intentAction: String = "fastReload",
private val actionHandler: (Intent?) -> Unit
): BroadcastReceiver() {

init {
context.registerReceiver(this, IntentFilter(intentAction))
}

override fun onReceive(context: Context?, intent: Intent?) {
actionHandler(intent)
}

fun unRegisterSelf() {
context.unregisterReceiver(this)
}
}

You can see, the default intent action is fastReload, you can set whatever action string you want. I provide the registration and un-registration functions in this receiver.

Let’s instantiate the receiver in your MainActivity (actually, you can set up in whatever activity you want) first.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class MainActivity : AppCompatActivity() {

private var receiver: Debugger? = null
private var debugTextView: TextView? = null


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

debugTextView = debuggerMessage

receiver = Debugger(this) { intent ->
intent?.let {

// update text size
it.getStringExtra("textSize")?.let { size ->
debugTextView?.textSize = size.toFloat()
}

// update text color
it.getStringExtra("textColor")?.let { color ->
val (r, g, b) = color.split(",").map(String::toInt)
val realColor = Color.argb(255, r, g, b)
debugTextView?.setTextColor(realColor)
}

// update message
it.getStringExtra("testMsg")?.let { message ->
debugTextView?.text = message
}

// update rotation
it.getStringExtra("rotation")?.let { rotation ->
debugTextView?.rotation = rotation.toFloat()
}
}
}
}

override fun onDestroy() {
super.onDestroy()
receiver?.unRegisterSelf()
}
}

Do receiver registration in the onCreate function, don’t forget to unregister in the onDestroy function. Then it’s done. It’s pretty easy.

I think this way draws another path to test our App. What are the benefits we can have in this method?

  1. Easily test different logic in the same screen without re-installation.

  2. Easily test intentions if we try to use MVI to construct our project. As you know, the intention is a considerably important concept in the MVI model. Using different intentions to trigger different UI actions. Here is the concept of MVI from Google sample:mvi

  3. You can create a script to debug and to speed up the test process.

  4. Quickly see the result.

Let’s talk about what are the drawbacks of using this way.

  1. Your code logic can not be able to update if you found there’s a bug in your function.
  2. You need to isolate the broadcast receiver with Gradle build flags, cause we don’t want to leak this communication channel when we publish the App. So, you need to use feature modules or build config to separate this from the original App module. That makes your App safer.

From this method, it shows another doable solution for us to test our Apps. Still, we need to see what we can do by using it.

Happy coding.

[Reference]

  1. Android Debug Bridge (adb) | Android Developers
  2. Android Debug Bridge Tricks and Tips | Inside PSPDFKit
  3. GitHub - wangchauyan/adb_debugger: Using ADB command line to fast apply value change in your project