[Android] Binder or AIDL easiest practice

1. Introduction:
In Android development between the rational use of multi-process communication IPC + process is a difficult point. In particular unique Android Binder mechanism is very complex, for the primary application layer development engineers to develop a deep understanding of the force Binder mechanism is unrealistic.
In fact, Android developers have already taken into account as we provide a convenient method we use the Binder.
This is AIDL: Android Interface definition language Android Interface Definition Language
accordance with the requirements AIDL simply writing a file, the current IDE, such as Android Sudio This automatically generated based on a Java class for Binder communication
through this class, we do not need to be concerned about Binder how to communicate through Java code, as long as we use the written AIDL files automatically generated classes can be
difficult to understand?

The principle is this: if we want to write the code Binder IPC communication through Java, it is difficult for a novice to write, in fact, and because Java code Binder communication process are similar, so Google would create a AIDL file, we write well AIDL file, to help us generate the corresponding Java code, eliminating the need for us to understand the process of abstraction Binder (computer to help us write code, ha ha)

2. From a simple starting AIDL Example:
In the development of the application layer, used to AIDL common scenario is generally as follows:

We have a Service, for example: DownloadService download files, in order to:

Do not let this DownloadService occupied App's UI process memory resources
DownService Ben collapse UI does not affect the process of the App
we need to register DownloadService in which AndroidManifest time by android: process =: Specifies the Service to run this "remote" in a
separate private process (process called application package name: remote) in (please Baidu android: process = "" usage)

because DownloadService run in a separate process, App default UI process and it needs to communicate that IPC, specifically, is the use of Binder
3. below a step by step demonstration of concrete, practical steps:
we want a MainActivity App represents the current process, DownloadService a process running in another
then:
MainActivity can bind DownloadService by bindService
then Binder implement IPC (interprocess communication) calls the two DownloadService Features:

public List getDownloadTasks (); // Get the current all download tasks
public void addDownloadTask (DownloadTask task) // add a download task

3.1 New two components: MainActivity and DownloadService
MainActivity.java
public class {AppCompatActivity the extends the MainActivity

private static final String TAG = "MainActivity";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

}
DownloadService.java
public class DownloadService extends Service {

private static final String TAG = "DownloadService";

@Override
public void onCreate() {
    super.onCreate();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return super.onStartCommand(intent, flags, startId);
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;//注意:这里我们后面需要返回一个IBinder
}

@Override
public boolean onUnbind(Intent intent) {
    return super.onUnbind(intent);
}

}
AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <service
        android:name=".DownloadService"
        android:process=":remote" />

</application>
3.2 Creating download tasks on behalf of the JavaBean DownloadTask / ** * download task JavaBean * Binder for transmission must be achieved Parcelable serialization * Created by lijian on 2017/3/23. * /

public class DownloadTask implements Parcelable {
public int taskId;
public String fileName;
public String downloadUrl;

public DownloadTask(int taksId, String fileName, String downloadUrl) {
    this.taskId = taksId;
    this.fileName = fileName;
    this.downloadUrl = downloadUrl;
}

@Override
public int describeContents() {
    return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeInt(this.taskId);
    dest.writeString(this.fileName);
    dest.writeString(this.downloadUrl);
}

protected DownloadTask(Parcel in) {
    this.taskId = in.readInt();
    this.fileName = in.readString();
    this.downloadUrl = in.readString();
}

public static final Parcelable.Creator<DownloadTask> CREATOR = new Parcelable.Creator<DownloadTask>() {
    @Override
    public DownloadTask createFromParcel(Parcel source) {
        return new DownloadTask(source);
    }

    @Override
    public DownloadTask[] newArray(int size) {
        return new DownloadTask[size];
    }
};

@Override
public String toString() {
    return "DownloadTask{" +
            "taskId=" + taskId +
            ", fileName='" + fileName + '\'' +
            ", downloadUrl='" + downloadUrl + '\'' +
            "}\n";
}

}
Observation DownloadTask codes, see DownloadTask implements the serialization interface
because DownloadTask the IPC is required, the transmission between two processes, after the
target - "Serialization -" deserialization - "regenerate an object process
note : Since this DownloadTask need to use AIDL in, so we need to declare it in AIDL:

That is DownloadTask.aidl

DownloadTask.aidl //
Package Penalty for com.lijian.binderdemo; // AIDL need to declare the package name

The Declare the any non-default // types here Wallpaper with Import statements
Parcelable DownloadTask;
3.3 create IDownload.aidl
AIDL name is Android interface defined language, the name suggests, aidl is used to define the interface, then the compiler automatically help us generate Binder IPC code

