16 января 2016 г.

Не используйте aapt для удаления файлов из apk

    Очень короткая запись про то, что aapt не стоит использовать для модификации apk файлов, лучше 7z подтянуть или иное решение. А ещё лучше, конечно, свалить в Новую Зеландию или Таиланд на ПМЖ, чего уж тут. Открыт для ваших предложений =)
    У aapt есть ключ r, означающий remove. Не только r есть, конечно же, но я уже писал об этом. Если же вы сейчас решите удалить файл из apk, то рискуете побить zip архив из-за некорректной работы appt. Или наоборот, слишком правильной — это вопрос открытый. В общем:
aapt.exe r test.apk assets/test.file
    Операция выполняется успешно. Можно убедиться, что файл реально удалён. Однако теперь и сам aapt, и zipalign, и вообще архиваторы ругаются на повреждённый zip:
aapt.exe a test.apk assets/test.file
    Получаем:
ERROR: failed opening Zip archive 'test.apk'
    Для пробы:
zipalign.exe -f 4 test.apk test.apk1
    Возвращает:
Unable to open 'test.apk' as zip archive
    «Решение» этой проблемы — последовательное удаление сначала META-INF/*.RSA, затем META-INF/*.SF, затем всё остальное. Когда META-INF будет удалён полностью, то можно будет использовать aapt без проблем. Но т.к. aapt не умеет удалять файлы по маскам и, тем более, не умеет удалять папки, то это то ещё развлечение.
    Лично для себя я решил переписать свои скрипты, которыми автоматизировал работу с apk так, чтобы не использовать aapt с ключами r и a. Сначала хотел использовать 7z, но всё-таки взялся на ванильный Python. Т.к. в Python нет возможности удалить файл из архива и нет возможности перезаписать файл, то для меня это ещё одна возможность изучить язык. Но более быстрым решением будет, конечно, использование 7z.
 
    Кстати. Не забывайте, что в zip допускается держать файлы с одинаковым именем в одной папке. Так что понятие «перезаписать файл» не совсем применимо к zip архивам:

Python 3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:54:25) [MSC v.1900 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import zipfile
>>> import os
>>> os.chdir('D:')
>>> zf = zipfile.ZipFile('test.zip', mode='a')
>>> zf.filelist
[<ZipInfo filename='demo.py' compress_type=deflate external_attr=0x20 file_size=8039 compress_size=2009>]
>>> zf.filename
'test.zip'
>>> for i in zf.filelist:
    print(i)

    
<ZipInfo filename='demo.py' compress_type=deflate external_attr=0x20 file_size=8039 compress_size=2009>
>>> zf.write('demo.py')

Warning (from warnings module):
  File "__main__", line 1
UserWarning: Duplicate name: 'demo.py'
>>> zf.write('demo.py')
>>> zf.write('demo.py')
>>> zf.write('demo.py')
>>> for i in zf.filelist:
    print(i)

    
<ZipInfo filename='demo.py' compress_type=deflate external_attr=0x20 file_size=8039 compress_size=2009>
<ZipInfo filename='demo.py' filemode='-rw-rw-rw-' file_size=8039>
<ZipInfo filename='demo.py' filemode='-rw-rw-rw-' file_size=8039>
<ZipInfo filename='demo.py' filemode='-rw-rw-rw-' file_size=8039>
<ZipInfo filename='demo.py' filemode='-rw-rw-rw-' file_size=8039>
>>> 
    Хотя, конечно, предупреждение о дублировании и было, но файл реально записался. Как и все последующие. Ну и ещё я не закрыл файл и побил его, конечно. Мораль такова — используйте контекстный менеджер.

    P.S. Можно было бы сказать, что мораль звучит "не убий файл", но это было бы как-то грубо. Потому лучше пусть мораль про контекстный менеджер.