android 32 64位apk运行环境机制

Android L开始 APK 64bit 32bit 运行环境原理及决定运行环境的规则

正在上传…重新上传取消​WalkerXu发布于 2018-04-03

🔥🔥🔥 SegmentFault D-Day Online 开源开放与新技术创新,快来报名 >>>

自开发64bit Android L以来,遇到了很多关于64bit的问题,有编译的,有运行时的;编译方面的已经有一个文档介绍,这篇主要介绍64bit运行时,主要是APK的运行环境原理,以及谁决定了或如何让你的app运行在64bit或32bit的运行环境。

Android L 64bit系统里的进程

说到这里值得注意的是,在64bit Android L里,也并不是所有的进程都运行在64bit下,而且有的进程只运行在32bit下,比如mediaserver进程只有32bit。32Bit进程和64bit进程间跟其他进程一样通过binder进行通信。比如meidaplayer app运行在64bit环境,它要同过jni/binder调用到mediaserver进程里的服务。(见图1)
操作系统教科书里说好啊,系统资源是按照进程分配的,每个进程之间的资源是独立的。

zygote进程

要说APK的运行空间,肯定要说到zygote,zygote是一个非常重要的进程,zygote进程的建立是真正的Android运行空间。
以mtk6595为例看看zygote进程,发现有两个zygote一个是zygote64一个是zygote,他们分别对应了64bit和32bit的运行空间。(见图2)

屏幕快照 2018-04-03 下午3.39.46

 

root@mt6595:/ # ps | grep "zy"
root 306 1 2013256 59424 ffffffff a9fe73e4 S zygote64
root 309 1 1449624 53036 ffffffff f754d5c0 S zygote
在脚本里确实可以看到起了两个zygote进程,也可以看到system_server是64bit的
root@mt6595:/ # cat init.zygote64_32.rc
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
zygote对应的执行文件在system/bin下
root@mt6595:/ # ls -l system/bin/ | grep "app"
lrwxr-xr-x root shell 2015-04-14 13:05 app_process -> app_process64
-rwxr-xr-x root shell 13672 2015-04-14 13:05 app_process32
-rwxr-xr-x root shell 18056 2015-04-14 13:05 app_process64
通过对zygote进程的分析,可以得出Android L 64bit APK运行环境可以是64bit也可以是32bit。

决定APK运行在32bit还是64bit环境下的规则

是谁决定了APK的运行时是32bit还是64bit呢?Packagemanager
Packagemanager为两个zygote进程做了处理,安装APP的时候,它把APP的ABI传给dexopt,这样dexopt就可以编译出对应abi的dex file。
比如app的abi是armeabi,那dexopt编译出dex file的运行环境就是32bit,如果app的abi是arm64-v8a那dexopt编译出的dex file运行环境是64bit.
那问题来了,如果app没有指定abi呢,如果app是system app呢??

这就涉及到了一些规则:

1.对于第三方安装的APP

如果没有指定ABI,在Android L 64bit系统默认编译成64bit dex;如果已经指定了ABI则根据ABI编译出对应的dex,比如app的abi是armeabi,那dexopt编译出dex file的运行环境就是32bit,如果app的abi是arm64-v8a那dexopt编译出的dex file运行环境是64bit.
注意abi文件夹下一定要有so文件。
System.Loadlibrary的路径就分别是(有先后顺序):
/data/data/packagename/lib
32bit的情况
/vender/lib
/system/lib
64bit的情况
/vender/lib64
/system/lib64
以dlna为例:
没指定abi:04-20 14:56:15.478 8844 8844 E AndroidRuntime: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.testsystemloadlibray-1/base.apk"],nativeLibraryDirectories=[/vendor/lib64, /system/lib64]]] couldn't find "libdlna_jni.so"
指定abi:04-20 14:57:08.551 8950 8950 E AndroidRuntime: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.testsystemloadlibray-1/base.apk"],nativeLibraryDirectories=[/data/app/com.example.testsystemloadlibray-1/lib/arm, /vendor/lib, /system/lib]]] couldn't find "libdlna_jni.so"

2.对于系统自带的放在/system/app或/system/priv-app根目录下的APP

