Android 多CPU架構支持所需要了解的知識

我的博客原文地址

前言

Android系統目前支持以下七種不同的CPU架構:ARMv5,ARMv7 (從2010年起),x86 (從2011年起),MIPS (從2012年起),ARMv8,MIPS64和x86_64 (從2014年起),每一種都關聯着一個相應的ABI。ABI是指應用基於哪種指令集來進行編譯。
如果項目中使用到了NDK,它將會生成.so文件,Android應用支持的ABI取決於APK中位於lib/ABI目錄中的.so文件,其中ABI可能是上面說過的七種ABI中的一種。 Android包管理器安裝APK時,如果在對應的lib/ABI目錄中存在.so文件的話,會自動選擇APK包中爲對應系統ABI預編譯好的.so文件。當一個應用安裝在設備上,只有該設備支持的CPU架構對應的.so文件會被安裝。在x86設備上,libs/x86目錄中如果存在.so文件的 話,會被安裝,如果不存在,則會選擇armeabi-v7a中的.so文件,如果也不存在,則選擇armeabi目錄中的.so文件(因爲x86設備也支 持armeabi-v7a和armeabi)。

問題

如果我們平時開發過程中沒有很好注意這些,那麼就會帶來一些兼容性的問題。
比如:你的App支持armeabi-v7ax86架構,然後使用 Android Studio 新增了一個函數庫依賴,這個函數庫包含.so文件並支持更多的CPU架構,那麼在某些進行上可能會發生Crash,會出現"UnsatisfiedLinkError""dlopen: failed"等等問題:

AndroidRuntime: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[***********] couldn't find "lib*******.so"

或者

AndroidRuntime: java.lang.UnsatisfiedLinkError: dlopen failed: "*********.so" is 32-bit instead of 64-bit

這是因爲,你添加的一些庫可能裏面做了更多的架構適配,因爲只要出現了這個目錄,系統就只會在這個目錄裏找.so文件而不會遍歷其他的目錄,所以就出現了找不到so文件的情況(因爲其他目錄沒有這個so文件)。
舉個例子來詳細說明一下:我們的APP只支持armeabi-v7a和x86架構,然後我們的APP使用了一個第三方的Library,而這個Library提供了AMR64等更多類型CPU架構的支持,構建APK的時候,這些ARM64的SO庫依然會被打包進APK裏面,也就是說我們自己的SO庫沒有對應的ARM64的SO庫,而第三方的Library卻有。這時候,某些ARM64的設備安裝該APK的時候,發現我們的APK裏帶有ARM64的SO庫,會誤以爲我們的APP已經做好了AMR64的適配工作,所以只會選擇安裝APK裏面ARM64類型的SO庫,這樣會導致我們自己項目的SO庫沒有被正確安裝(雖然armeabi-v7a和x86類型的SO庫確實存在APK包裏面)。

解決辦法

給我們自己的SO庫也提供AMR64支持,或者不打包第三方Library項目的ARM64的SO庫。
使用第二種方案時,可以把APK裏面不需要支持的ABI文件夾給刪除,然後重新打包,而在Android Studio下,則可以通過以下的構建方式指定需要類型的SO庫。
這個時候我們就需要使用:

    defaultConfig {
        ......
        ndk {
            abiFilters "***", "***"
        }
    }

來解決這些問題。
如果你用的Android Studio編譯器,那麼abiFilters後面加的應該是jniLibs/ABI裏面所支持的機型。(當然這個目錄也可以通過在build.gradle文件中的設置jniLibs.srcDir屬性自己指定)。

總結

APP多架構支持

我們平時用的so的存放位置:

  • Android Studio工程放在jniLibs/ABI目錄中(當然也可以通過在build.gradle文件中的設置jniLibs.srcDir屬性自己指定)
  • Eclipse工程放在libs/ABI目錄中(這也是ndk-build命令默認生成.so文件的目錄)
  • AAR壓縮包中位於jni/ABI目錄中(.so文件會自動包含到引用AAR壓縮包的APK中)
  • 最終這些so會放在編譯好的APK文件中的lib/ABI目錄中
    所有的x86/x86_64/armeabi-v7a/arm64-v8a設備都支持armeabi架構的.so文件,x86設備能夠很好的運行ARM類型函數庫,但並不保證100%不發生crash,特別是對舊設備。64位設備(arm64-v8a, x86_64, mips64)能夠運行32位的函數庫,但是以32位模式運行,在64位平臺上運行32位版本的ART和Android組件,將丟失專爲64位優化過的性能(ART,webview,media等等)。
    基於這些問題,我們應該儘可能爲每種CPU類型都提供對應的SO庫。
    因此以減少APK包大小爲由來減少架構的支持顯然不是一個好的理由,因爲也可以選擇在應用市場上傳指定ABI版本的APK,生成不同ABI版本的APK可以在build.gradle中如下配置:
android {
   ... 
   splits {
	   abi {
		   enable true
		   reset()
		   include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' //select ABIs to build APKs for
		   universalApk true //generate an additional APK that contains all the ABIs
	   }
   }
   // map for the version code
   project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9]
   android.applicationVariants.all { variant ->
	   // assign different version code for each output
	   variant.outputs.each { output ->
		   output.versionCodeOverride =
				   project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * 1000000 + android.defaultConfig.versionCode
	   }
   }
}

NDK生成支持多種CPU架構的SO

在android NDK項目的根目錄下面有一個libs文件夾,這個文件夾下面有下面七個文件夾中的一個或者多個:arm64-v8a,armeabi,armeabi-v7a,mips,mips64,x86,x86_64。
默認情況下,NDK的編譯系統根據 “armeabi” ABI生成機器代碼。可以使用APP_ABI 來選擇一個不同的ABI,我們可以通過下面的配置來制定支持的ABI:

TARGET_CPU_API := all
APP_ABI := all

或者是

TARGET_CPU_API :=  armeabi armeabi-v7a x86 x86_64 arm64-v8a mips mips64
APP_ABI :=  armeabi armeabi-v7a x86 x86_64 arm64-v8a mips mips64
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章