手机游戏开发过程中用什么掌阅语音朗读插件下载插件比较好

问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
用H5开发app时,你们喜欢用的框架插件是什么?或者说现在最流行的是哪个?
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
vue和react吧,react-native开发原生app啊,很吊啊
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
同步到新浪微博
分享到微博?
你好!看起来你挺喜欢这个内容,但是你还没有注册帐号。 当你创建了帐号,我们能准确地追踪你关注的问题,在有新答案或内容的时候收到网页和邮件通知。还能直接向作者咨询更多细节。如果上面的内容有帮助,记得点赞 (????)? 表示感谢。
明天提醒我
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:使用 Jenkins 实现软件开发的持续集成
传统开发流程存在的一些问题开发团队在日常工作中,主要是围绕着需求、编码、代码提交、打 build、分配环境、安装 build、BVT(Build Verification Test)、发现 defect、修复并提交新代码,然后重复进行打 build 到 BVT 测试的工作,在这个过程中,大部分工作的串连还是需要人工进行操作,且软件开发周期比较漫长。对于以上存在的问题,分析起来主要有以下几个原因:首先,传统的开发团队中需要一个专门从事 build 构建的人员,负责整个开发团队的代码集成,并打包成应用程序,再进行 BVT。这个过程非常耗时,重复性高,周期长。因为 build 构建的人员需要等待所有开发人员完成代码的交付后才可以进行这部分的工作,而在实际开发过程中,往往在代码交付前还存在一些问题,不但使得 build 构建的工作需要加班或者延期,且这样等待完成的 build 再拿到测试部门进行测试,开发人员再进行缺陷修复,再到产品交付,整个软件开发周期会耗用更多时间。其次,每次构建 build 的过程中,虽然构建 build 本身是可以实现代码自动化的,但是在做完 build 后需要上传到一个公共的地址,再分配环境进行安装。安装完成后的 BVT,需要在每次 build 构建后反复的一次次进行,这些过程包含了太多流程重复及人工资源占用。面对以上存在问题,我们使用 Jenkins 持续集成却可以实现整个流程的自动化,即自动构建 build,上传到指定地址,再下载安装包到指定的环境中进行安装,并进行 BVT 测试。这个流程可以设置每日定时进行,这样就可以加快整个开发过程,使得每个开发人员更好的掌握和控制开发流程。Jenkins 是什么?简单的说,Jenkins 是一种基于 Java 开发的持续集成工具,前身称作Hudson,它是一个开源软件项目,提供了用于监控持续重复工作的软件平台。Jenkins 发布和运行的形式都很简单,您可以去 下载安装包后,只需一个“java -jar jenkins.war”命令就能将其运行起来。Jenkins 持续集成在开发流程中的优势持续集成开发的流程相对于传统方法有很多不同,在软件开发流程中有很多优势:第一,快速迭代。由于 Jenkins 集成开发开发过程中,整个流程能实现自动化,每日可以自行生成新的 build,在软件项目被分成多个子项目后,这些子项目可能是一些独立运行的小项目,也可能是互相联系的。由于每个功能可以一点点的加在 build 中,那么这样就能保证每次的新 build 可以交付新的功能,保障测试人员能一直有最新的 build 进行测试,从而使产品的缺陷能够在更早的时间里被发现,开发人员修复起来也更加容易,甚至可以在修复产品的过程中避免后续可能随之产生的问题,确保产品在整个的开发过程中更加积极、有效。同时,通过快速迭代,开发人员可以对产品的用户和市场趋势保持较强敏感度,且产品也在不断的迭代中愈加成熟,可以使用户持续保持最好体验感;第二,适应变化。由于持续集成开发过程中代码是每日集成生成 build, 产品功能是逐步增加的,这样使得开发人员可以积极应对软件需求的多变性。根据用户的需求可以随时增加新的功能而不会对整个项目产生过多的影响,根据用户的反馈情况及时调整开发方向,降低项目风险,保证市场竞争力。这样,通过用户的评价和反馈来更好的完善、适应市场变化而生产出的产品才是最有生命力的产品,Jenkins 持续集成开发流程,无疑给实验室的开发人员提供了不少的见解与帮助;第三,建立团队信心和提高开发人员的创新能力。传统的开发流程需要在项目经理的管理下,严格地按照计划进行,长期过程中,这种模式会限制开发成员的创新能力。Jenkins 集成开发可以持续不断的发现问题,测试和验证功能模块的开发程度,增强开发人员对整个开发过程的了解和信心,同时还能快速实现开发人员的创新想法,及时在用户那里得到反馈,并且还可以在迭代的过程中不断优化。这些都可以带给开发人员更多的机会尝试和信心鼓励,对于产品的最终完成起着非常重要的作用。综上所述,这些优势给整个软件开发团队带来的好处是不可小觑的。那么如何将 Jenkins 自动化持续集成应用到开发流程的实际工作中呢?下面我们通过一个示例来演示具体的操作步骤。Jenkins 自动化持续集成开发的步骤安装 Jenkins关于如何下载并安装的内容在上文介绍 Jenkins 部分已经有交代过,在此不再重复阐述。然而,需要注意的是,在安装前,需要检查机器的 JRE 版本是否在 1.6 或以上,若不是,需要升级或下载新版本后才能保证 Jenkins 的正常运行。启动 Jenkins通过命令行进入安装包所在的目录,运行“java - jar jenkins.war”命令来启动 Jenkins。成功启动后的界面状态如下图所示。图 1. Jenkins 成功启动访问 Jenkins访问 Jenkins 的方式分为两种:第一种是在安装 Jenkins 的机器上访问,即 master 上直接访问,使用的地址为:http://localhost:8080;第二种是在其它任何一台机器上远程访问,使用的地址为:http://IP:8080,这里的 IP 是指 Jenkins 安装所在的机器 IP 地址。在实际应用中,用户可以根据需要任意选择一种访问方式,而且,多个不同的用户可以同时访问一个共同地址互不干扰地进行自己的操作,可以说 Jenkins 提供的这种访问方式给用户带来了很大的便捷。建立 Jenkins 自动化持续集成项目建议开发部门可以选用一台固定的物理机或者虚拟机作为安装 Jenkins 的机器,机器需要有连接外网的功能,这样方便下载和更新一些需要使用的插件。安装插件一般情况下,常使用到如下这些插件:vSphere Plugin:针对那些使用了 vSphere 虚拟化的基础设施,这个插件提供了实现“云”功能的工具。SSH Plugin:这个插件使用 SSH 协议执行远程 shell 命令。RTC Plugin:这个插件集成了 IBM 的 Rational Team Concert(RTC) 与 Hudson。Team Concert Plugin:集成了 Rational Team Concert。Multijob Plugin:这个插件是一个将多个项目连接在一起的插件。建立项目一:Request Build即从统一代码库下载最新代码,自动构建应用程序包,上传到统一的地址。构建程序包有两种方法,一种是使用 RTC Plugin 和 Team Concert Plugin 来连接开发人员代码所在的统一代码库。我们可以创建一个 Job,选择 Job 类型是 Build a free-style software project,将 Project name 命名为 Request Build,并选择在 master 上执行这个 Job。采取这种方式的时候,需要在 Job 进行配置之前,进入 Configure System 配置系统信息,如下图所示:图 2. 系统配置中 RTC 的信息系统信息配置完成后,再对 Request Build Job 进行设置。Source Code Management 配置的信息如下图所示:图 3. 项目 Request Build 中 Source Code Management 的配置这种方式中的代码资源管理是通过开发人员使用自己的账户连接到 RTC,从 RTC 抓取最新代码到 master 上的 Jenkins 目录中,Jenkins 会创建一个 workspace 来存放这些代码。接下来,再将抓取的代码在 master 上自动构建成应用程序包,需要使用到 build XML 文件和相关的属性信息。然后,继续在 Request Build Job 的 Build 选项中进行设置,如下图所示:图 4. 项目 Request Build 中 Ant 相关的配置上图中设置的 Properties 信息只是一个示例,读者需要根据 build 过程中使用的具体值来进行修改。到这里,项目一设置完成,保存后点击 Build now 按钮就可以 kick off Build 了。至此,我们讲的都是采用第一种方式来完成项目一的任务。然而对于第二种方式,简单来说,就是将 Jenkins 代码管理的工作用命令来实现,这涉及到构建程序包的开发人员的工作内容,不是本文的重点,故而不详细叙述如何来写这样的命令,读者有兴趣可以自行研究。由于第二种是利用已经完成的,包含着能实现代码管理和构建程序包命令的文件来实现自动下载代码和构建程序包的工作,所以先假设这个文件名为 requestBuild.bat 或者 requestBuild.sh。同样,我们在 Jenkins 上创建一个叫 Request Build 的 Job,选择在 master 上执行任务的话,本文中的 master 是 windows 机器,所以文件为 requestBuild.bat。在 Build 选项中选择 Execute Windows batch command,填入执行 requestBuild.bat 的 bat command,保存 Job 后点击 Build now 就可以进行代码下载和构建程序包的过程了。这样项目一的任务就完成了。读到这儿您可能会有疑问,打好的安装包是怎么上传到特定的地址呢?关于这个问题,是 kick off Build 的内容,在 build XML 文件和图 4 Properties 中有设置。建立项目二:Download build to install server(Specific build)&Install build即 download Build 到需要安装的环境上,自动安装软件。这个任务分为两个 Job 来实现,第一个 Job 命名为 Download build to install server(Specific build),主要是从上文中提到的特定地址下载需要的文件到安装环境上。同样,这里指定这个 Job 在 master 上执行。为了提升下载过程中的灵活性,可以给 Job 设置 build with Parameters,这样在执行这个 Job 的时候能选择下载哪些内容。如下图所示:图 5. 项目 Download build to install server(Specific build) 的参数配置上图中设置的信息,例如,FileList 是需要下载的文件的文件名称,buildTag 是下载的 build version。这些是使用 String Parameters。Password 是使用 Password Parameters。除了设置参数以外,Job 的内容是为了实现下载 Build 到指定的安装环境中。在 Build 选项中点击 Add build step 选择 Execute shell script on remote host using ssh,在 SSH site 中填写的机器上执行 Command 中的命令,将 build 下载到 install server 这台 Linux 机器的环境上。下图列出了如何配置的信息:图 6. 项目 Download build to install server(Specific build) 的 Command 配置完成配置后保存,点击 build with Parameters,填上需要下载 build 的版本和文件名,就可以将 build 下载到 install Server 这套环境上。然后,下载完成后要进行安装的操作,这时候,需要建立第二个 Job,命名为 Install build,也是在 master 上执行这个任务。这个 Job 通过 Command 中添加安装的 shell 命令来执行自动安装的过程。如下面图 7 给出了详细的信息。图 7. 项目 Install build 的 Command 配置运行这个 Jenkins Job 的时候,还可以在 Job 的 Console Output 中查看运行日志,监控软件安装的状态。这样,就实现了软件安装的过程了。在两个 Job 按顺序执行完成后,下一步需要做的是对安装好的软件进行 BVT 测试。建立项目三:Run BVT Automation case即调用批处理文件,自动进行 BVT 测试。创建一个 Job,project name 命名为 Run BVT Automation case。本文例子中的测试机的类型是 windows 机器,于是选择 Add build step 中的 Execute Windows batch command 选项,通过在测试机器上运行 bat 命令来实现测试用例的运行。配置信息很简单,如下图所示:图 8. 项目 Run BVT Automation case 的 Windows command 配置在这里需要注意的是,仍然要选择执行 Job 的位置是在 master 上还是 slave 上。如果测试用例的代码不在 master 上,可以选择测试机器作为 Jenkins 的 slave 节点。在 Manage Jenkins 选项中点击 Manage nodes 来创建 slave 节点,命名为 Test_node,创建完成后便可以在 Run BVT Automation case Job 中选择执行的地方是 Test_node 的 slave 节点。配置信息完成后,保存并运行 Job,Jenkins 就会通过上图中的 DDtest.bat 来运行测试用例。同理,为了追踪测试用例执行的情况,可以从 Job 的 Console Output 中查看运行日志。当然,这个任务是否能够顺利执行,还需要提前准备好测试代码。本例中的测试代码是一个独立的 Java 工程,bat 文件可以在测试机器上手工运行,进行 BVT 测试。Jenkins 只是将这个手工操作包装了一下。在上述三个项目完成后,我们会思考怎么将这几个流程串连起来呢?如何将不同的项目联系起来?文章前面提到的 Multijob Plugin 插件就是来解决这个问题的,安装 Multijob Plugin 插件后,就可以建立 Multijob Project。这里建立一个名为 Jenkins automation for development 的项目,并选择在 master 上执行这个 project,在 project 中配置需要串连起来的 Job 名称,在运行这个 project 的时候,就会顺序地将几个不同的 Job 一个个执行起来。做点锦上添花的事情在整个 Jenkins 持续集成的自动化流程中,仍然有很多需要改进的地方,笔者也在不停探索,先列出下面几点以供参考:
设置 Email Notification使用 MultiJob Project 后,Jenkins 就可以自动运行整个流程,那么如果能够在执行结束后自动给用户发一个通知的信息就再好不过了。这就需要在 Jenkins E-mail Notification 中配置,Reply-To Address 中填上收件人的邮箱,Charset 的内容为 UTF-8,并通过 Test configuration 按钮来测试是否成功发送到邮箱。
准备环境在开发和测试过程中都需要准备环境,我们可以通过使用 Jenkins 的 vSphere Plugin 来实现虚拟机环境的回滚,不过,这里仍然需要在 VMware 查看 Snapshot Name。首先需要在系统配置中设置 vSphere Cloud 的信息。例如,vSphere Host、为这个 vSphere Cloud 命名、登陆的用户名、密码等。然后,再创建准备环境的 Job,在 Build 中设置 vSphere Build Step 各个选项中的值。其中,Server 信息就是刚刚 vSphere Cloud 的名称,vSphere Action 是回滚环境的话就选择 Revert to Snapshot,Snapshot Name 可以从 VMware 中获得。这些关键信息完成后,运行 Job 就可以为安装 build 和测试提供可执行的环境了。
参数的使用这里的参数和平常说到的变量很类似,也有局部和全局的概念。全局的意思即在系统配置中可以设置 Global Choice Parameter,而在具体 Job 中设置的 Parameter 应用范围只在当前 Job 中。参数的种类也有很多,在这里便不再阐述,读者可以在实际使用中视情况选择。结束语Jenkins 持续集成给开发团队带来了很多便利,在这里我们只是初步的总结,为了让 Jenkins 持续集成的功能更加完善,还需要不停的实践和探索。希望通过本文的介绍让更多的软件开发人员和测试团队了解持续集成的平台所带来的好处。在当今软件产业迅速发展的趋势下,软件开发团队也正在经历着相应的发展和改变,作为程序员的我们,不仅要开发出一个个新的软件,也要利用已经存在的软件工具来提高我们的工作效率,从容应对瞬息万变的市场。
相关主题:搜索 Jenkins 相关文章,可以对 Jenkins 持续集成有更多的了解。
:了解 Jenkins 的更多信息。
:介绍了 Continuous integration 的内容。
:查看 Hudson 的详细信息。
: 查看 Jenkins 持续集成环境搭建的更多信息。
:这里有数百篇关于 Java 编程各个方面的文章。
添加或订阅评论,请先或。
有新评论时提醒我
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=Java technologyArticleID=988906ArticleTitle=使用 Jenkins 实现软件开发的持续集成publish-date=http://blog.csdn.net/jiangwei/article/details/
Android中有时候为了效率以及平台开发库的支持,难免会用到NDK开发,那么都会产生一个so文件,通过native方法进行调用,开发和调用步骤很简单,这里就不多说了,本文主要来介绍,我们在使用so的时候总是会出现一些常见的问题,而现在插件化开发也很普遍了,有时候插件中也会包含一些so文件,需要加载,这时候也会出现一些问题。本文就来详细总结一下这些问题出现的原因,以及解决方法,主要还是通过源码来分析。
二、涉及到的源码类
因为本文主要通过分析源码来分析so使用的知识点和问题总结,所以涉及到了很多的源码类,这里就现提供一下:
1、PackageManagerService.java
+setNativeLibraryPaths:设置应用的native库路径
+scanPackageDirtyLI:扫描包内容初始化应用信息
2、ActivityManagerService.java
+startProcessLocked:发送命令给Zygote进程启动一个虚拟机
3、NativeLibraryHelper.java
底层实现类:com_android_internal_content_NativeLibraryHelper.cpp
+copyNativeBinariesWithOverride:释放apk中的so文件到本地目录
+findSupportedAbi:遍历apk中的so文件结合abiList值得到应用支持的abi类型索引值
4、LoadApk类和ApplicationLoaders类
5、VMRuntime.java
底层实现类:dalvik_system_VMRuntime.c
+getInstructionSet:获取虚拟机的指令集类型
+is64BitAbi:判断VM是否为64位
6、Runtime.java
底层实现类:dalvik/vm/native/java_lang_Runtime.cpp,dalvik/vm/Native.cpp
+nativeLoad:加载so文件
三、Android中so文件的编译平台
Android中在进行NDK开发的时候,都知道因为机型杂而多的原因,没有一个大的标准,所以很多厂商都会采用不同型号的cpu,那么在编译so文件的时候,就需要进行交叉编译出多个cpu平台版本,现在主流的cpu架构版本:
armeabi/armeabi-v7a:这个架构是arm类型的,主要用于Android4.0之后的,cpu值32位的
x86/x86_64:这个架构是x86类型的,有32位和64位,占用的设备比例比较小
arm64-v8:这个架构是arm类型,主要用于Android5.0之后,cpu是64位的
这里可以看到,其中arm类型的是往下兼容策略,比如arm64-v8a肯定兼容armeabi/armeabi-v7a,也就是说armeabi/armeabi-v7a架构的so文件可以用在arm64-v8a的设备中的,而armeabi-v7a也是兼容armeabi的,但是因为cpu型号不同,所以arm体系和x86体系之间是不能相互兼容的。
四、Android中so加载流程
在Android中如果想使用so的话,首先得先加载,加载现在主要有两种方法,一种是直接System.loadLibrary方法加载工程中的libs目录下的默认so文件,这里的加载文件名是xxx,而整个so的文件名为:libxxx.so。还有一种是加载指定目录下的so文件,使用System.load方法,这里需要加载的文件名是全路径,比如:xxx/xxx/libxxx.so。
上面的两种加载方式,在大部分场景中用到的都是第一种方式,而第二种方式用的比较多的就是在插件中加载so文件了。
不管是第一种方式还是第二种方式,其实到最后都是调用了Runtime.java类的加载方法doLoad:
这里会先从类加载中获取到nativeLib路径,然后在调用native方法nativeLoad(java_lang_Runtime.cpp):
这里调用了一个核心的方法dvmLoadNativeCode(dalvik/vm/Native.cpp):
这里有一个检测异常的代码,而这个错误,是我们在使用插件开发加载so的时候可能会遇到的错误,比如现在我们使用DexClassLoader类去加载插件,但是因为我们为了插件能够实时更新,所以每次都会赋值新的DexClassLoader对象,但是第一次加载so文件到内存中了,这时候退出程序,但是没有真正意义上的退出,只是关闭了Activity了,这时候再次启动又会赋值新的加载器对象,那么原先so已经加载到内存中了,但是这时候是新的类加载器那么就报错了,解决办法其实很简单,主要有两种方式:
第一种方式:在退出程序的时候采用真正意义上的退出,比如调用System.exit(0)方法,这时候进程被杀了,加载到内存的so也就被释放了,那么下次赋值新的类加载就在此加载so到内存了,
第二种方式:就是全局定义一个static类型的类加载DexClassLoader也是可以的,因为static类型是保存在当前进程中,如果进程没有被杀就一直存在这个对象,下次进入程序的时候判断当前类加载器是否为null,如果不为null就不要赋值了,但是这个方法有一个弊端就是类加载器没有从新赋值,如果插件这时候更新了,但是还是使用之前的加载器,那么新插件将不会进行加载。
继续往下看:
这里主要调用了两个核心的系统方法,dlopen和dlsym,这两个方法用途还是很多的,一般是先加载so文件,然后得到指定函数的指针,最后直接调用即可,主要用于调用动态的调用so中的指定函数功能。而且这里注意到了最开始先调用so中的JNI_OnLoad函数,这个函数是so被加载之后调用的第一个方法。
到这里我们就总结一下Android中加载so的流程:
1、调用System.loadLibrary和System.load方法进行加载so文件
2、通过Runtime.java类的nativeLoad方法进行最终调用,这里需要通过类加载器获取到nativeLib路径。
3、到底层之后,就开始使用dlopen方法加载so文件,然后使用dlsym方法调用JNI_OnLoad方法,最终开始了so的执行。
五、Android中类加载器关联so路径
上面分析so加载过程中可以发现有一个地方,就是通过类加载器来获取到so的路径,那么Android中的主要类加载器有两个,一个是PathClassLoader和DexClassLoader,关于这两个类加载不多说了,网上资料很多可以自行查找阅读。而PathClassLoader是我们Android中默认的类加载器,也就是apk文件就是由他来加载的,我们可以通过查看源码得知,Android中加载apk的类加载可以从LoadApk.java类查找到:
这个类很重要的,而这个类加载器也是我们在做插件的时候,需要做一些操作,比如需要把加载插件的DexClassLoader类给添加到这个系统加载器中,就可以解决插件中组件的生命周期问题。
看看这个类加载器在哪里赋值的:
去看看ApplicationLoaders.java类:
看到了,这里就是定义了PathClassLoader类了,所以我们Android中应用的默认加载器是PathClassLoader,再去看看这个类加载器的nativeLib是哪里:
六、Android中so文件如何释放
我们在使用System.loadLibrary加载so的时候,传递的是so文件的libxxx.so中的xxx部分,那么系统是如何找到这个so文件然后进行加载的呢?这个就要先从apk文件安装时机说起。
我们如果还没有分析源码之前,大致能够猜想到的流程是:
在安装apk的时候,系统解析apk文件,因为so文件肯定是存放在libs下指定平台目录中的,而apk文件本身就是一个压缩文件,所以可以进行解压,然后读取libs目录下的so文件,进行本地释放解压到指定目录,然后在加载的时候就先拼接so文件的全路径,最后在进行加载工作即可。
通过猜想,下面就通过源码来分析一下流程,系统在安装apk的时候,是调用系统类:PackageManagerService.java类:
主要的核心方法是scanPackageDirtyLI:
这个方法主要通过传递的pkg变量,开始构造applicationInfo信息。我们往下面看,找到设置nativeLib信息的代码:
这里注意有一个判断,是不是多平台架构的应用:
所以,我们看看info.flags有没有设置这个标志,我们看到上面的pkg变量是通过解析apk文件的类PackageParser.java类中获取到的,所以可以去这个类中找这个标志位的设置。
这里看到了,如果在AndroidManifest.xml中设置了Application中的multiArch属性值的话就有,但是我们默认都没有设置这个属性值,那么就是false,也就是说一般应用都不是多平台的。所以上面的isMultiArch方法就返回false,代码就走到了这里:
在这里就有很多知识点了,而这里可以看到,就涉及到了so文件的释放工作了,主要是在NativeLibraryHelper类中,但是这里看到首先获取abiList值:
通过Build.SUPPORTED_ABIS来获取到的:
最终是通过获取系统属性:ro.product.cpu.abilist的值来得到的,我们可以使用getprop命令来查看这个属性值:
这里获取到的值是:arm64-v8a,armeabi-v7a,armeabi,我用的是64位的cpu设备,所以可以看到他有多个cpu架构可选,而且看到这个顺序会想到,这个顺序正好是向下兼容的顺序。
现在去看看NativeLibraryHelper类的copyNativeBinariesForSupportedAbi方法:
这个方法中主要干了三件事:
第一件事是获取应用所支持的arch架构类型
第二件事是通过架构类型获取so释放的目录
第三件事是native层中释放apk中的指定架构的so到设备目录中
第一件事:获取应用所支持的arch架构类型
NativeLibraryHelper类的findSupportedAbi方法,其实这个方法就是查找系统当前支持的架构型号索引值:
看看native方法的实现:
这里看到了,会先读取apk文件,然后遍历apk文件中的so文件,得到全路径然后在和传递进来的abiList进行比较,得到合适的索引值,其实实现逻辑很简单:abiList是:arm64-v8a,armeabi-v7a,armeabi,然后就开始比例apk中有没有这些架构平台的so文件,如果有,就直接返回abiList中的索引值即可,比如说apk中的libs结构如下:
那么这时候返回来的索引值就是0,代表的是arm64-v8a架构的。如果apk文件中没有arm64-v8a目录的话,那么就返回1,代表的是armeabi-v7a架构的。依次类推。得到应用支持的架构索引之后就可以获取so释放到设备中的目录了。
第二件事:获取so释放之后的目录
这里主要通过VMRuntime.java中的getInstructionSet方法:
这里调用了一个map结构值:
这里的arch架构和目录对应关系,如果arch是arm64-v8a的话,那么目录就是arm64了。
第三件事:释放apk中的so文件
直接调用的是native层方法iterateOverNativeFiles:
好了到这里就讲完了上面的三件事了,而这三件事做完之后,apk中的so文件就会被释放到本地设备中的指定目录中了,当然这里系统会根据abiList中的值以及apk中包含的arch类型的so来决定释放哪个目录中的so文件,比如这里通过ApplicationInfo类来打印当前应用的nativeLibraryDir值:
打印的结果:
看到了,因为是arm64-v8a类型的,所以目录是arm64的,而且可以看到这个应用不是多平台的。
我们可以看到Android中是如何释放apk中的so文件到本地目录的:
1、通过遍历apk文件中的so文件的全路径,然后和系统的abiList中的类型值进行比较,如果匹配到了就返回arch类型的索引值
2、得到了应用所支持的arch类型之后,就开始获取创建本地释放so的目录
3、然后开始释放so文件
我们在PackageMangerService类中继续往下看:
这里还要保存上面获取到应用支持的arch类型值,我们可以使用反射打印这个值:
打印结果:
这个值在后面应用创建VM的时候会用到。
接着开始设置应用的nativeLib路径了:
看看这个方法的实现:
这里先判断是不是64位:
通过arch类型对应的目录来判断的:
这里如果是64位,目录就是lib,如果是32位就是lib64:
这样就和我们上面释放so文件的目录保持一致了,所以这里的ApplicationInfo类中的lib路径就是我们上面释放so之后的路径了。
在之前说到了类加载器中的lib路径,我们可以打印一下库路径的,这里直接使用getClassLoader得到加载器打印即可:
这里看到Library的目录包含很多路径。
七、Android中64位系统如何兼容32位的so
上面分析完了,so文件的释放工作,下面继续来看一下如果一个64位系统的Android设备如何做到能够运行32位的so文件,这个就需要从应用的启动说起了,那么这个类就是ActivityManagerService.java,有一个核心的方法:startProcessLocked,这个方法就是向Zygote进程发送一个消息,为这个应用创建虚拟机开始运行程序了:
这里在发送消息给Zygote进程,看到这里通过ApplicationInfo中的primaryCpuAbi类型告诉Zygote改创建多少位的虚拟机,我们查看系统启动文件init.rc内容:
这里会启动一个64位的Zygote进程
然后启动一个32位的Zygot进程
所以这里应该就可以想明白了,原来系统启动的时候,如果是64位的系统设备,会启动两个Zygote进程用来兼容32位类型的应用,我们可以使用ps命令查看进程:
看到了,这里果然启动了两个Zygote进程,一个64位的,一个是32位的。所以兼容功能的大致流程图应该是这样的:
上层启动应用的时候会把应用的abi类型带过来,然后这里会根据这个类型发送给具体的Zygote进程消息,来创建虚拟机开始运行程序,这样就做到了兼容。
八、Android插件中如何加载so文件
有时候我们在开发插件的时候,可能会调用so文件,一般来说有两种方案:
一种是在加载插件的时候,先把插件中的so文件释放到本地目录,然后在把目录设置到DexClassLoader类加载器的nativeLib中。
一种在插件初始化的时候,释放插件中的so文件到本地目录,然后使用System.load方法去全路径加载so文件
这两种方式的区别在于,第一种方式的代码逻辑放在了宿主工程中,同时so文件可以放在插件的任意目录中,然后在解压插件文件找到这个so文件释放即可。第二种方式的代码逻辑是放在了插件中,同时so文件只能放在插件的assets目录中,然后通过把插件文件设置到程序的AssetManager中,最后通过访问assets中的so文件进行释放。
上面就全部分析完了Android中关于so加载的相关内容:
1、so编译平台问题
2、so加载流程分析
3、so文件释放功能分析
4、so文件兼容功能分析
5、插件中so文件调用功能分析
九、常见问题分析
第一个问题:Could not find libxxx.so
这个问题看上去很好理解,就是在调用加载so的方法的时候,到底层使用dlopen方法打开so文件,发现找不到这个so文件,那么这个问题产生的原因主要有两个:
第一个是我们的确忘了在工程的libs下存放so文件了;
第二个是我们把so文件放错目录了;
第一个原因就不多说了,主要来看第二原因:
有时候我们在开发项目的时候,可能会放多个架构类型的so文件,那么现在假如我的设备是arm64-v8a类型的,我的项目中有三个so文件,比如叫做AAA.so,BBB.so,CCC.so,然后我再arm64-v8a目录中放了AAA.so,BBB.so,而CCC.so忘了放了,但是会放到armeabi-v7a和armeabi目录中,那么这时候就会发生找不到CCC.so的错误,原因很简单:
上面分析了apk中so文件的释放逻辑,系统会先遍历apk中所有so文件的全路径,然后在结合abiList的值来决定最终释放哪个目录中的so文件,那么现在系统是arm64-v8a了,而apk中的libs下也有arm64-v8a,所以这里就会把apk中的libs\arm64-v8a中的所有so文件释放解压到本地目录中,而不会在去释放armeabi/armeabi-v7a了。因为arm64-v8a中没有CCC.so文件,所以最终释放到本地目录中也是没有这个so文件的,所以加载时找不到文件了。
解决办法:就是在使用so文件的时候,需要确定在每个架构类型目录中都要有相同的so文件即可。
第二个问题:32-bit instead of 64-bit
这个问题的原因主要是因为64位的Zygote进程创建的虚拟机中加载了32位的so文件,这个问题的产生原因主要有两个:
第一个是我们把不同架构类型的so文件放错目录了,比如armeabi/armeabi-v7a的so文件放到了arm64-v8a中了
第二个是我们在开发插件的过程中,宿主工程中有arm64-v8a目录,但是插件中加载so却是armeabi/armeabi-v7a类型的
第一个原因就不多说了,主要是因为so放错目录了,来看一下第二个原因,我们在开发插件的时候有时候需要在插件中去加载so文件,一般都是使用System.load方式去加载全路径的so文件,那么这里就可能存在一个问题,比如宿主工程中,放了所有架构的目录,包括了64位的,因为考虑插件的大小,所以在插件中只放了armeabi-v7a目录的so文件,如果设备是64位的系统,那么这时候插件加载so文件就会报错。原因就在于上面分析的so兼容问题中说到了,因为宿主工程中包含了64位的架构arm64-v8a类型,系统的abiList中也有arm64-v8a类型,所以这时候应用的ApplicationInfo的abi就是arm64-v8a了,那么就会发送消息给Zygote64的进程,创建的也是64位的虚拟机了,而最后插件中加载so的类型是32位的armeabi-v7a,那么就会报错了,因为32位的so文件不能运行在64位的虚拟机中的。
解决办法:宿主工程和插件工程中的so文件的架构类型保持一致,这个将会带来一个很大的问题,就是插件包会变得很大,因为宿主工程为了兼容多数机型,加入了多个类型的架构so文件,但是插件为了减小包大小,就放了指定类型的so文件,但是最终会存在这种问题,所以这个解决办法就要看项目需要了。
还有一个类似的问题:64-bit instead of 32-bit:
原理都是一样的,32位的虚拟机中加载了64位的so文件问题导致的。
第三个问题:Shared library already opened
这个问题在上面介绍so加载流程中已经介绍过了,原因主要是因为之前使用DexClassLoader加载so之后,so没有释放还在内存中,而在此启动有弄了一个新的DexClassLoader对象去加载so问题,就出错了。
我们使用DexClassLoader类去加载插件,但是因为我们为了插件能够实时更新,所以每次都会赋值新的DexClassLoader对象,但是第一次加载so文件到内存中了,这时候退出程序,但是没有真正意义上的退出,只是关闭了Activity了,这时候再次启动又会赋值新的加载器对象,那么原先so已经加载到内存中了,但是这时候是新的类加载器那么就报错了。
解决办法:
第一种方式:在退出程序的时候采用真正意义上的退出,比如调用System.exit(0)方法,这时候进程被杀了,加载到内存的so也就被释放了,那么下次赋值新的类加载就在此加载so到内存了。
第二种方式:就是全局定义一个static类型的类加载DexClassLoader也是可以的,因为static类型是保存在当前进程中,如果进程没有被杀就一直存在这个对象,下次进入程序的时候判断当前类加载器是否为null,如果不为null就不要赋值了,但是这个方法有一个弊端就是类加载器没有从新赋值,如果插件这时候更新了,但是还是使用之前的加载器,那么新插件将不会进行加载。
十、技术概要
本文主要介绍了Android中关于so的相关知识,主要包括so编译多架构问题,so加载流程问题,so释放问题,so系统兼容问题以及插件中加载so文件的功能解析,看完本文之后,我们需要了解到的知识点:
1、在NDK开发时,可以指定多种架构类型编译出多种类型的so文件。
2、so的加载流程主要是System类中的两个加载方法,最终都会调用Runtime中的nativeLoad的native方法,而这个native方法最终会调用dlopen来打开so文件,然后在调用dlsym方法调用so的JNI_OnLoad方法。
3、关于apk文件在安装的时候释放so文件到本地目录中,主要是结合当前设备的abiList信息(这个信息主要是通过系统属性:ro.product.cpu.abilist值来获取的)和apk中不同类型架构,来决定最终释放哪个类型目录中的so文件,释放完成之后,还需要设置应用的nativeLib路径,以及应用的abi信息,因为这个abi信息在后面启动虚拟机的时候需要用到。
4、因为现在有很多设备已经是64位系统了,但是为了兼容32位的so文件,所以这些64位系统就会在系统启动的时候创建两个Zygote进程,一个是64位的,一个是32位的,当一个应用启动的时候,需要创建虚拟机,那么这时候就会把应用的架构类型传递过去,系统会根据这个类型来交给哪个Zygote进程来处理这个应用启动事件。这样就可以做到so调用的兼容问题了。
5、插件中加载so文件现阶段主要有两种方式,一种是先释放插件中的so文件到本地目录,然后设置DexClassLoader的nativeLib路径;还有一种方式是先释放插件中的so文件,然后调用System.load来加载全局路径的so文件。
十一、问题总结
本文还总结了在使用so文件的时候,会遇到的一些问题,主要是三个问题:
1、so文件找不到问题
这个问题一般是因为我们忘记放了so文件,或者是so文件没有放置全部,也就是没有在libs目录中所有的架构类型目录中放置。
2、不同位数的虚拟机运行了不同位数的so文件
这个问题一般是因为我们在libs目录中把so文件放错目录了,或者是宿主工程和插件工程中的so文件架构类型目录没有保持一致。
3、类加载器加载so文件再次加载
这个问题一般是因为插件开发中使用了不同的DexClassLoader去加载多次相同的so文件导致的。
十二、知识延展
我们在开发的过程中有时候想知道系统的位数,那么这里网上告知说有好几种方法,其实那些都是忽悠人的,特别是在使用这个api的时候:android.os.Build.CPU_ABI,我就是在项目中被这个方法坑爹了,这个方法其实不是获取系统的位数,而是获取当前应用的架构类型位数,就是我们前面分析的ApplicationInfo中的abi信息,我们可以查看一下源码:
这里可以看到,这个字段已经被废弃了,因为他不靠谱呀,这个字段在Build类的static块中进行赋值的:
这里会通过VMRuntime类的is64Bit方法来判断当前虚拟机的位数,来获取这个值
这里还有两个系统属性:
ro.product.cpu.abilist32是32位的所有arch架构类型
ro.product.cpu.abilist64是64位的所有arch架构类型
而这两个字段值的合集就是前面的ro.product.cpu.abilist属性值。
而VMRuntime的is64Bit方法是native方法,实现如下:
看到了,这里得到的是虚拟机的位数,那么就是上面的Zygote进程的位数了。那么问题就来了,假如我的设备是64位的,但是我的项目中没有arm64-v8a类型的so文件,这时候在解析apk进行释放so文件的时候,就会得知架构类型是armeabi/armeabi-v7a了,因为遍历apk文件,没有找到arm64-v8a类型的so文件,这时候应用的abi类型就是armeabi/armeabi-v7a了,这就是32位的了,就会通知32位的Zygote进程创建了一个32位的虚拟机,那么此时我的项目中通过Build.CPU_ABI得到的系统位数就是32了,那么完全不是我们想要的了。
所以正确的获取系统位数的方法是:
Android5.0系统之后,可以通过ro.product.cpu.abilist属性字段值来判断,如果这个字段值中包含了64的话,那么就是64位系统了
Android5.0系统之前,需要通过ro.product.cpu.abi属性字段值来判断,不过5.0系统之前都是32位的,还没有出现64位呢。
十三、选择适当架构类型减小包大小
我们上面分析之后可以看到,如果想做到万无一失即,项目不报错,而且so运行效率也是非常高的话,就需要把那几个架构类型的so文件都要在项目中放一遍,那么这个问题就来了,如果so文件较大的话,apk包最终也是很大的,所以这里就需要做一次选择了。
1、我们在开发一个项目的时候因为,整个项目的so文件结构我们可以控制,所以为了防止apk包增大,我们可以考虑只放几个架构类型的so文件,比如最好的是放armeabi类型的,因为首先现在大部分设备采用cpu型号都是arm的,少数采用x86或者是mips类型的,其次是防止了armeabi类型之后,对于armeabi-v7a和arm64-v8a就可以兼容了,不会存在报错问题。但是因为系统需要兼容所以就会出现so运行效率的问题了,最好的效率就是指定架构类型的so运行在对应架构类型的设备中。因为现在大部分的设备系统版本都是4.0以上了,所以armeabi-v7a架构类型用的比较多了,所以有时候为了效率问题,项目中只放了这个架构类型的so文件,那么像老版本的手机armeabi的话就会报错了,当然这个错误是可以接受的即可。
2、有时候像x86和mips等少数类型架构的设备,开发程序的时候会单独出一个版本比如叫做xxx应用x86版本
3、在开发SDK的时候,因为开发之后的SDK包是给其他app接入的,而对于接入的app,我们不能做太多的限制,所以理论上应该把所有架构类型的so都要提供,这样给需要接入的app进行选择即可,比如像百度地图SDK:
十四、总结
本文主要是介绍了Android中关于so的相关知识,而这些知识点都是在使用so文件中会经常用到的,同时一些问题也是我们会遇到的,这里只是做了一个总结,同时也给出了插件中加载so文件的方案已经遇到的问题解决思路等内容。
本文已收录于以下专栏:
相关文章推荐
简介前几天做一个视频播放的功能,用到了bilibili开源ijkplayer播放器的(集成ijkplayer),功能确实强大,但就是用到的ffmpeg解码库太大,不得已只能只能将so文件拿出来,通过动...
1、动态链接库的加载流程
首先从宏观流程上来看,对于 load 过程我们分为 find&load,首先是要找到 so 所在的位置,然后才是 load 加载进内存,同时对于 dalvik 和 art 虚...
/content/13/99.shtml
helloworld程序中的printf函数是怎样调用的?库分为两种...
linux动态链接库的加载顺序:
它有5个地方会查找,
1. 编译时指定的run path
2. LD_LIBRARY_PATH 指定的地方
3. ldconfig 指定的地方
一、UnsatisfiedLinkError基本介绍
全名     java.lang.UnsatisfiedLinkError
官方解释     Throw if the java Virtural...
Android中有时候为了效率以及平台开发库的支持,难免会用到NDK开发,那么都会产生一个so文件,通过native方法进行调用,开发和调用步骤很简单,这里就不多说了,本文主要来介绍,我们在使用so的...
之前,都是在32位android手机上,做测试。然后搞了一个64位手机,就不行了。
分析后,先是报:is 32-bit instead of 64-bit
然后,再自己的CPP的库中,修改Appl...
简单模式中,使用ClassLoader加载外部的Dex或Apk文件,可以加载一些本地APP不存在的类,从而执行一些新的代码逻辑。但是使用这种方法却不能直接启动插件里的Activity。启动插件中Act...
Java代码都是写在Class里面的,程序运行在虚拟机上时,虚拟机需要把需要的Class加载进来才能创建实例对象并工作,而完成这一个加载工作的角色就是ClassLoader。类加载器ClassLoad...
Java程序中,JVM虚拟机是通过类加载器ClassLoader加载.jar文件里面的类的。Android也类似,不过Android用的是Dalvik/ART虚拟机,不是JVM,也不能直接加载.jar...
他的最新文章
讲师:姜飞俊
讲师:汪木铃
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)}

我要回帖

更多关于 弹幕姬语音插件 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信