欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android开发——关于32位与64位so的加载问题

程序员文章站 2022-06-15 21:42:12
有时候会为了减少apk的大小,只设置支持“armeabi-v7a”SO库架构apk在安装的过程中,系统就会对apk进行解析根据里面so文件类型,确定这个apk安装是在32 还是 64位的虚拟机上,如果是32位虚拟机那么就不能使用64位so,如果是64位虚拟机也不能使用32位so。而64位设备可以提供32和64位两种虚拟机,根据apk选择开启哪一种,因此说64位设备兼容32的so库。具体机制,分下面四种情况:1.假设apk的lib目录放置了32和64位两种so,那么安装时根据当前设备的cp...

Android加载so文件的机制

有时候会为了减少apk的大小,只设置支持 “armeabi-v7a” so库架构,然而在apk在安装的过程中,系统就会对apk进行解析根据里面so文件类型,确定这个apk安装是在32 还是 64位的虚拟机上,如果是32位虚拟机那么就不能使用64位so,如果是64位虚拟机也不能使用32位so。而64位设备可以提供32和64位两种虚拟机,根据apk选择开启哪一种,因此说64位设备兼容32的so库;加载so机制分下面四种情况:

1. 假设apk的lib目录放置了32和64位两种so,那么安装时根据当前设备的cpu架构从上到下筛选(X86 > arm64 > arm32),一旦发现lib里面有和设备匹配的so文件,那么直接选定这种架构为标准。比如当前设备是64位并且发现lib有一个64位的so,那么apk会拷贝lib下所有64位的so文件到data/data/packageName/lib64/目录下;

2. apk的lib目录只有32位的so,由于64位设备时兼容32位的库,所以安装时根据当前设备的cpu架构从上到下筛选(X86 > arm64 > arm32),能够正常的运行在32位设备和大部分64位设备上(目前没有遇到不正常运行的状态)。

3. apk的lib目录只有64位的so时,那这个apk只能运行在64位的设备上。

4. apk的lib不放任何的so文件,全部动态加载时,安装在32位设备就只能加载32位so,安装在64位的设备系统会默认将apk运行在64位虚拟机,不过可以通过指定当前加载。

TODO:

1. 如果apk的lib不放任何的so文件,并在build.grade中通过abiFilters设置过滤,是否能起到加载特定的so的目的

ndk {
    abiFilters 'armeabi-v7a'
}

Android so的动态加载

有时会为了减少apk的大小,会将so放到服务器上,比如我们的小游戏大厅中,在引擎框架下,会将每个小游戏编译成so,再启动相应的游戏时去下载这个游戏的so,然后再去加载so。

对于动态加载so方式的apk需要通过上传的方式进行处理,然后下载使用合适的so,下面来看下so的动态加载过程(8.0.0_r4):

/**
1625     * Loads the native library specified by the <code>libname</code>
1626     * argument.  The <code>libname</code> argument must not contain any platform
1627     * specific prefix, file extension or path. If a native library
1628     * called <code>libname</code> is statically linked with the VM, then the
1629     * JNI_OnLoad_<code>libname</code> function exported by the library is invoked.
1630     * See the JNI Specification for more details.
1631     *
1632     * Otherwise, the libname argument is loaded from a system library
1633     * location and mapped to a native library image in an implementation-
1634     * dependent manner.
1635     * <p>
1636     * The call <code>System.loadLibrary(name)</code> is effectively
1637     * equivalent to the call
1638     * <blockquote><pre>
1639     * Runtime.getRuntime().loadLibrary(name)
1640     * </pre></blockquote>
1641     *
1642     * @param      libname   the name of the library.
1643     * @exception  SecurityException  if a security manager exists and its
1644     *             <code>checkLink</code> method doesn't allow
1645     *             loading of the specified dynamic library
1646     * @exception  UnsatisfiedLinkError if either the libname argument
1647     *             contains a file path, the native library is not statically
1648     *             linked with the VM,  or the library cannot be mapped to a
1649     *             native library image by the host system.
1650     * @exception  NullPointerException if <code>libname</code> is
1651     *             <code>null</code>
1652     * @see        java.lang.Runtime#loadLibrary(java.lang.String)
1653     * @see        java.lang.SecurityManager#checkLink(java.lang.String)
1654     */
1655    @CallerSensitive
1656    public static void loadLibrary(String libname) {
1657        Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
1658    }

