這篇文章將為大家詳細講解有關(guān)Android中方法數(shù)超限問題與啟動優(yōu)化的示例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
創(chuàng)新互聯(lián)長期為上千家客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為建安企業(yè)提供專業(yè)的成都網(wǎng)站制作、網(wǎng)站設(shè)計,建安網(wǎng)站改版等技術(shù)服務(wù)。擁有10余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
前言
傳統(tǒng)的 Eclipse 解決方法數(shù)超限的辦法,就是在 project.properties
中加上 dex.force.jumbo=true
,然后清理工程重新編譯。但是,當方法數(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存在的問題
雖然谷歌的分包方案很簡單,但是效果并不是那么好,谷歌本身也枚舉了分包方案的缺點:
如果在主線程中執(zhí)行MultiDex.install,加載second dex,因為加載從dex是同步的,會阻塞線程,second dex太大的話,有可能導(dǎo)致ANR
API Level 14之前,由于Dalvik LinearAlloc bug(問題22586,就是上文提到的LinearAlloc問題),很可能會出問題的
應(yīng)用程序使用了multiedex配置的,會造成使用比較大的內(nèi)存
對于應(yīng)用程序比較復(fù)雜的,存在較多的library的項目。multidex可能會造成不同依賴項目間的dex文件函數(shù)相互調(diào)用,找不到方法
啟動優(yōu)化
官方的解決方案雖然簡單,但是也存在一定的局限。比如,首次加載應(yīng)用時,由于需要加載 DEX 文件,會消耗較多的時間,導(dǎo)致啟動速度慢,影響用戶體驗,甚至很可能引發(fā) ANR 。
針對加載 Dex 問題,美團技術(shù)團隊是這樣做的:精簡主 Dex 包,應(yīng)用啟動起來后再異步加載第二個 Dex 包。這是一個很不錯的想法,但是實現(xiàn)起來有一定的難度。需要編寫腳本,區(qū)分哪些類要放在主 Dex 包中,而且一般項目中都會用到很多第三方 SDK,這很可能導(dǎo)致主 Dex 包的精簡程度不能達到我們想要的狀態(tài)。
當然,除此之外,還有更適合我們的方案,微信開發(fā)團隊的解決思路就很有意思,他們逆了不少 APP,最后參考并改進了 Facebook 的解決方案。大概的思路就是,新開一個進程來執(zhí)行 Multidex.install()
方法。
微信開發(fā)團隊的思路實現(xiàn)起來也比較簡單,下面直接上我的代碼(順便把啟動體驗也優(yōu)化了~):
Application 中的 attachBaseContext 方法:
@Override protected void attachBaseContext(Context context) { super.attachBaseContext(context); if (MultiDexUtils.isMainProcess(context)) { // 判斷是否是主進程,避免重復(fù)執(zhí)行 MultiDexUtils.setMainActivityStarted(this, false); // 保存本地數(shù)據(jù),標記主頁面是否已經(jīng)開啟 MultiDexUtils.setLoadDexActivityClosed(this, false); // 保存本地數(shù)據(jù),標記加載Dex進程是否已經(jīng)關(guān)閉 MultiDexUtils.startLoadDexActivity(context); // 打開加載 Dex 的進程頁面,這樣我們的APP就變成后臺進程了 } }
加載 Dex 的進程:
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: // 退出當前進程 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進程,主界面已啟動 ... }
MultiDexUtils 工具類:
public class MultiDexUtils { public static final String KEY_ACTIVITY_STARTED = "activity-started-"; public static final String KEY_LOADDEX_CLOSED = "loaddex-closed-"; /** * 當前進程是否是主進程 */ public static boolean isMainProcess(Context context) { return "com.***.***(進程名一般是包名)".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(); } /** * 開啟等待頁面,新的進程 */ 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); } /** * 獲取進程名 */ 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)化啟動體驗,并不能解決 ANR 問題。
在點擊桌面圖標啟動應(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é)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
網(wǎng)站題目:Android中方法數(shù)超限問題與啟動優(yōu)化的示例分析
轉(zhuǎn)載注明:http://muchs.cn/article20/ghsjjo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供云服務(wù)器、自適應(yīng)網(wǎng)站、動態(tài)網(wǎng)站、網(wǎng)站導(dǎo)航、網(wǎng)站設(shè)計公司、搜索引擎優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)