Android AIDL Development
Introduction
In Android, each application runs in its own process and has independent memory space. But sometimes our applications need to communicate with other applications. What should we do at this time? Obviously, cross-Process Memory sharing is not allowed in Java. data cannot be exchanged directly. Android can useAIDLInterprocess communication (IPC )).
The original Android Developer introduction is as follows: AIDL (Android Interface Definition Language) is similar to other IDLs you might have worked. it allows you to define the programming interface that both the client and service agree upon in order to communicate with each other using interprocess communication (IPC ). on Android, one process cannot normally access the memory of another process. so to talk, they need to decompose their objects into primitives that the operating system can understand, and marshall the objects should SS that boundary for you. the code to do that should alling is tedious to write, so Android handles it for you with AIDL.
Scenario (Application Scenario)
AIDL is required only when you want clients from different applications to implement IPC through your Service and want to handle multithreading issues in your Service.
The original Android Developer introduction is as follows:
Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. if you do not need to perform concurrent IPC extends SS different applications, you shocould create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. regardless, be sure that you understand Bound Services before implementing an AIDL.
Thread blocking and security issues
Before you begin designing your AIDL interface, be aware that callto an AIDL interface are direct function CILS. you shoshould not make assumptions about the thread in which the call occurs. what happens is different depending on whether the call is from a thread in the local process or a remote process. specifically:
Callmade from the local process are executed in the same thread that is making the call. if this is your main UI thread, that thread continues to execute in the AIDL interface. if it is another thread, that is the one that executes your code in the service. thus, if only local threads are accessing the service, you can completely control which threads are executing in it (but if that is the case, then you shouldn't be using AIDL at all, but shoshould instead create the interface by implementing a Binder ). callfrom a remote process are dispatched from a thread pool the platform maintains inside of your own process. you must be prepared for incoming callfrom unknown threads, with multiple callhappening at the same time. in other words, an implementation of an AIDL interface must be completely thread-safe.The oneway keyword modifies the behavior of remote CILS. when used, a remote call does not block; it simply sends the transaction data and immediately returns. the implementation of the interface eventually generated es this as a regular call from the Binder thread pool as a normal remote call. if oneway is used with a local call, there is no impact and the call is still synchronous.
Defining an AIDL Interface
You need to define your AIDL interface according to the Java programming language syntax and save it to the file with the suffix. aidl .. Aidl files must be stored in the src directory of the main application and Client application. When your application contains. aidl, Android SDK tools automatically generates an IBinder interface in the application's gen/directory according to the definition of. aidl. The Service in the main application needs to implement the IBinder interface, and then the Client application can bind to this Service and implement IPC through the IBinder object.
AIDL practice
After fully understanding the above knowledge, you can start to implement the AIDL interface and implement inter-process communication.
For better understanding, we will first demonstrate the directory structure of the entire project.
1. Create a. aidl File
You must construct the. aidl file according to the Java syntax structure. Each. aidl file can only define one interface.
By default, AIDL supports the following data types:
All primitive typesIn the Java programming language (such as int, long, char, boolean, and so on)
String
CharSequence
ListAll elements in the List must be one of the supported data types in this list or one of the other AIDL-generated interfaces or parcelables you 've declared. A List may optionally be used as a generic class (for example, List ). The actual concrete class that the other side functions ES is always an ArrayList, although the method is generated to use the List interface.
MapAll elements in the Map must be one of the supported data types in this list or one of the other AIDL-generated interfaces or parcelables you 've declared. generic maps, (such as those of the form Map Are not supported. The actual concrete class that the other side functions ES is always a HashMap, although the method is generated to use the Map interface.
If you want to use other types of objects, you must use the import Statement to import them, even if it is consistent with the package name defined in the. aidl file.
The following is the. aidl sample file.
// IRemoteService.aidlpackage com.ricky.android.aidl.protocal;import com.ricky.android.aidl.model.Student;import com.ricky.android.aidl.model.Address;// Declare any non-default types here with import statements/** Example service interface */interface IRemoteService { /** Request the process ID of this service, to do evil things with it. */ int getPid(); /** Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ int sum(int number, String process); void say(int time, String message); Student getStudent(int id); List getAddress(int id);}
The AIDL interface must pass the Student object and Address object, both of which implement the Parcelable interface. The Code is as follows:
Student. java
package com.ricky.android.aidl.model;import java.util.List;import android.os.Parcel;import android.os.Parcelable;public class Student implements Parcelable {private int id;private String name;private List
hobbies;private Address addr;public static final Parcelable.Creator
CREATOR = new Parcelable.Creator
() {@Overridepublic Student createFromParcel(Parcel in) {// TODO Auto-generated method stubreturn new Student(in);}@Overridepublic Student[] newArray(int size) {// TODO Auto-generated method stubreturn new Student[size];}};public Student(){}protected Student(Parcel in){readFromParcel(in);}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public List
getHobbies() {return hobbies;}public void setHobbies(List
hobbies) {this.hobbies = hobbies;}public Address getAddr() {return addr;}public void setAddr(Address addr) {this.addr = addr;}@Overridepublic int describeContents() {// TODO Auto-generated method stubreturn 0;}@Overridepublic void writeToParcel(Parcel out, int flags) {out.writeInt(id);out.writeString(name);out.writeList(hobbies);out.writeParcelable(addr, 0);}@SuppressWarnings(unchecked)public void readFromParcel(Parcel in) { id = in.readInt(); name = in.readString(); hobbies = in.readArrayList(getClass().getClassLoader()); addr = in.readParcelable(getClass().getClassLoader()); }@Overridepublic String toString() {return Student [id= + id + , name= + name + , hobbies= + hobbies+ , addr= + addr + ];}}
Address. java
package com.ricky.android.aidl.model;import android.os.Parcel;import android.os.Parcelable;public class Address implements Parcelable {private String province;private String city;private String district;private String street ;private int postcode;public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {@Overridepublic Address createFromParcel(Parcel in) {return new Address(in);}@Overridepublic Address[] newArray(int size) {return new Address[size];}};public Address(){}public Address(Parcel in){readFromParcel(in);}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getDistrict() {return district;}public void setDistrict(String district) {this.district = district;}public String getStreet() {return street;}public void setStreet(String street) {this.street = street;}public int getPostcode() {return postcode;}public void setPostcode(int postcode) {this.postcode = postcode;}@Overridepublic String toString() {return Address [province= + province + , city= + city+ , district= + district + , street= + street+ , postcode= + postcode + ];}@Overridepublic int describeContents() {// TODO Auto-generated method stubreturn 0;}@Overridepublic void writeToParcel(Parcel out, int flags) {out.writeString(province);out.writeString(city);out.writeString(district);out.writeString(street);out.writeInt(postcode);}public void readFromParcel(Parcel in) {province = in.readString();city = in.readString();district = in.readString();street = in.readString();postcode = in.readInt(); }}
Note: The. aidl file must be stored in the package name directory defined in the. aidl file. For example, in this example, package com. ricky. android. aidl. protocal;
2. Implement the IBinder Interface
Package com. ricky. android. aidl. protocal; import java. util. arrayList; import java. util. arrays; import java. util. list; import com. ricky. android. aidl. model. address; import com. ricky. android. aidl. model. student; import com. ricky. android. aidl. util. logger; import android. app. service; import android. content. intent; import android. OS. IBinder; import android. OS. process; import android. OS. remoteException; public class MyService extends Service {private static final String TAG = MyService. class. getSimpleName (); @ Overridepublic void onCreate () {Logger. I (TAG, onCreate); super. onCreate () ;}@ Overridepublic IBinder onBind (Intent intent) {Logger. I (TAG, onBind); return mBinder;} private final IRemoteService. stub mBinder = new IRemoteService. stub () {@ Overridepublic int getPid () throws RemoteException {return Process. myPid () ;}@ Overridepublic int sum (int number, String process) throws RemoteException {Logger. I (TAG, call sum, number = + number + ** process = + process); int sum = 0; for (int I = 0; I
GetAddress (int id) throws RemoteException {List list = new ArrayList (); Address addr = new Address (); addr. setProvince (Beijing); addr. setCity (Beijing); addr. setDistrict (Chaoyang District); addr. setStreet (Xianghe Garden); addr. setpostcodes (100010); list. add (addr); return list ;}};}
Register a Service in the Manifest File
Here, the AIDL Server is complete, and the Client can bind MyService to obtain the IBinder object to implement IPC.
There are a few rules you shoshould be aware of when implementing your AIDL interface:
Incoming callare not guaranteed to be executed on the main thread, so you need to think about multithreading from the start and properly build your service to be thread-safe.By default, RPC callare synchronous. if you know that the service takes more than a few milliseconds to complete a request, you shocould not call it from the activity's main thread, because it might hang the application (Android might display an Application is Not Responding dialog)-you shoshould usually call them from a separate thread in the client. no exceptions that you throw are sent back to the caller.
3. Expose the interface to the Client to call
3. 1. Create a client Application with the following structure:
3.2 introduce AIDL files and related classes, and copy them directly from the server.
3.3 bind a remote Service
package com.ricky.android.aidlclient;import java.util.List;import com.ricky.android.aidl.model.Address;import com.ricky.android.aidl.model.Student;import com.ricky.android.aidl.protocal.IRemoteService;import com.ricky.android.aidlclient.util.Logger;import android.app.Activity;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;public class MainActivity extends Activity implements OnClickListener {protected static final String TAG = null;private Button bt_bind;private IRemoteService mIRemoteService;private Button bt_unbind;private TextView tv_status;private boolean isBind;private Button bt_call;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViewById();setListener();processLogic();}private void findViewById() {bt_bind = (Button) findViewById(R.id.bt_bind);bt_unbind = (Button) findViewById(R.id.bt_unbind);bt_call = (Button) findViewById(R.id.bt_call);tv_status = (TextView) findViewById(R.id.tv_status);}private void setListener() {bt_bind.setOnClickListener(this);bt_unbind.setOnClickListener(this);bt_call.setOnClickListener(this);}private void processLogic() {tv_status.setText(Unbinding);}private ServiceConnection mConnection = new ServiceConnection() {// Called when the connection with the service is established@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Logger.i(TAG, onServiceConnected);// Following the example above for an AIDL interface, // this gets an instance of the IRemoteInterface, which we can use to call on the service mIRemoteService = IRemoteService.Stub.asInterface(service);}// Called when the connection with the service disconnects unexpectedly@Overridepublic void onServiceDisconnected(ComponentName name) {Logger.i(TAG, onServiceDisconnected);mIRemoteService = null;}};@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.bt_bind:bindService(new Intent(com.ricky.android.aidl), mConnection, Context.BIND_AUTO_CREATE);isBind = true;tv_status.setText(Binding);break;case R.id.bt_unbind:if (isBind) {try {unbindService(mConnection);isBind = false;tv_status.setText(Unbinding);} catch (Exception e) {e.printStackTrace();}}break;case R.id.bt_call:if (isBind) {try {int pid = mIRemoteService.getPid();Logger.i(TAG, call mIRemoteService's getPid pid=+pid);mIRemoteService.say(1, hello world);int sum = mIRemoteService.sum(100, ricky);Logger.i(TAG, call mIRemoteService's sum result=+sum);Student stu = mIRemoteService.getStudent(3);Logger.i(TAG, call mIRemoteService's getStudent stu=+stu);List list = mIRemoteService.getAddress(2);Logger.i(TAG, call mIRemoteService's getAddress list=+list);} catch (RemoteException e) {e.printStackTrace();}}else{Logger.i(TAG, not bind remote service);Toast.makeText(getApplicationContext(), not bind remote service,Toast.LENGTH_SHORT).show();}break;default:break;}}@Overrideprotected void onDestroy() {if (isBind) {try {unbindService(mConnection);isBind = false;tv_status.setText(Unbinding);} catch (Exception e) {e.printStackTrace();}}super.onDestroy();}}
Use Context. bindService (Intent service, ServiceConnection conn, int flags); bind the method to the remote Service, and then use IRemoteService com. ricky. android. aidl. protocal. IRemoteService. stub. asInterface (IBinder obj); method to get IRemoteService, then the Client can perform IPC through the obtained IRemoteService object.
Activity_main.xml
The running interface is as follows: