一次Android相册的开发日志记录 Day3

在开发阶段进入到Day2之后,工作开始变得繁忙了起来,加之生活上也有许多琐事要安排,导致相册开发进度陷入停滞。
但是与此同时,群晖的DSM7.0正式版发布了,跟随着新系统到来的,是Moments和DSPhoto两个相册软件废弃,随之而来的是整合了两者功能的SynologyPhoto,
于是我升级了并体验了一波,UI风格也与时俱进,同时整合了Moments和DSPhoto的优点,可以时间轴、文件夹、相册、多维度的管理照片,网络连接也稳定了非常多,这基本就是我想要实现的。但是还是有小遗憾的,文件夹视图模式下,仍然和DSPhoto一样,只有两列,在图片数量巨多的时候,查找就变成了一个很痛苦的事,这也是阻挠我使用这个作为相册系统的一个重要因素。

但是!等等!是不是只有这一点不满意?那么…我如果能修改一下APP,让他变成3列呢?

说干就干,试试看。

APK解包

工具和操作步骤如下:(版本会随着时间的推移而改变,这里只取当前用到的版本)

APKTool:用于解包和最后的封包

解包命令如下:

1
java -jar apktool_2.6.0.jar d -s <APK文件路径> -o <解包出的文件夹名称>

这里说一下-s的指令,apk中的classes.dex文件,是讲APP代码打包编译后封装在一个或多个文件中的。
不加入-s命令,则会讲dex也进行解包操作,解出来的是smali文件,这是一种Android Dalvik虚拟机使用的一种类似汇编的语言,后面要修改代码也是修改smali文件。
如果加入-s命令,则不会解包dex文件,如果不需要对代码进行修改则基本不需要解包dex

命令执行完毕之后,就可以看到一个近似Android工程的文件夹结构,这里面有熟悉的资源文件,AndroidManifest.xml等。

直接看smali文件来修改不现实,需要一个反编译工具来帮我们定位具体要修改代码的位置。

反编译dex2jar

使用的工具是dex2jar,这个工具可以将刚才看到的classes.dex文件反编译为jar文件,但是如果代码在编译前是经过混淆的,那么这里的反编译后的代码可读性也非常的差。反编译后的代码只是用来定位修改位置,并不可以再反向编回去。

反编命令如下:

1
d2j-dex2jar.bat classes.dex

执行完毕之后会出现一个classes-dex2jar.jar这样的文件,这个文件就是反编译之后的文件了。

查看反编译后的文件

jd-gui,是一个非常好用的反编译查看工具,双击打开程序,将jar拖入即可看到一个类似于IDE的界面,左边是文件结构,右侧是反编译后的代码。

具体代码就不展示了,这个APP的工程代码虽然繁杂但是目录结构十分清晰,涨知识了。由于文件夹页面是网格布局,并且代码就放在ui/folder文件夹下,很轻松就找到了,大致结构就是RecyclerView+GridLayoutManager的结构,项目自定义了一个GridLayoutManager但是可以看出来第三个参数是spanCount,找到计算spanCount的工具类Util,那么我们要修改的就是这个,让他返回一个3即可。

那么修改的操作流程就是,定位到Util的该计算方法,更改smali代码的返回值为3即可。由于我这里已经确认该函数只在这里使用了,所以可以直接修改,如果是其他修改要记得检查影响范围,避免把其他功能改崩。

dex与smali文件互转

baksmali-2.5.2.jar:将dex转换成smali文件

1
java -jar baksmali-2.5.2.jar disassemble <dex文件路径>

smali-2.5.2.jar:将smali文件打包成dex文件

1
java -jar smali-2.5.2.jar assemble <smali文件夹路径>

smali代码的修改

打开Utils.smali文件之后我们就可以找到这个计算Folder显示Colums数量的方法了,虽然说比较接近汇编的感觉,但是慢慢看还是可以理解每一步的操作。

1
2
3
4
5
6
7
# virtual methods
.method public final calculateFolderColumns(Landroid/content/Context;)I
//无关代码省略
invoke-direct {p0, p1, v0, v1}, Lcom/synology/projectkailash/util/Utils;->calculateItemColumns(III)I
move-result p1
return p1
.end method

