溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android中方法數(shù)超限問題與啟動優(yōu)化的示例分析

發(fā)布時間:2021-08-05 10:04:44 來源:億速云 閱讀:111 作者:小新 欄目:移動開發(fā)

這篇文章將為大家詳細(xì)講解有關(guān)Android中方法數(shù)超限問題與啟動優(yōu)化的示例分析,小編覺得挺實(shí)用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

前言

傳統(tǒng)的 Eclipse 解決方法數(shù)超限的辦法,就是在 project.properties 中加上 dex.force.jumbo=true ,然后清理工程重新編譯。但是,當(dāng)方法數(shù)越來越多,這個方法也會解決不了問題,這個時候,就要用到 Google 官方給出的方案 MultiDex 了。

MultiDex 解決方案

一、 如果你的 minSdkVersion >= 21  ,那么只要在模塊的 build.gradle 中添加:

android {
 defaultConfig {
  ...
  multiDexEnabled true
 }
 ...
}

二、 如果你的 minSdkVersion < 21  ,那么只要在模塊的 build.gradle 中添加:

android {
 defaultConfig {
  ...
  multiDexEnabled true
 }
 ...
}
dependencies {
 compile 'com.android.support:multidex:1.0.1'
}

然后,如果你沒有寫自己的 Application 類,那么你只要寫上自己的 Application 類,并繼承 MultiDexApplication ;如果你寫過自己的 Application 類,并且/或者不希望 Application 類繼承 MultiDexApplication ,那么你需要重寫 Application 的 attachBaseContext 方法:

@Override
protected void attachBaseContext(Context base) {
 super.attachBaseContext(context);
 Multidex.install(this);
}

谷歌multiDex存在的問題

雖然谷歌的分包方案很簡單,但是效果并不是那么好,谷歌本身也枚舉了分包方案的缺點(diǎn):

  • 如果在主線程中執(zhí)行MultiDex.install,加載second dex,因?yàn)榧虞d從dex是同步的,會阻塞線程,second dex太大的話,有可能導(dǎo)致ANR

  • API Level 14之前,由于Dalvik LinearAlloc bug(問題22586,就是上文提到的LinearAlloc問題),很可能會出問題的

  • 應(yīng)用程序使用了multiedex配置的,會造成使用比較大的內(nèi)存

  • 對于應(yīng)用程序比較復(fù)雜的,存在較多的library的項(xiàng)目。multidex可能會造成不同依賴項(xiàng)目間的dex文件函數(shù)相互調(diào)用,找不到方法

啟動優(yōu)化

官方的解決方案雖然簡單,但是也存在一定的局限。比如,首次加載應(yīng)用時,由于需要加載 DEX 文件,會消耗較多的時間,導(dǎo)致啟動速度慢,影響用戶體驗(yàn),甚至很可能引發(fā) ANR 。

針對加載 Dex 問題,美團(tuán)技術(shù)團(tuán)隊(duì)是這樣做的:精簡主 Dex 包,應(yīng)用啟動起來后再異步加載第二個 Dex 包。這是一個很不錯的想法,但是實(shí)現(xiàn)起來有一定的難度。需要編寫腳本,區(qū)分哪些類要放在主 Dex 包中,而且一般項(xiàng)目中都會用到很多第三方 SDK,這很可能導(dǎo)致主 Dex 包的精簡程度不能達(dá)到我們想要的狀態(tài)。

當(dāng)然,除此之外,還有更適合我們的方案,微信開發(fā)團(tuán)隊(duì)的解決思路就很有意思,他們逆了不少 APP,最后參考并改進(jìn)了 Facebook 的解決方案。大概的思路就是,新開一個進(jìn)程來執(zhí)行 Multidex.install() 方法。

微信開發(fā)團(tuán)隊(duì)的思路實(shí)現(xiàn)起來也比較簡單,下面直接上我的代碼(順便把啟動體驗(yàn)也優(yōu)化了~):

Application 中的 attachBaseContext 方法:

@Override
protected void attachBaseContext(Context context) {
 super.attachBaseContext(context);
 if (MultiDexUtils.isMainProcess(context)) { // 判斷是否是主進(jìn)程,避免重復(fù)執(zhí)行
  MultiDexUtils.setMainActivityStarted(this, false); // 保存本地?cái)?shù)據(jù),標(biāo)記主頁面是否已經(jīng)開啟
  MultiDexUtils.setLoadDexActivityClosed(this, false); // 保存本地?cái)?shù)據(jù),標(biāo)記加載Dex進(jìn)程是否已經(jīng)關(guān)閉
  MultiDexUtils.startLoadDexActivity(context); // 打開加載 Dex 的進(jìn)程頁面,這樣我們的APP就變成后臺進(jìn)程了
 }
}

加載 Dex 的進(jìn)程:

public class LoadDexActivity extends Activity {
 private static final int MULTIDEX_ERROR = 0;
 private static final int MULTIDEX_ACTIVITY_STARTED = 1;
 Handler handler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
   switch (msg.what) {
    case MULTIDEX_ERROR:
    case MULTIDEX_ACTIVITY_STARTED: // 退出當(dāng)前進(jìn)程
     MultiDexUtils.setLoadDexActivityClosed(getApplication());
     finish();
     System.exit(0);
     break;
    default:
     break;
   }
  }
 };
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_loaddex);
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
   getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  }
  new Thread() {
   @Override
   public void run() {
    Message message = handler.obtainMessage();
    long startTime = System.currentTimeMillis();
    long timeout = 10 * 1000; // 加載超時時間
    try {
     MultiDex.install(getApplication());
     Thread.sleep(500);
     // 等待主界面啟動
     while (!MultiDexUtils.isMainActivityStarted(getApplication()) &&
       (System.currentTimeMillis() - startTime) < timeout) {
      Thread.sleep(200);
     }
     message.what = MULTIDEX_ACTIVITY_STARTED;
     handler.sendMessage(message);
    } catch (Exception e) {
     message.what = MULTIDEX_ERROR;
     handler.sendMessage(message);
    }
   }
  }.start();
 }
 @Override
 public void onBackPressed() {
  //cannot backpress
 }
}

