过程
昨天在编译某个android项目的时候,发现dex打包出错。
后来检查发现编译生成的SplashScreenActivity$1.class格式出错。
后来经常测试,发现jdk6正常,jdk7不正常,包括最新的u51版本。
bug分析
相关的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| private void checkUpdate() {
if (UpgradeInfo.isApkLocalExist(SplashScreenActivity.this, UpgradeInfo.getFilePath())) {
updateInstall();
} else if (AUTO_UPDATE) {
initDialog();
mUpgradeInfo = new UpgradeInfo();
mGetVersionConfig = new GetVersionConfig(SplashScreenActivity.this,
new CheckUpgradeHandler(), mUpgradeInfo, getUpgradeRequestParam());
mGetVersionConfig.start();
} else {
// /没版本且不升级
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent i = new Intent(SplashScreenActivity.this, LoginActivity.class);
startActivity(i);
finish();
}
}, LOADING_TIME);
}
}
|
正常情况下,Runnable匿名类会生成一个class,但是这里的AUTO_UPDATE被定义为static final,并且设置为true。
在这种情况下,jdk会优化掉后面的分支,但是jdk6不会生成class,但是jdk7会生成一个格式有误的class,反编译后如下:
1
2
3
4
5
| class SplashScreenActivity$1
implements Runnable
{
public void run();
}
|
所以,在进行dex打包的时候,就会检测到不规范的class,进行报错。
我想,如果在web应用上的话,应该是不会有问题的,因为这个类没有机会被使用。
后来,我发现在其他項目中也有类似写法,但是编译不会有问题。如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| if (UpgradeInfo.isApkLocalExist(LogoActivity.this,UpgradeInfo.getFilePath())) {
// /检测到有新版本可以安装
String msg = getString(R.string.dialog_check_version);
DialogUtil.dialogForTwoButton(... , new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//..
}
});
} else if (AUTO_UPDATE) {
// ....
} else {
///没版本且不升级
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent i = new Intent(LogoActivity.this, OperatorLoginActivity.class);
startActivity(i);
finish();
}
}, LOADING_TIME);
}
|
故猜测,这个bug只出现在第一个不被使用匿名类身上(因为这个类前面还有个OnClickListener的匿名类)。
经过测试,发现也的确是存在这样的情况。
规避方式
乖乖使用jdk6,不要使用jdk7。