上面的源码很简单,直接看Runtime.loadLibrary0具体实现:

998    synchronized void loadLibrary0(ClassLoader loader, String libname) {
999        if (libname.indexOf((int)File.separatorChar) != -1) {
1000            throw new UnsatisfiedLinkError(
1001    "Directory separator should not appear in library name: " + libname);
1002        }
1003        String libraryName = libname;
1004        if (loader != null) {//ClassLoader非空时,利用ClassLoader的findLibrary()方法来获取library的path
1005            String filename = loader.findLibrary(libraryName);
1006            if (filename == null) {
1007                // It's not necessarily true that the ClassLoader used
1008                // System.mapLibraryName, but the default setup does, and it's
1009                // misleading to say we didn't find "libMyLibrary.so" when we
1010                // actually searched for "liblibMyLibrary.so.so".
1011                throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
1012                                               System.mapLibraryName(libraryName) + "\"");
1013            }
1014            String error = doLoad(filename, loader);
1015            if (error != null) {
1016                throw new UnsatisfiedLinkError(error);
1017            }
1018            return;
1019        }
1020        //当loader为空时, 则从默认目录mLibPaths下来查找是否存在该动态库;
1021        String filename = System.mapLibraryName(libraryName);
1022        List<String> candidates = new ArrayList<String>();
1023        String lastError = null;
1024        for (String directory : getLibPaths()) {
1025            String candidate = directory + filename;
1026            candidates.add(candidate);
1027
1028            if (IoUtils.canOpenReadOnly(candidate)) {
1029                String error = doLoad(candidate, loader);
1030                if (error == null) {
1031                    return; // We successfully loaded the library. Job done.
1032                }
1033                lastError = error;
1034            }
1035        }
1036
1037        if (lastError != null) {
1038            throw new UnsatisfiedLinkError(lastError);
1039        }
1040        throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
1041    }

1. loader为null 时在做什么?

a. mapLibraryName会调用System的native方法,System_mapLibraryName是其具体的实现,改方法是将xxx动态库的名字转换为libxxx.so(目前还不知道为啥要这么麻烦通过native来实现)。

 

147JNIEXPORT jstring JNICALL
148System_mapLibraryName(JNIEnv *env, jclass ign, jstring libname)
149{
150    int len;
151    int prefix_len = (int) strlen(JNI_LIB_PREFIX);
152    int suffix_len = (int) strlen(JNI_LIB_SUFFIX);
153
154    jchar chars[256];
155    if (libname == NULL) {
156        JNU_ThrowNullPointerException(env, 0);
157        return NULL;
158    }
159    len = (*env)->GetStringLength(env, libname);
160    if (len > 240) {
161        JNU_ThrowIllegalArgumentException(env, "name too long");
162        return NULL;
163    }
164    cpchars(chars, JNI_LIB_PREFIX, prefix_len);
165    (*env)->GetStringRegion(env, libname, 0, len, chars + prefix_len);
166    len += prefix_len;
167    cpchars(chars + len, JNI_LIB_SUFFIX, suffix_len);
168    len += suffix_len;
169
170    return (*env)->NewString(env, chars, len);
171}

b. getLibPaths的实现如下,只是为了获取加载库时搜索的路径列表:

 