由于获取不到abi信息,所以默认编译成64bit dex;也可以指定编译成32bit的dex。
如果想要APP编译出32bit的dex,可以在/system/lib下建一个与apk同名的文件夹,即/system/lib/apkname,该文件夹可以是空的,也可以放入对应的该app的32bit的so库,这样编译出的dex是32bit,即在32bit运行环境下运行。
反之,如果想强制编译出64bit的dex,也可以在/system/lib64/下做同样操作。
另外手动修改建文件夹必须重启机器。
比如/system/priv-app/TestSystemLoadlibrary.apk
System.Loadlibrary的路径就分别是(有先后顺序):
默认编译成64bit的情况
/vender/lib64
/system/lib64
指定32bit编译
/system/lib/ TestSystemLoadlibrary
/vender/lib
/system/lib
指定64bit编译
/system/lib64/ TestSystemLoadlibrary
/vender/lib64
/system/lib64

3.对于系统自带的放在/system/app或/system/priv-app子目录下的APP

比如/system/priv-app/Video/Video.apk,也获取不到abi信息,所以默认编译成64bit dex;也可以编译成32bit dex。
方法是以Video为例:在/system/priv-app/Video下建立两级目录即/system/priv-app/Video/lib/arm该目录可以是空也可以放app的32bit so库,那app编译出的dex是32bit的运行在32bit的环境。
同理64bit也可以这样:/system/priv-app/Video/lib/arm64,app编译出的即是32bit dex,运行在32bit运行环境。
System.Loadlibrary的路径就分别是(有先后顺序):
默认编译成64bit的情况
/vender/lib64
/system/lib64
指定32bit编译
/system/priv-app/lib/arm
/vender/lib
/system/lib
指定64bit编译
/system/priv-app/lib/arm64
/vender/lib64
/system/lib64

4.对于预编译的apk的情况

预编译时Android.mk中的LOCAL_MULTILIB不直接影响运行时,它的编译结果在运行时仍遵循上述规则1,2,3
肯定会想到Android.mk的LOCAL_MULTILIB := 32; LOCAL_MULTILIB := both,通过LOCAL_MULTILIB来指定编译的abi
通过实验,得出结论:
4.1.Android.mk的LOCAL_MULTILIB在纯java app编译中,编译出的apk运行时不受LOCAL_MULTILIB影响,也就是说LOCAL_MULTILIB不起作用,解开LOCAL_MULTILIB 不设置和设置LOCAL_MULTILIB := 32编译出来的apk结构上没有区别。
4.2.Android.mk的LOCAL_MULTILIB在纯java app带jni或第三方库的编译中,
C/C++工程中的Android.mk中的LOCAL_MULTILIB用来指定编译出来的so/.a的是32bit还是64bit,该flag不设置,默认编译出64bit so,设置该flag为32编译出32bit so,设置该flag为both编译出32bit和64bit的so。
Java工程中的Android.mk中的LOCAL_MULTILIB配合LOCAL_JNI_SHARED_LIBRARIES := libxxx_jni,如果
LOCAL_MULTILIB不设置,默认将64bit的so拷贝到/system/app/appname/lib/arm64下,如果LOCAL_MULTILIB设置为32,则将32bit的so拷贝到/system/app/appname/lib/arm下,如果LOCAL_MULTILIB设置为both,则将32bit和64bit的so分别拷贝到/system/app/appname/arm和/system/app/appname/arm64,这样间接对运行时产生影响。

4.1.如下试验(纯java app):分别不设置LOCAL_MULTILIB和LOCAL_MULTILIB为32编译出两个HTMLViewer.apk,该apk activity create的时候会System.loadlibrary(“dlna_jni”),libdlna_jni.so已经从系统移除。
两个apk都去load lib64下的so 。可见LOCAL_MULTILIB对纯java app运行时无影响。
04-21 12:34:53.139 11007 11007 E AndroidRuntime: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/system/app/HTMLViewer/HTMLViewer.apk"],nativeLibraryDirectories=[/vendor/lib64, /system/lib64]]] couldn't find "libdlna_jni.so"

