Inter-Process Communication (IPC) Implementation for Android

TRUETECH is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Inter-Process Communication (IPC) Implementation for Android
Complex
~3-5 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Implementing Inter-Process Communication (IPC) for Android

Most Android applications run in a single process and never encounter IPC. But as soon as a separate Service with android:process=":remote" appears, a third-party SDK running in its own process, or you need to pass data between applications—serious work with the Binder mechanism, AIDL, and Messenger begins.

IPC Mechanisms in Android

Android provides several levels of abstraction over Binder:

Mechanism When to Use Complexity
Intent Launch Activity/Service, pass small data Low
Messenger One-way messages, no parallelism needed Medium
AIDL Two-way interaction, parallel calls High
ContentProvider Structured data between applications Medium
BroadcastReceiver Broadcast events to all listeners Low

Binder is the transport layer for all of these. The Linux kernel transfers data between processes via /dev/binder. The maximum transaction buffer size is 1 MB (shared among all active transactions). Attempting to send a large Bitmap through Binder results in TransactionTooLargeException.

AIDL: When You Need Real IPC

AIDL (Android Interface Definition Language) generates Binder proxies on both sides. Suitable when a Service provides an API with multiple methods and synchronous responses are needed.

Interface definition (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 on the callback interface is asynchronous—doesn't block the calling thread. Without oneway, the callback blocks the Service thread until processing completes on the client side.

Service implementation:

class DataService : Service() {

    private val binder = object : IDataService.Stub() {
        override fun getData(key: String, callback: IDataCallback) {
            // AIDL calls arrive in Binder thread pool, not 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
}

Client-side connection:

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)
            // now you can call methods
        }

        override fun onServiceDisconnected(name: ComponentName) {
            dataService = null
            // Service crashed or was killed by system—reconnect
        }
    }

    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) }
            }
        })
    }
}

Critical point: the callback IDataCallback.Stub is invoked in the Binder thread pool on the client side, not on the main thread. runOnUiThread or lifecycleScope.launch(Dispatchers.Main) are mandatory for UI updates.

Security: Who Can Connect

By default, a Service with exported="true" is accessible to any application. To restrict access:

<service
    android:name=".DataService"
    android:exported="true"
    android:permission="com.example.permission.DATA_SERVICE">
</service>
// Check in onBind or in each method
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() is the UID of the client process. It lets you implement UID whitelisting or verify APK signature via PackageManager.checkSignatures().

Messenger: Simpler Than AIDL

For simple scenarios (command queue from client to service), Messenger is more convenient:

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)
                    // reply to client
                    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's weakness: all messages are processed sequentially in the Handler. If processing one message is slow, the queue backs up. AIDL with Binder thread pool is parallel by default.

Transferring Large Data: SharedMemory

If you need to pass more than a few hundred KB (image, audio buffer), use SharedMemory (API 27+) or MemoryFile (older versions). Only the descriptor is passed through Binder; data is transferred via shared memory:

// Service side
val sharedMemory = SharedMemory.create("image_buffer", bitmap.byteCount)
val buffer = sharedMemory.mapReadWrite()
bitmap.copyPixelsToBuffer(buffer)
SharedMemory.unmap(buffer)

// Send ParcelFileDescriptor through Binder
val pfd = sharedMemory.fdDup // ParcelFileDescriptor for Binder transfer

This bypasses the 1 MB Binder transaction limit and is the only correct way to transfer media data between processes.

Common Issues

DeadObjectException. The Service is killed by the system, but the client doesn't know—the next method call throws an exception. Handle with try/catch, call bindService again in onServiceDisconnected.

ServiceConnection leak. If bindService() is called in onCreate() but unbindService() is forgotten, the Service is held in memory until the process is destroyed. Pair bind/unbind in onStart/onStop or onCreate/onDestroy—this is a rule without exceptions.

AIDL interface on two different project sides. If Service and client are separate APKs, .aidl files must be identical (including package name). Package mismatch causes SecurityException or ClassCastException during asInterface().

Implementing IPC via AIDL takes 3–7 days for interface design, security, connection loss handling, and testing. Integrating with an existing Service is 1–2 days. Cost is calculated individually.