背景
1. 应用市场要求
2019年8月1号,在Google Play上发布应用必须支持64位架构,紧接着 小米应用商店、OPPO应用商店、vivo应用商店、腾讯应用宝和百度手机助手等五大应用商店宣布,为更好提升App性能体验并降低功耗,五方将共同推进国内安卓生态对64位架构的升级支持。
2. 增加寻址空间,减少崩溃
cpu架构说明
目前手机cpu架构是armeabi、armeabi-v7a、arm64-v8a、x86、x86_64,但是目前手机基本上都是arm架构,x86架构的手机基本上没有,基本上是平板,可以忽略。
armeabi是十年前的手机CPU架构,基本上没有了。
armeabi-v7a的CPU架构是32位。
Arm64-v8a的CPU架构是64位。
所以目前需要考虑的armeabi-v7a、arm64-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-v7a、arm64-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))