4.2.试验结果如下(带jni,java app):
不设置LOCAL_MULTILIB编译结果
xuzhenming@xuzhenming:~/platform/m85_base/out/target/product/meizu6795_lwt_l/system/app/Bluetooth$ ls
Bluetooth.apk lib
xuzhenming@xuzhenming:~/platform/m85_base/out/target/product/meizu6795_lwt_l/system/app/Bluetooth$ ls lib/
arm64
xuzhenming@xuzhenming:~/platform/m85_base/out/target/product/meizu6795_lwt_l/system/app/Bluetooth$ ls lib/arm
64
libbluetooth_jni.so
设置LOCAL_MULTILIB为32的编译结果
xuzhenming@xuzhenming:~/platform/m85_base/out/target/product/meizu6795_lwt_l/system/app/Bluetooth$ ls
Bluetooth.apk lib
xuzhenming@xuzhenming:~/platform/m85_base/out/target/product/meizu6795_lwt_l/system/app/Bluetooth$ ls lib/
arm
xuzhenming@xuzhenming:~/platform/m85_base/out/target/product/meizu6795_lwt_l/system/app/Bluetooth$ ls lib/arm
libbluetooth_jni.so
设置LOCAL_MULTILIB为both的编译结果
xuzhenming@xuzhenming:~/platform/m85_base/out/target/product/meizu6795_lwt_l/system/app/Bluetooth$ ls
Bluetooth.apk lib
xuzhenming@xuzhenming:~/platform/m85_base/out/target/product/meizu6795_lwt_l/system/app/Bluetooth$ ls lib/
arm arm64
xuzhenming@xuzhenming:~/platform/m85_base/out/target/product/meizu6795_lwt_l/system/app/Bluetooth$ ls lib/arm
libbluetooth_jni.so
xuzhenming@xuzhenming:~/platform/m85_base/out/target/product/meizu6795_lwt_l/system/app/Bluetooth$ ls lib/arm64
libbluetooth_jni.so

总结:

Android L 64bit通过双zygote的设计兼容64bit和32bit APP运行;app是运行在32bit还是64bit是由编译出的dex file决定的,而dex file是32bit还是64bit是由一系列规则决定的,善加利用这些规则,可以让你的app随意load /system/lib或/system/lib64下的库,因为load哪个下边的库是由该进程的运行环境来决定的,对于app就是dex决定的,进程是32bit运行环境laod /system/lib是64bit运行环境laod /system/lib64

刚出64bit的时候写的内容可能没有那么准确了。

apk so库打包规则

apk 在安装的时候 package manager service 会将 libs 下的库拷贝到两个文件夹
一个是/data/data/YourPackageName/lib
一个是/data/app/YourPackageName-1/lib/arm
目前经过试验和参考 android 代码发现,在 4.2 及以后的版本中,拷贝规则如下
1.首选 abi 规则
及你的配置文件中 abi 有如下定义
ro.product.cpu.abi=armeabi-v7a ro.product.cpu.abi2=armeabi ro.product.cpu.abilist=armeabi-v7a,armeabi ro.product.cpu.abilist32=armeabi-v7a,armeabi ro.product.cpu.abilist64=
那首选 abi 就是 armeabi-v7a
对应的如果你到 app 下有 armeabi 和 armeabi-v7a 两个文件夹,那只有 armeabi-v7a
下边的库会被拷贝到/data/data/YourPackageName/lib 2.次 abi 规则
如果你的 app 下只有 armeabi 文件夹,那 armeabi 下的库会被拷贝到 /data/data/YourPackageName/lib
armeabi 编译的库在 arm 架构中是比较通用的
3.google 的设计估计是想每个 arch 都让你准备一份代码,更容易跨 cpu 使用,因为 可能架构多了,涉及到 arm32,arm64,x86_32,x86_64,mips_32, mips_64

按之前的设计无论首选 abi 和次 abi 中的 so 库都会拷贝到 /data/data/YourPackageName/lib,如果首选 abi 和次 abi 文件夹中有重复的库首选 abi 的库会覆盖次 abi 的库,以此类推,架构多了,好多架构的库就会都在同一个 文件夹/data/data /YourPackageName/lib 和/data/app/YourPackageName-1/lib/arm
有点乱,我猜 google 是这么想的 这样的话 app 又会变大
对于我们 app 打包 so
1.把所有库打包到一个 abi 文件夹,仅限于 32 位,64 位没试过,不管是 armeabi
还是 armeabi-v7a 都打包到一个 abi 文件夹,也可以用
2.按照 google 的设计,每个 abi 文件夹里打包一份全的 so,app 会变大 google 怎么解

apk链接系统库有两点注意:

1.Android N上的规则变化:https://developer.android.goo...

针对ndk的应用,简言之就是app不要随便链接system/lib下的系统库或放在system/lib下的私有库

2.app使用的私有库最好起个专有的名字,不要和system/lib下的库重名

针对以上两点,第三方应用非system app和system app要分开来看

1).对于第三方应用,必须遵循第1点,保证能在所有android版本上运行,尤其是android N;

可以通过readelf -dl libxxx.so来查看链接了哪些库,如果有些库不是自己私有的又不在这个列表里就要小心了