1043    private volatile String[] mLibPaths = null;
1044
1045    private String[] getLibPaths() {
1046        if (mLibPaths == null) {
1047            synchronized(this) {
1048                if (mLibPaths == null) {
1049                    mLibPaths = initLibPaths();
1050                }
1051            }
1052        }
1053        return mLibPaths;
1054    }
1055
1056    private static String[] initLibPaths() {
1057        String javaLibraryPath = System.getProperty("java.library.path");
1058        if (javaLibraryPath == null) {
1059            return EmptyArray.STRING;
1060        }
1061        String[] paths = javaLibraryPath.split(":");
1062        // Add a '/' to the end of each directory so we don't have to do it every time.
1063        for (int i = 0; i < paths.length; ++i) {
1064            if (!paths[i].endsWith("/")) {
1065                paths[i] += "/";
1066            }
1067        }
1068        return paths;
1069    }
序号 属性 说明
1 java.version Java 运行时环境版本
2 java.vendor Java 运行时环境供应商
3 java.vendor.url Java 供应商的 URL
4 java.home Java 安装目录
5 java.vm.specification.version Java 虚拟机规范版本
6 java.vm.specification.vendor Java 虚拟机规范供应商
7 java.vm.specification.name Java 虚拟机规范名称
8 java.vm.version Java 虚拟机实现版本
9 java.vm.vendor Java 虚拟机实现供应商
10 java.vm.name Java 虚拟机实现名称
11 java.specification.version Java 运行时环境规范版本
12 java.specification.vendor Java 运行时环境规范供应商
13 java.specification.name Java 运行时环境规范名称
14 java.class.version Java 类格式版本号
15 java.class.path Java 类路径
16 java.library.path 加载库时搜索的路径列表
17 java.io.tmpdir 默认的临时文件路径
18 java.compiler 要使用的 JIT 编译器的名称
19 java.ext.dirs 一个或多个扩展目录的路径
20 os.name 操作系统的名称
21 os.arch 操作系统的架构
22 os.version 操作系统的版本
23 file.separator 文件分隔符(在 UNIX 系统中是“/”)
24 path.separator 路径分隔符(在 UNIX 系统中是“:”)
25 line.separator 行分隔符(在 UNIX 系统中是“/n”)
26 user.name 用户的账户名称
27 user.home 用户的主目录
28 user.dir 用户的当前工作目录

c. canOpenReadOnly方法来判定目标动态库是否存在

151    /**
152     * Do not use. This is for System.loadLibrary use only.
153     *
154     * Checks whether {@code path} can be opened read-only. Similar to File.exists, but doesn't
155     * require read permission on the parent, so it'll work in more cases, and allow you to
156     * remove read permission from more directories. Everyone else should just open(2) and then
157     * use the fd, but the loadLibrary API is broken by its need to ask ClassLoaders where to
158     * find a .so rather than just calling dlopen(3).
159     */
160    public static boolean canOpenReadOnly(String path) {
161        try {
162            // Use open(2) rather than stat(2) so we require fewer permissions. http://b/6485312.
163            FileDescriptor fd = Libcore.os.open(path, O_RDONLY, 0);
164            Libcore.os.close(fd);
165            return true;
166        } catch (ErrnoException errnoException) {
167            return false;
168        }
169    }

d. 如找到指定的目标动态库,调用doLoad来加载,在loader为null 时直接调用了nativeLoad

1070    private String doLoad(String name, ClassLoader loader) {
1071        // Android apps are forked from the zygote, so they can't have a custom LD_LIBRARY_PATH,
1072        // which means that by default an app's shared library directory isn't on LD_LIBRARY_PATH.
1073
1074        // The PathClassLoader set up by frameworks/base knows the appropriate path, so we can load
1075        // libraries with no dependencies just fine, but an app that has multiple libraries that
1076        // depend on each other needed to load them in most-dependent-first order.
1077
1078        // We added API to Android's dynamic linker so we can update the library path used for
1079        // the currently-running process. We pull the desired path out of the ClassLoader here
1080        // and pass it to nativeLoad so that it can call the private dynamic linker API.
1081
1082        // We didn't just change frameworks/base to update the LD_LIBRARY_PATH once at the
1083        // beginning because multiple apks can run in the same process and third party code can
1084        // use its own BaseDexClassLoader.
1085
1086        // We didn't just add a dlopen_with_custom_LD_LIBRARY_PATH call because we wanted any
1087        // dlopen(3) calls made from a .so's JNI_OnLoad to work too.
1088
1089        // So, find out what the native library search path is for the ClassLoader in question...
1090        String librarySearchPath = null;
1091        if (loader != null && loader instanceof BaseDexClassLoader) {
1092            BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
1093            librarySearchPath = dexClassLoader.getLdLibraryPath();
1094        }
1095        // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
1096        // of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
1097        // internal natives.
1098        synchronized (this) {
1099            return nativeLoad(name, loader, librarySearchPath);
1100        }
1101    }