Manifest 中 LoadDexActivity 的配置:

<activity
 android:name=".LoadDexActivity"
 android:alwaysRetainTaskState="false"
 android:excludeFromRecents="true"
 android:launchMode="singleTask"
 android:process=":loadDex"
 android:screenOrientation="portrait">
</activity>

主界面 onCreate 方法:

@Override
public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 ...
 MultiDexUtils.setMainActivityStarted(getApplication()); // 告訴loadDex進(jìn)程,主界面已啟動
 ...
}

MultiDexUtils 工具類:

public class MultiDexUtils {
 public static final String KEY_ACTIVITY_STARTED = "activity-started-";
 public static final String KEY_LOADDEX_CLOSED = "loaddex-closed-";
 /**
  * 當(dāng)前進(jìn)程是否是主進(jìn)程
  */
 public static boolean isMainProcess(Context context) {
  return "com.***.***(進(jìn)程名一般是包名)".equals(getCurProcessName(context));
 }
 /**
  * 設(shè)置-主界面已經(jīng)打開
  */
 public static void setMainActivityStarted(Context context) {
  setMainActivityStarted(context, true);
 }
 /**
  * 設(shè)置-主界面是否已經(jīng)打開
  */
 public static void setMainActivityStarted(Context context, boolean b) {
  SharedPreferences sp = context.getSharedPreferences("multidex", Context.MODE_MULTI_PROCESS);
  sp.edit().putBoolean(KEY_ACTIVITY_STARTED + getPackageInfo(context).versionCode, b).commit();
 }
 /**
  * 是否需要等待主界面
  */
 public static boolean isMainActivityStarted(Context context) {
  SharedPreferences sp = context.getSharedPreferences("multidex", Context.MODE_MULTI_PROCESS);
  return sp.getBoolean(KEY_ACTIVITY_STARTED + getPackageInfo(context).versionCode, false);
 }
 /**
  * 判斷加載頁面是否關(guān)閉
  */
 public static boolean isLoadDexActivityClosed(Context context) {
  SharedPreferences sp = context.getSharedPreferences("multidex", Context.MODE_MULTI_PROCESS);
  return sp.getBoolean(KEY_LOADDEX_CLOSED + getPackageInfo(context).versionCode, false);
 }
 /**
  * 設(shè)置加載頁面已經(jīng)關(guān)閉
  */
 public static void setLoadDexActivityClosed(Context context) {
  setLoadDexActivityClosed(context, true);
 }
 /**
  * 設(shè)置-加載頁面是否已經(jīng)關(guān)閉
  */
 public static void setLoadDexActivityClosed(Context context, boolean b) {
  SharedPreferences sp = context.getSharedPreferences("multidex", Context.MODE_MULTI_PROCESS);
  sp.edit().putBoolean(KEY_LOADDEX_CLOSED + getPackageInfo(context).versionCode, b).commit();
 }
 /**
  * 開啟等待頁面,新的進(jìn)程
  */
 public static void startLoadDexActivity(Context context) {
  Intent intent = new Intent();
  ComponentName componentName = new ComponentName("com.***.***(包名)", LoadDexActivity.class.getName());
  intent.setComponent(componentName);
  intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  context.startActivity(intent);
 }
 /**
  * 獲取進(jìn)程名
  */
 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;
 }
 /**
  * 獲取包信息
  */
 private static PackageInfo getPackageInfo(Context context) {
  PackageManager pm = context.getPackageManager();
  try {
   return pm.getPackageInfo(context.getPackageName(), 0);
  } catch (PackageManager.NameNotFoundException e) {
//   Log.i(TAG, e.getLocalizedMessage());
  }
  return new PackageInfo();
 }
}

另一種啟動優(yōu)化方案

還有一種簡單的啟動優(yōu)化方案,只能優(yōu)化啟動體驗(yàn),并不能解決 ANR 問題。

在點(diǎn)擊桌面圖標(biāo)啟動應(yīng)用時,給個背景圖片,啟動完成后,將背景設(shè)回空。

1.在入口 Activity 中加入主題背景

android:theme="@style/SplashTheme"

style.xml 中加入配置:

value:

<style name="SplashTheme" parent="@android:style/Theme.NoTitleBar">
 <item name="android:background">@drawable/logo_splash</item>
</style>

value-v21:(解決21版本及以上的過度動畫效果問題)

<style name="SplashTheme" parent="@android:style/Theme.NoTitleBar.Fullscreen">
 <item name="android:windowBackground">@drawable/logo_splash</item>
</style>

2.將背景設(shè)回原樣

@Override
public void setTheme(int resid) {
 super.setTheme(R.style.CustomTransparent);
}

style.xml 配置如下:

<style name="CustomTransparent" parent="@android:style/Theme.Translucent">
 <item name="android:background">@null</item>
 <item name="android:windowBackground">@color/curve_floater_frameColor</item>
</style>

關(guān)于“Android中方法數(shù)超限問題與啟動優(yōu)化的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI