In this article we will try to describe your experience with AIDL in Android IPC.
It contains an example of an application with a service that is running in a separate process.
The article should be considered as:
- An example of an application architecture that uses remote Android Services and AIDL.
- useful code examples.
- solely as a supplement to the main documentation on Android Developers (see links at the end of the article).
Basic concepts
Service is a component of an Android application without a user interface that is designed for demanding and / or long-running operations.
Types of Android services
- Started (running) - services that are started by any other component of the application (Activity, BrodcastReceiver, Service) and work until they stop themselves or someone does not stop them.
- Bound (related) - a service that acts as a server in a client-server architecture. Such a service is created during the first connection (request) from another component of the application. The service stops when the last client disconnects.
- Service can be both Started and Bound. This service is able to "live forever" and serve customer requests.
The service can be launched in a separate process from the Activity.
')
Benefits:
- The maximum memory size is increased by 2 times, for example 32 MB / process (depending on the platform).
- GC behaves less aggressively if you have 2 snapshot processes in N MB each than 1 process and 2 * N MB.
- The standard advantages of Android services: background, independence from the Activity, etc.
Disadvantages:
- additional system resources for low-level serialization and deserialization.
- the need to control the life cycle of the process.
- a bit more code.
AIDL
Literally translated - the language of the description of the interfaces Android. It is used to describe the composition and decomposition of Java objects into OS primitives for direct transfer between processes.
AIDL files are very similar to standard interfaces in java except for:
- You need to import even those aidl files that are in the same package.
- The oneway keyword in the void method declaration means that the method will be invoked asynchronously (the client does not wait for its execution).
- You can only use primitives, String, List and Parcelable classes declared in other aidl files.
Using AIDL, java code is automatically generated to generate stabs.
Application architecture
The application we developed is an Android gallery that allows you to view photos from a memory card and photo-sharing networks.
The main tasks of the service in this application are: obtaining metadata (information about albums, photos, friends), monitoring their updates and everything else associated with them. The service constantly stores up-to-date information and is ready at any moment to give away its main Activity for display.
Below are the key sections of the code and the process of creating a primitive service is described:
The following AIDL files are used to communicate between the service and the Activity:
IDataSourceService.aidl - service interface:
packagecom.umobisoft.habr.aidlexample.common; import com.umobisoft.habr.aidlexample.common.IDataSourceServiceListener; interfaceIDataSourceService{ voidloadAlbums(in IDataSourceServiceListener listener); … }
IDataSourceServiceListener.aidl - interface of message listeners from the service:
package com.umobisoft.habr.aidlexample.common; import com.umobisoft.habr.aidlexample.common.pojo.Album; interface IDataSourceServiceListener{ oneway void albumItemLoaded(in Album a); }
The data is transferred using two classes that implement the Parcelable interface - Album and Photo. The declaration of aidl files for these classes is required. When converting from OS primitives to java Objects, the Creator class is used.
To write data, use the Parcelable interface's writeToParcel method:
@Override public void writeToParcel(Parcel out, int flags) { try{ out.writeLong(id); out.writeString(name); out.writeTypedList(photos); }catch (Exception e) { Log.e(TAG, "writeToParcel", e); } }
There is also a describeContents helper method, its task is to describe special cases / states of an object that can be used during serialization and descripting:
@Override public int describeContents() {
Data reading methods turned out to be unworthy of putting them into the Parcelable interface, but the standard practice is to use Creator along with:
private void readFromParcel(Parcel in) { try{ id = in.readLong(); name = in.readString(); photos.clear(); in.readTypedList(photos, Photo.CREATOR); }catch (Exception e) { Log.e(TAG, "readFromParcel", e); } }
Activity starts the service (making it thus StartedService) in the onCreate method
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textView = (TextView)findViewById(R.id.album_text); Intent serviceIntent = newIntent(this, DataSourceService.class); startService(serviceIntent); connectToService(); }
At the same stage of the life cycle, it connects to the service:
private void connectToService() { Intent intent = newIntent(this, DataSourceService.class); this.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); }
The process of connecting to the service is asynchronous, it is affected by the implementation of the ServiceConnection interface. During the connection with the server, the Activity is registered in the service as a message listener using the implementation of IDataSourceServiceListener.Stub:
private ServiceConnection serviceConnection = newServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i(TAG, "Service connection established"); serviceApi = IDataSourceService.Stub.asInterface(service); try { mainListener = newIDataSourceServiceListener.Stub() { @Override publicvoidalbumItemLoaded(final Album a) throwsRemoteException { mToastHandler.post(new Thread(){ publicvoid run(){ Toast.makeText(HabrahabrAIDLExampleActivity.this, a.toString(), Toast.LENGTH_LONG).show(); textView.setText(a.toString()); } }); } }; serviceApi.loadAlbums(mainListener); } catch (RemoteException e) { Log.e(TAG, "loadAlbums", e); } } @Override publicvoidonServiceDisconnected(ComponentName name) { Log.i(TAG, "Service connection closed"); serviceApi = null; connectToService(); } };
While StartedService is running, if the amount of free memory decreases to a certain threshold, the system can kill the service without warning. After that, the system must restart the service. Thus, in the onServiceDisconnected method, we again initialize the connection with the service.
I hope the posted source code will help developers to get acquainted with AIDL. Archive with a full example
here .
The main application is available for review in the
Android Market .
Official documentation on AndroidDevelopers:
Services:
developer.android.com/guide/topics/fundamentals/services.htmlAIDL:
developer.android.com/guide/developing/tools/aidl.html