system_libs := \
android \
c \
dl \
jnigraphics \
log \
m \
m_hard \
stdc++ \
z \
EGL \
GLESv1_CM \
GLESv2 \
GLESv3 \
vulkan \
OpenSLES \
OpenMAXAL \
mediandk \
atomic

可以不遵循第2点,因为第三方app,无论是androidruntime加载比如System.loadlibrary,还是动态链接比如a.so被runtime加载,它又链接了b.so,

这种情况下加载的第一优先目录永远是/data/app/com.xxx.appname/lib/arm(arm64);

所以即使你的库与system/lib下的库重名,只要在apk里都会被正确加载

2).对于system app,可以不遵循第1点,但必须遵循第2点

因为system app

(1)runtime比如System.loadlibrary加载的路径优先顺序是:

/system/priv-app/Music/lib/arm, /system/fake-libs,
/system/priv-app/Music/app-xxxhdpi-debug.apk!/lib/armeabi, /system/lib, /vendor/lib, /system/vendor/lib, /custom/lib

(2)但动态链接的时候,优先级发生了变化,system/lib是第一位置的,所以如果你的被动态链接的私有库和system/lib的库重名了,那你的库不能被动态加载

这个变化应该是在android6.0上开始的,之前应该和runtime加载库的行为一致

最后

当然最好是同时满足这两点,那你的apk被放在哪个android版本,放在哪个位置都是ok的。

另外可通过gradle中配置

splits {
      abi {
          enable true
          reset()
          include *//*'x86', 'x86_64', *//*'armeabi-v7a', 'arm64-v8a'
          universalApk false
      }
  }

生成各平台包

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 是的,一个APK文件可以包含多个应用。这种情况通常是通过将多个不同的功能分别打包到单独的模块中,然后在同一个APK文件中打包这些模块,实现多个应用的打包。这样做可以节约下载和安装的时间,并使用户可以一次安装多个应用。 ### 回答2: 在Android中,通常一个APK文件只能安装一个应用程序。这是因为APK文件包含了应用程序的所有资源和代码,包括主程序、库文件、资源文件等。然而,有一种特殊的情况可以在一个APK中安装多个应用程序,那就是使用“Split APKs”(分离的APK)技术。 使用Split APKs技术可以将一个APK文件分割成多个独立的APK文件,每个文件对应一个应用程序。这样,用户只需下载一个APK文件并安装,就能同时安装多个应用程序。 在实现这个功能的过程中,需要使用Android的动态模块加载技术。通过动态加载功能,可以在运行时加载和卸载分离的APK文件,并调用其中的应用程序。这些分离的APK文件可以通过远程服务器下载,也可以作为本地资源存储在设备上。 使用Split APKs技术的一个常见应用场景是游戏应用程序。在游戏中,可能会通过分离APK技术将游戏的主体和扩展内容(例如新关卡、道具等)分开存储,以便实现动态更新和灵活扩展的目的。 总之,通过Split APKs技术,可以在一个APK文件中安装多个应用程序。这种方式在某些特定的应用场景下非常有用,可以提供更好的用户体验和开发灵活性。 ### 回答3: 在Android系统中,一个APK文件通常只能安装一个应用程序。APK文件是Android应用程序的安装包,每个APK文件都会对应一个独立的应用程序。当用户在设备上点击APK文件进行安装时,系统会将APK文件解压并安装对应的应用程序,将应用程序的相关文件和数据存储在设备的特定目录中。 然而,有一些特殊情况下可以在一个APK文件中安装多个应用程序。例如,可以使用"多渠道打包"的技术来在一个APK文件中打包多个应用程序。多渠道打包是一种将多个APK合并为一个APK的技术,不同的渠道可以安装不同的应用程序。这种方式通常用于应对市场分段、产品定制或者企业内部分发等需求。 另外,有些应用程序可能提供插件或者扩展机制,允许在应用程序中安装额外的功能模块或者插件。这些插件或者扩展模块可以作为单独的APK文件进行安装,与主应用程序进行交互并提供额外的功能。但是这种方式与一个APK文件中安装多个独立的应用程序并不相同,插件或者扩展模块仍然需要依赖主应用程序的运行环境,无法独立运行。 总而言之,通常情况下,一个APK文件只能安装一个应用程序。如果需要在一个APK文件中安装多个应用程序,可以使用多渠道打包的技术。此外,还可以通过插件或者扩展机制来在应用程序中安装额外的功能模块或者插件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值