例:AndroidはAIDLを使用してプロセス間通信(IPC)を実現する

26753 ワード

AIDLの概要
AIDL(Android Interface Definition Language)は、クライアントとサーバの通信インタフェースの標準、規範を定義するAndroidインタフェース定義言語です.Google公式AIDLの説明は以下の通りです.
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 across different applications, you should 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.
意味は「AIDLは、異なるアプリケーションからのサービスによるプロセス通信を許可し、サービスでマルチスレッドを処理する必要がある場合にのみ使用されます.異なるアプリケーション間のインスタントプロセス通信を実現する必要がない場合は、Binderを実現するインタフェースを作成する必要があります.または、プロセス通信を実現したいが、マルチスレッドを処理する必要がない場合は、つのMessengerはあなたのインタフェースを実現して、しかし、いずれにしても、あなたは先にローカルのサービスがあなたがAIDLを実現する前に理解しなければなりません"上のこの言葉を通じて私達はAIDLの作用が2つの異なるアプリケーションの間でServiceを通じて通信することであることをよく知っています(プロセス通信IPC)簡単に言えば、一つのアプリケーションは対外的に一つのリモートサービスを提供し、他のアプリケーションは同時にこのサービス、すなわちC/Sモードにアクセスすることができる.
AIDL使用例
サービス側実装
1.Android Studioを使用して、サービス側アプリケーションとして新しいModuleを作成します.
2.AIDLファイルを作成します.src/mainディレクトリの下にaidlフォルダを新規作成し、このディレクトリの下にIremoteService.aidlファイルを新規作成します.次のようにします.
// IRemoteService.aidl
package com.lonly.example.aidlservicer;
//                  
import com.lonly.example.aidlservicer.Person;

// Declare any non-default types here with import statements

interface IRemoteService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
     /**
      * Request the process ID of this service, to do evil things with it
      */
     int getPid();
     /**
      * get name by id
      */
     String getName(int id);
     /**
     * get Person
     */
     Person getPerson(int id);

     /**
     *      in:  tag。
     *   ,
      * in                ,
     * out                ,
     * inout                     。
     */
     void addPerson(in Person person);

     List getPersons();
}

注意:ここでPersonクラスはカスタマイズされているので、IremoteService.aidlとPerson.javaが同じパッケージに入っている場合でも、「import com.lonly.example.aidlservicer.Person;」という文を追加する必要があります.追加しない場合は、コンパイルタイムズ「Error:Execution failed for task':android:compileDebugAidl...」エラーでコンパイルに失敗しました.IremoteService.aidlファイルでは、シーケンス化インタフェースParcelableを実装する必要があるカスタムPersonクラス(同じくsrc/main/aidlディレクトリの下に配置)を使用します.定義は次のとおりです.
public class Person implements Parcelable{
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    protected Person(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    /**
     *                         。      CREATOR,      
     * Parcelable.Creator  T:        
     */
    public static final Creator CREATOR = new Creator() {
        /***
         *       Parcel  ,            
         *       writeToParcel       
         */
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in.readString(),in.readInt());
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[0];
        }
    };
    @Override
    public int describeContents() {
        return 0;
    }
    /**
     *       Parcel(   )
     * @param dest:             
     * @param flags:              
     *      ,        createFromParcel            。         name,
     *    createFromParcel    name
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }
    /**
     *      Parcel,          
     * @param dest
     */
    private void readFromParcel(Parcel dest) {
        //  ,           writeToParcel()      
        this.name = dest.readString();
        this.age = dest.readInt();
    }
}

Android Studioのショートカットヒント機能により、Personクラスを簡単に作成できますが、以下の方法は手動で追加したものです.
 /**
     *      Parcel,          
     * @param dest
     */
    private void readFromParcel(Parcel dest) {
        //  ,           writeToParcel()      
        this.name = dest.readString();
        this.age = dest.readInt();
    }
ずっと便利にするために、Person.javaクラスもIremoteService.aidlファイルと一致するsrc/main/aidlディレクトリの下に保存します.ただし、build.gradleファイルを修正し、android{}の間に次の内容を加えます. 
sourceSets {
    main {
        java.srcDirs = ['src/main/java', 'src/main/aidl']
    }
}

また、Personクラスを定義した後、拡張子は.aidlと同じファイル名を定義する必要があります.
// Person.aidl
package com.lonly.example.aidlservicer;

// Declare any non-default types here with import statements
parcelable Person;

注意:parcelableの頭文字は小文字です.
3.実行項目のコンパイル.コンパイルに成功すると、buildディレクトリの下にIremoteServices.aidlに対応するIremoteServices.javaファイルが自動的に生成されます.次のようになります.
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: E:\\ASWorkSpace\\AIDLDemo\\aidlservicer\\src\\main\\aidl\\com\\lonly\\example\\aidlservicer\\IRemoteService.aidl
 */
package com.lonly.example.aidlservicer;
// Declare any non-default types here with import statements

public interface IRemoteService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.lonly.example.aidlservicer.IRemoteService
{
private static final java.lang.String DESCRIPTOR = "com.lonly.example.aidlservicer.IRemoteService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.lonly.example.aidlservicer.IRemoteService interface,
 * generating a proxy if needed.
 */
public static com.lonly.example.aidlservicer.IRemoteService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.lonly.example.aidlservicer.IRemoteService))) {
return ((com.lonly.example.aidlservicer.IRemoteService)iin);
}
return new com.lonly.example.aidlservicer.IRemoteService.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_basicTypes:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_getPid:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getName:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.getName(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_getPerson:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
com.lonly.example.aidlservicer.Person _result = this.getPerson(_arg0);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_addPerson:
{
data.enforceInterface(DESCRIPTOR);
com.lonly.example.aidlservicer.Person _arg0;
if ((0!=data.readInt())) {
_arg0 = com.lonly.example.aidlservicer.Person.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getPersons:
{
data.enforceInterface(DESCRIPTOR);
java.util.List _result = this.getPersons();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.lonly.example.aidlservicer.IRemoteService
{
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;
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public int getPid() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
      * get name by id
      */
@Override public java.lang.String getName(int id) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(id);
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
     * get Person
     */
@Override public com.lonly.example.aidlservicer.Person getPerson(int id) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.lonly.example.aidlservicer.Person _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(id);
mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.lonly.example.aidlservicer.Person.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
     *      in:  tag。
     *   ,
      * in                ,
     * out                ,
     * inout                     。
     */
@Override public void addPerson(com.lonly.example.aidlservicer.Person person) 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 ((person!=null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.util.List getPersons() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPersons, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.lonly.example.aidlservicer.Person.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_getPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_getPersons = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public int getPid() throws android.os.RemoteException;
/**
      * get name by id
      */
public java.lang.String getName(int id) throws android.os.RemoteException;
/**
     * get Person
     */
public com.lonly.example.aidlservicer.Person getPerson(int id) throws android.os.RemoteException;
/**
     *      in:  tag。
     *   ,
      * in                ,
     * out                ,
     * inout                     。
     */
public void addPerson(com.lonly.example.aidlservicer.Person person) throws android.os.RemoteException;
public java.util.List getPersons() throws android.os.RemoteException;
}
このAIDLで生成されたJavaインタフェースを開くと、Binderを継承し、このインタフェースを実現した内部静的抽象クラスStubがあることがわかります.そのため、このStubクラスを直接使用してリモートサービスの構築を完了することができます.
4.RemoteServiceクラスを作成し、そのAIDLファイルから生成されたJavaインタフェースを実装する.onBindメソッドでは、AIDL Javaインタフェースを実装したBinderクラス(new 1つのStubクラスがちょうどよい)を返し、このServiceエージェントクラスとして使用する.以下のようにする.
public class RemoteService extends Service{

    private List persons = new ArrayList<>();
    /**
     *     RemoteService    IBinder      
     * @param intent
     * @return
     */

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mRemoteService;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //   persons
        persons.add(new Person("  ",20));
        persons.add(new Person("  ",23));
        persons.add(new Person("  ",21));
        persons.add(new Person("   ",19));
    }

    /**
     *     
     */
    private IRemoteService.Stub mRemoteService = new IRemoteService.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            System.out.println("Thread: " + Thread.currentThread().getName());
            System.out.println("basicTypes aDouble: " + aDouble +" anInt: " + anInt+" aBoolean " + aBoolean+" aString " + aString);
        }

        @Override
        public int getPid() throws RemoteException {
            System.out.println("Thread: " + Thread.currentThread().getName());
            System.out.println("RemoteService getPid ");
            return android.os.Process.myPid();
        }

        @Override
        public String getName(int id) throws RemoteException {
            return persons.get(id).getName();
        }

        @Override
        public Person getPerson(int id) throws RemoteException {
            return persons.get(id);
        }

        @Override
        public void addPerson(Person person) throws RemoteException {
            persons.add(person);
        }

        @Override
        public List getPersons() throws RemoteException {
            return persons;
        }

    };
}

5.このサービスをMenifestで宣言します.
  
            
                
                
            
        

クライアント実装
1.クライアントアプリケーションとしてのMoudleを新規作成します.2.サービス側で定義されたAIDLファイルと関連javaクラスをクライアントに移植します.関連ファイルが上記の手順でsrc/main/aidlディレクトリに格納されている場合は、aidlフォルダを直接コピーしてください.パッケージ名とサービス側の一致を保証します.最後にbuild.gradleファイルを変更することを忘れないでください.android{}に次の内容を追加します.
sourceSets {
    main {
        java.srcDirs = ['src/main/java', 'src/main/aidl']
    }
}

3.MainActivityを定義し、サービス・エンドを呼び出す方法.ただし、その前に、まずサービス・エンドに接続します.次のようにします.
 private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //    IBinder  service     ,              AIDL    
            mRemoteService = IRemoteService.Stub.asInterface(service);
            int pid = 0;
            try {
                pid = mRemoteService.getPid();
                int currentPid = android.os.Process.myPid();
                System.out.println("currentPID: " + currentPid + "  remotePID: " + pid);
                mRemoteService.basicTypes(12, 1223, true, 12.2f, 12.3, "      ,  !");
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            System.out.println("bind success! " + mRemoteService.toString());
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteService = null;
            System.out.println(mRemoteService.toString() + " disconnected! ");
        }
    };

次に、リモート・サービスを暗黙的にバインドすることによって、サービス接続を確立します.次のようにします.
//         
        Intent intent = new Intent();
        intent.setAction("com.lonly.example.aidl");
        intent.setPackage("com.lonly.example.aidlservicer");
        isConnSuccess = bindService(intent, conn, Context.BIND_AUTO_CREATE);

注意:ここのintent.setAction(「com.lonly.example.aidl」)は、サービス側のMenifrstファイルServiceで宣言されたactionプロパティとパラメータが一致する必要があります.
完全なクライアント・コードは次のとおりです.
public class MainActivity extends AppCompatActivity {
    private IRemoteService mRemoteService;
    private TextView mTv_result;
    private TextView mAll_result;
    private EditText mEditText;
    private EditText mEditText1;
    private EditText mEditText2;
    private boolean isConnSuccess;

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

        mEditText = (EditText) findViewById(R.id.editText);
        mEditText1 = (EditText) findViewById(R.id.editText1);
        mEditText2 = (EditText) findViewById(R.id.editText2);

        mTv_result = (TextView) findViewById(R.id.tv_result);
        mAll_result = (TextView) findViewById(R.id.all_result);

        //         
        Intent intent = new Intent();
        intent.setAction("com.lonly.example.aidl");
        intent.setPackage("com.lonly.example.aidlservicer");
        isConnSuccess = bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    public void search(View view) {
        if (isConnSuccess) {
            //     
            String keyStr = mEditText.getText().toString();
            if (!TextUtils.isEmpty(keyStr)) {
                int id = Integer.valueOf(keyStr);
                try {
                    String name = mRemoteService.getName(id);
                    mTv_result.setText(name);
                } catch (RemoteException ex) {
                    ex.printStackTrace();
                }
            } else {
                Toast.makeText(this, "     ", Toast.LENGTH_SHORT).show();
            }


        } else {
            System.out.println("    !");
        }
    }

    public void searchPerson(View view) {
        if (isConnSuccess) {
            //     
            String keyStr = mEditText.getText().toString();
            if (!TextUtils.isEmpty(keyStr)) {
                int id = Integer.valueOf(keyStr);
                try {
                    Person person = mRemoteService.getPerson(id);
                    mTv_result.setText(new StringBuffer("  :").append(person.getName()).append("\t  :").append(String.valueOf(person.getAge())));
                } catch (RemoteException ex) {
                    ex.printStackTrace();
                }
            } else {
                Toast.makeText(this, "     ", Toast.LENGTH_SHORT).show();
            }
        } else {
            System.out.println("    !");
        }
    }
    public void add(View view) {
        if (isConnSuccess) {
            //     
            String nameStr = mEditText1.getText().toString();
            String ageStr = mEditText2.getText().toString();
            if (!TextUtils.isEmpty(nameStr) && !TextUtils.isEmpty(ageStr) ) {
                int age = Integer.valueOf(ageStr);
                try {
                    //        
                    List list = mRemoteService.getPersons();
                    for (Person per:list) {
                        if(TextUtils.equals(nameStr,per.getName())){
                            Toast.makeText(this, "      ", Toast.LENGTH_SHORT).show();
                            return;
                        }
                    }
                    //  
                    Person person = new Person(nameStr,age);
                    mRemoteService.addPerson(person);

                    //    
                    List persons = mRemoteService.getPersons();

                    StringBuffer  sb = new StringBuffer ("");
                    for (Person per:persons) {
                        sb.append("  :")
                                .append(per.getName())
                                .append("\t  :")
                                .append(per.getAge())
                                .append("
"); } mAll_result.setText(sb.toString()); } catch (RemoteException ex) { ex.printStackTrace(); } } else { Toast.makeText(this, " ", Toast.LENGTH_SHORT).show(); } } else { System.out.println(" !"); } } private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // IBinder service , AIDL mRemoteService = IRemoteService.Stub.asInterface(service); int pid = 0; try { pid = mRemoteService.getPid(); int currentPid = android.os.Process.myPid(); System.out.println("currentPID: " + currentPid + " remotePID: " + pid); mRemoteService.basicTypes(12, 1223, true, 12.2f, 12.3, " , !"); } catch (RemoteException e) { e.printStackTrace(); } System.out.println("bind success! " + mRemoteService.toString()); } @Override public void onServiceDisconnected(ComponentName name) { mRemoteService = null; System.out.println(mRemoteService.toString() + " disconnected! "); } }; @Override protected void onDestroy() { super.onDestroy(); unbindService(conn); } }

Activity_mainレイアウトファイルコード:
テスト
サービス側アプリケーションを実行し、クライアントアプリケーションを実行し、テスト結果:
ソースゲート:https://github.com/legallonly/AIDLDemo