e. nativeLoad是通过native来实现的,JVM_NativeLoad来完成具体的实现细节:

322JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
323                                 jstring javaFilename,
324                                 jobject javaLoader,
325                                 jstring javaLibrarySearchPath) {
326  ScopedUtfChars filename(env, javaFilename);
327  if (filename.c_str() == NULL) {
328    return NULL;
329  }
330
331  std::string error_msg;
332  {
333    art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
334    bool success = vm->LoadNativeLibrary(env,
335                                         filename.c_str(),
336                                         javaLoader,
337                                         javaLibrarySearchPath,
338                                         &error_msg);
339    if (success) {
340      return nullptr;
341    }
342  }
343
344  // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
345  env->ExceptionClear();
346  return env->NewStringUTF(error_msg.c_str());
347}

f. 真正加载so的实现来了,OpenNativeLibrary 来处理加载,加载完成之后创建动态库,并放到libraries_中,找到JNI_OnLoad符号所对应的方法, 并调用该方法:

766bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
767                                  const std::string& path,
768                                  jobject class_loader,
769                                  jstring library_path,
770                                  std::string* error_msg) {
771  error_msg->clear();
772
773  // See if we've already loaded this library.  If we have, and the class loader
774  // matches, return successfully without doing anything.
775  // TODO: for better results we should canonicalize the pathname (or even compare
776  // inodes). This implementation is fine if everybody is using System.loadLibrary.
777  SharedLibrary* library;
778  Thread* self = Thread::Current();
779  {
780    // TODO: move the locking (and more of this logic) into Libraries.
781    MutexLock mu(self, *Locks::jni_libraries_lock_);
782    library = libraries_->Get(path);
783  }
784  void* class_loader_allocator = nullptr;
785  {
786    ScopedObjectAccess soa(env);
787    // As the incoming class loader is reachable/alive during the call of this function,
788    // it's okay to decode it without worrying about unexpectedly marking it alive.
789    ObjPtr<mirror::ClassLoader> loader = soa.Decode<mirror::ClassLoader>(class_loader);
790
791    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
792    if (class_linker->IsBootClassLoader(soa, loader.Ptr())) {
793      loader = nullptr;
794      class_loader = nullptr;
795    }
796
797    class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader.Ptr());
798    CHECK(class_loader_allocator != nullptr);
799  }
800  if (library != nullptr) {
801    // Use the allocator pointers for class loader equality to avoid unnecessary weak root decode.
802    if (library->GetClassLoaderAllocator() != class_loader_allocator) {
803      // The library will be associated with class_loader. The JNI
804      // spec says we can't load the same library into more than one
805      // class loader.
806      StringAppendF(error_msg, "Shared library \"%s\" already opened by "
807          "ClassLoader %p; can't open in ClassLoader %p",
808          path.c_str(), library->GetClassLoader(), class_loader);
809      LOG(WARNING) << error_msg;
810      return false;
811    }
812    VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
813              << " ClassLoader " << class_loader << "]";
814    if (!library->CheckOnLoadResult()) {
815      StringAppendF(error_msg, "JNI_OnLoad failed on a previous attempt "
816          "to load \"%s\"", path.c_str());
817      return false;
818    }
819    return true;
820  }
821
822  // Open the shared library.  Because we're using a full path, the system
823  // doesn't have to search through LD_LIBRARY_PATH.  (It may do so to
824  // resolve this library's dependencies though.)
825
826  // Failures here are expected when java.library.path has several entries
827  // and we have to hunt for the lib.
828
829  // Below we dlopen but there is no paired dlclose, this would be necessary if we supported
830  // class unloading. Libraries will only be unloaded when the reference count (incremented by
831  // dlopen) becomes zero from dlclose.
832
833  Locks::mutator_lock_->AssertNotHeld(self);
834  const char* path_str = path.empty() ? nullptr : path.c_str();
835  bool needs_native_bridge = false;
836  void* handle = android::OpenNativeLibrary(env,
837                                            runtime_->GetTargetSdkVersion(),
838                                            path_str,
839                                            class_loader,
840                                            library_path,
841                                            &needs_native_bridge,
842                                            error_msg);
843
844  VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_NOW) returned " << handle << "]";
845
846  if (handle == nullptr) {
847    VLOG(jni) << "dlopen(\"" << path << "\", RTLD_NOW) failed: " << *error_msg;
848    return false;
849  }
850
851  if (env->ExceptionCheck() == JNI_TRUE) {
852    LOG(ERROR) << "Unexpected exception:";
853    env->ExceptionDescribe();
854    env->ExceptionClear();
855  }
856  // Create a new entry.
857  // TODO: move the locking (and more of this logic) into Libraries.
858  bool created_library = false;
859  {
860    // Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering.
861    std::unique_ptr<SharedLibrary> new_library(
862        new SharedLibrary(env,
863                          self,
864                          path,
865                          handle,
866                          needs_native_bridge,
867                          class_loader,
868                          class_loader_allocator));
869
870    MutexLock mu(self, *Locks::jni_libraries_lock_);
871    library = libraries_->Get(path);
872    if (library == nullptr) {  // We won race to get libraries_lock.
873      library = new_library.release();
874      libraries_->Put(path, library);
875      created_library = true;
876    }
877  }
878  if (!created_library) {
879    LOG(INFO) << "WOW: we lost a race to add shared library: "
880        << "\"" << path << "\" ClassLoader=" << class_loader;
881    return library->CheckOnLoadResult();
882  }
883  VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
884
885  bool was_successful = false;
886  void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
887  if (sym == nullptr) {
888    VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
889    was_successful = true;
890  } else {
891    // Call JNI_OnLoad.  We have to override the current class
892    // loader, which will always be "null" since the stuff at the
893    // top of the stack is around Runtime.loadLibrary().  (See
894    // the comments in the JNI FindClass function.)
895    ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
896    self->SetClassLoaderOverride(class_loader);
897
898    VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
899    typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
900    JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
901    int version = (*jni_on_load)(this, nullptr);
902
903    if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) {
904      // Make sure that sigchain owns SIGSEGV.
905      EnsureFrontOfChain(SIGSEGV);
906    }
907
908    self->SetClassLoaderOverride(old_class_loader.get());
909
910    if (version == JNI_ERR) {
911      StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
912    } else if (JavaVMExt::IsBadJniVersion(version)) {
913      StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
914                    path.c_str(), version);
915      // It's unwise to call dlclose() here, but we can mark it
916      // as bad and ensure that future load attempts will fail.
917      // We don't know how far JNI_OnLoad got, so there could
918      // be some partially-initialized stuff accessible through
919      // newly-registered native method calls.  We could try to
920      // unregister them, but that doesn't seem worthwhile.
921    } else {
922      was_successful = true;
923    }
924    VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure")
925              << " from JNI_OnLoad in \"" << path << "\"]";
926  }
927
928  library->SetResult(was_successful);
929  return was_successful;
930}
458void* OpenNativeLibrary(JNIEnv* env,
459                        int32_t target_sdk_version,
460                        const char* path,
461                        jobject class_loader,
462                        jstring library_path,
463                        bool* needs_native_bridge,
464                        std::string* error_msg) {
465#if defined(__ANDROID__)
466  UNUSED(target_sdk_version);
467  if (class_loader == nullptr) {
468    *needs_native_bridge = false;
469    return dlopen(path, RTLD_NOW);
470  }
471
472  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
473  NativeLoaderNamespace ns;
474
475  if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
476    // This is the case where the classloader was not created by ApplicationLoaders
477    // In this case we create an isolated not-shared namespace for it.
478    if (!g_namespaces->Create(env,
479                              target_sdk_version,
480                              class_loader,
481                              false,
482                              library_path,
483                              nullptr,
484                              &ns,
485                              error_msg)) {
486      return nullptr;
487    }
488  }
489
490  if (ns.is_android_namespace()) {
491    android_dlextinfo extinfo;
492    extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
493    extinfo.library_namespace = ns.get_android_ns();
494
495    void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
496    if (handle == nullptr) {
497      *error_msg = dlerror();
498    }
499    *needs_native_bridge = false;
500    return handle;
501  } else {
502    void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns.get_native_bridge_ns());
503    if (handle == nullptr) {
504      *error_msg = NativeBridgeGetError();
505    }
506    *needs_native_bridge = true;
507    return handle;
508  }
509#else
510  UNUSED(env, target_sdk_version, class_loader, library_path);
511  *needs_native_bridge = false;
512  void* handle = dlopen(path, RTLD_NOW);
513  if (handle == nullptr) {
514    if (NativeBridgeIsSupported(path)) {
515      *needs_native_bridge = true;
516      handle = NativeBridgeLoadLibrary(path, RTLD_NOW);
517      if (handle == nullptr) {
518        *error_msg = NativeBridgeGetError();
519      }
520    } else {
521      *needs_native_bridge = false;
522      *error_msg = dlerror();
523    }
524  }
525  return handle;
526#endif
527}

