今天写的内容主要是针对公司的项目中,方法数超过65535的问题,并给出解决方案。
本文是在前人的基础上稍作总结,具体见文末。
应用中的Dex 文件方法数超过了最大值65536的上限,简单来说,应用爆棚了,报错如下:
UNEXPECTED TOP-LEVEL EXCEPTION:
java.lang.IllegalArgumentException: method ID not in [0, 0xffff]: 65536
defaultConfig {
applicationId "com.rytong.app.bankhx"
minSdkVersion 8
targetSdkVersion 8
multiDexEnabled true
}
dependencies {
compile 'com.android.support:multidex:1.0.1'
}
@Override
protected void attachBaseContext(Context base) {
super .attachBaseContext(base);
MultiDex.install (this);
}
到这个时候,我发现,运行项目app百分百ANR,百度后认为是google官方的补丁方案的bug,于是进行下面的第三步。
大致意思如下:
1. 在安卓应用时,如果应用比较大,第二个dex文件过大,可能会导致ANR,请用ProGuard优化代码;
2. 使用了multidex的app可能在系统版本在4.0以下的手机启动失败,请多多测试,并使用ProGuard优化代码减少bug发生的几率;
3. 使用了mulitDex的App在runtime期间有可能因为Dalvik linearAlloc limit Crash。该内存分配限制在 4.0版本被增大,但是5.0以下的机器上的Apps依然会存在这个限制;
4. 在主dex执行时,需要保证一些类必须在主dex中,build tools 可以搞定这个问题。但是如果你代码存在反射和native的调用也不保证100%正确。
由于代码较多,在本文档中不做过多阐述,以附件形式提供。在application启动时,启动一个新的activity加载classes2.dex,加载完之后再调用Application的onCreate方法启用应用。
具体流程图如下:
关键代码如下:
@Override
protected void attachBaseContext(Context base) {
super .attachBaseContext(base);
Log.e( "attachBaseContext", "App attachBaseContext ");
if (!quickStart() && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {//>=5.0的系统默认对dex进行oat优化
if (needWait(base)){
Log.e( "attachBaseContext", "needWait");
waitForDexopt(base);
}
Log.e( "attachBaseContext", "MultiDex.install");
MultiDex.install (this );
} else {
return;
}
}
public void waitForDexopt(Context base) {
Intent intent = new Intent();
ComponentName componentName = new
ComponentName( "com.rytong.app.bankhx", "com.rytong.newengine.LoadResActivity");
// ComponentName( "com.bank.hx", LoadResActivity.class.getName());
intent.setComponent(componentName);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
base.startActivity(intent);
long startWait = System.currentTimeMillis ();
long waitTime = 10 * 1000 ;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1 ) {
waitTime = 20 * 1000 ;//实测发现某些场景下有些2.3版本有可能10s都不能完成optdex
}
while (needWait(base)) {
try {
long nowWait = System.currentTimeMillis() - startWait;
Log.d("loadDex" , "wait ms :" + nowWait);
if (nowWait >= waitTime) {
return;
}
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//neead wait for dexopt ?
private boolean needWait(Context context){
String flag = get2thDexSHA1(context);
Log.d( "loadDex", "dex2-sha1 "+flag);
SharedPreferences sp = context.getSharedPreferences(
PackageUtil.getPackageInfo(context). versionName, MODE_MULTI_PROCESS);
String saveValue = sp.getString(KEY_DEX2_SHA1, "");
Log.e( "needWait", flag+"..."+saveValue);
if (!TextUtils.isEmpty(flag)) {
return !flag.equals(saveValue);
}else if(!TextUtils.isEmpty(saveValue)){
return !saveValue.equals(flag);
}
return true;
}
/**
* Get classes.dex file signature
* @param context
* @return
*/
private String get2thDexSHA1(Context context) {
ApplicationInfo ai = context.getApplicationInfo();
String source = ai.sourceDir;
try {
JarFile jar = new JarFile(source);
Manifest mf = jar.getManifest();
Map<String, Attributes> map = mf.getEntries();
Attributes a = map.get("classes2.dex");
return a.getValue("SHA1-Digest");
} catch (Exception e) {
e.printStackTrace();
}
return null ;
}
public boolean quickStart() {
if (!TextUtils.isEmpty(getCurProcessName(this))) {
Log.d( "loadDex", ":mini start!");
return getCurProcessName(this).contains(":mini");
}
return false ;
}
public static String getCurProcessName(Context context) {
try {
int pid = android.os.Process.myPid();
ActivityManager mActivityManager = (ActivityManager) context
.getSystemService(Context. ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager
.getRunningAppProcesses()) {
if (appProcess.pid == pid) {
return appProcess. processName;
}
}
} catch (Exception e) {
// ignore
}
return null ;
}
经排查发现,在启动Activity里面做了很多内容的初始化,比如
我将其中一部分注释掉后,app正常运行。在4.0以上5.0以下手机上,第一次安装或者覆盖安装第一次启动会显示loading页面3s左右,所以,能不用该方案则不用。(multidex方案大同小异,没有任何方案一点牺牲都不做的。)
在经过此番排查后发现其实我们只需要按照google的标准做法即可,优化启动Activity中阻塞线程操作,就不会出现ANR(也就是说这个方案有可能都用不上)。
如果有65535问题,则建议项目上如此处理:
1. 按照google官方文档添加配置,在Application中添加相应代码;
2. 优化启动Activity中的阻塞线程操作,关闭或者放到异步线程执行。
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。