Android Studio之Debug运行期代码植入的方法

作者:袖梨 2022-06-25

我们开发中经常使用IDE的Debug功能,使用最频繁的就是断点查看变量内容,这篇文章主要是介绍在Debug时很常用的一些功能,如Debug变量更新、动态代码植入等。

背景

看下面一段代码

@Data
@ToString
@AllArgsConstructor
public class User {
  private String name;
  private String sex;
}
public class Client {
  private static final String[] NAMES = {"美队", "浩克", "黑寡妇"};
  private static final List USERS = Arrays.stream(NAMES)
      .map(name -> new User(name, "男"))
      .collect(Collectors.toList());

  public static void main(String[] args) {
    for (User user : USERS) {
      System.out.println(user);
    }
  }
}

我们直接运行这段代码,得到的输出如下

Client.User(name=美队, sex=男)
Client.User(name=浩克, sex=男)
Client.User(name=黑寡妇, sex=男)

接下来都会围绕这段代码进行说明

普通断点

开发过程中普通断点在Debug过程中使用的算是最频繁的,只要在代码处添加该类型的断点,在Debug模式下,一旦代码执行到改行,程序将进入挂起状态,此时我们能查看到当前的运行状态,如图

条件断点

当添加断点的方法会被多次执行到,而我们只希望程序在特定的一些特定的地方(如只有在当前的user是 黑寡妇 时)断下来,此时我们可以借助条件断点,如下

注意看一下 Condition 部分,此处需要返回一个 boolean 类型的值,该值就是为了告诉程序当执行到该行代码时是否进行断点,true表示进行断点,false反之。 上面只需要对 黑寡妇 进行断点,所以添加的 Condition"黑寡妇".equals(user.getName())

改变变量状态

有时需要对断点时的变量赋值操作,已更改此时变量的值,这时可以使用 Set Value 方式,如下

除了 Set Value ,还可以通过估值器进行改变值状态,如下

条件断点 + 代码植入

改变变量状态能够动态地设置变量值,条件断点可以控制断点是能在此处挂起,那将两者结合就能达到在程序不挂起的情况下进行动态代码植入的效果。

Condition 里插入的代码如下

if ("黑寡妇".equals(user.getName())) {
  user.setSex("女");
}
// 此处返回false为了告诉IDE在该断点出不对程序执行挂起操作
return false;

这就相当于提供了运行期代码的动态植入功能,而且可以通过 Enable/Disable 断点的状态来进行开启/关闭该部分动态代码是否执行。我们可以通过这种方式添加任何希望添加的代码,比如在每个user对象print之前添加自定一段print

应用场景

Debug期间动态植入的应用场景其实有很多的,对应Android端,有时候只需要改变一个变量,添加一行代码就需要重新run整个Project,需要花费整个compile和install的时间才能看出改动之后的效果,如果当前这次更改无效的话,甚至需要再次修改再次运行,而通过debug方式,可以直接对其进行操作。对于后端每次改动之后的重新部署也一样(不考虑动态部署的情况)。

以Android场景为例,如下的代码,接收一个 imageUrl 参数并加载出来

public class ImageActivity extends Activity {

  private static final String KEY_CONTENT = "imageUrl";

  public static void open(Context context, String content) {
    Intent intent = new Intent(context, ImageActivity.class);
    intent.putExtra(KEY_CONTENT, content);
    context.startActivity(intent);
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ImageView imageView = findViewById(R.id.image);
    // 取出参数内容
    String imageUrl = getIntent().getStringExtra(KEY_CONTENT);
    Glide.with(this).load(imageUrl).into(imageView);
  }

}

未植入代码之前,跳转到当前页面是这样的

此时如果需要动态改变当前的 imageUrl 参数,可以进行Debug模式,并通过动态代码植入方式进行 imageUrl 变量的重赋值,如下

添加植入的复制代码之后,进入Debug模式,然后重新进入该页面,显示的图片便替换为修改后的了。

相关文章

精彩推荐