Upload
mailru-group
View
264
Download
2
Embed Size (px)
Citation preview
Droidcon Moscow 2015
Взаимодействие между потоками в Android Максим Ефимов
Кто я такой
0
- Android team lead @ Redmadrobot
- Делал «Мой Билайн», «Открытие», «Альфастрахование»
- Всего выпустил больше 30 Android приложений
- Заморачиваюсь по архитектуре
- Модель потоков в Android
- Какие сложности таит жизненный цикл
- Чем плохи стандартные решения
- Хорошие решения
О чём будем говорить
1
Модель потоков в Android
2
- Main поток - обрабатывает циклы GUI
- Looper - обеспечивает очередь сообщений внутри потока
- Handler - умеет писать и читать сообщения из очереди Looper’а
- Остальные потоки - по умолчанию не имею цикла, делают «долгие операции»
Примеры долгих операций
3
- Сетевое взаимодействие
- Обращения к базе данных
- Чтение дискового каша
- Преобразование изображений
Жизненный цикл I - Связь с background
4
onCreate onStart onResume onPause
starLoading endLoading
Main thread
Background thread
onClick
Main thread looper
Жизненный цикл II - В идеальном мире
5
onResume onPause
starLoading endLoading
Main thread
Background thread
onClick
Main thread looper
Loop showData
Жизненный цикл III - Повернули экран
6
onResume onDestroy
starLoading endLoading
Main thread - первый Activity Instance
Background thread
onClick
Main thread looper
showData
onResume Loop showData
Main thread - второй Activity Instance
Жизненный цикл IV - Свернули приложение
7
onResume onDestroy
starLoading endLoading
Main thread - первый Activity Instance
Background thread
onClick
Main thread looper
showData
onResume Loop showData
Main thread - второй Activity
Жизненный цикл - сложности
8
- Поток может вернуть результат в уничтоженный объект
- Поток может потерять результат
- Необходимо где-то кешировать результаты
- Нужно, чтобы кеширование было удобным
Решения – Thread
9
Button signInButton = (Button) findViewById(R.id.button_auth); signInButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { final Activity activity = AuthActivity.this; showProgress(); new Thread(new Runnable() { @Override public void run() { APIFactory.getApi().signIn(); activity.runOnUiThread(new Runnable() { @Override public void run() { goToMainContent(); } }); } }).start(); }});
- Течет память
- Возврат в уничтоженный объект
- Необходимо руками писать в основной поток
Решения – AsyncTask
10
private class AuthTask extends AsyncTask<Void, Void, Boolean> { @Override protected void onPreExecute() { showProgress(); } @Override protected Boolean doInBackground(final Void... params) { try { APIFactory.getApi().signIn(); }catch (Exception e){ return false; } return true; } @Override protected void onPostExecute(final Boolean result) { if(!isCancelled() && result) { goToMainContent(); } }}
Button signInButton = (Button) findViewById(R.id.button_auth);signInButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { new AuthTask().execute(); }});
- Течет память
- Возврат в уничтоженный объект
Решения – Loader
11
Кода не будет
Пример из документации: 154 строки
Решения – Callback
12
Button signInButton = (Button) findViewById(R.id.button_auth);signInButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { new AuthRequest(new Callback<Result>(){ @Override public void handleMessage(final Result result) { showData(); } @Override public void handleError(final Exception exception) { showError(exception); } }).run(); }});
- Retrofit
- Volley
- RxJava
На самом деле не решают поставленных проблем
Проблемы решений
13
- Нет привязки к жизненному циклу
- Утечки памяти
- Потеря данных
- Сложная реализация
Хорошие решения – Robospice
14
- Следит за жизненным циклом
- Заточена под сеть
- Есть свой кэш
- Память не течет
- Имеет 100500 плагинов
Хорошие решения – Chronos
15
- Следит за жизненным циклом
- Подхватывает запущенные задачи
- Минималистичен
Chronos - запуск операции
16
class MyActivity extends ChronosActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Button startButton = (Button) findViewById(R.id.button_start); startButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { runOperation(new MyOperation()); } }); }}
Chronos - операция
17
class MyOperation extends ChronosOperation<BusinessObject> { @Nullable @Override public BusinessObject run() { final BusinessObject result ; // here you should write what you do to get the BusinessObject return result; } @NonNull @Override public Class<? extends ChronosOperationResult<BusinessObject>> getResultClass(){ return Result.class; } public final static class Result extends ChronosOperationResult<BusinessObject> { }}
Chronos - обработка результата
18
class MyActivity extends ChronosActivity { public void onOperationFinished(final MyOperation.Result result) { if (result.isSuccessful()) { showData(result.getOutput()); } else { showDataLoadError(result.getError()); } } private void showData(BusinessObject data){ //... } private void showDataLoadError(Exception exception){ //... } }
Chronos - трекинг запусков
19
class MyActivity extends ChronosActivity { private Data mData; @Override protected void onResume() { super.onResume(); if (mData == null) { runOperation(new LoadData(), "load_data"); } else { showData(mData); } } public void onOperationFinished(final LoadData.Result result) { if (result.isSuccessful()) { showData(result.getOutput()); } else { showDataLoadError(result.getError()); } }}
Chronos - что еще?
20
- Отмена операций
- Синхронный запуск
- Bradcast результатов
- Opensource