move-result p1就是讲上方函数的计算结果赋值给p1并且返回,那么最简单的办法就是将这个p1的值,修改为3即可,不过从代码上看,这东西应该是可以适配平板的,但是不重要,我们还是改成3试试看。

具体的操作需要查阅smali是如何赋值的,操作代码如下:

1
const/4 p1, 0x3

将这句代码加在return p1之前即可。

APK封包

修改好之后,需要重新打包回APK,这里依旧使用ApkTool工具:

1
apktool.bat b <解包的文件夹位置>

运行之后,会在文件夹内部生成builddist文件夹,dist中就是apk文件了。

重签名

这时的APK如果不进行重签名是无法安装的,签名工具有很多,也可以使用AndroidStudio带的apksigner,也可以使用第三方工具签名。
不过这里提示一句,Android11之后,仅有V1签名且targetSdk为30+的APK是无法安装,需要至少V2的签名才可以。

1
apksigner sign --ks <.jks文件路径> --ks-key-alias <jks别名> --ks-pass pass:<密码> --v2-signing-enabled true -v --out <签名后文件的路径> <待签名文件路径>

Zipalign

zipalign 是一种 zip 归档文件对齐工具。它可确保归档中的所有未压缩文件相对于文件开头都是对齐的。
如果需要对齐,且使用apksigner签名,则需要在签名之前执行zipalign。
工具的位置同apksigner一样,是在sdktools下面。有些时候打包签名不成功,是因为没有进行对齐的操作,可以注意一下。

操作命令如下:

1
zipalign -p -f -v 4 infile.apk outfile.apk

安装及问题排查

安装时发现报错无法安装,一直认为是签名没打对,前前后后折腾了很多次。又或是认为dex打包存在问题无法正确安装。在不修改包的情况下重签名依旧无法安装。
由于系统的报错信息语焉不详,查询问题的时候突然意识到可以使用adb安装来试着看看能不能发现报错。
结果发现了一个一直都没意识到的问题。

1
2
adb install out.apk
adb: failed to install out.apk: Failure [INSTALL_FAILED_DUPLICATE_PERMISSION: Package xxx attempting to redeclare permission com.xxx already owned by com.xxx]

由于测试机中安装了DSPhoto和群晖管家套件,这两者也使用了同样的自定义权限,但是由于我重签名后,签名不一致,所以无法安装。这里由于这两个APP已经用不到了,所以我的解决办法是直接删除这两个APP,由于DSFile并没使用此自定义权限可以共存。
如果需要共存:
1.可以修改权限名称,但需要检查是否存在潜在问题。如果需要共存可以更改包名等。
2.将这两个APK也用自己的签名重签名。

不过这点也给了我一个提示:如果无法安装出现语焉不详的问题时,可以试试使用adb来看看是否能返回具体的错误信息。

移除冲突APP之后,安装成功!!功能正常。完美的实现了我需要的功能,暂时可以不用急着写相册APP了~

MT管理器

在研究相关反编译的过程中,发现了一个非常好用的工具,MT管理器。它可以直接在手机上执行上述的操作,修改dex,重签名等而且速度也非常的快。
如果只是小部分的修改,可以直接通过这个工具来进行APK的修改,非常推荐。

后记

修改不是如文章上面一次就成功的,修改崩溃了两次,最后才找到的真正修改位置,同时也要对Android常用组件比较了解才可以做到如此快的定位到位置。
当然这里面最主要的是代码命名和分包比较规范且没有混淆,不然修改的难度就大上天了。(赞美群晖)
这就是我的第一次反编译体验,非常有趣,并且学到了很多的新知识。虽然花费了接近一天的时间,但是暂时节省了自制相册APP的开发时间(咕)。
这里面每走一步,都遇到了各种各样的问题,命令错误,解包失败,smali语法不懂,签名失败,安装失败等等等等。
但是不断的查找、重试,击破每一个问题,最后成功完成的修改,想想也是一件很不可思议且非常有成就感的事情。

工具地址与参考:

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×