Android关于64位应用适配

752 阅读4分钟

背景

1. 应用市场要求

2019年8月1号,在Google Play上发布应用必须支持64位架构,紧接着 小米应用商店、OPPO应用商店、vivo应用商店、腾讯应用宝和百度手机助手等五大应用商店宣布,为更好提升App性能体验并降低功耗,五方将共同推进国内安卓生态对64位架构的升级支持。

2. 增加寻址空间,减少崩溃

cpu架构说明

目前手机cpu架构是armeabiarmeabi-v7aarm64-v8ax86x86_64,但是目前手机基本上都是arm架构,x86架构的手机基本上没有,基本上是平板,可以忽略。

armeabi是十年前的手机CPU架构,基本上没有了。

armeabi-v7a的CPU架构是32位。

Arm64-v8a的CPU架构是64位。

所以目前需要考虑的armeabi-v7aarm64-v8a这两款就可以了, 之前开发者在打包的时候,为了包大小的考虑,常常指定打包的时候,只指定armeabi-v7a架构的so包,这样包大小可以小很多,尤其是so库多的应用 在项目的gradle中配置ndk就可以,64位的手机会自适应32位的应用,这样可以完美的兼容市面上99.9%的手机,这样包大小也会小很多 但是这样64位处理器的性能就无法完美的发挥出来,那么应用64位有哪些好处呢?

由于一些软件功能越来越多,安装包的体积、运行时需要消耗的运行内存越来越大,32位应用的局限性越来越突出。而64位系统,可以在单个线程里使用超过4GB的运行内存,当处理一些大型软件、或者进行高像素图像、视频处理的时候,就更能够发挥手机硬件的优势。比如一些大型游戏、网络视频直播、高画质影音播放等等。而且64位系统相比32位系统,会带来至少20%以上效率的提升。

打入64位的so包必然导致包大小增大很多,导致用户下载体验差,目前Google paly支持上传32位和64位的两个apk,这样用户可以根据手机cpu架构动态的下载那个apk

国内市场暂不支持,但是这个是趋势,之后必然要上这个功能的,我们在开发的时候,暂时还需要支持'armeabi-v7a', 'arm64-v8a’两个架构,包大小也会增大很多

方案流程

1. 配置gradle中ndk,支持armeabi-v7aarm64-v8a架构

2. 使用py脚本反编译分析apk文件

得到armeabi-v7a目录下存在,而arm64-v8a目录下不存在的so文件名称
得到所有so文件的加载位置
得到未加载使用的so文件

3. 整理文档,完善so来源,对应版本,对应第三方库版本

4. 找到arm64-v8a目录so,升级第三方sdk

5. 打包,利用第一步py脚本验包

python脚本如下(python3)

使用方式

python3 py_tool.py ....apk

import os
import subprocess
def file_name(file_dir1,file_dir2):
    print("v7a have,v8a not have so:")
    print("v7a:"+file_dir2)
    print("v8a:"+file_dir1)
    result_list=[]
    for root1, dirs1, files1 in os.walk(file_dir1):
        for root2, dirs2, files2 in os.walk(file_dir2):          
            for index in range(len(files2)):
                flag = False
                for jndex in range(len(files1)):
                    if files2[index]==files1[jndex]:
                        flag=True
                
                if flag==False:
                    result_list.append(files2[index])
                    print(files2[index])
    print("\n")
    return result_list

def all_so_list(file_dir1):
    for root1, dirs1, files1 in os.walk(file_dir1):
        return files1
                
def jadxApk(out_dir,apk_path):
    #de_commands="jadx -d %s %s" % (out_dir,apk_path)
    de_commands="java -jar /Users/liwenjiang/Desktop/x64_1/apktool_2.4.1.jar d -f %s -o %s" % (apk_path,out_dir)
    subprocess.call(de_commands,shell=True)


def search_so(java_path,so_list):
    result_dict={}
    print("start search:"+java_path)
    #截取so名称
    so_name_list=[]
    if len(so_list)==0:
        print("so is null,search end")
        return
    for item in so_list:
        item=item.replace("lib","")
        item=item.replace(".so","")
        so_name_list.append(item)
    flielen=0
    for root,dirs,files in os.walk(java_path):
        #print(len(files))
        flielen+=len(files)
        for f in files:
            if not f.endswith(".smali"):
                continue
            #print(os.path.join(root,f))
            with open(os.path.join(root,f),"r") as foo:
                for line in foo.readlines():
                    for so_name in so_name_list:
                        if ("\""+so_name+"\"" in line) | (so_name+".so" in line) |  ("lib"+so_name in line):
                            result_dict["lib"+so_name+".so"]=os.path.join(root,f)
                foo.close()
    print("查找文件数量:"+str(flielen))
    return result_dict


import sys
apkPath=sys.argv[1]
if not apkPath.endswith(".apk"):
    print("file is not apk")
else:
    outPath=apkPath.replace(".apk","")
    jadxApk(outPath,apkPath)
    so_list=file_name(outPath+"/lib/arm64-v8a",outPath+"/lib/armeabi-v7a")
    #查找v8a中不存在的so使用位置
    allso_list=all_so_list(outPath+"/lib/armeabi-v7a")
    strreslut="apk下所有so:\nso文件数量:"+str(len(allso_list)) +"\n"
    listresult=search_so(outPath+"/",allso_list)
    strreslut=strreslut+"load so数量:"+str(len(listresult))+"\n"
    strreslut=strreslut+"所有so的使用路径:\n"

    print("\n未加载so如下:")
    for item in allso_list:
        flag=True
        for item1 in listresult:
            if item==item1:
                flag=False
        if flag:
            print(item)


    for key in listresult:
        strreslut=strreslut+key+":"+listresult[key]+"\n\n"
    fo = open(outPath+".txt", "w")
    fo.write(strreslut)
    fo.close()
    #print(search_so(outPath+"/smali/",so_list))