让人纠结的finally

最近分析某一apk,被其中的各种try-catch-finally块虐得死去活来,有木有。。。
由于喜欢刨根,各种谷歌都没有给出解释。 一番研究之后,发现确实有些蹊跷。(本人水平有限,不喜勿喷,谢谢)

切入正题:
try{}catch反编译后,格式:    
    eg: .catch Ljava/io/FileNotFoundException; {:try_start_0 .. :try_end_0} :catch_0
加上finally,那就多一行声明:
   eg: .catchall {:try_start_0 .. :try_end_0} :catchall_0
大家都知道,finally块中的代码始终都要执行,那么是不是类似goto到finally块中去执行代码呢? 可惜不是,是直接复制了3份。
比如:
  try {
    FileInputStream stream = new FileInputStream(new File("/mnt/sdcard/test.txt"));
  } catch (FileNotFoundException e) {
    e.printStackTrace();
  }finally{
    System.out.println("Fin");
  }
smali:
:try_start_0
    new-instance v1, Ljava/io/FileInputStream;

    new-instance v2, Ljava/io/File;

    const-string v3, "/mnt/sdcard/test.txt"

    invoke-direct {v2, v3}, Ljava/io/File;-><init>(Ljava/lang/String;)V

    invoke-direct {v1, v2}, Ljava/io/FileInputStream;-><init>(Ljava/io/File;)V
    :try_end_0
    .catchall {:try_start_0 .. :try_end_0} :catchall_0
    .catch Ljava/io/FileNotFoundException; {:try_start_0 .. :try_end_0} :catch_0

    .line 28
    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;

    const-string v2, "Fin"

    invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V


    .line 30
    :goto_0
    return-void

    .line 24
    :catch_0
    move-exception v0

    .line 26
    .local v0, e:Ljava/io/FileNotFoundException;
    :try_start_1
    invoke-virtual {v0}, Ljava/io/FileNotFoundException;->printStackTrace()V
    :try_end_1
    .catchall {:try_start_1 .. :try_end_1} :catchall_0

    .line 28
    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;

    const-string v2, "Fin"

    invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

    goto :goto_0

    .line 27
    .end local v0           #e:Ljava/io/FileNotFoundException;

    :catchall_0
    move-exception v1

    .line 28
    sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;

    const-string v3, "Fin"

    invoke-virtual {v2, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

    .line 29
    throw v1

(由于黄色看不清,这里修改为红色)

不难发现,正常执行流程和catch块中增加了finally中的代码,而且二者是同一个出口。(goto_0)
那黄色(第三块红色)那块是什么???
经过多次测试,只要catch块中调用了函数,就会隐式生成一个try,且只有.catchall 
:try_start_1
    invoke-virtual {v0}, Ljava/io/FileNotFoundException;->printStackTrace()V
 :try_end_1
 .catchall {:try_start_1 .. :try_end_1} :catchall_0

catchall_n一般结构是:
move-exception vm
......
throw vm

另外,经过多次动态调试smali,都没有出现程序进入.catchall_n模块。估计是没有构造出相应的触发条件。由于运行时异常不需要显式声明,猜测可能运行时异常触发。故构造一下代码:
  public void fun(int flag) throws IllegalArgumentException
  {
    switch(flag){
      case 1: throw new IllegalArgumentException();
      case 2: throw new RuntimeException();
      default: break;
    }
  }
  public void Test(int flag)
  {
    try {
      fun(flag);    
      foo();
    } catch (Exception e) {
      foo();
    }finally{
      Log.i("Finally", "Exit");
    }
  }
  public void foo(){
    throw new RuntimeException();
  }
由于Log.i("Finally", "Exit");会被拷贝三份,故修改.catall中的输出,进行区别。具体如下:
    :catchall_1a
    move-exception v1

    .line 39
    const-string v2, "Finally"

    const-string v3, "Exit_Finally"
修改"Exit"为"Exit_Finally"

    invoke-static {v2, v3}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I

    .line 40
    throw v1
onCreate分别测试调用Test(0)和Test(1),均打印出"Exit_Finally",并未打印出"Exit"。
点击图片以查看大图图片名称:	1.png查看次数:	3文件大小:	11.4 KB文件 ID :	90388

这下终于明白了.catchall的作用,确实保证了finally块中的代码一定会被执行。

PS:要是多重try-catch-finally嵌套,而且finally块中有很多代码,那就。。。 万恶的finally块,虐了我千百遍。。。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值