Реализация межпроцессного взаимодействия (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
    756
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    445

Реализация межпроцессного взаимодействия (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 на интерфейсе callback — асинхронный вызов, не блокирует вызывающий поток. Без oneway callback блокирует поток 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) }
            }
        })
    }
}

Критический момент: callback 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 процесса-клиента. Позволяет имплементировать whitelist по UIDs или проверить подпись 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, в onServiceDisconnected повторно вызывать bindService.

Утечка 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 дней. Стоимость рассчитывается индивидуально.