記事のディレクトリ
AIDLの紹介
AIDLはAndroidインターフェース定義言語であり、私たちの開発における通常のインターフェースに少し似ています。異なるプロセス間でメモリを共有することはできないため、プロセス間通信の問題を解決するために、AIDLインターフェイス言語を介してプロセス間通信を実現できます。
AIDLファイルでサポートされているデータ型
- 基本的なデータ型(int、long、char、boolean、double)
- 文字列とCharSequence
- リストと地図のコレクション
- コレクション内の要素は、AIDLでサポートされているデータ型である必要があります
- サーバーが使用する特定のコレクションは、ArrayListとHashMapである必要があります
- Parcelable:Parcelableインターフェースを実装するオブジェクト
- AIDL独自のインターフェースはAID1ファイルでも使用できます。
AIDLの手順
3つの主要なステップがあります
- AIDLを作成する
- インスタンスクラスを作成するには、シリアル化と逆シリアル化のためのParcelableインターフェイスを実装する必要があります
- 新しいaidlフォルダーを作成し、インスタンスクラスと同じ名前のaidlファイルとその中の他のaidlインターフェイスファイルを作成します
- プロジェクトを再構築して、BinderのJavaインターフェイスファイルを生成します。
- サーバーを作成する(サービス)
- サービスを作成し、onBindメソッドでBinderインスタンスオブジェクトを返し、aidlインターフェイスメソッドを実装します。
- カスタマーサービス端末の作成(アクティビティまたはその他)
- bindServiceを使用してサービスを開始し、ServiceConnectionインターフェースを実装し、aidlによって生成されたBinderオブジェクトを取得します。
- aidlで定義された関数メソッドを呼び出します。
AIDLの具体的な実現
AIDLの開発を容易にするために、AIDLに関連するすべてのクラスとファイルを同じパッケージに配置して、他の場所で使用したり、直接コピーしたりできるようにすることをお勧めします。
以下は、特定の例のファイルディレクトリです。
aidlファイルディレクトリはjavaディレクトリと同じレベルにあります
1.プロセス間で送信する必要のあるエンティティクラスを作成し、Parcelableインターフェイスを実装します
public class Book implements Parcelable{
private String bookName;
public Book(String bookName) {
this.bookName = bookName;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.bookName);
}
protected Book(Parcel in) {
this.bookName = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel source) {
return new Book(source);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public String toString() {
return "Book{" +
"bookName='" + bookName + '\'' +
'}';
}
}
パーセラブルインターフェイスは、インスタンスクラスがプロセス間で通信できるように実装されています。
実際、プロセス間通信はシリアル化と逆シリアル化のプロセスです。シリアル化については、この記事「Android Process Communication-Serialization SerialzableandParcelable」を参照してください。
2.新しいaidlフォルダーを作成し、インスタンスクラスと同じ名前のaidlファイルとその中の他のaidlインターフェイスファイルを作成します
a。新しいaidlフォルダーを作成します。メインaidlディレクトリとjavaディレクトリは、メインディレクトリの下の同じレベルにあります。
b。エンティティクラス(Book)と同じ名前のaidlファイルを作成します:Book.aidl
// Book.aidl
package com.hzw.progress.aidl;
//声明Book实体类已序列化,可用于进程间传输
parcelable Book;
これの目的は、BookクラスがParcelableインターフェイスを実装し、Bookオブジェクトをプロセス間でデータを転送するためのキャリアとして使用できることをaidlファイルに通知することです。
注:BookエンティティクラスとBook.aidlファイルは同じパッケージ構造ディレクトリにある必要があります。そうでない場合、クライアントはサーバーのAIDLインターフェイス関連クラスを逆シリアル化する必要があるため、エラーが発生します。クラスのパスが異なる場合、クライアントのDesequenceは失敗します。
IBookManager.aidlインターフェース
// IBookManager.aidl
package com.hzw.progress.aidl;
// Declare any non-default types here with import statements
//虽然在IBookManager.aidl和Book.aidl同一个包名下,但必须以import方式将Book.aial文件导入(手动导入)
import com.hzw.progress.aidl.Book;
import com.hzw.progress.aidl.OnNewBookAddListener;
//定义客户端与服务端间的回调接口
interface IBookManager {
//得到全部数据(全部书籍)
List<Book> getBookList();
//添加数据的操作(添加书籍)
void addBook(in Book book);
//注册监听,用于监听新数据的变化。是典型的观察者模式的运用
void registerListener(OnNewBookAddListener listener);
//解注册
void unregisterListener(OnNewBookAddListener listener);
}
OnNewBookAddListener.aidlインターフェースファイル
// OnNewBookAddListener.aidl
package com.hzw.progress.aidl;
import com.hzw.progress.aidl.Book;
// Declare any non-default types here with import statements
//定义监听新数据变化的接口(添加新书籍)
interface OnNewBookAddListener {
void onNewBookAdd(in Book book);
void onAllBook();
}
上記の点に注意してください。addBookおよびonNewBookAddメソッドパラメーターにはinがあります。これは、入力タイプパラメーターを意味します。AIDLの基本的なデータ型に加えて、他のデータ型には方向、入力、出力、または入力のマークを付ける必要があります。
- inは入力パラメータを意味します
- outは出力パラメータを意味します
- inoutは入力パラメータと出力パラメータを表します
c。aidlファイルを作成した後、プロジェクトを再構築してBinderjavaインターフェースファイルを生成する必要があります。
3.サービスから継承されたサーバー側のBookManagerServiceを作成します
public class BookManagerService extends Service {
private static final String TAG = "BookManagerService";
private List<Book> mBookList=new ArrayList<>();
private static List<OnNewBookAddListener> mAddListenerList=new ArrayList<>();
public BookManagerService() {
}
@Override
public void onCreate() {
super.onCreate();
//初始化书籍
mBookList.add(new Book("Android群英传"));
mBookList.add(new Book("Android开发艺术探索"));
}
@Override
public IBinder onBind(Intent intent) {
//获取Service需要的权限,进行自我验证,防止其他进程访问该服务
int check= checkCallingOrSelfPermission("com.hzw.progress.aidl.permission.LOCAL");
//未授权该应用,则无法访问
if (check== PackageManager.PERMISSION_DENIED){
return null;
}
//添加新书的过程
BookFactory factory = new BookFactory();
factory.execute(5); //添加五本书
return mBinder;
}
/*
* 初次new IBookManager方式构建Binder对象,会报错找不到IBookManager对象
* 需要Rebuild下项目
* */
private Binder mBinder=new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public void registerListener(OnNewBookAddListener listener) throws RemoteException {
if (!mAddListenerList.contains(listener)){
mAddListenerList.add(listener);
}
Log.i(TAG, "registerListener: "+mAddListenerList.size());
}
@Override
public void unregisterListener(OnNewBookAddListener listener) throws RemoteException {
if (mAddListenerList.contains(listener)){
mAddListenerList.remove(listener);
}
}
};
/**
* 使用静态内部类,避免泄漏
*/
private static class BookFactory extends AsyncTask<Integer,Book,Book>{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Book doInBackground(Integer... integers) {
//每隔2秒添加新建一本新书,最多只能添加5本
int i=0;
Book mBook=null;
while (i< integers[0]){
mBook=new Book("新书"+(i+1));
try {
Thread.sleep(2000);
publishProgress(mBook);
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return mBook;
}
@Override
protected void onProgressUpdate(Book... values) {
super.onProgressUpdate(values);
//监听每次添加数据内容
for (int i = 0; i <mAddListenerList.size() ; i++) {
OnNewBookAddListener listener = mAddListenerList.get(i);
try {
if (listener!=null){
listener.onNewBookAdd(values[0]);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
protected void onPostExecute(Book book) {
super.onPostExecute(book);
//得到全部的书籍
for (int i = 0; i <mAddListenerList.size() ; i++) {
OnNewBookAddListener listener = mAddListenerList.get(i);
try {
if (listener!=null){
listener.onAllBook();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
サーバーはサービスの典型的な実装です。特定の使用法については、サービスの例を深く理解するためにこの記事を参照してください。上記の点に注意してください。
- onBindメソッドでは、悪意のあるアクセスを防ぐために、アクセス許可の判断を追加するのが最善です。Permissionを使用して、マニフェストで確認および宣言できます。
- Binderオブジェクトを作成する場合、新しいBinder()メソッドを使用することはできません。新しいIBookManager.Stub()を使用して、AIDLインターフェイスファイルによって自動的に生成されるBinderオブジェクトであるBinderオブジェクトを作成する必要があります。
最後に、Mainfestファイルにサービスを登録すると同時に、サーバーのアクセス権をカスタマイズして参照する必要があります。
<!--自定义访问Service的权限-->
<permission android:name="com.hzw.progress.aidl.permission.LOCAL"/>
<!--声明本拥有该权限-->
<uses-permission android:name="com.hzw.progress.aidl.permission.LOCAL"/>
<service
android:name=".aidl.BookManagerService"
android:enabled="true"
android:exported="true"
android:process=":remote"
android:permission="com.hzw.progress.aidl.permission.LOCAL">
</service>
注:BookManagerServiceは別のプロセスで必要であり、属性android:process = ":remote"を定義する必要があります
3.カスタマーサービス端末の作成
リモートサービスをバインドした後、ServiceConnectionを介してプロキシオブジェクトBinderを取得すると同時に、BinderオブジェクトをAIDLインターフェイスに変換し、AIDLインターフェイスを介してサーバーのメソッドを呼び出します。
public class AIDLActivity extends AppCompatActivity {
private static final String TAG = "AIDLActivity";
private Intent mIntent;
private IBookManager mIBookManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
mIntent = new Intent(this, BookManagerService.class);
}
public void onClick(View view) {
switch (view.getId()){
case R.id.bindService:
//绑定服务
bindService(mIntent,mConnection,BIND_AUTO_CREATE);
break;
case R.id.unbindService:
//解绑服务
unbindService(mConnection);
break;
}
}
private ServiceConnection mConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
//ClassCastException
// IBookManager iBookManager=(IBookManager)service;
mIBookManager = IBookManager.Stub.asInterface(service);
mIBookManager.addBook(new Book("Android进阶之光"));
List<Book> bookList = mIBookManager.getBookList();
// Log.i(TAG, "全部书籍: "+bookList.toString());
mIBookManager.registerListener(sListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
try {
mIBookManager.unregisterListener(sListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private OnNewBookAddListener sListener=new OnNewBookAddListener.Stub() {
@Override
public void onNewBookAdd(Book book) throws RemoteException {
mIBookManager.addBook(book);
Log.i(TAG, "onNewBookAdd: "+book.toString());
}
@Override
public void onAllBook() throws RemoteException {
List<Book> bookList = mIBookManager.getBookList();
Log.i(TAG, "onAllBook: "+bookList.toString());
}
};
}
上記はクライアントの基本的な実装です。次の点に注意する必要があります。
- プロキシオブジェクトBinderを取得する場合、強制変換メソッドを直接使用することはできません。そうしないと、型変換例外が報告されます。AIDLインターフェイスファイルによって生成されたBinderオブジェクトを取得するには、IBookManager.Stub.asInterface()を使用する必要があります。
- AIDlインターフェイスインスタンスオブジェクトを取得する必要がある場合は、インスタンスを取得するために.Stub()メソッドが必要です。
上記の3つのステップは、AIDLによるプロセス通信の実現の簡単な例であり、実行結果は次のとおりです。
07-14 17:59:47.974 6970-6988/com.hzw.progress I/AIDLActivity: onNewBookAdd: Book{bookName='新书1'}
07-14 17:59:49.974 6970-6989/com.hzw.progress I/AIDLActivity: onNewBookAdd: Book{bookName='新书2'}
07-14 17:59:51.974 6970-6988/com.hzw.progress I/AIDLActivity: onNewBookAdd: Book{bookName='新书3'}
07-14 17:59:53.975 6970-6989/com.hzw.progress I/AIDLActivity: onNewBookAdd: Book{bookName='新书4'}
07-14 17:59:55.976 6970-6988/com.hzw.progress I/AIDLActivity: onNewBookAdd: Book{bookName='新书5'}
07-14 17:59:55.977 6970-6989/com.hzw.progress I/AIDLActivity: onAllBook: [Book{bookName='Android群英传'},
Book{bookName='Android开发艺术探索'}, Book{bookName='Android进阶之光'}, Book{bookName='新书1'},
Book{bookName='新书2'}, Book{bookName='新书3'}, Book{bookName='新书4'}, Book{bookName='新书5'}]
総括する
上記の例は基本的にAIDLプロセス間の通信を実現しますが、次のような多くの質問がまだあります。
1.コミュニケーションを実現する過程でAIDLは正確に何をしますか?
2.サーバーはBinderオブジェクトを作成するか、AIDLインターフェイスオブジェクトを取得します。どちらもStub()メソッドを呼び出すことで実装されます。このメソッドの具体的な操作は何ですか?
3.バインダーとは何ですか?
AIDLを使用する際の注意点
- AIDLインターフェイスファイルのパッケージ構造は、エンティティクラスと同じ構造である必要があります。そうでない場合、逆シリアル化は失敗します。
- エンティティクラスと同じ名前のAIDLインターフェイスファイルは、parcelableを使用して、Bookクラスがシリアル化されていることを宣言する必要があり、すべて小文字であることに注意してください。
- 1つのAIDLファイルで別のAIDLファイルを使用する場合は、手動で明示的に(完全なパッケージ名)インポートする必要があります。
上記のいずれかを誤って誤ると、AIDLはプロセス通信を行うことができなくなります。
上記の質問については、次の記事でAndroidプロセス通信を学ぶ予定
です-AIDL使用分析とバインダートーク
参照
- Android開発アート探索
- https://blog.csdn.net/luoyanglizi/article/details/51980630
- https://www.jianshu.com/p/b9b15252b3d6
- https://blog.csdn.net/u011240877/article/details/72765136