Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Как наладить взаимодействие между приложениями

MOSDROID
September 18, 2017

Как наладить взаимодействие между приложениями

Антон Дудаков – Яндекса

Антон расскажет о том, зачем вообще приложениям взаимодействовать между собой и какие есть способы для взаимодействия в Android. Как эти способы устроены внутри, как работают на системном — из всей этой информации можно будет сделать выводы о том, какие ограничения есть в каждом из механизмов и для каких целей подходит тот или иной инструмент.

#MOSDROID

MOSDROID

September 18, 2017
Tweet

More Decks by MOSDROID

Other Decks in Programming

Transcript

  1. › запуск приложениями друг друга (и получение результатов работы) ›

    обмен уведомлениями о событиях › предоставление данных › предоставление функций › предоставление элементов пользовательского интерфейса Задачи решаемые взаимодействием 3
  2. › startActivity, startActivityForResult › sendBroadcast › ContentProvider › AIDL (+Messenger)

    › Специализированные средства, обёрнутые в системные сервисы (widgethost, mediasession, remote controller, notification listener) API предоставляемый Anroid 4
  3. Binder на примере startActivityForResult 7 Activity (app1) ActivityManager Activity (app2)

    startActivityForResult startActivityForResult running setResult finish finishActivity onActivityResult
  4. Binder на примере startActivityForResult 2 8 Activity ActivityManager Activity startActivityForResult

    running setResult finish attachApplication (ApplicationInterface) ActivityManager Proxy ActivityManager Stub startActivity startActivity Application Stub ActivityManager Proxy startProcess attachApplication scheduleLaunchActivity startActivity finishActivity finishActivity finishActivity Application Stub scheduleSendResult onActivityResult
  5. Приложения и ActivityManager находятся в разных процессах 9 Activity ActivityManager

    startActivityForResult ActivityManager Proxy ActivityManager Stub . transaction(J) on Descriptor(X) startActivity Application Stub scheduleSendResult onActivityResult BinderDriver onTransaction(J) AppProxy transaction(K) on Descriptor(Y) onTransaction(K) binder binder binder binder
  6. Binder на примере startActivityForResult 3 10 Activity ActivityManager Activity ActivityManager

    Proxy ActivityManager Stub Application Stub ActivityManager Proxy Application Stub BinderDriver startActivityForResult startProcess Zygote startActivity 2Application ThreadProxy 1Application ThreadProxy App1 App2 scheduleLaunchActivity startActivity setResult; finish finishActivity scheduleSendResult startActivity startActivity scheduleLaunchActivity scheduleLaunchActivity finishActivity finishActivity scheduleSendResult scheduleSendResult onResult
  7. /frameworks/native/libs/binder/ProcessState.cpp android.os.TransactionTooLargeException 11 #define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2)) …

    // mmap the binder, providing a chunk of virtual address // space to receive transactions.
 mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
  8. › 1Mb на транзакцию › пользователь покидает ваше приложение ›

    как следствие система вас может убить или пользователь просто не вернётся в него › передавать можно примитивы, String, Parcelable, Serializable и их списки › Основное назначение – запуск других activity, получение результата их работы по завершению Особенности startActivityForResult 12
  9. sendBroadcast на схеме 14 app 1 Activity ActivityManager sendBroadcast getFromQueue

    ActivityManager Proxy ActivityManager Stub broadcastIntent broadcastIntent addToQueu deliver app 2 deliver repeat
  10. › В системе всего три (4.2+) очереди бродкастов: 1. mStickyBroadcasts

    (Deprecated) 2. mFgBroadcastQueue 3. mBgBroadcastQueue sendBroadcast очереди 15
  11. › 1Mb на транзакцию › время доставки Intent’а не гарантировано

    › полностью асинхронно › вы не знаете когда получатель(и) получит ваш Intent и как он на него ответит. И ответит ли ( кроме orderedBroadcast) › передавать можно примитивы, String, Parcelable, Serializable и их списки Особенности sendBroadcast 16
  12. › сообщать о событиях, если время доставки вас не сильно

    беспокоит › или время доставки вас беспокоит чуть больше чем «не сильно» и приложение, которому вы отправляете находится в Foreground › registerReceiver 
 принимать сообщения от системы (CONNECTIVITY_ACTION, ACTION_HEADSET_PLUG, ACTION_MEDIA_MOUNTED, …) › Основное назначение – рассылка сообщений/ уведомлений Для чего подходит Broadcast 17
  13. AIDL – Android Interface Definition Language IPC – InterProcess Communications

    RPC – Remote Procedure Call AIDL – термины 18
  14. AIDL данные 1 19 // CLIENT Intent service = new

    Intent();
 service.setComponent(component);
 bindService(service, mServiceConnection, BIND_AUTO_CREATE);
 //… ServiceConnection mServiceConnection = new ServiceConnection() {
 
 void onServiceConnected(ComponentName name, IBinder service) {
 mService = ITestService.Stub.asInterface(service);
 SomeData someData = mService.getData(SIZE);
 mService.setData(getProcessedData(someData));
 unbindService(this);
 }
 
 void onServiceDisconnected(ComponentName name) {
 mService = null;
 }
 }; //SERVER class TestService extends Service {
 ITestService.Stub mBinder = new ITestService.Stub() { 
 SomeData getData(int listSize) {
 return new SomeData(listSize);
 }
 
 void setData(SomeData someData){
 someData = someData;
 }
 };
 }
  15. AIDL данные 2 20 // ITestService.aidl
 package ru.example.testservice;
 
 import

    ru.example.testservice.SomeData;
 
 interface ITestService {
 SomeData getData(int listSize);
 void setData(in SomeData route);
 }
 // SomeData.aidl
 package ru.example.testservice;
 
 parcelable SomeData;
  16. AIDL данные 3. Схема 21 Context App1 Service App2 ActivityManager

    ServiceConnection bindService(serviceConnection) Binder Driver Service.Proxy startService() Service.Stub new Service.Stub() setData() getData()
  17. › Примитивы › String › Parcelable–объекты › IInterface › Списки

    поддерживаемых типов AIDL что можно передать 22
  18. AIDL Callback 1 23 // ITestService.aidl
 package ru.example.testservice;
 
 import

    ru.example.testservice.SomeData;
 
 interface ITestService {
 SomeData getData(int listSize);
 void setData(in SomeData route);
 }
 // SomeData.aidl
 package ru.example.testservice;
 
 parcelable SomeData; // ISomeCallback.aidl
 package ru.example.testservice;
 
 interface ISomeCallback {
 void onFinish(boolean success);
 } // ITestService.aidl
 package ru.example.testservice;
 
 import ru.example.testservice.SomeData;
 import ru.example.testservice.ISomeCallback;
 
 interface ITestService {
 SomeData getData(int listSize);
 void setData(in SomeData route);
 
 void setCallBack( in ISomeCallback someCallback);
 }
  19. AIDL Callback 2 24 // CLIENT ISomeCallback.Stub someCallback = new

    ISomeCallback.Stub() {
 void onFinish(boolean success) {/*.*/}
 }; ServiceConnection mServiceConnection = new ServiceConnection() {
 void onServiceConnected(ComponentName name, IBinder service) {
 mService = ITestService.Stub.asInterface(service);
 mService.setCallBack(someCallback);
 }
 
 void onServiceDisconnected(ComponentName name) {
 mService = null;
 }
 }; //... bindTestService(); //SERVER class TestService extends Service {
 ITestService.Stub mBinder = new ITestService.Stub() {
 SomeData getData(int listSize) {
 return new SomeData(listSize);
 }
 void setData(SomeData someData) {
 CurrentSomeData = someData;
 } 
 void setCallBack(ISomeCallback callback) {
 new Thread() {
 void run() {
 callback.onFinish(isEverythingOk());
 }
 }.start();
 }
 };
 //... }
  20. AIDL Callback 3 25 Context App1 Service App2 ActivityManager ServiceConnection

    bindService(serviceConnection) Binder Driver Service.Proxy startService() Service.Stub new Service.Stub() setCallback(callback) CallbackStub CallbackProxy onFinish(boolean)
  21. В качестве Callback можно использовать объект Messenger AIDL Callback 4.1

    Messenger 26 // ITestService.aidl
 package ru.example.testservice;
 
 import ru.example.testservice.SomeData;
 
 interface ITestService {
 SomeData getData(int listSize);
 void setData(in SomeData route);
 void setMessenger(in Messenger messenger);
 } // Messenger.aidl
 package android.os;
 
 import android.os.Message;
 
 /** @hide */
 oneway interface IMessenger {
 void send(in Message msg);
 }
  22. AIDL Callback 4.2 Messenger 27 // CLIENT 
 Messenger messenger

    = new Messenger(new Handler() {
 void handleMessage(Message msg) {
 showToast(msg.getData().getString("0"));
 }
 });
 ServiceConnection mServiceConnection = new ServiceConnection() { 
 void onServiceConnected(ComponentName name, IBinder service) {
 mService = ITestService.Stub.asInterface(service);
 mService.setMessenger(messenger);
 } //...
 }; //... bindTestService(); //SERVER ITestService.Stub mBinder = new ITestService.Stub() {
 // ..
 void setMessenger(Messenger messenger) {
 Message message = Message.obtain();
 Bundle bundle = new Bundle();
 bundle.putString("0", "Hello world");
 message.setData(bundle);
 messenger.send(message);
 }
 };

  23. › для всего ;) › вызов методов другого приложение как

    синхронно, так и асинхронно › подписка на получение уведомлений без задержек (собственным Callback-объектом или используя Messenger или ResultReceiver) › передача и получение небольших (до 1Мб) объектов › Основное назначение – удалённый вызов процедур Для чего подходит AIDL 28
  24. ContentProvider 29 // Client void onClick() {
 try (Cursor cursor

    = getContentResolver().query(uri, null, null, null, null)) {
 if (cursor != null && cursor.moveToFirst()) {
 byte[] blob = cursor.getBlob(cursor.getColumnCount() - 1);
 }
 } catch (Exception e) {
 }
 }
 
 ............... // Server Cursor query(@NonNull Uri uri, String[] projection, String selection,
 String[] selectionArgs, String sortOrder) {
 MatrixCursor cursor = new MatrixCursor(new String[]{"_ID", "blob"}, 0);
 cursor.newRow().add("0").add(Byte.MAX_VALUE);
 return cursor;
 }
  25. ContentProvider2 30 Context App1 ContentProvider App2 ActivityManager query() ContentResolver acquireProvider()

    ContentProviderProxy Binder Driver ContentProviderProxy query() BulkCursor BulkCursorProxy moveToFirst() CursorWindow (parcelable) new CursorWindow() Anonymous Shared memory BLOCK 2MB Data new BulkCursor() Cursor BulkCursorProxy CursorWindow CursorWindow (parcelable) getData()
  26. › 2Мб на каждую строку в курсоре › строк может

    быть сколько хочешь › запросы происходят синхронно › передавать можно примитивы, byte[], String. Для остальных данных выполняется toString() › а ещё есть метод call(), который возвращает Bundle › Основное назначение – передача данных Особенности ContentProvider 31
  27. › AccountManager – место для хранения аккаунтов с возможностью поделиться

    данными аккаунтов › App Widgets – передача приложению реализующему AppWidgetHost (Launcher) своих View › MyService extends NotificationListenerService – в этом случае ваш сервис будет получать все публикуемые уведомления (разрешение включается отдельной галочкой) › remotecontroller (14 - 21) и mediasession (21+) – чтобы получить управление аудиосессиями нужно реализовать NotificationListenerService Спецсредства для взаимодействия 32
  28. › startActivity – временно передать UI другому › sendBroadcast –

    сказать всем желающим что что-то случилось или узнать что что-то случилось › ContentProvider – поделиться или получить списки данных › AIDL – включить другое приложение в фоне, чтобы оно работало, мы бы им управляли, а оно бы нам ещё и присылало что-нибудь › Спецсредства – управление аккаунтами, рисование виджетов, чтение уведомлений, управление музыкой и много ещё чего другого не менее интересного… Выводы 33