Главная » Хабрахабр » [Из песочницы] Уменьшение размера APK (в разумных пределах)

[Из песочницы] Уменьшение размера APK (в разумных пределах)

На Habr.com уже была подобная статья, доказывающая, что можно ужать APK файл с 1.5 МБ до 1757 байт и меньше. Цель данной статьи — уменьшить размер приложения до разумного предела, сохранив его функциональность и осветить некоторые тонкости и неявные моменты.

Начало

Создадим проект в Android Studio, выберем Empty Activity. Затем в файле styles.xml заменим Activity c ActionBar'ом

Theme.AppCompat.Light.DarkActionBar

на Activity без ActionBar'а

Theme.AppCompat.Light.NoActionBar

Итог:

image

В анализаторе APK видим следующее:

5 МБ, при том, что оно только выводит надпись «Hello World!». Итак, APK весит 1.

Этап первый (минификация)

В файле build.gradle пишем:


android { buildTypes { debug { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile
('proguard-android.txt'), 'proguard-rules.pro' } }
}

Синхронизируем Android Studio, чтобы изменения вступили в силу.

Пояснение:

minifyEnabled true

уберёт ненужный код в приложении

shrinkResources true

удалит из APK не используемые ресурсы.

Вес APK стал 960 КБ, без изменений в работе.

Этап второй (добавление функциональности)

Чтобы приложение имело смысл, добавим ему функциональность, например, кликер.

Код activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:textSize="20dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ImageButton android:id="@+id/imageButton" android:layout_width="90dp" android:layout_height="90dp" android:layout_marginBottom="32dp" android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:background="#000000FF" android:cropToPadding="false" android:scaleType="fitXY" android:visibility="visible" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:srcCompat="@mipmap/ic_launcher_round" /> </android.support.constraint.ConstraintLayout>

Код MainActivity.java

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView; public class MainActivity extends AppCompatActivity else { num = 31; text.setText("Нажмите, чтобы начать заново"); } } return false; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Settings = getSharedPreferences("settings", Context.MODE_PRIVATE); if (Settings.contains("left")) num = Settings.getInt("left", 0); button = findViewById(R.id.imageButton); button.setOnTouchListener(on); text = findViewById(R.id.number); if(num > 0) { text.setText(Integer.toString(num)); } else { num = 31; text.setText("Нажмите, чтобы начать заново"); } } @Override protected void onPause() { super.onPause(); SharedPreferences.Editor editor = Settings.edit(); editor.putInt("left", num); editor.apply(); }
}

Приложение приобрело следующий вид:

1 МБ, увеличение на 140 КБ. Размер приложения 1.

Этап третий (убираем android.support и AppCompat)

На данный момент анализатор APK показывает следующее:

Нажмите Alt + Enter, чтобы Android Studio импортировала библиотеки. Заменим public class MainActivity extends AppCompatActivity на public class MainActivity extends Activity в MainActivity.java.

Размер приложения не изменился, но…

Где же кнопка?

Но у неё просто нет картинки. На самом деле всё в порядке, она осталась кликабельной.

Идем в activity_main.xml, находим внизу строчку

app:srcCompat="@mipmap/ic_launcher_round"

и меняем её на

android:src="@mipmap/ic_launcher_round"

Теперь всё в порядке:

Но размер не изменился.

Идём опять в build.gradle и очищаем блок зависимостей:


dependencies {
}

И синхронизируем Android Studio… с ошибками.

1. Идём в файл res/values/styles.xml и заменяем всё его содержимое следующим кодом:


<resources> <style name="AppTheme" parent="android:Theme.DeviceDefault.NoActionBar"> </style>
</resources>

2. ConstraintLayout, который используется в Android Studio, зависит от android.support, который уже был удалён из блока dependencies, поэтому заменим ConstraintLayout на RelativeLayout, который не зависит от android.support.

Код activity_main.xml:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFFFFF"> <TextView android:id="@+id/number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="Hello World!" android:textSize="20dp" /> <ImageButton android:id="@+id/imageButton" android:layout_width="90dp" android:layout_height="90dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="32dp" android:background="#000000FF" android:scaleType="fitXY" android:src="@mipmap/ic_launcher_round" android:visibility="visible" /> </RelativeLayout>

Компилируем APK и смотрим итог:

5 раз меньше его начального размера. Наш APK весит 202 КБ, что в 7.

Большую часть занимают ресурсы, ими и займемся.

1. Удалим файлы в папке res/drawable и очистим папку res/mipmap
2. Нарисуем свою иконку и кнопку, затем уменьшим её размер с помощью ImageOptim.

Кнопка:

Иконка:

3. Загрузим их в Android Studio, для этого выделим их в папке, нажмём Ctrl + C, перейдём в Android Studio, выберем папку res/drawable и нажмём Ctrl + V, после чего Andoid Studio предложит несколько вариантов, куда именно перенести изображения в зависимости от их разрешения.

В папке drawable можно переименовать файл, выбрав Refactor -> Rename.
Иконка приложения имеет имя i.png, картинка для кнопки b.png

