Реалізація міжпроцесної взаємодії (IPC) для Android

TRUETECH займається розробкою, підтримкою та обслуговуванням мобільних додатків iOS, Android, PWA. Маємо великий досвід та експертизу для публікації мобільних додатків до популярних маркетів Google Play, App Store, Amazon, AppGallery та інші.

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1735 послуг
Реалізація міжпроцесної взаємодії (IPC) для Android
Складний
~3-5 днів
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    495

Впровадження міжпроцесної комунікації (IPC) для Android

Більшість Android-додатків працюють в одному процесі й ніколи не зустрічаються з IPC. Але щойно з'являється окремий Service з android:process=":remote", SDK третьої сторони, що працює у своєму процесі, чи потребує передачі даних між додатками—починається серйозна робота з механізмом Binder, AIDL та Messenger.

IPC-механізми в Android

Android надає кілька рівнів абстракції над Binder:

Механізм Коли використовувати Складність
Intent Запустити Activity/Service, передати малі дані Низька
Messenger Односторонні повідомлення, паралелізм не потрібен Середня
AIDL Двостороння взаємодія, паралельні виклики Висока
ContentProvider Структуровані дані між додатками Середня
BroadcastReceiver Трансляція подій всім слухачам Низька

Binder — транспортний шар для всього перерахованого. Ядро Linux передає дані між процесами через /dev/binder. Максимальний розмір буфера транзакції — 1 МБ (спільно використовується всіма активними транзакціями). Спроба відправити велику Bitmap через Binder викликає TransactionTooLargeException.

AIDL: коли вам потрібна реальна IPC

AIDL (Android Interface Definition Language) генерує Binder-проксі з обох сторін. Підходить, коли Service надає API з кількома методами та потрібні синхронні відповіді.

Визначення інтерфейсу (IDataService.aidl):

// IDataService.aidl
package com.example.service;

import com.example.service.IDataCallback;

interface IDataService {
    void getData(String key, IDataCallback callback);
    boolean setData(String key, String value);
    List<String> getKeys();
}
// IDataCallback.aidl
package com.example.service;

oneway interface IDataCallback {
    void onResult(String key, String value);
    void onError(int code, String message);
}

oneway в інтерфейсі зворотного виклику — це асинхронне—не блокує потік, що викликає. Без oneway зворотний виклик блокує потік Service до завершення обробки на стороні клієнта.

Впровадження Service:

class DataService : Service() {

    private val binder = object : IDataService.Stub() {
        override fun getData(key: String, callback: IDataCallback) {
            // Виклики AIDL приходять у Binder thread pool, а не в main thread
            val value = dataStore.get(key)
            if (value != null) {
                callback.onResult(key, value)
            } else {
                callback.onError(404, "Key not found: $key")
            }
        }

        override fun setData(key: String, value: String): Boolean {
            return try {
                dataStore.set(key, value)
                true
            } catch (e: Exception) {
                false
            }
        }

        override fun getKeys(): List<String> = dataStore.getAllKeys()
    }

    override fun onBind(intent: Intent): IBinder = binder
}

З'єднання на стороні клієнта:

class ClientActivity : AppCompatActivity() {
    private var dataService: IDataService? = null

    private val serviceConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            dataService = IDataService.Stub.asInterface(service)
            // тепер ви можете викликати методи
        }

        override fun onServiceDisconnected(name: ComponentName) {
            dataService = null
            // Service упав чи був убит системою—переінституюватися
        }
    }

    override fun onStart() {
        super.onStart()
        val intent = Intent().apply {
            component = ComponentName("com.example.service", "com.example.service.DataService")
        }
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
    }

    override fun onStop() {
        super.onStop()
        unbindService(serviceConnection)
        dataService = null
    }

    private fun fetchData(key: String) {
        dataService?.getData(key, object : IDataCallback.Stub() {
            override fun onResult(key: String, value: String) {
                runOnUiThread { updateUI(key, value) }
            }
            override fun onError(code: Int, message: String) {
                runOnUiThread { showError(message) }
            }
        })
    }
}

Критичний момент: зворотний виклик IDataCallback.Stub викликається у Binder thread pool на стороні клієнта, а не у main thread. runOnUiThread або lifecycleScope.launch(Dispatchers.Main) обов'язкові для оновлення UI.

Безпека: хто може з'єднатися

За замовчуванням, Service з exported="true" доступний будь-якому додатку. Щоб обмежити доступ:

<service
    android:name=".DataService"
    android:exported="true"
    android:permission="com.example.permission.DATA_SERVICE">
</service>
// Перевірка в onBind або в кожному методі
override fun onBind(intent: Intent): IBinder? {
    val callerUid = Binder.getCallingUid()
    if (checkPermission("com.example.permission.DATA_SERVICE", callerUid) != PackageManager.PERMISSION_GRANTED) {
        return null
    }
    return binder
}

Binder.getCallingUid() — UID процесу клієнта. Це дозволяє вам впровадити UID-вайтлист або перевірити підпис APK через PackageManager.checkSignatures().

Messenger: простіший, ніж AIDL

Для простих сценаріїв (черга команд від клієнта до сервісу) Messenger зручніший:

class MessengerService : Service() {
    private val handler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_DO_WORK -> {
                    val data = msg.data.getString("payload")
                    processData(data)
                    // відповідь клієнту
                    msg.replyTo?.send(Message.obtain(null, MSG_RESULT, 0, 0).apply {
                        this.data = Bundle().apply { putString("result", "done") }
                    })
                }
            }
        }
    }

    override fun onBind(intent: Intent): IBinder = Messenger(handler).binder

    companion object {
        const val MSG_DO_WORK = 1
        const val MSG_RESULT = 2
    }
}

Слабість Messenger: усі повідомлення обробляються послідовно в Handler. Якщо обробка одного повідомлення довга, черга застрягає. AIDL з Binder thread pool паралельний за замовчуванням.

Передача великих даних: SharedMemory

Якщо вам потрібно передати більше кількох сотень КБ (зображення, аудіобуфер), використовуйте SharedMemory (API 27+) або MemoryFile (старіші версії). Тільки дескриптор передається через Binder; дані передаються через спільну пам'ять:

// Сторона Service
val sharedMemory = SharedMemory.create("image_buffer", bitmap.byteCount)
val buffer = sharedMemory.mapReadWrite()
bitmap.copyPixelsToBuffer(buffer)
SharedMemory.unmap(buffer)

// Надіслати ParcelFileDescriptor через Binder
val pfd = sharedMemory.fdDup // ParcelFileDescriptor для передачі через Binder

Це обходить обмеження 1 МБ Binder-транзакції й є єдиним правильним способом передачі медіаданих між процесами.

Поширені проблеми

DeadObjectException. Service убит системою, але клієнт про це не знає—наступний виклик методу викликає виключення. Обробляйте за допомогою try/catch, знову викличте bindService в onServiceDisconnected.

Витік ServiceConnection. Якщо bindService() викликається в onCreate(), але unbindService() забувається, Service утримується в пам'яті до знищення процесу. Парна bind/unbind в onStart/onStop або onCreate/onDestroy — це правило без винятків.

AIDL-інтерфейс на двох різних сторонах проекту. Якщо Service та клієнт — окремі APK, файли .aidl повинні бути однаковими (включаючи назву пакету). Невідповідність пакету викликає SecurityException або ClassCastException під час asInterface().

Впровадження IPC через AIDL займає 3–7 днів на проектування інтерфейсу, безпеку, обробку розриву з'єднання та тестування. Інтеграція з існуючим Service — 1–2 дні. Вартість розраховується індивідуально.