2. loader不为null时做了什么?

a. findLibrary从所有的native目录中查找库文件,如果找到返回查找结果:

515    /**
516     * Finds the named native code library on any of the library
517     * directories pointed at by this instance. This will find the
518     * one in the earliest listed directory, ignoring any that are not
519     * readable regular files.
520     *
521     * @return the complete path to the library or {@code null} if no
522     * library was found
523     */
524    public String findLibrary(String libraryName) {
525        String fileName = System.mapLibraryName(libraryName);
526
527        for (NativeLibraryElement element : nativeLibraryPathElements) {
528            String path = element.findNativeLibrary(fileName);
529
530            if (path != null) {
531                return path;
532            }
533        }
534
535        return null;
536    }

b. findNativeLibrary查询当前nativeLibrary下面是否存在库文件,存在就返回:

781        public String findNativeLibrary(String name) {
782            maybeInit();
783
784            if (zipDir == null) {
785                String entryPath = new File(path, name).getPath();
786                if (IoUtils.canOpenReadOnly(entryPath)) {
787                    return entryPath;
788                }
789            } else if (urlHandler != null) {
790                // Having a urlHandler means the element has a zip file.
791                // In this case Android supports loading the library iff
792                // it is stored in the zip uncompressed.
793                String entryName = zipDir + '/' + name;
794                if (urlHandler.isEntryStored(entryName)) {
795                  return path.getPath() + zipSeparator + entryName;
796                }
797            }
798
799            return null;
800        }

c. 如果上述可以找到文件则执行doLoad加载:

1003        String libraryName = libname;
1004        if (loader != null) {
1005            String filename = loader.findLibrary(libraryName);
1006            if (filename == null) {
1007                // It's not necessarily true that the ClassLoader used
1008                // System.mapLibraryName, but the default setup does, and it's
1009                // misleading to say we didn't find "libMyLibrary.so" when we
1010                // actually searched for "liblibMyLibrary.so.so".
1011                throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
1012                                               System.mapLibraryName(libraryName) + "\"");
1013            }
1014            String error = doLoad(filename, loader);
1015            if (error != null) {
1016                throw new UnsatisfiedLinkError(error);
1017            }
1018            return;
1019        }

 

以上参考:

本文地址:https://blog.csdn.net/qq_25065595/article/details/107941428

相关标签: Android