4. Теперь переходим в файл AndroidManifest.xml, строчки


android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"

заменим на


android:icon="@drawable/i"
android:roundIcon="@drawable/i"

В файле activity_main.xml заменим поле в ImageButton


android:src="@mipmap/ic_launcher_round"

на


android:src="@drawable/b"

Итог

Итоговый размер файла составляет 13.4 КБ, что в 112 раз меньше начального объёма!

Итоговый код MainActivity.java

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView; public class MainActivity extends Activity { SharedPreferences Settings; ImageButton button; TextView text; int num = 31; View.OnTouchListener on = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { num--; if(num > 0) { text.setText(Integer.toString(num)); } else { num = 31; text.setText("Нажмите, чтобы начать заново"); } } return false; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.m); Settings = getSharedPreferences("settings", Context.MODE_PRIVATE); if (Settings.contains("left")) num = Settings.getInt("left", 0); text = findViewById(R.id.number); button = findViewById(R.id.button); button.setOnTouchListener(on); if(num > 0) { text.setText(Integer.toString(num)); } else { num = 31; text.setText("Нажмите, чтобы начать"); } } @Override protected void onPause() { super.onPause(); SharedPreferences.Editor editor = Settings.edit(); editor.putInt("left", num); editor.apply(); }
}

Итоговый код activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFFFFF"> <TextView android:id="@+id/number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="Hello World!" android:textSize="20dp" /> <ImageButton android:id="@+id/button" android:layout_width="90dp" android:layout_height="90dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="32dp" android:background="#000000FF" android:scaleType="fitXY" android:src="@drawable/b" android:visibility="visible" /> </RelativeLayout>

На этом оканчивается разумное уменьшение APK файла, далее идёт инструкция по дальнейшему уменьшению приложения в ущерб удобства разработки.

Удаляем ресурсы

Удалим папку res/values, в файле AndroidManifest.xml заменим блок application на следующий код:

<application android:icon="@drawable/i" android:roundIcon="@drawable/i" android:label="Clicker" android:theme="@style/android:Theme.DeviceDefault.NoActionBar"> <activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /></intent-filter> </activity> </application>

Также заменим идентификаторы на однобуквенные, файл activity_main.xml переименуем в m.xml

Изменим обработку нажатия:

Удалим строку

button.setOnTouchListener(on);

в MainLayout.java.
Заменим функцию

View.OnTouchListener on = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { num--; if(num > 0) { text.setText(Integer.toString(num)); } else { num = 31; text.setText("Нажмите, чтобы начать заново"); } } return false; } };

на

public void o(View v) { num--; if(num > 0) { text.setText(Integer.toString(num)); } else { num = 31; text.setText("Нажмите, чтобы начать заново"); } }

В файле m.xml (бывшем activity_main) в структуре ImageButton добавим строчку

android:onClick="o"

Итоговый размер составил 10.2 КБ, в 147 раз меньше начального размера. Я считаю, что это хороший результат.

Код MainActivity.java

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView; public class MainActivity extends Activity { SharedPreferences Settings; ImageButton button; TextView text; int num = 31; public void o(View v) { num--; if(num > 0) { text.setText(Integer.toString(num)); } else { num = 31; text.setText("Нажмите, чтобы начать заново"); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.m); Settings = getSharedPreferences("settings", Context.MODE_PRIVATE); if (Settings.contains("left")) num = Settings.getInt("left", 0); text = findViewById(R.id.n); button = findViewById(R.id.b); if(num > 0) { text.setText(Integer.toString(num)); } else { num = 31; text.setText("Нажмите, чтобы начать"); } } @Override protected void onPause() { super.onPause(); SharedPreferences.Editor editor = Settings.edit(); editor.putInt("left", num); editor.apply(); }
}

Код m.xml


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFFFFF"> <TextView android:id="@+id/n" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:textColor="#000000" android:textSize="20dp" /> <ImageButton android:id="@+id/b" android:layout_width="90dp" android:layout_height="90dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="32dp" android:background="#000000FF" android:scaleType="fitXY" android:src="@drawable/b" android:onClick="o" android:visibility="visible" /> </RelativeLayout>


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Нейронные сети с нуля. Обзор курсов и статей на русском языке, бесплатно и без регистрации

На Хабре периодически появляются обзоры курсов по машинному обучению. Но такие статьи чаще добавляют в закладки, чем проходят сами курсы. Причины для этого разные: курсы на английском языке, требуют уверенного знания матана или специфичных фреймворков (либо наоборот не описаны начальные ...

Как не выстрелить себе в ногу из конечного автомата

Конечный автомат редко применяется мобильными разработчиками. Хотя большинство знает принципы работы и легко реализует его самостоятельно. В статье разберемся, какие задачи решаются конечным автоматом на примере iOS-приложений. Рассказ носит прикладной характер и посвящен практическим аспектам работы. Под катом вы найдете дополненную расшифровку выступления Александра Сычева ...