How Andromeda Enables Seamless Multi‑Process Communication in Android Apps
Andromeda is an open‑source Android framework that unifies local and cross‑process component communication by providing blocking IPC, a global event bus, remote callbacks, and a dispatcher‑based architecture, solving the limitations of existing single‑process routing solutions.
Background
Existing Android componentization solutions such as Alibaba's ARouter, Tmall's unified jump protocol, and Airbnb's DeepLinkDispatch focus on page navigation within a single process. iQIYI’s app requires both single‑process and cross‑process communication, including callbacks and a global event bus, which these solutions do not fully support.
Andromeda Overview
In May 2018 iQIYI released Andromeda as an open‑source project ( https://github.com/iqiyi/Andromeda). Andromeda provides a blocking‑style inter‑process communication (IPC) mechanism that removes the need for cumbersome asynchronous code.
Core Features
Local Service Routing : Register with registerLocalService(Class, Object) and retrieve via getLocalService(Class). Local services can use any Java type for parameters and return values.
Remote Service Routing : Register with registerRemoteService(Class, Object) and retrieve via getRemoteService(Class). Remote services are limited to Binder‑compatible types (primitives and Parcelable objects).
Global Event Bus : Subscribe with subscribe(String, EventListener) and publish with publish(Event). Events are simple Parcelable objects containing a name and a Bundle of data.
Remote Method Callback : Define AIDL‑style callbacks using IPCCallback for asynchronous results. A BaseCallback variant posts results on the UI thread.
Why Separate Local and Remote Services?
Local services are unrestricted because they run in the same process. Remote services must obey the Binder IPC contract, which only supports primitive types and Parcelable objects.
Architecture: Dispatcher & RemoteTransfer
The framework introduces two central components:
Dispatcher : Holds proxies to all process‑wide RemoteTransfer binders, manages registration of remote services, and forwards event publications.
RemoteTransfer : Deployed in each process, it manages that process’s module services and communicates with the Dispatcher.
Key AIDL‑style interfaces (simplified):
interface IRemoteTransfer {
oneway void registerDispatcher(IBinder dispatcherBinder);
oneway void unregisterRemoteService(String serviceCanonicalName);
oneway void notify(in Event event);
} interface IDispatcher {
BinderBean getTargetBinder(String serviceCanonicalName);
IBinder fetchTargetBinder(String uri);
void registerRemoteTransfer(int pid, IBinder remoteTransferBinder);
void registerRemoteService(String serviceCanonicalName, String processName, IBinder binder);
void unregisterRemoteService(String serviceCanonicalName);
void publish(in Event event);
}During process initialization each RemoteTransfer sends its binder to a same‑process DispatcherService. The Dispatcher registers the proxy, enabling other processes to locate and invoke services.
Process Priority Management
Binding to a service automatically raises the provider’s process priority. Andromeda binds a generated stub service the first time a remote service is used and unbinds it in the host Activity/Fragment ’s onDestroy(). This is automated by a hidden fragment that listens to lifecycle callbacks.
Lifecycle Automation
A lightweight fragment attached to the host’s FragmentManager receives onDestroy() and performs the required unbind() without developer intervention.
IPCCallback Example
IBinder binder = Andromeda.getRemoteService(IBuyApple.class);
IBuyApple service = IBuyApple.Stub.asInterface(binder);
service.buyAppleOnNet(10, new IPCCallback.Stub() {
@Override public void onSuccess(Bundle result) { /* handle success */ }
@Override public void onFail(String reason) { /* handle failure */ }
});For main‑thread callbacks, use BaseCallback which posts results onto the UI thread.
Event Bus
Event definition:
public class Event implements Parcelable {
private String name;
private Bundle data;
// getters, setters, parceling logic omitted for brevity
}Usage example:
// Subscribe
Andromeda.subscribe(EventConstants.APPLE_EVENT, MainActivity.this);
// Publish
Bundle b = new Bundle();
b.putString("Result", "gave u five apples!");
Andromeda.publish(new Event(EventConstants.APPLE_EVENT, b));InterStellar – Hermes‑Like Enhancement
InterStellar extends Andromeda by supporting IPC modifiers ( @in, @out, @inout, @oneway) and allowing developers to define remote interfaces exactly like local ones, eliminating the need for AIDL files.
Example remote interface with modifiers:
public interface IAppleService {
int getApple(int money);
float getAppleCalories(int appleNum);
String getAppleDetails(int appleNum, String manufacturer, String retailer, String userName, int userId);
@oneway void oneWayTest(Apple apple);
String outTest1(@out Apple apple);
String outTest2(@out int[] appleNum);
String outTest3(@out int[] array1, @out String[] array2);
String outTest4(@out Apple[] apples);
String inoutTest1(@inout Apple apple);
String inoutTest2(@inout Apple[] apples);
}The implementation mirrors a normal local service. At runtime InterStellar creates a dynamic proxy that packages the method name and arguments, sends them via Binder, and unmarshals the result.
Dynamic Proxy Architecture
Instead of the static AIDL proxy, InterStellar’s Transfer layer generates a dynamic proxy. The proxy reflects the invoked method, serializes arguments (respecting @in, @out, @inout), forwards the call to the remote side, and returns the result.
Binder Management without Service Binding
Traditional approaches couple Binder acquisition with bindService(), which introduces extra IPC hops and prevents true blocking calls. Andromeda instead uses a binder manager:
Each process registers its RemoteTransfer binder with the Dispatcher.
The Dispatcher maintains a map of process‑wide binders ( BinderBean).
When a client needs a remote service, it queries the Dispatcher for the target binder. The Dispatcher can be accessed synchronously via a ContentProvider that returns the binder wrapped in a MatrixCursor:
public static Bundle call(Context context, Uri uri, String method, String arg, Bundle extras) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
return context.getContentResolver().call(uri, method, arg, extras);
}
ContentProviderClient client = tryGetContentProviderClient(context, uri);
if (client == null) {
Logger.i("ContentProviderClient is null");
return null;
}
try {
return client.call(method, arg, extras);
} catch (RemoteException e) {
e.printStackTrace();
return null;
} finally {
client.close();
}
}The returned Bundle contains the binder wrapped in a BinderWrapper Parcelable:
public class BinderWrapper implements Parcelable {
private final IBinder binder;
public BinderWrapper(IBinder binder) { this.binder = binder; }
protected BinderWrapper(Parcel in) { this.binder = in.readStrongBinder(); }
public IBinder getBinder() { return binder; }
@Override public void writeToParcel(Parcel dest, int flags) { dest.writeStrongBinder(binder); }
// CREATOR omitted for brevity
}Clients extract the binder via DispatcherCursor.stripBinder(Cursor) and then invoke remote methods through Andromeda’s API.
Process Placement of Dispatcher
The Dispatcher should run in the longest‑living process to avoid being killed. For most apps this is the main process, but apps with a dedicated playback process can configure the Dispatcher via the Gradle extension:
dispatcher {
process ":downloader"
}If no configuration is provided, the plugin defaults to the main process.
Service Priority Boost & Lifecycle
Binding to a stub service raises the provider’s process priority, reducing the risk of binderDied. Andromeda automates binding on first use and unbinding in the host’s onDestroy() via a hidden fragment that listens to the host’s lifecycle.
Summary
Andromeda unifies local and remote component communication on Android by decoupling Binder from Service, offering a blocking API, a global event bus, automatic lifecycle‑aware binding, and an extensible architecture (InterStellar) that supports AIDL‑free remote interfaces with full IPC modifier control. The project is open source at https://github.com/iqiyi/Andromeda.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
