Android開発(54)AIDL例

14988 ワード

背景
最近、プロジェクトの再構築を考慮すると、プロジェクトを2つのAPKに分割し、1つはデータサービス、1つはUI展示に使用することを考慮しています.データサービスAPKは、自身が作成したAPKにデータを提供するとともに、第三者にデータを提供することもできる.jar形式のsdkパケットをサードパーティに提供する代わりに、このような方法を用いることが考えられる.複数のAPKに分割すると,プロセス間通信(IPC)の問題を考慮せざるを得ない.AndroidはIPCの実現を提供していますAIDLです.
AIDLを学習する際の記述例は本明細書を形成する.Githubのdemoプロジェクトに入れます.次のアドレスでソースgithubにダウンロードできます.https://github.com/vir56k/demo/tree/master/aidlDemo
AIDLとは
AIDL(Android Interface Definition Language,Androidインタフェース定義言語)は、異なるプロセス(アプリケーション)間でデータ交換を行い、間の通信インタフェースを約束する.
オブジェクト向けの観点から,インタフェース設計は状態と挙動を考慮する.一般に、インタフェース定義の内容は、1.方法操作(動作の記述)2.パラメータ(記述状態、データの種類、データのキャリア/エンティティ)
AIDLは、特有の構文記述を持つIDLです.約束としてAIDLファイルを作成する必要があります.その構文はjava構文によく似ています.int,String,floatなどの基礎データ型をサポートします.エンティティクラスをサポートし、Parcelableインタフェースを実装し、シーケンス化をサポートする必要があります.
AIDLは、サービスバインディングによって使用される.IBinderオブジェクトを渡すサービスを定義する必要があります.このIBinderオブジェクトには必要な方法があります.このオブジェクトを取得して具体的な方法を実行します.
AIDLは,サービス側とクライアントサービス側,すなわちサービスが提供しており,操作可能な方法とデータを提供している.クライアントは呼び出し者、使用方法、データです.
いつAIDLを使用するのに適していますか:公式ドキュメントでは、クライアントが異なるアプリケーションからプロセス間の通信のためにサービスにアクセスすることを許可し、サービスでマルチスレッドを処理することをお勧めします.
手順の説明
サービス・エンドの開発手順は次のとおりです.
1.AIDLファイルを定義する2.説明のインタフェースを実現し、サービス3を記述する.エンティティクラスがある場合は、エンティティクラス(jarパッケージ形式)を指定する必要があります.
クライアント
1.AIDLファイルを入手する2.サービスをバインドし、インタフェースの所有オブジェクトを取得します.

サービス開発
1.AIDLファイルの宣言
Androidが提供する特殊なフォルダは、src/mian/aidlフォルダの下にAIDLファイルを配置します.Javaクラス/インタフェースにはpackage(ネーミングスペース)があるためです.一般的にファイルの場所と一致するネーミングスペースを定義する必要があります.ここでは、src/mian/aidlフォルダの下にpackageを作成します.名前はcomです.example.myserver. 対応するフォルダパスはsrc/mian/aidl/com/example/myserverです.このファイルの下でaidlファイルを作成します.内容は以下の通りです.
IRemoteService.aidl
    package com.example.myserver;
    import com.example.myserver.Entity;
    import com.example.myserver.IMyCallback;
    
    // Declare any non-default types here with import statements
    
    interface IRemoteService {
    
        void doSomeThing(int anInt,String aString);
    
        void addEntity(in Entity entity);
    
        List getEntity();
    
    }

Entity.aidl、これはエンティティクラスで、java classファイルに対応する必要があります.
// Entity.aidl
package com.example.myserver;

parcelable Entity;

2.インタフェースの実装、サービスの作成
src/javaフォルダにMyService classを書き、サービスServiceクラスを統合します.mainifestファイルにこのサービスクラスを登録します.aidl記述ファイルの作成に間違いがなければ、android studioは自動的に補助クラスを生成し、次のディレクトリで見つけることができます.
build/generated/source/debug

このフォルダの下にIremoteServiceクラスとそのサブクラスIremoteServicesが自動的に生成されます.Stubクラスその他.興味のある学生は読むことができます.
IRemoteService.Stubは抽象クラスであるルートファイルです.次のコードはIremoteServicesを示しています.Stubの匿名クラスの実装.このサービスクラスのpublic IBinder onBind(Intent intent)メソッドでは、IremoteServicesをreturnします.Stubの匿名クラス実装.
お客様がこのサービスにバインドされると、この実装のインスタンスが得られ、そのメソッドが呼び出されます.
コードは次のとおりです.
package com.example.myserver;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by zhangyunfei on 16/10/12.
 */
public class MyService extends Service {
    public static final String TAG = "MyService";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, String.format("on bind,intent = %s", intent.toString()));
        return binder;
    }


    private final IRemoteService.Stub binder = new IRemoteService.Stub() {
        public static final String TAG = "IRemoteService.Stub";
        private List data = new ArrayList();

        @Override
        public void doSomeThing(int anInt, String aString) throws RemoteException {
            Log.d(TAG, String.format("  :%s, %s", anInt, aString));
        }

        @Override
        public void addEntity(Entity entity) throws RemoteException {
            Log.d(TAG, String.format("  :entity = %s", entity));
            data.add(entity);
        }

        @Override
        public List getEntity() throws RemoteException {
            return data;
        }


    };
}

3.エンティティークラスの作成
インタフェースのパラメータは、エンティティクラスであってもよい.前にentityを定義しましたaidl、その中に一言書いてあります
     parcelable Entity;  

この文は、特定のエンティティクラスに関連付ける必要があることを示します.src/javaフォルダでこのようなクラスの実装を記述する必要があります.parcelableインタフェースを実装する必要があります.まずpackageを作成することに注意してください.このpackageはaidlインタフェース宣言と一致します.Android studioは、macの下にcommand+スペースを持つparcelableを自動的に生成するショートカットキーを便利に提供します.実装後のコードは次のとおりです.
package com.example.myserver;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by zhangyunfei on 16/10/12.
 */
public class Entity implements Parcelable {
    int age;
    String name;

    public Entity() {
    }

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

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

    public static final Creator CREATOR = new Creator() {
        @Override
        public Entity createFromParcel(Parcel in) {
            return new Entity(in);
        }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(age);
        dest.writeString(name);
    }

    @Override
    public String toString() {
        return String.format("age=%s, name=%s", age, name);
    }
}

クライアント開発-AIDLインタフェースの呼び出し
再開する前にappを新規作成してプレゼンテーションをすることができます.手順は次のとおりです.
1.AIDLを取得し、プロジェクトに入れる
私たちはまずAIDL記述ファイルを手に入れてから使用し、AIDLファイルをaidlフォルダの下に置きます.Android studioはルートファイルクラスを自動的に生成します.エンティティーを取得します.classはプロジェクトに入れます.
2.activityで呼び出す
MainActivityでサービスをバインド
     Intent intent = new Intent();
        intent.setAction("com.example.REMOTE.myserver");
        intent.setPackage("com.example.myserver");
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);

サービス名を指定します.bindServiceメソッドでは、ServiceConnectionオブジェクトを転送する必要があります.ServiceConnectionの匿名クラスを書き、そのonServiceConnectedメソッドでaidl定義のインタフェース保有クラスを取得します.
                    iRemoteService = IRemoteService.Stub.asInterface(service);

サービスクラスが返すbinderを作成したばかりのことを覚えていますか.ここで得られたのはそのbinderの例です.この例を変換したオブジェクトからインタフェース定義のメソッドを呼び出すことができます.
3.インタフェースメソッドの呼び出し
iRemoteServices経由addEntity(entity)メソッドでは、パラメータとしてエンティティクラスに入力する特定のエンティティを操作できます.
     if (!mBound) {
                    alert("        ");
                    return;
                }
                try {
                    Entity entity = new Entity(1, "zhang");
                    if (iRemoteService != null)
                        iRemoteService.addEntity(entity);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

完全なコードは次のとおりです.
package com.example.zhangyunfei.myapplication;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.example.myserver.Entity;
import com.example.myserver.IMyCallback;
import com.example.myserver.IRemoteService;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private boolean mBound = false;
    private IRemoteService iRemoteService;

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

        findViewById(R.id.btnAdd).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!mBound) {
                    alert("        ");
                    return;
                }
                try {
                    Entity entity = new Entity(1, "zhang");
                    if (iRemoteService != null)
                        iRemoteService.addEntity(entity);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }


        });

        findViewById(R.id.btnList).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!mBound) {
                    alert("        ");
                    return;
                }
                if (iRemoteService != null) {
                    try {
                        List entityList = iRemoteService.getEntity();

                        StringBuilder sb = new StringBuilder("    :" + entityList.size() + "\r
"); for (int i = 0; i < entityList.size(); i++) { sb.append(i + ": "); sb.append(entityList.get(i) == null ? "" : entityList.get(i).toString()); sb.append("
"); } alert(sb.toString()); } catch (RemoteException e) { e.printStackTrace(); } } } }); findViewById(R.id.btnCallback).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!mBound) { alert(" "); return; } try { if (iRemoteService != null) { final String para = "canshu"; iRemoteService.asyncCallSomeone(para, new IMyCallback.Stub() { @Override public void onSuccess(String aString) throws RemoteException { alert(String.format(" : %s, : %s", para, aString)); } }); } } catch (RemoteException e) { e.printStackTrace(); } } }); } private void alert(String str) { Toast.makeText(this, str, 0).show(); } @Override protected void onStart() { super.onStart(); if (!mBound) { attemptToBindService(); } } @Override protected void onStop() { super.onStop(); if (mBound) { unbindService(mServiceConnection); mBound = false; } } /** * */ private void attemptToBindService() { Intent intent = new Intent(); intent.setAction("com.example.REMOTE.myserver"); intent.setPackage("com.example.myserver"); bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); } private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e(getLocalClassName(), "service connected"); iRemoteService = IRemoteService.Stub.asInterface(service); mBound = true; if (iRemoteService != null) { try { iRemoteService.doSomeThing(0, "anything string"); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void onServiceDisconnected(ComponentName name) { Log.e(getLocalClassName(), "service disconnected"); mBound = false; } }; }

コールバック
AIDLでは、コールバックを実装し、コールバックcallbak、またはlistenerクラスに転送する必要がある場合があります.どのように実現しますか?
1.コールバッククラスaidlファイルの作成
IMyCallbackクラスにはonSuccessコールバックメソッドIMyCallbackがある.aidl、このファイルにはコールバックインタフェースが記述されています
// IMyCallback.aidl
package com.example.myserver;

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

interface IMyCallback {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void onSuccess(String aString);
}

2.パラメータとしてコールバック・クラスを使用するメソッドを宣言します.例:
IRemoteService.aidl
    package com.example.myserver;
    import com.example.myserver.Entity;
    import com.example.myserver.IMyCallback;
    
    // Declare any non-default types here with import statements
    
    interface IRemoteService {
   
        void asyncCallSomeone( String para, IMyCallback callback);
    }

3.実現方法、コールバック通知を開始する
コールバックを開始するには、ブロードキャストのような方法があります.例:
                @Override
        public void asyncCallSomeone(String para, IMyCallback callback) throws RemoteException {
            RemoteCallbackList remoteCallbackList = new RemoteCallbackList<>();
            remoteCallbackList.register(callback);
            final int len = remoteCallbackList.beginBroadcast();
            for (int i = 0; i < len; i++) {
                remoteCallbackList.getBroadcastItem(i).onSuccess(para + "_callbck");
            }
            remoteCallbackList.finishBroadcast();
        }

コールバックするクラスの例callbackの例をこのセットに入れるRemoteCallback Listコレクションクラスが必要です.この集合クラスRemoteCallback Listを呼び出す次の2つの方法:beginBroadcastがブロードキャストを開始し、finishBroadcastがブロードキャストを終了し、協力して使用します.
4.クライアント呼び出しの例:
クライアントは、インタフェース操作オブジェクトを取得した後、コールバッククラスに転送します.例:
        try {
                if (iRemoteService != null) {
                    final String para = "canshu";
                    iRemoteService.asyncCallSomeone(para, new IMyCallback.Stub() {
                        @Override
                        public void onSuccess(String aString) throws RemoteException {
                            alert(String.format("  : %s,   : %s", para, aString));
                        }
                    });
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }

リファレンス
Google公式ドキュメント