編輯:關於Android編程
目前64bitandroid系統也慢慢的多了,看到也有apk聲稱支持64bitsystem,然後就往裡面打包搞了個arm64-v8a 目錄,放了個64bit的so,但是apk代碼裡面卻不按規范去load so ,導致一系列 file not found 異常。
先看下apk中的lib打包的目錄:
依次代表不同類型的cpu<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMiBpZD0="pms安裝路徑">PMS安裝路徑://blog.csdn.net/sergeycao
pms install 流程比較繁雜,只關注so相關的scanPackageDirtyLI函數中:
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
...
//invoke installer to do the actual installation //作為外部apk 創建data目錄相關項
//\frameworks\native\cmds\installd\commands.c install()中創建
int ret = **createDataDirsLI**(pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.seinfo);
...
if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
...
setBundledAppAbisAndRoots(pkg, pkgSetting);
...
setNativeLibraryPaths(pkg);
}
else
{
setNativeLibraryPaths(pkg);
...
if (isAsec) {
copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
} else {
copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
}
setNativeLibraryPaths(pkg);
if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
final int[] userIds = sUserManager.getUserIds();
synchronized (mInstallLock) {
// Create a native library symlink only if we have native libraries
// and if the native libraries are 32 bit libraries. We do not provide
// this symlink for 64 bit libraries.
if (pkg.applicationInfo.primaryCpuAbi != null &&
**!VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)**) {
final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
for (int userId : userIds) {
if (mInstaller.linkNativeLibraryDirectory(pkg.packageName, nativeLibPath, userId) < 0) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Failed linking native library dir (user=" + userId + ")");
}
}
}
}
}
}
看下system app 的安裝配置函數setBundledAppAbisAndRoots:
/**
* Calculate the abis and roots for a bundled app. These can uniquely
* be determined from the contents of the system partition, i.e whether
* it contains 64 or 32 bit shared libraries etc. We do not validate any
* of this information, and instead assume that the system was built
* sensibly.
*/
private void setBundledAppAbisAndRoots(PackageParser.Package pkg,
PackageSetting pkgSetting) {
final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
// If "/system/lib64/apkname" exists, assume that is the per-package
// native library directory to use; otherwise use "/system/lib/apkname".
final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir);
setBundledAppAbi(pkg, apkRoot, apkName);
// pkgSetting might be null during rescan following uninstall of updates
// to a bundled app, so accommodate that possibility. The settings in
// that case will be established later from the parsed package.
//
// If the settings aren't null, sync them up with what we've just derived.
// note that apkRoot isn't stored in the package settings.
if (pkgSetting != null) {
pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
}
}
主要是在setBundledAppAbi中:
/**
* Deduces the ABI of a bundled app and sets the relevant fields on the
* parsed pkg object.
*
* @param apkRoot the root of the installed apk, something like {@code /system} or {@code /oem}
* under which system libraries are installed.
* @param apkName the name of the installed package.
*/
private static void setBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) {
final File codeFile = new File(pkg.codePath);
...
if (has64BitLibs && !has32BitLibs) {
// The package has 64 bit libs, but not 32 bit libs. Its primary
// ABI should be 64 bit. We can safely assume here that the bundled
// native libraries correspond to the most preferred ABI in the list.
pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
pkg.applicationInfo.secondaryCpuAbi = null;
} else if (has32BitLibs && !has64BitLibs) {
// The package has 32 bit libs but not 64 bit libs. Its primary
// ABI should be 32 bit.
pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
pkg.applicationInfo.secondaryCpuAbi = null;
} else if (has32BitLibs && has64BitLibs) {
// The application has both 64 and 32 bit bundled libraries. We check
// here that the app declares multiArch support, and warn if it doesn't.
//
// We will be lenient here and record both ABIs. The primary will be the
// ABI that's higher on the list, i.e, a device that's configured to prefer
// 64 bit apps will see a 64 bit primary ABI,
if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) {
Slog.e(TAG, "Package: " + pkg + " has multiple bundled libs, but is not multiarch.");
}
if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) {
pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
} else {
pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
}
} else {
pkg.applicationInfo.primaryCpuAbi = null;
pkg.applicationInfo.secondaryCpuAbi = null;
}
}
根據file 查找 確定primaryCpuAbi secondaryCpuAbi 變量值,這個也就決定了 這個 apk 由64bit 還是32bit 的zygote去fork 還有nativelibrary 查找的path
其中nativelibrary的幾個主要函數setNativeLibraryPaths:
/**
* Derive and set the location of native libraries for the given package,
* which varies depending on where and how the package was installed.
*/
private void setNativeLibraryPaths(PackageParser.Package pkg) {
final ApplicationInfo info = pkg.applicationInfo;
final String codePath = pkg.codePath;
final File codeFile = new File(codePath);
final boolean bundledApp = isSystemApp(info) && !isUpdatedSystemApp(info);
final boolean asecApp = isForwardLocked(info) || isExternal(info);
info.nativeLibraryRootDir = null;
info.nativeLibraryRootRequiresIsa = false;
info.nativeLibraryDir = null;
info.secondaryNativeLibraryDir = null;
if (isApkFile(codeFile)) {
// Monolithic install
if (bundledApp) {
// If "/system/lib64/apkname" exists, assume that is the per-package
// native library directory to use; otherwise use "/system/lib/apkname".
final String apkRoot = calculateBundledApkRoot(info.sourceDir);
final boolean is64Bit = VMRuntime.is64BitInstructionSet(
getPrimaryInstructionSet(info));
// This is a bundled system app so choose the path based on the ABI.
// if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
// is just the default path.
final String apkName = deriveCodePathName(codePath);
final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
apkName).getAbsolutePath();
if (info.secondaryCpuAbi != null) {
final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
secondaryLibDir, apkName).getAbsolutePath();
}
} else if (asecApp) {
info.nativeLibraryRootDir = new File(codeFile.getParentFile(), LIB_DIR_NAME)
.getAbsolutePath();
} else {
final String apkName = deriveCodePathName(codePath);
info.nativeLibraryRootDir = new File(mAppLib32InstallDir, apkName)
.getAbsolutePath();
}
info.nativeLibraryRootRequiresIsa = false;
info.nativeLibraryDir = info.nativeLibraryRootDir;
} else {
// Cluster install
info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
info.nativeLibraryRootRequiresIsa = true;
info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
getPrimaryInstructionSet(info)).getAbsolutePath();
if (info.secondaryCpuAbi != null) {
info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,
VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath();
}
}
}
根據pkg application info 來確定nativelibrarydir 依賴info中的info.primaryCpuAbi
private static String getPrimaryInstructionSet(ApplicationInfo info) {
if (info.primaryCpuAbi == null) {
return getPreferredInstructionSet();
}
return VMRuntime.getInstructionSet(info.primaryCpuAbi);
}
非system apk 會調用NativeLibraryHelper的findSupportedAbi去解析 .apk 文件,根據系統suportabilist 去查找 lib目錄下的打包子目錄 找到匹配的abi
向文章開頭的那個 lib目錄 ,在64bit 機器上suportabilist為:
public static final String[] SUPPORTED_ABIS = getStringList("ro.product.cpu.abilist", ",");
root@:/ # getprop ro.product.cpu.abilist
arm64-v8a,armeabi-v7a,armeabi
會匹配arm64-v8a賦值給info.primaryCpuAbi
copyNativeBinariesForSupportedAbi會去copy 前面匹配的lib 目錄到本地
最後設置NativeLibraryPaths ,
如果匹配的是64bit的,也就是arm64-v8a那麼就不為/data/data/../lib 建立軟鏈接,這是與32bit 不同的地方
作為動態庫加載的標准接口,直接看實現:
public static void loadLibrary(String libName) {
Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
}
到Runtime.java中:
/*
* Searches for and loads the given shared library using the given ClassLoader.
*/
void loadLibrary(String libraryName, ClassLoader loader) {
if (loader != null) {
String filename = loader.findLibrary(libraryName);
if (filename == null) {
// It's not necessarily true that the ClassLoader used
// System.mapLibraryName, but the default setup does, and it's
// misleading to say we didn't find "libMyLibrary.so" when we
// actually searched for "liblibMyLibrary.so.so".
throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
System.mapLibraryName(libraryName) + "\"");
}
String error = doLoad(filename, loader);
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
return;
}
String filename = System.mapLibraryName(libraryName);
List<String> candidates = new ArrayList<String>();
String lastError = null;
for (String directory : mLibPaths) {
String candidate = directory + filename;
candidates.add(candidate);
if (IoUtils.canOpenReadOnly(candidate)) {
String error = doLoad(candidate, loader);
if (error == null) {
return; // We successfully loaded the library. Job done.
}
lastError = error;
}
}
if (lastError != null) {
throw new UnsatisfiedLinkError(lastError);
}
throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
}
這裡的ClassLoader loader實際上會在 apk啟動的時候 初始化好一些相關的 子類 父類 還有參數
大體記錄一下 啟動時 初始流程 :
ActivityThread.java - handleBindApplication
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true, false);
ContextImpl.java -getClassLoader()
LoadedApk.java -getClassLoader() : mLibDir = aInfo.nativeLibraryDir;
mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib,
mBaseClassLoader);
ApplicationLoaders.java -getClassLoader(...)
PathClassLoader pathClassloader = new PathClassLoader(zip, libPath, parent); //這裡的libPath 就是上面傳下來的aInfo.nativeLibraryDir
public class PathClassLoader extends BaseDexClassLoader
public class BaseDexClassLoader extends ClassLoader
loader.findLibrary(libraryName);
/**
* Finds the named native code library on any of the library
* directories pointed at by this instance. This will find the
* one in the earliest listed directory, ignoring any that are not
* readable regular files.
*
* @return the complete path to the library or {@code null} if no
* library was found
*/
public String findLibrary(String libraryName) {
String fileName = System.mapLibraryName(libraryName);
for (File directory : nativeLibraryDirectories) {
String path = new File(directory, fileName).getPath();
if (IoUtils.canOpenReadOnly(path)) {
return path;
}
}
return null;
}
這裡的nativeLibraryDirectories即為前面一系列 構造時 設置了值 其中就有aInfo.nativeLibraryDir
後面的邏輯就不去敘述了, 根據名字在這個目錄下去找 ,然後調用到本地JNI 最終調用dlopen加載打開so,必須是相同位數, 而這個關乎當前進程是屬於64bit 還是 32bit,這個會在zygote fork時區分, 同樣也是由PMS解析時得到的 info.primaryCpuAbi
只關心 相關代碼startProcessLocked函數:
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
...
String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
if (requiredAbi == null) {
requiredAbi = Build.SUPPORTED_ABIS[0];
}
String instructionSet = null;
if (app.info.primaryCpuAbi != null) {
instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
}
app.gids = gids;
app.requiredAbi = requiredAbi;
app.instructionSet = instructionSet;
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
boolean isActivityProcess = (entryPoint == null);
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
checkTime(startTime, "startProcess: asking zygote to start proc");
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
checkTime(startTime, "startProcess: returned from zygote!");
...
}
Process中真正的socket 請求實現:
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
openZygoteSocketIfNeeded會根據傳下來的abi 去選擇 通信的socket
而在64bit 機器上,啟動時會 啟動 兩個 zygote service ,用於接收 64 32 的apk 請求:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
socket zygote_secondary stream 660 root system
onrestart restart zygote
pms install 流程比較繁雜,只關注so相關的scanpackageDirtyLI函數中:
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {...//invoke installer to do the actual installation //作為外部apk 創建data目錄相關項 //\frameworks\native\cmds\installd\commands.c install()中創建 int ret = **createDataDirsLI**(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.seinfo);...if(isSystemApp(pkg) && !isupdatedSystemApp(pkg)) {...setBundledAppAbisAndRoots(pkg, pkgSetting);...setNativeLibraryPaths(pkg); }else{ setNativeLibraryPaths(pkg);...if(isAsec) { copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList); }else{ copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, nativeLibraryRoot, abiList, useIsaSpecificSubdirs); } setNativeLibraryPaths(pkg);if(DEBUG_INSTALL) Slog.i(TAG,"Linking native library dir for "+ path); final int[] userIds = sUserManager.getUserIds(); synchronized (mInstallLock) { // Create a nativelibrarysymlink onlyifwe have native libraries // andifthe native libraries are32bit libraries. We do not provide // this symlinkfor64bit libraries.if(pkg.applicationInfo.primaryCpuAbi != null && **!VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)**) { final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;for(int userId : userIds) {if(mInstaller.linkNativeLibraryDirectory(pkg.packageName, nativeLibPath, userId) <0) { throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,"Failed linking native library dir (user="+ userId +")"); } } } } } }
看下system app 的安裝配置函數setBundledAppAbisAndRoots:
/** * Calculate the abis and roots for a bundled app. These can uniquely * be determined from the contents of the system partition, i.e whether * it contains 64 or 32 bit shared libraries etc. We do not validate any * of this information, and instead assume that the system was built * sensibly. */privatevoidsetBundledAppAbisAndRoots(PackageParser.Package pkg, PackageSetting pkgSetting) {finalString apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());// If "/system/lib64/apkname" exists, assume that is the per-package// native library directory to use; otherwise use "/system/lib/apkname".finalString apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir); setBundledAppAbi(pkg, apkRoot, apkName);// pkgSetting might be null during rescan following uninstall of updates// to a bundled app, so accommodate that possibility. The settings in// that case will be established later from the parsed package.//// If the settings aren't null, sync them up with what we've just derived.// note that apkRoot isn't stored in the package settings.if(pkgSetting !=null) { pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi; pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi; } }
主要是在setBundledAppAbi中:
/** * Deduces the ABI of a bundled app and sets the relevant fields on the * parsed pkg object. * *@paramapkRoot the root of the installed apk, something like {@code /system} or {@code /oem} * under which system libraries are installed. *@paramapkName the name of the installed package. */privatestaticvoidsetBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) {finalFile codeFile =newFile(pkg.codePath); ...if(has64BitLibs && !has32BitLibs) {// The package has 64 bit libs, but not 32 bit libs. Its primary// ABI should be 64 bit. We can safely assume here that the bundled// native libraries correspond to the most preferred ABI in the list.pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; pkg.applicationInfo.secondaryCpuAbi =null; }elseif(has32BitLibs && !has64BitLibs) {// The package has 32 bit libs but not 64 bit libs. Its primary// ABI should be 32 bit.pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; pkg.applicationInfo.secondaryCpuAbi =null; }elseif(has32BitLibs && has64BitLibs) {// The application has both 64 and 32 bit bundled libraries. We check// here that the app declares multiArch support, and warn if it doesn't.//// We will be lenient here and record both ABIs. The primary will be the// ABI that's higher on the list, i.e, a device that's configured to prefer// 64 bit apps will see a 64 bit primary ABI,if((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) ==0) { Slog.e(TAG,"Package: "+ pkg +" has multiple bundled libs, but is not multiarch."); }if(VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) { pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; }else{ pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; } }else{ pkg.applicationInfo.primaryCpuAbi =null; pkg.applicationInfo.secondaryCpuAbi =null; } }
根據file 查找 確定primaryCpuAbi secondaryCpuAbi 變量值,這個也就決定了 這個 apk 由64bit 還是32bit 的zygote去fork 還有nativelibrary 查找的path
其中nativelibrary的幾個主要函數setNativeLibraryPaths:
/** * Derive and set the location of native libraries for the given package, * which varies depending on where and how the package was installed. */privatevoidsetNativeLibraryPaths(PackageParser.Package pkg) {finalApplicationInfo info = pkg.applicationInfo;finalString codePath = pkg.codePath;finalFile codeFile =newFile(codePath);finalbooleanbundledApp = isSystemApp(info) && !isUpdatedSystemApp(info);finalbooleanasecApp = isForwardLocked(info) || isExternal(info); info.nativeLibraryRootDir =null; info.nativeLibraryRootRequiresIsa =false; info.nativeLibraryDir =null; info.secondaryNativeLibraryDir =null;if(isApkFile(codeFile)) {// Monolithic installif(bundledApp) {// If "/system/lib64/apkname" exists, assume that is the per-package// native library directory to use; otherwise use "/system/lib/apkname".finalString apkRoot = calculateBundledApkRoot(info.sourceDir);finalbooleanis64Bit = VMRuntime.is64BitInstructionSet( getPrimaryInstructionSet(info));// This is a bundled system app so choose the path based on the ABI.// if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this// is just the default path.finalString apkName = deriveCodePathName(codePath);finalString libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME; info.nativeLibraryRootDir = Environment.buildPath(newFile(apkRoot), libDir, apkName).getAbsolutePath();if(info.secondaryCpuAbi !=null) {finalString secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME; info.secondaryNativeLibraryDir = Environment.buildPath(newFile(apkRoot), secondaryLibDir, apkName).getAbsolutePath(); } }elseif(asecApp) { info.nativeLibraryRootDir =newFile(codeFile.getParentFile(), LIB_DIR_NAME) .getAbsolutePath(); }else{finalString apkName = deriveCodePathName(codePath); info.nativeLibraryRootDir =newFile(mAppLib32InstallDir, apkName) .getAbsolutePath(); } info.nativeLibraryRootRequiresIsa =false; info.nativeLibraryDir = info.nativeLibraryRootDir; }else{// Cluster installinfo.nativeLibraryRootDir =newFile(codeFile, LIB_DIR_NAME).getAbsolutePath(); info.nativeLibraryRootRequiresIsa =true; info.nativeLibraryDir =newFile(info.nativeLibraryRootDir, getPrimaryInstructionSet(info)).getAbsolutePath();if(info.secondaryCpuAbi !=null) { info.secondaryNativeLibraryDir =newFile(info.nativeLibraryRootDir, VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath(); } } }
根據pkg application info 來確定nativelibrarydir 依賴info中的info.primaryCpuAbi
privatestaticStringgetPrimaryInstructionSet(ApplicationInfo info) {if(info.primaryCpuAbi ==null) {returngetPreferredInstructionSet(); }returnVMRuntime.getInstructionSet(info.primaryCpuAbi); }
非system apk 會調用NativeLibraryHelper的findSupportedAbi去解析 .apk 文件,根據系統suportabilist 去查找 lib目錄下的打包子目錄 找到匹配的abi
向文章開頭的那個 lib目錄 ,在64bit 機器上suportabilist為:
publicstaticfinalString[] SUPPORTED_ABIS = getStringList("ro.product.cpu.abilist",",");
root@:/# getprop ro.product.cpu.abilistarm64-v8a,armeabi-v7a,armeabi
會匹配arm64-v8a賦值給info.primaryCpuAbi
copyNativeBinariesForSupportedAbi會去copy 前面匹配的lib 目錄到本地
最後設置NativeLibraryPaths ,
如果匹配的是64bit的,也就是arm64-v8a那麼就不為/data/data/../lib 建立軟鏈接,這是與32bit 不同的地方
作為動態庫加載的標准接口,直接看實現:
publicstaticvoidloadLibrary(String libName) { Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader()); }
到Runtime.Java-0.html" target="_blank">Java中:
/* * Searches for and loads the given shared library using the given ClassLoader. */voidloadLibrary(StringlibraryName, ClassLoader loader) {if(loader !=null) {Stringfilename = loader.findLibrary(libraryName);if(filename ==null) {// It's not necessarily true that the ClassLoader used// System.mapLibraryName, but the default setup does, and it's// misleading to say we didn't find "libMyLibrary.so" when we// actually searched for "liblibMyLibrary.so.so".thrownewUnsatisfiedLinkError(loader +" couldn't find \""+ System.mapLibraryName(libraryName) +"\""); }Stringerror = doLoad(filename, loader);if(error !=null) {thrownewUnsatisfiedLinkError(error); }return; }Stringfilename = System.mapLibraryName(libraryName); List
這裡的ClassLoader loader實際上會在 apk啟動的時候 初始化好一些相關的 子類 父類 還有參數
大體記錄一下 啟動時 初始流程 :
ActivityThread.java - handleBindApplicationfinalContextImpl appContext = ContextImpl.createAppContext(this, data.info); LoadedApk pi = getPackageInfo(instrApp, data.compatInfo, appContext.getClassLoader(),false,true,false); ContextImpl.java -getClassLoader() LoadedApk.java -getClassLoader() : mLibDir = aInfo.nativeLibraryDir; mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib, mBaseClassLoader); ApplicationLoaders.java -getClassLoader(...) PathClassLoader pathClassloader =newPathClassLoader(zip, libPath, parent);//這裡的libPath 就是上面傳下來的aInfo.nativeLibraryDirpublicclassPathClassLoaderextendsBaseDexClassLoaderpublicclassBaseDexClassLoaderextendsClassLoader
loader.findLibrary(libraryName);
/** * Findsthenamed native code libraryonanyofthelibrary * directories pointedatbythis instance. This will findthe* oneintheearliest listed directory,ignoringanythatarenot* readable regular files. * * @returnthecompletepath tothelibraryor{@code null}ifno * library was found */ public String findLibrary(String libraryName) { String fileName = System.mapLibraryName(libraryName);for(File directory : nativeLibraryDirectories) { String path = new File(directory, fileName).getPath();if(IoUtils.canOpenReadOnly(path)) {returnpath; } }returnnull; }
這裡的nativeLibraryDirectories即為前面一系列 構造時 設置了值 其中就有aInfo.nativeLibraryDir
後面的邏輯就不去敘述了, 根據名字在這個目錄下去找 ,然後調用到本地JNI 最終調用dlopen加載打開so,必須是相同位數, 而這個關乎當前進程是屬於64bit 還是 32bit,這個會在zygote fork時區分, 同樣也是由PMS解析時得到的 info.primaryCpuAbi
只關心 相關代碼startProcessLocked函數:
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {...String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;if(requiredAbi == null) { requiredAbi = Build.SUPPORTED_ABIS[0]; } String instructionSet = null;if(app.info.primaryCpuAbi != null) { instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi); } app.gids = gids; app.requiredAbi = requiredAbi; app.instructionSet = instructionSet; // Start the process. It will either succeed andreturna result containing // the PID of the new process, orelsethrow a RuntimeException. boolean isActivityProcess = (entryPoint == null);if(entryPoint == null) entryPoint ="android.app.ActivityThread"; checkTime(startTime,"startProcess: asking zygote to start proc"); Process.ProcessStartResult startResult = Process.start(entryPoint, app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkversion, app.info.seinfo, requiredAbi, instructionSet, app.info.dataDir, entryPointArgs); checkTime(startTime,"startProcess: returned from zygote!");...}
Process中真正的socket 請求實現:
returnzygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
openZygoteSocketIfNeeded會根據傳下來的abi 去選擇 通信的socket
而在64bit 機器上,啟動時會 啟動 兩個 zygote service ,用於接收 64 32 的apk 請求:
service zygote /system/bin/app_process64 -Xzygote /system/bin--zygote --start-system-server --socket-name=zygoteclass mainsocketzygote stream660rootsystemonrestartwrite/sys/android_power/request_state wake onrestartwrite/sys/power/stateononrestart restart media onrestart restart netd service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin--zygote --socket-name=zygote_secondaryclass mainsocketzygote_secondary stream660rootsystemonrestart restart zygote
可以看到兩個 zygote 進程,基本一致 ,區別在於 64bit 32bit ,注冊socket不同
關於這兩個 zygote 進程啟動時的socket 注冊
如果APP運行在Android 6.0或以上版本的手機,並且target sdk>=23,那麼在使用一些相對敏感的權限時,需要征求用戶的許可。比如讀寫sdcard,
在我們開發應用程序時,通常都會用到獲取手機聯系人信息這一十分常用的功能,最近項目裡也要實現此功能,想到以後的APP還十分可能還有此功能,就干脆把這個小功能放到一個類中去,
本文將告訴你如何讓你的應用程序支持各種不同屏幕大小,主要通過以下幾種辦法:讓你的布局能充分的自適應屏幕 根據屏幕的配置來加載合適的UI布局 確保正確的布局應用在正確的設備
[Android]仿京東手機端類別頁京東手機端的類別標簽頁, 是一個左側滑動可選擇類別, 右側一個類別明細的列表聯動頁面. 當用戶選擇左側選項, 可在右側顯示更多選項來選