20.43M
Category: programmingprogramming

Технология разработки мобильных приложений. Android: тогда и сейчас

1.

ТЕХНОЛОГИЯ РАЗРАБОТКИ
МОБИЛЬНЫХ ПРИЛОЖЕНИЙ

2.

ANDROID: ТОГДА И СЕЙЧАС
2008 год
T-Mobile G1
2017 год
Google Pixel 2 XL

3.

РАЗРАБОТКА ПОД ANDROID
Java
2008 – Java 6
2013 – Java 7
2017 – Java 8 (некоторый функционал доступен только на
API 24)
Kotlin
до 2017 – поддержка с помощью плагинов
2017 – поддержка в Android Studio 3.0 из коробки

4.

ПРОЦЕНТ УСТРОЙСТВ С ANDROID РАЗЛИЧНЫХ ВЕРСИЙ
Version
Codename
API
Distribution
2.3.3 –
2.3.7
Ginderbread
10
0.5%
4.0.3 –
4.0.4
Ice Cream
Sandwich
15
0.5%
4.1x
Jelly Bean
16
2.2%
4.2x
17
3.1%
4.3
18
0.9%
4.4
KitKat
19
13.8%
5.0
Lollipop
21
6.4%
22
20.8%
5.1
6.0
Marshmallow
23
30.9%
7.0
Nougat
24
17.6%
25
3.0%
7.1

5.

ПРОЦЕНТ УСТРОЙСТВ С ANDROID РАЗЛИЧНЫХ ВЕРСИЙ
Процент устройств
Ginderbread
Ice Cream
Sandwitch
Jelly Bean
KitKat
Lollipop
Marshmallow
Nougat
Oreo

6.

ПОДДЕРЖКА СТАРЫХ ВЕРСИЙ ANDROID
Библиотеки поддержки
com.android.support:appcompat-v7:27.0.1
com.android.support:design:27.0.1
com.android.support:support-v13:27.0.1
и другие
Библиотеки добавляются в файл gradle уровня app

7.

ИСТОЧНИКИ ИНФОРМАЦИИ
Официальная документация
Гайды, обучение, дизайн, классы, best practices, обзор новых
инструментов
https://developer.android.com/develop/index.html
Stack Overflow
99,9% вопросов, которые у вас возникнут, уже были заданы и
отвечены тут
https://stackoverflow.com/questions/tagged/android
Блок разработчиков Android
Новые инструменты, best practices и немного историй из жизни

8.

ОСНОВНЫЕ КОМПОНЕНТЫ ANDROID ПРИЛОЖЕНИЯ
Application
Activity
Service
AndroidManifes
t.xml
BroadcastReceiver
ContentProvider

9.

APPLICATION

10.

ACTIVITY
public class MainActivity extends
AppCompatActivity {
@Override
protected void onCreate(Bundle
savedInstanceState) {
<activity android:name=“.MainActivity”>
<intent-filter>
<action
android:name=“android.intent.action.MAI
N”/>
super.onCreate(savedInstanceState); <category
android:name=“android.intent.category.L
setContentView(R.layout.activity_main); AUNCHER”/>
}
}
</intent-filter>
</activity>

11.

SERVICE
public class CustomService extends
IntentService {
public CustomService() {
super(“CustomService”);
}
@Override
protected void onHandleIntent(Intent
intent) {
//TODO: load file
}
}
<service
android:name=“.CustomService”
android:exported=“false”/>

12.

BROADCASTRECEIVER
public class CustomReceiver extends BroadcastReceiver {
@Override
public void onReceiver(Context context, Intent intent) {
//TODO: start service
}
}

13.

CONTEXT. ДОСТУП К РЕСУРСАМ
String appName =
context.getString(R.string.app_name);
int colorAccent =
ContextCompat.getColor(context, R.color.colorAccent);
try {
context.getAssets().open(“filename.txt”);
} catch (IOException e) {
e.printStackTrace();
}

14.

CONTEXT. ДОСТУП К СИСТЕМНЫМ ВОЗМОЖНОСТЯМ
УСТРОЙСТВА
AlarmManager am = (AlarmManager)
context.getSystemService(Context.
ALARM_SERVICE);
FingerprintManager fm = (FingerprintManager)
context.getSystemService(Context.
FINGERPRINT_SERVICE);

15.

CONTEXT. ДИНАМИЧЕСКОЕ ИЗМЕНЕНИЕ ИНТЕРФЕЙСА
TextView textView = new TextView(context);
textView.setText(“Тонких Артём Петрович”);

16.

CONTEXT. СОЗДАНИЕ ФАЙЛОВ
SharedPreferences preferences =
context.getSharedPreferences(“myAppPrefs”,
Context.MODE_PRIVATE);
preferences.edit()
.putString(“KING_OF_THE_NORTH”,
“Тонких Артём Петрович”)
.apply();
String kingOfTheNorth =
preferences.getString(“KING_OF_THE_NORTH”,
“open vacancy”);

17.

ИСТОЧНИКИ CONTEXT И ИХ РАЗЛИЧИЕ
getApplicationContext() != getContext();
ApiManager mApiManager =
ApiManager.getLocalManager
(getApplicationContext());
WeakReference<Context>weakContext =
new WeakReference<Context>(getContext());
context = weakContext.get();
if (context != null) {
// do something
}

18.

ACTIVITY
Главный компонент приложения, с помощью которой
пользователь взаимодействует с приложением
посредством UI – пользовательского интерфейса.

19.

ЖИЗНЕННЫЙ ЦИКЛ ACTIVITY (УПРОЩЕННАЯ ВЕРСИЯ)
Resumed
(visible)
onResume()
onPause()
onResume()
Paused
(partly visiable)
Started
(visible)
onStart()
onStop()
onStart()
Created
onCreate()
onRestart()
Stopped
(hidden)
onDestroy()
Destroyed

20.

ПОРЯДОК ВЫЗОВА МЕТОДОВ
ActivityЖИЗНЕННОГО
2
ИКЛА ПРИ ЗАПУСКЕ ДОЧЕРНЕЙ ACTIVITY
Activity 1
onCreate()
onStart()
onResume()
*Запуск второй активити*
onPause()
onStop()
onRestart()
onCreate()
onStart()
onResume()
*возврат назад*
onPause()

21.

УНИЧТОЖЕНИЕ АКТИВИТИ
Система считает за нормальное поведение:
нажатие кнопки «назад»
вызов метода finish()
Система сохраняет стейт при следующих случаях:
изменении конфигурации (поворот экрана, смена языка,
доступность клавиатуры)
уничтожение фоновой Activity при нехватке памяти для
рабочей Activity

22.

VIEW И VIEWGROUP
<ViewGroup>
//Иерархическая структура
разметки, XML правила
<View/>
<View/>
<ViewGroup>
<View/>
<View/>
</ViewGroup>
</ViewGroup>

23.

ЧАСТО ИСПОЛЬЗУЕМЫЕ VIEW
Элементы интерфейса
из Android SDK.
Большая их часть настраивается
и редактируется

24.

ADAPTERVIEW
Список однородных
элементов.
Данные поставляет одна
из реализации Adapter

25.

LinearLayout
Располагает свои элементы друг за другом вертикально
или горизонтально.
Направление определяется атрибутом orientation.
Распределяет свободное пространство по длине или
ширине через веса:
•Атрибут layout_weight – на дочерних View-элементах
•Атрибут weightSum – на LinearLayout-контейнере.
В качестве значения эти атрибуты принимают любые
числа.
Если значение weightSum не равно сумме layout_weight
дочерних элементов, то на разметке останется
неиспользованное пространство.

26.

LinearLayout
Корневой LinearLayout
(фиолетовый):
android:orientation=“vertical”
Вложенный LinearLayout
(малиновый):
android:orientation=“horizontal”
android:weightSum=“4”
Кнопки горизонтального LinearLayout:
android:layout_weight=“1” / ”2”
android:layout_width=“0dp”

27.

RelativeLayout
View элементы располагаются относительно контейнера и
друг друга.
Больше гибкости без необходимости верстать вложенные
контейнеры.
Большой выбор различных атрибутов для выстраивания
интерфейса.

28.

RelativeLayout
•layout_alignParentLeft(Right, Top, Bottom, Start, End) –
выравнивает элемент по указанному краю родителя
•layout_alignLeft(Right, Top, Bottom, Start, End) –
выравнивает сторону элемента по соответствующей
стороне указанного элемента
•layout_below(above) – устанавливает элемент под/над
указанным элементом
•layout_toLeftOf(Right, Start, End) - устанавливает элемент
с указанной стороны указанного элемента
•layout_centerHorizontal(Vertical) – устанавливает
элемент по центру контейнера
•и другие

29.

RelativeLayout
Button 1 – выровнена по верхнему левому
краю RelativeLayout.
Button 2 – находится под и справа от
Button 1.
Button 3 – выровнена по центру
RelativeLayout.
Button 4- находится справа от Button 3 и
нижняя граница соответствует нижней
границе Button 3.
Button 5 – левый край соответствует
левому краю Button 3, правый край
соответствует правому краю Button 4,

30.

FrameLayout
Используется как контейнер для программно добавляемых
View.
Используется, когда нужно установить одну View над
другой. Порядок отрисовки View соответствует их порядку
в файле разметки.

31.

ConstraintLayout
Новый ViewGroup.
Идейный наследник RelativeLayout, но превосходит его по
возможностям.
Для разметки используются констрейнты – правила.
Следует использовать только тогда, когда необходимо.
Начать использовать легко – добиться мастерства гораздо
сложнее.

32.

GridLayout и TableLayout
Используются для формирования разметки в виде
таблицы. Не очень удобны.
TableLayout – каждый элемент находится в отельной
строке.
Для верстки элементов в строке используется TableRow.
GridLayout – каждый элемент имеет атрибуты layout_row и
layout_column для определения местоположения в
таблице.

33.

ImageView
ImageView – основной элемент для показа изображений.
В XML
android:src=“@drawable/my_image”
В Java
image.setImageResource(R.drawable.my_image);

34.

ScaleType
Для ImageView можно задать правило для
масштабирования в разметке через атрибут scaleType или
программно через метод setScaleType()
Варианты значений:

35.

EditText
Стандартное поле ввода
Полезные атрибуты:
•hint – подсказка
•maxLength –
ограничение по количеству символов
•inputType – выбор вводимых
данных (текст, число, номер, почта,
пароль и др.)
•imeOptions – кастомизация кнопки
ввода (done, send, next,
search и другие)

36.

Что такое Gradle?
Gradle – система автоматической сборки, построенная на
принципах Apache Ant и Apache Maven, но
предоставляющая DSL на языке Groovy вместо
традиционной XML-образной формы представления
конфигурации проекта.

37.

Dependencies
dependencies {
})
implementation fileTree(dir: ‘libs’,
include: [‘*.jar’])
androidTestImplementation(
implementation
‘com.android.support:appcompatv7:26.1.0’
implementation ‘com.android.
‘com.android.support.
support.constraint:
test.espresso:espresso-core:2.2.2’, {
constraint-layout:1.0.2’
exclude group:
‘com.android.support’,
annotations’
module: ‘support-
testImplementation ‘org.jetbrains:
annotations:15.0’
}

38.

Product Flavors
flavorDimensions ‘buildType’
yourapi.com/free/”’
productFlavors {
}
free {
pro {
buildConfigField ‘String’,
‘YOUR_PARAM_NAME’,
‘”YOUR STRING VALUE”’
versionName “0.0.1-pro”
buildConfigField ‘String’,
API_BASE_URL’,
yourapi.com/pro/”’
versionName “0.0.1-free”
buildConfigField ‘String’,
‘API_BASE_URL’,
}
}
‘”https://

39.

BuildTypes
buildTypes {
yourapi.com/beta/"‘
debug {
}
buildConfigField 'String',
'API_BASE_URL',
'"https://
yourapi.com/debug/"‘
}
release {
buildConfigField 'String',
'API_BASE_URL',
release/"‘
beta {
}
buildConfigField 'String',
'API_BASE_URL',
'"https://
}
'"https://yourapi.com/

40.

Build Variant

41.

BuildConfigField
defaultConfig {
applicationId “com.e_legion.coursera”
minSdkVersion 16
targetSdkVersion 26
versionCode 1
versionName “1.0”
testInstrumentationRunner
“android.support.test.runner.AndroidJUnitRunner”
buildConfigField 'String', 'YOUR_NAME',
'"DEFAULT STRING"'

42.

Toast
Toast (также тост) – это всплывающее сообщение.
Легко реализуется.
Может использоваться как в целях дебага, так и для
уведомления пользователя.

43.

Создание обычного Toast
Toast.makeText(
<– вызов статического метода для создания
MainActivity.this,
<– первый параметр – контекст
“Connection lost”, <– второй параметр – текст (или
R.string.###ресурс)
Toast.LENGTH_LONG)
<– третий параметр – продолжительность
.show();
<– вызов метода для показа
LENGTH_SHORT
<– 2 секунды
LENGTH_LONG
<– 3.5 секунды

44.

Порядок создания кастомного Toast
Чтобы создать кастомный Toast, нужно:
1. Создать желаемую XML верстку
2. Объявить и инициализировать переменную класса
Toast
3. Преобразовать верстку во View с помощью
LayoutInflater
4. Задать желаемые значения для элементов View
5. Применить метод toast.setView(View v).
Дополнительно можно задать длительность и gravity.

45.

Menu. Добавление ресурса

46.

Добавление меню
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu,menu);
return true;
}

47.

Обработка нажатий
@Override
public boolean onOptionsItemSelected
(MenuItem item) {
switch (item.getItemId()) {
“Search clicked”,
Toast.LENGTH_SHORT).show();
return true;
case R.id.settings:
“Settings clicked”,
Toast.makeText(this,
default: return
Toast.makeText(this,
super.onOptionsItemSelected(item);
Toast.LENGTH_SHORT).show();
return true;
case R.id.search:
}
}

48.

Редактирование в runtime
@Override
public boolean onPrepareOptionsMenu (Menu menu) {
if (!isUserAuthorized()) {
menu.removeItem(R.id.logout);
return true;
}
return super.onPrepareOptionsMenu(menu);
}

49.

Обработка нажатия на контекстное меню
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ITEM_ID :
Toast.makeText(this, “Context menu clicked”,
Toast.LENGTH_SHORT).show();
return true;
default:
return super.onContextItemSelected(item);
}
}

50.

Знакомство с Fragment
<?xml version=“1.0” encoding=“utf-8”?/
<FrameLayout xmlns:android=
“http://schemas.android.com/apk/res/android” xmlns:tools=
“http://schemas.android.com/tools”
android:id=“@+id/fr_container”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:fitsSystemWindows=“true”/>

51.

Варианты использования Fragment
<fragment
android:id=“@+id/fragment_example”
android:name=“com.e_legion.coursera.ExampleFragment”
android:layout_width=“match_parent”
android:layout_height=“match_parent”

52.

Основные классы при работе с Fragment
•Fragment
•FragmentManager
•FragmentTransaction

53.