IDownload.aidl //
Package com.lijian.binderdemo;
// the Declare the any non-default types here Wallpaper statements with Import
Import com.lijian.binderdemo.DownloadTask;
interface IDownload {
List getTasks ();
void addTask (in DownloadTask Task); / / use in that this is a variable input
}
there we can see the code defines an interface IDownload
it declares two methods

List

addTask void (in DownloadTask Task);
Build it, a miracle happened:

Here IDownload generate a Java interface, which is based IDownload.aidl we write automatically generated, its role is to facilitate communication IPC carried Binder

/*

  • This file is auto-generated. DO NOT MODIFY.
  • Original file: G:\Android_demo\BinderDemo\app\src\main\aidl\com\lijian\binderdemo\IDownload.aidl
    /
    package com.lijian.binderdemo;
    public interface IDownload extends android.os.IInterface
    {
    /
    * Local-side IPC implementation stub class. /
    public static abstract class Stub extends android.os.Binder implements com.lijian.binderdemo.IDownload
    {
    private static final java.lang.String DESCRIPTOR = “com.lijian.binderdemo.IDownload”;
    /
    * Construct the stub at attach it to the interface. /
    public Stub()
    {
    this.attachInterface(this, DESCRIPTOR);
    }
    /
    *
  • Cast an IBinder object into an com.lijian.binderdemo.IDownload interface,
  • generating a proxy if needed.
    */
    public static com.lijian.binderdemo.IDownload asInterface(android.os.IBinder obj)
    {
    if ((obj==null)) {
    return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof com.lijian.binderdemo.IDownload))) {
    return ((com.lijian.binderdemo.IDownload)iin);
    }
    return new com.lijian.binderdemo.IDownload.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {
    return this;
    }
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
    switch (code)
    {
    case INTERFACE_TRANSACTION:
    {
    reply.writeString(DESCRIPTOR);
    return true;
    }
    case TRANSACTION_getTasks:
    {
    data.enforceInterface(DESCRIPTOR);
    java.util.List<com.lijian.binderdemo.DownloadTask> _result = this.getTasks();
    reply.writeNoException();
    reply.writeTypedList(_result);
    return true;
    }
    case TRANSACTION_addTask:
    {
    data.enforceInterface(DESCRIPTOR);
    com.lijian.binderdemo.DownloadTask _arg0;
    if ((0!=data.readInt())) {
    _arg0 = com.lijian.binderdemo.DownloadTask.CREATOR.createFromParcel(data);
    }
    else {
    _arg0 = null;
    }
    this.addTask(_arg0);
    reply.writeNoException();
    return true;
    }
    }
    return super.onTransact(code, data, reply, flags);
    }
    private static class Proxy implements com.lijian.binderdemo.IDownload
    {
    private android.os.IBinder mRemote;
    Proxy(android.os.IBinder remote)
    {
    mRemote = remote;
    }
    @Override public android.os.IBinder asBinder()
    {
    return mRemote;
    }
    public java.lang.String getInterfaceDescriptor()
    {
    return DESCRIPTOR;
    }
    @Override public java.util.List<com.lijian.binderdemo.DownloadTask> getTasks() throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    java.util.List<com.lijian.binderdemo.DownloadTask> _result;
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    mRemote.transact(Stub.TRANSACTION_getTasks, _data, _reply, 0);
    _reply.readException();
    _result = _reply.createTypedArrayList(com.lijian.binderdemo.DownloadTask.CREATOR);
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    return _result;
    }
    @Override public void addTask(com.lijian.binderdemo.DownloadTask task) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    if ((task!=null)) {
    _data.writeInt(1);
    task.writeToParcel(_data, 0);
    }
    else {
    _data.writeInt(0);
    }
    mRemote.transact(Stub.TRANSACTION_addTask, _data, _reply, 0);
    _reply.readException();
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    }
    }
    static final int TRANSACTION_getTasks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    Public java.util.List <com.lijian.binderdemo.DownloadTask> getTasks () throws android.os.RemoteException;
    public void addTask (com.lijian.binderdemo.DownloadTask Task) throws android.os.RemoteException;
    }
    Code seemingly very confused, it is not true, the level is still very clear, we peel layer by layer code analysis

IDownload Interface 3.3.1
public interface IDownload the extends android.os.IInterface {
// Code omitted
public java.util.List <com.lijian.binderdemo.DownloadTask> getTasks () throws android.os.RemoteException;
public void addTask (COM. Task lijian.binderdemo.DownloadTask) throws android.os.RemoteException;
}
according to our definition IDownload.aidl file, which declares two methods, it generates a Java code
IDownload Interface, extends android.os.IInterface,

foundation class android.os.IInterface Binder interface, a new Binder Interface, this interface must inherit IInterface definition
/ **

  • Base class for Binder interfaces. When defining a new interface,
  • you must derive it from IInterface.
    /
    public interface IInterface
    {
    /
    *
    • Retrieve the Binder object associated with this interface.
    • You must use this instead of a plain cast, so that proxy objects
    • . CAN return at The correct the Result
      * /
      public IBinder asBinder ();
      }
      IDownload the Java declared in AIDL we define the corresponding method, the only difference is that declares these methods may throw an exception Exception Remote call of
      public java.util .list <com.lijian.binderdemo.DownloadTask> getTasks () throws android.os.RemoteException;
      public void addTask (com.lijian.binderdemo.DownloadTask Task) throws android.os.RemoteException;
      3.3.2 static public abstract class the Stub
      in we have said above it is actually the shadow of Client calls, Mami in Server process.
      IDownload static inner classes assist our Client operating in the shadow, created in Mami Server:

它就是public static abstract class Stub
/** Local-side IPC implementation stub class. /
public static abstract class Stub extends android.os.Binder implements com.lijian.binderdemo.IDownload
{
private static final java.lang.String DESCRIPTOR = “com.lijian.binderdemo.IDownload”;
/
* Construct the stub at attach it to the interface. /
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/
*

  • Cast an IBinder object into an com.lijian.binderdemo.IDownload interface,
  • generating a proxy if needed.
    */
    public static com.lijian.binderdemo.IDownload asInterface(android.os.IBinder obj)
    {
    if ((obj==null)) {
    return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof com.lijian.binderdemo.IDownload))) {
    return ((com.lijian.binderdemo.IDownload)iin);
    }
    return new com.lijian.binderdemo.IDownload.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {
    return this;
    }
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
    switch (code)
    {
    case INTERFACE_TRANSACTION:
    {
    reply.writeString(DESCRIPTOR);
    return true;
    }
    case TRANSACTION_getTasks:
    {
    data.enforceInterface(DESCRIPTOR);
    java.util.List<com.lijian.binderdemo.DownloadTask> _result = this.getTasks();
    reply.writeNoException();
    reply.writeTypedList(_result);
    return true;
    }
    case TRANSACTION_addTask:
    {
    data.enforceInterface(DESCRIPTOR);
    com.lijian.binderdemo.DownloadTask _arg0;
    if ((0!=data.readInt())) {
    _arg0 = com.lijian.binderdemo.DownloadTask.CREATOR.createFromParcel(data);
    }
    else {
    _arg0 = null;
    }
    this.addTask(_arg0);
    reply.writeNoException();
    return true;
    }
    }
    return super.onTransact(code, data, reply, flags);
    }
    private static class Proxy implements com.lijian.binderdemo.IDownload
    {
    //省略代码
    }
    static final int TRANSACTION_getTasks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    为了更清晰

Stub.png

You can see the inside

Three constants
Private static DESCRIPTOR = Final java.lang.String "com.lijian.binderdemo.IDownload";
static int TRANSACTION_getTasks = Final (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static int TRANSACTION_addTask = Final (android.os. +. 1 IBinder.FIRST_CALL_TRANSACTION);
the Stub constructors
public the Stub ()
{
this.attachInterface (the this, DESCRIPTOR);
}
a static method: public static com.lijian.binderdemo.IDownload asInterface (android.os.IBinder obj )
this method can be converted into a IBinder com.lijian.binderdemo.IDownload interface

/**

  • Cast an IBinder object into an com.lijian.binderdemo.IDownload interface,
  • . Generating A Proxy IF needed
    * /
    public static com.lijian.binderdemo.IDownload asInterface (android.os.IBinder obj)
    {
    IF ((obj == null)) {
    return null;
    }
    android.os.IInterface Iin = obj. queryLocalInterface (DESCRIPTOR);
    IF (! ((Iin = null) && (Iin the instanceof com.lijian.binderdemo.IDownload))) {
    return ((com.lijian.binderdemo.IDownload) Iin); // this is a local Description IPC call instead of calling directly back to Mami, Mami called directly, without binder, more efficient
    }
    return new new com.lijian.binderdemo.IDownload.Stub.Proxy (obj); // illustrate Yes IPC call, the proxy returns a, proxy class, for Binder calls through his
    }
    two member methods:
    public android.os.IBinder asBinder ()
    @Override
    public android.os.IBinder asBinder () {
    return the this;
    }
    public boolean onTransact (int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    This method operates Server side Binder thread pool, it was observed parameters:
    int code: for different methods of identifying
    android.os.Parcel data: Client is used to pass parameters
    android.os.Parcel reply, the return value for writing Server
    @Override
    public Boolean onTransact (int code, android.os.Parcel Data, Android. Reply os.Parcel, the flags int) throws android.os.RemoteException {
    Switch (code) // Binder by code Client by a method which is called
    {
    Case INTERFACE_TRANSACTION:
    {
    reply.writeString (DESCRIPTOR);
    return to true;
    }
    Case TRANSACTION_getTasks :
    {
    data.enforceInterface (DESCRIPTOR);
    java.util.List <com.lijian.binderdemo.DownloadTask> _result = this.getTasks ( ); // this is a specific implementation of the interface
    reply.writeNoException (); // write Binder remote call no abnormality
    reply.writeTypedList (_result ); // write the return value
    return to true;
    }
    Case TRANSACTION_addTask:
    {
    data.enforceInterface (DESCRIPTOR);
    com.lijian.binderdemo.DownloadTask _arg0;
    ! IF ((0 = data.readInt ())) {
    _arg0 = COM. lijian.binderdemo.DownloadTask.CREATOR.createFromParcel (Data);
    }
    the else {
    _arg0 = null;
    }
    this.addTask (_arg0);
    reply.writeNoException ();
    return to true;
    }
    }
    return super.onTransact (code, Data, Reply, the flags);
    }
    summary:
    After analyzing the IDownload.java, we can see the code generated by IDownload.aidl that ultimately only java code we need, that is, if we are familiar with the routines automatically generate code, in fact, no aidl, handwriting Java code to achieve AIDL calls are possible.
  1. Generated using IDownload.java interface:
    4.1 Server side:
    public class the extends DownloadService {-Service

    private static final String TAG = “DownloadService”;

    private List mTasks = new CopyOnWriteArrayList<>();

    @Override
    public void onCreate() {
    super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    return super.onStartCommand(intent, flags, startId);
    }

// Note that this method, which is the interface specific implementation-defined, specific functions should be granted Server implementation
@Nullable
@Override
public IBinder onBind (the Intent the Intent) {
return new new IDownload.Stub () {
@Override
public List getTasks () throws {RemoteException
return mTasks;
}

        @Override
        public void addTask(DownloadTask task) throws RemoteException {
            if (mTasks != null) {
                mTasks.add(task);
            }
        }
    };
}

@Override
public boolean onUnbind(Intent intent) {
    return super.onUnbind(intent);
}

}
4.2 Client端:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private static final String TAG = "MainActivity";

private IDownload download;//注意这里

private Button addTaskBtn;
private TextView addTaskInfo;
private Button getTasksBtn;
private TextView getTasksInfo;

private int index = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    bindDownloadService();
    setContentView(R.layout.activity_main);
    setupView();
}

private void setupView() {
    addTaskBtn = (Button) findViewById(R.id.btn_add_task);
    getTasksBtn = (Button) findViewById(R.id.btn_get_tasks);
    addTaskInfo = (TextView) findViewById(R.id.tv_add_task_info);
    getTasksInfo = (TextView) findViewById(R.id.tv_tasks_info);
    addTaskBtn.setOnClickListener(this);
    getTasksBtn.setOnClickListener(this);
}

private void bindDownloadService() {
    Intent intent = new Intent(this, DownloadService.class);
    bindService(intent, new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            download = IDownload.Stub.asInterface(service);//我们把bindService返回的 IBinder Service转换为一个接口实例
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }, Context.BIND_AUTO_CREATE);
}


@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.btn_get_tasks:
            getTasks();
            break;
        case R.id.btn_add_task:
            addTask();
            break;
        default:
            break;
    }
}

private void addTask() {
    if (download != null) {
        DownloadTask task = new DownloadTask(index++, "下载文件" + index, "www.baidu.com/" + index + ".txt");
        try {
            download.addTask(task);//这里就可以调用接口实例,看到没有,Binder的IPC操作对于Client端来说就是简化为调用一个对象了
            addTaskInfo.setText(task.toString());
        } catch (RemoteException e) {
            Log.w(TAG, e.toString());//Binder调用可能尝试RemoteException,需要捕获异常
        }
    }
}

private void getTasks() {
    if (download != null) {
        try {
            List<DownloadTask> tasks = download.getTasks();/这里就可以调用接口实例
            getTasksInfo.setText(tasks == null ? "no task" : tasks.toString());
        } catch (RemoteException e) {
            Log.w(TAG, e.toString());
        }
    }
}

}

Posts https://www.cnblogs.com/bylijian/p/7372232.html

Published 14 original articles · won praise 4 · Views 3516

Guess you like

Origin blog.csdn.net/lisiwei1994/article/details/86301715