Методы FragmentTransaction
FragmentTransaction transaction =
getFragmentManager().beginTransaction();
transaction.add(R.id.fr_container, fragment);
transaction.replace(R.id.fr_container, fragment);
transaction.remove(fragment);
transaction.hide(fragment);
transaction.show(fragment);
transaction.detach(fragment);
transaction.attach(fragment);
transaction.commit();

54.

Backstack
transaction.addToBackStack(fragment.getClass().getSimpleName())
;
getFragmentManager().popBackStack();
@Override
public void onBackPressed() {
if (getFragmentManager().getBackStack EntryCount()>0) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}

55.

Жизненный цикл фрагмента
Created
onAttach()
onCreate()
onCreateView()
onActivityCreated()
Started
onStart()
Resumed
onResume()

56.

Жизненный цикл фрагмента
Paused
onPause()
Stopped
onStop()
Destroyed
onDestroyView()
onDestroy()
onDetach()

57.

JSON
JSON – это текстовый формат данных,
легко читаемый человеком и используемый
для сериализации объектов и обмена
данными.

58.

Создание POJO на основе json
http://www.jsonschema2pojo.org
Плагины для Android Studio, поиск по слову “pojo” или
“json”

59.

GSON
Подключение в файле gradle уровня app
dependencies {
implementation
‘com.google.code.gson:gson:2.8.2’
}
Аннотации GSON
@SerializedName (“name”)
@Espose
Мы оставляем поведение по умолчанию, сериализуя и
десериализуя все поля. Но есть возможность выбрать поля
которые будут только сериализованы или только

60.

Объект класса Human
Human father = new Human();
father.setAge(38);
father.setName(“Тонких Артём Петрович”);
Human kid = new Human();
kid.setName(“Тонких Артём Артёмович”);
kid.setAge(4);
kid.setKids(null);
List<Human> kids = new ArrayList<>();
kids.add(kid);
father.setKids(kids);

61.

Сериализация
String json = new Gson().tojson(human);
System.out.print(json);
{“name”:”Тонких Артём Петрович”, “age”:42,
“kids”:[{“name”: “Тонких Артём Артёмович”, “age”:8}]}

62.

Генерация метода toString()
@Override
public String toString() {
return “Human{“ +
“name=” + name + ‘\’ +
“,age=” + age +
“,kids=” + kids +
‘}’;
}

63.

Десериализация
Human fromJson = new Gson().fromJson(json,
Human.class);
System.out.println(fromJson.toString());
toString() – Human{name=‘Тонких Артём Петрович’,
age=42, kids=[Human{name=‘Тонких Артём Артёмович’,
age=8, kids=null}]}
json – {“name”:”Тонких Артём Петрович”, “age”:42, “kids”:
[{“name”:”Тонких Артём Артёмович”, “age”:8}]}

64.

Многопоточность. Процессы и потоки
Процесс – глобальная сущность,
выделенные ресурсы.
Поток – внутренняя сущность процесса,
выполняет программный код.

65.

Что с Android?
Пользователь
нажал на кнопку
Запрос
передается
в фоновый
поток
Запуск
фонового
потока
Показ
результата
Загрузка
Сетевой
запрос
BG
TH read
Результат
передается
в главный
поток
Завершение
фонового
потока
UI
TH read

66.

Thread и Runnable
public class MyThread extends Thread{
}
@Override
});
public void run() {
new MyThread().start();
//do something
}
}
new Thread (new Runnable() {
@Override
public void run() {
//do something

67.

synchronized
public synchronized void doSomething() { }
//метод синхронизирован на
уровне объекта
private final static Object lock = new
Object();
}
public void doSomethingStaticBlock() {//…
public void doSomethingBlock {
synchronized (lock) {//блок
синхронизирован на уровне класса с
помощью статистического объекта
lock}
//…
synchronized (this) {
//блок синхронизирован на уровне
объекта}
//…
}

68.

wait/notify/notifyAll
Thread 2
Thread 1
1
3
synchronized
2
if (condition()){
wait()
}
4
5
7
notify()
6

69.

ExecutorService/Callable/Future
ExecutorService executorService =
Executors.newFixedThreadPool(4);
Future<String> future = executorService.submit(new
Callable<String>() {
@Override
public String call() throws Exception {
return result; }
});
while (!future.isDone()) {
TimeUnit.MILLISECONDS.sleep(100); }
String result = future.get();

70.

java.util.concurrent
Блокирующая очередь
Thread 1
Thread 2
BlockingQueue
Put
Take

71.

Знакомство с Service
Service – это компонент приложения, который
используется для выполнения долгих операций в
бэкграунде, без взаимодействия с пользовательским
интерфейсом.
<service
android:name=“.CustomService”
android:enabled=“true”
android:exported=“true” />

72.

Виды Service
Foreground – заметно для пользователя
Background – незаметно для пользователя
Bound – взаимодействие через интерфейс

73.

Создание Service
public class MyService extends Service {
public CustomService() {
}
@Override
@Nullable
public Ibinder onBind(Intent intent) {
return null;
}
}

74.

Создание Service
public class AnotherService extends IntentService {
public AnotherService(String name) {
super (name);
}
@Override
protected void onHandleIntent (Intent intent) {
//do something with data
}
}

75.

Жизненный цикл Service
Call to
startService()
Call to
bindService()
onCreate()
onCreate()
onStartCommand()
onBind()
Service
running
Clients are
bound to
service
The service is
stopped by itself or a
client
Active
LifeTime
The service is
stopped by itself or
a client
onUnBind()

76.

Что такое BroadcastReceiver?
BroadcastReceiver – это приемник сообщений,
посылаемых системой, либо другими
приложениями при каком-либо событии,
реагирующий на них и выполняющий
какую-либо работу, отдельно от обычного
процесса взаимодействия с приложением.

77.

Создание BroadcastReceiver
•Добавляем новый класс, отнаследованный от
BroadcastReceiver
•Переопределяем метод onReceive()
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//TODO: do something
}
}

78.

Регистрация в манифесте
<receiver
android:name=“.MyReceiver”
android:enabled=“true”
android:exported=“true”>
<intent-filter>
<action android:name=
“android.intent.action.HEADSET_PLUG”/>
</intent-filter>
</receiver>

79.

Регистрация в коде
MyReceiver myReceiver = new MyReceiver();
IntentFilter filter = new IntentFilter(Intent.
ACTION_HEADSET_PLUG);
registerReceiver (myReceiver, filter);

80.

Отправка сообщений
Мы можем использовать BroadcastReceiver, чтобы
передавать свои собственные сообщения.
Intent intent = new Intent();
intent.setAction
(“com.e_legion.coursera.SOMETHING_JUST_HAPPEN”);
intent.putExtra (“ARG_DATA”, “SOME_VALUE”);
sendBroadcast(intent);

81.

Дополнительные возможности
• sendOrderedBroadcast()
приоритет задается в IntentFilter
• LocalBroadcastManager.sendBroadcast()
LocalBroadcastManager.registerReceiver()
сообщения будут видны только в
пределах приложения

82.

Интерфейс AsyncTask
mSampleTask.execute(10L)
mSampleTask.cancel(true)
class SampleTask extends AsyncTask<Long, Integer, String> {
void onPreExecute();
String doInBackground(Long…longs);
void onPostExecute(String s);
void publishProgress(1,2,3,4)
void onProgressUpdate(Integer… values);
boolean isCanceled();
void onCanceled();
}

83.

Загружаем картинку с помощью AsyncTask

84.

DownloadImageTask
private class DomnloadImageTask extends AsyncTask<String, Void, Bitmap> {
@Override
protected void onPreExecute() {
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
protected Bitmap doInBackground(String… strings) {
return getBitmap(strings[0]);
}
@Override

85.

Код getBitmap()
private Bitmap getBitmap(String url) {
try {
InputStream is = (InputStream) new
URL(url).getContent();
Bitmap d = BitmapFactory.decodeStream(is);
is.close();
return d;
} catch (Execution e) {
return null;
}
}

86.

В Activity.onCreate()
mProgressBar = findViewById(R.id.progressBar);
mImageView = findViewById(R.id.image);
Button startBtn = (Button)
findViewById(R.id.btn_start);
startBtn.setOnClickListener (new View.OnClickListener() {
@Override
public void onClick(View v) {
String url =
“http://i0.kym-cdn.com/entries/icons/
mobile/000/013/564/doge.jpg”;
new DownloadImageTask().execute(url);

87.

Утечка Context
@Override
protected void onPostExecute(Bitmap bitmap) {
mImageView.setImageBitmap(bitmap);
//ссылка на поле активити
mProgressBar.setVisibility(View.INVISIBLE);
//тоже
}

88.

B Activity
new DownloadImageTask(MainActivity.this).execute(url);
/// –
ImageView getImageView() {
return findViewById(R.id.image);
}
ProgressBar getProgressBar() {
return findViewById(R.id.progressBar);
}

89.

Плюсы и минусы AsyncTask
Плюсы
•Прост для восприятия
•Гибко настраивается
•Хороший вариант для недолгих задач
Минусы:
•Возможность утечки контекста усложняет использование
•Определенно не подходит для долгих задач
•Создание и запуск только в UI-потоке

90.

HaMeR. О процессах и потоках
Каждое приложение в Android по умолчанию работает в
своем собственном процессе, в своей области памяти. В
память чужого процесса попасть нельзя, это
гарантируется системой.
Первый поток, запущенный в процессе, называется
главным потоком.
Главный поток – main thread – UI thread – в контексте
Android синонимы.

91.

MainThread не уничтожается сам
Почему MainThread не умирает?
•Благодаря Looper.
Что делает Looper?
•Looper в бесконечном цикле проверяет MessageQueue.
Что такое MessageQueue?
•Очередь объектов Message, которые нужно обработать.
Как сообщения попадают в MessageQueue?
•Handler, связанный с Looper, отправляет сообщения в
очередь Looper.
Кто обрабатывает сообщения?
•В сообщении есть ссылка на Handler, который это
сообщение должен обработать

92.

HaMeR = Handler + Message + Runnable
Бесконечно проверяет
очередь сообщений
Looper
0
Message
1 2 …

n
Target:
to Handler 2
Message
Handler 2
Handler 1
Обрабатывает
сообщение
Постит сообщение
в очередь

93.

Handler
Handler handler = new Handler();
Handler bgHandler = new Handler(bgLooper);
Handler mainHandler = new Handler(Looper.getMainLooper());
handler.post (new Runnable() {
@Override
public void run() {
}
});
handler.postDelayed (new Runnable(), 4000);

94.

HandlerThread
HandlerThread – это поток, в котором есть Looper и
MessageQueue.
MyHandlerThread extends HandlerThread
mMyHandlerThread.start();
mMyHandlerThread.getLooper();
@Override
protected void onLooperPrepared() {
mMainHandler = new
Handler(Looper.getMainLooper());
}

95.

Loader
Loader – это API, позволяющее загружать данные из
различных источников (сервер, БД, content-provider) вне
зависимости от жизненного цикла.
LoaderManager- класс, который запускает Loader. Для
получения экземпляра менеджера – LoaderManager
getSupportLoaderManager()
LoaderManager.LoaderCallbacks – содержит методы,
которые вызываются при определенных событиях у
лоадера

96.

LoaderManager
LoaderManager- класс, который запускает Loader. Для
получения экземпляра менеджера – LoaderManager
getSupportLoaderManager()
Loader<D> initLoader(int id, Bundle args,
LoaderManager.LoaderLoaderCallbacks<D>callback)
Loader<D> restartLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<D> callback)
boolean hasRunningLoaders();
Loader<D> getLoader(int id);
void destroyLoader(int id);

97.

LoaderCallbacks
LoaderManager.LoaderCallbacks – содержит методы,
которые вызываются при определенных событиях у
лоадера
onCreateLoader(int, Bundle)
onLoadFinished(Loader<D>, D)
onLoaderReset(Loader<D>)

98.

Loader
Loader – базовый класс. Наследуемся от него, если нужно
сделать какую-то уникальную реализацию. Иначе, можно
использовать системные реализации.
AsyncTaskLoader – лоадер, который инкапсулирует
AsyncTask.
CursorLoader – наследуется от AsyncTaskLoader,
инкапсулирует Cursor.

99.

ContentProviders
ContentProvider – механизм, который
инкапсулирует доступ к данным,
дополнительно позволяя обращаться к ним
из разных процессов.
ContentProvider позволяет безопасно
делиться данными с другими
приложениями

100.

Экраны со списками

101.

Системный ContentProvider
Телефонная книга
•Чаты
•Банки
Мультимедиа
•Чаты
•Плееры
Календарь
•Органайзеры
•Почта

102.

Зачем нужна архитектура? Что такое бизнес-логика?
Бизнес-логика (бизнес-правила) – это то, что делает
приложение.
Описание бизнес-логики не требует значительных
технических знаний.
Бизнес-правила одного и того же приложения на
различных платформах должны быть максимально
идентичны.

103.

Смешивание отображения и логики
@Override
lers.mainThread())
public void onCreate
(@Nullable Bundle
savedInstanceState) {
.subscribe(
response -> {
mErrorView.setVisibility(Vie
w.VISIBLE);
super.onCreate(savedInsta mErrorView.setVisible(View. mRecyclerView.setVisibility(
View.GONE);
nceState);
GONE);
});
setContentView(R.layout.act
ivity_main);
mRecyclerView.setVisibility( }
mRecyclerView.setAdapter( View.VISIBLE);
mProjectsAdapter);
ApiUtils.getApiService().get mProjectsAdapter.addData(
Projects(BuildConfig.API_Q response.getProjects(),
UERY)
true);

104.

MVC
Тонких Артём Петрович в 1978-79 гг.
― Model – бизнес-логика
―View – отображение
―Controller – обработка ввода
View!=android.view.View

105.

Зачем нужна архитектура?
•Лучшее понимание и больший
контроль над приложением
•Легче ввод новых программистов
на проект
•Проще тестировать бизнес-логику
•Возможность переиспользования
компонентов в других модулях

106.

SOLID – это…
S – Принцип единой ответственности (SPR)
O – Принцип открытости/закрытости (OCP)
L – Принцип заменяемости (LCP)
I – Принцип разделения интерфейсов (ISP)
D – Принцип инверсии зависимостей (DIP)

107.

Single Responsibility Principle. Принцип единой
ответственности
A class should have only one reason to change.
Каждый объект должен иметь одну
ответственность и эта ответственность
должна быть полностью инкапсулирована
в класс.

108.

Выносим лишние методы в другие классы
public class Employee{
private long employeeId;
private String name;
private Date dateOfJoining;
public boolean isPromotionDueThisYear(){ }
public Double calcIncomeTaxForCurrentYear(){ }
public class FinITCalculations {
public Double calcIncomeTaxForCurrentYear(Employee emp){ }
}
public class HRPromotions {
public boolean isPromotionDueThisYear(Employee emp){ }

109.

The Open Closed Principle. Принцип
открытости/закрытости
Software entities (classes, modules,
functions, etc) should be open for extension,
but closed for modification.
Программные сущности(классы, модули,
функции и т.д.) должны быть открыты для
расширения,
но закрыты для изменения

110.

The Substitution Principle. Принцип подстановки Тонких Артёма
Петровича
Functions that use pointers to base classes
must be able to use objects of derived classes
without knowing it.
Функции, которые используют базовый
тип, должны иметь возможность
использовать подтипы базового типа, не
зная об этом.

111.

Нарушение принципа подстановки Тонких Артёма
Петровича
public class Parent{
//x.value теперь равно 0
protected int value;
if (x.value > 0) {…}
private String text;
//код не выполнится
void setText(String text){
public class Child extends
Parent{
@Override
void setText(String text){
super.setText(text);
this.text = text;
}
}
Parent x = new Child();
x.value = 5;
x.setText (“Name”);
value = 0;
}
}

112.

The Interface Segregation Principle. Принцип разделения
интерфейса
Clients should not be forced to depend
on methods that they do not use.
Клиенты не должны зависеть от методов,
которые они не используют.

113.

Неправильно
public interface OnAllClickListener {
void onSubmitClick();
void onCancelClick();
void onDenyClick();
void onRetryClick();
}

114.

Правильно
public interface OnSubmitClickListener {
void onSubmitClick();
}
public interface OnCancelClickListener {
void onCancelClick();
}
public interface OnDenyClickListener {
void onDenyClick();
}
public interface OnRetryClickListener {
void onRetryClick();

115.

The Dependency Inversion Principle. Принцип инверсии
зависимостей
A. High-level modules should not depend on low-level
modules. Both should depend on abstractions.
B. Abstractions should not depend on details. Details should
depend on abstractions.
А. Модули верхних уровней не должны зависеть от
модулей нижних уровней. Оба типа модулей должны
зависеть от абстракции.
В. Абстракции не должны зависеть от деталей. Детали
должны зависеть от абстракции.

116.

Reporter
public class Reporter
{
public void sendReports()
{
ReportBuilder reportBuilder = new
ReportBuilder();
List<Report> reports =
reportBuilder.createReports();
if (reports.size() == 0)
throw new NoReportsException();
EmailReportSender reportSender = new
EmailReportSender();

117.

Дополнительные принципы
DRY
Don’t repeat yourself –
Не повторяйся.
KISS
Keep it simple, stupid –
Не усложняй, дурачок.
YAGNI
You aren’t gonna need it –
Тебе это не понадобится

118.

Что нам дает соблюдение принципов?
Соблюдая принципы, мы получаем классы, которые
можем с легкость
•понять, что он делает и за что отвечает
•переиспользовать в другом модуле или проекте
•протестировать с помощью unit тестов
•изменить, не ломая другие классы

119.

MVP
Model – данные и методы их получения,
сохранения, обработки
View – визуальное представление
данных, экран
Presenter – получение данных,
управление отображением

120.

Схема взаимодействия
Действие пользователя
События
GUI
View
-presentor
Обновление вида
Событие изменения модели
Presenter
-model
-IView
Model
Изменение модели

121.

View – SampleView
public interface SampleView {
void setData(List<Item>items);
}

122.

Presenter – SamplePresenter
public class SamplePresenter {
private SampleView mView;
public SamplePresenter(SampleView view) {
mView = view;
}
void loadData() {
//load data from server
mView.setData(items);
}
}

123.

Плюсы и минусы MVP
Плюсы:
•возможность модульного тестирования
•упрощение написания и понимания кода
Минусы:
•увеличение кодовой базы

124.

Что нам даёт Moxy?
Presenter
View
View State
View
Commands
Model

125.

Как использовать Moxy
public class ProjectsFragment
extends MvpAppCompatFragment
implements ProjectsView {
@InjectPresenter
public ProjectsPresenter mPresenter;
@ProvidePresenter
ProjectsPresenter providePresenter() {
D comeDependency = new D;
return new
ProjectsPresenter(someDependency);
}

126.

MvpView
public interface ProjectsView extends BaseView, MvpView {
@StateStrategyType(value = SingleStateStrategy.class)
void showProjects(@NonNull List<Project> projects);
@StateStrategyType(value = SkipStrategy.class)
void openProfileFragment(@NonNull String username);
}

127.

Стандартные стратегии
AddToEndStrategy – выполнить команду
и добавить команду в конец очереди
AddToEndSingleStrategy – выполнить
команду, добавить ее в конец очереди
и удалить все ее предыдущие экземпляры
SingleStateStrategy – выполнить команду,
очистить очередь и добавить в нее команду
SkipStrategy – выполнить команду
OneExecuteStrategy – выполнить команду
при первой возможности

128.

MVVM
Model – данные и методы их получения,
сохранения, обработки
View – визуальное представление
данных, экран
ViewModel – абстракция представления,
прослойка между View и Model

129.

ViewModel
public class ViewModel {
private String mUserName;
void updateUserName() {
mUserName = “John Smith”;
}
}

130.

Схема взаимодействия
вызывает
вызывает
View
Model
ViewModel
связывание
databinding
уведомляет

131.

Двухстороннее связывание
View
ViewModel
новый символ
в поле
новое назначение
поля
валидация
показ
состояния
ошибки
изменение
состояния
ошибки

132.

Плюсы и минусы MVVM
Плюсы:
•компоненты слабо связаны
•Databinding уменьшает количество кода
•Несколько View → одна ViewModel
Минусы:
•Показ Toast и диалогов
•Показ анимаций или данных с задержкой
•Необходимость обработки команды во View

133.

Итоги MVVM
Когда использовать MVVM?
•Экран только показывает данные
•Экран с простой логикой
Когда следует воздержаться?
•Экраны со сложной логикой или сложно определяемым
состоянием
•Экраны с множеством диалогов и
сложными/прерываемыми анимациями

134.

Обзор Android Architecture Components
Activity/Fragment
ViewModel
LiveData3
Repository
Model
Room
SQLite
Remote Data Source
Retrofit
webservice

135.

Основные тезисы
•Нельзя хранить контент или состояние
в компонентах и компоненты не должны
зависеть друг от друга
•Интерфейс и контент приложения
должны заполняться, опираясь
на модель. Модель не должна зависеть
от компонентов андроид

136.

LifeCycle
STATES
INITIALIZED
(initial start)
DESTROYED
(dead state)
RESUMED
STARTED
CREATED
ON_CREATE
ON_START
EVENTS
ON_RESUME
ON_PAUSE
ON_STOP
ON_DESTROY
INITIALIZED
(initial start)
STATES
DESTROYED
(dead state)
CREATED
STARTED
RESUMED

137.

Dependency Inversion Principle
•Модули верхних уровней не должны
зависеть от модулей нижних уровней.
Оба типа модулей должны зависеть от абстракций.
•Абстракции не должны зависеть
от деталей. Детали должны зависеть
от абстракций.
Классы должны зависеть от абстракций

138.

Inversion of Control
Фреймворк
Custom module
2
3
библиотеки
Приложение В
custom vodule
1
Приложение А
1
2
3
Инверсия управления – это принцип проектирования, в
котором части программы, написанные вручную
разработчиком, получают поток управления из какого-

139.

Цели IoC
Отделение вызова от релизации задачи
Концентрация на самой задаче, а не на
обработке вызова
Отсутствие допущений о том, как система
работает внутри, использование вместо
этого общепринятых контрактов
Предотвращение сайд-эффектов от
замены модуля

140.

Реализация IoC
―Dependency Injection
―Service Locator
―Контекстуальный список
―Паттерн «Абстрактная фабрика»
―Паттерн «Стратегия»

141.

Dependency Injection
ID-контейнер – класс или библиотека,
которая занимается только тем, что готовит
зависимости для других классов.
Внедрение зависимости – это техника
программирования, в которой один объект
(контейнер) снабжает зависимостями
(сервисами) другие объекты (клиентов).

142.

Какие вопросы решает DI?
Как сделать работу класса независимой
от того, как были созданы его зависимости?
Как перенести сборку зависимостей
в отдельные конфигурационные файлы?
Как сделать несколько различных
конфигураций для приложения?
Как сделать всю работу приложения
независимой от того, как были созданы
его компоненты?

143.

Типы внедряемых зависимостей
Constructor Injection – внедрение через
конструктор обязательной зависимости
Property (Setter) Injection –
переопределение через сеттер
необязательной зависимости
Method Injection – передача в метод
зависимости, которая пользуется
только в этом методе

144.

Внедрение тестовых зависимостей
Dependency
Unit Test
Unit
Mock
Dependency

145.

Плюсы и минусы DI
Плюсы:
•Выбор реализации
•Независимое тестирование
•Слабая связность кода
•Параллельная разработка функциональности
Минусы:
•Сложность отладки из-за разделения мест и
использования компонентов
•Зависимость приложения от фреймворка для внедрения
зависимости

146.

Clean Architecture
•Entities – Сущности
•Use Cases – Методы использования
•Interface Adapters – Интерфейс-Адаптеры
•Frameworks and Drivers – Место скопления деталей

147.

User
•Быть тестируемой
•Не зависеть от UI
•Не зависеть от БД
•Не зависеть от внешних фреймворков и библиотек
•Не зависеть от какого-либо внешнего сервиса

148.

Model view
presenter
Data
layer
Domian
layer
Boundaries
Presentation
layer
Interactions
Реализация User
Regular Java
Objects
Repository
pattern

149.

Схема работы данных
View
Presenter
Service
Use Case
Repository

150.

Плюсы и минусы Clean Architecture
Плюсы:
Минусы
1.Модульное разделение;
1.Громоздкость;
2. Правило зависимостей;
2.Порог вхождения.
3. Тестирование;
4. Наличие Domain слоя;
5. Использовать любой
понравившийся вам MV-паттерн для
UI-слоя;
6. Кастомизация;
7. Хорошее комьюнити;
8. Гайды и богатая база исходных

151.

Итоги Clean Architecture
Когда использовать Clean Architecture?
•Много бизнес-логики
•Действительно большое приложение
•Необходимо качественное покрытие тестами
•Постоянный рост или нужен задел на будущее
•Планируется мультиплатформа
Когда следует воздержаться?
•Простое приложение в котором практически нет логики
English     Русский Rules