サービスの熟練度


このセクションの概要:

このセクションでは、引き続き Service コンポーネントについて学習し、Android における AIDL クロスプロセス通信の一部を学習します。 この概念はソース コード レベルには深く入りません。それが何であるかを知り、その使用方法を知るだけです。このセクションを始めてください~ このセクションは、公式ドキュメントに対応しています: Binder


1. Binder メカニズムの最初の紹介


1) IBinder と Binder とは何ですか?

公式ドキュメントの内容を見てみましょう:

1.png

中国語訳:

IBinder は、リモート オブジェクトの基本インターフェイスであり、高パフォーマンス向けに設計された軽量リモート呼び出しメカニズムの中核部分です。でも彼は リモート呼び出しだけでなく、インプロセス呼び出しにも対応します。このインターフェイスは、リモート オブジェクトと対話するためのプロトコルを定義します。ただし、直接実装しないでください このインターフェースは代わりにBinderを継承(拡張)します。

IBinder のメイン API は

transact() で、対応する API は Binder.onTransact() です。前者では、次のことができます。 呼び出しをリモート IBinder オブジェクトに送信します。これにより、リモート オブジェクトが着信呼び出しに応答できるようになります。 IBinder の API は、相手の Binder.onTransact() メソッドが呼び出されるまで、transact() など、すべて Syncronous で実行されます。 彼が戻ってきたのは後になってからだった。 これは間違いなく、呼び出しがプロセス内で発生する場合に当てはまりますが、プロセス間で行われる場合も、IPC の助けを借りて、同じ効果が達成されます。

transact() を通じて送信されるデータは Parcel であり、データに加えて一般的なバッファーも含まれます。 その内容を説明するいくつかのメタデータ。メタデータは、バッファーをあるプロセスから別のプロセスに移動できるように、IBinder オブジェクトへの参照を管理するために使用されます。 別のプロセスに進むときにこれらの参照を保存します。これにより、IBinder が Parcel に書き込まれ、別のプロセスに送信されるときに、 別のプロセスが同じ IBinder への参照を元のプロセスに送り返すと、元のプロセスは送信されたデータを受信できます。 その IBinder への参照。このメカニズムにより、IBinder と Binder を一意の識別子のようにプロセス間で管理できるようになります。

システムは、対話型スレッドを保存するためにプロセスごとにスレッド プールを維持します。これらの対話型スレッドは、他のプロセスから送信されたすべての IPC をディスパッチするために使用されます。 移行。たとえば、IPC がプロセス A からプロセス B に送信されると、A の呼び出しスレッド (これはスレッド プール内に存在すべきではありません) がブロックされます。

transact()内。プロセス B の対話型スレッド プール内のスレッドがこの呼び出しを受け取り、Binder.onTransact() を呼び出し、完了後に結果として Parcel を返します。次に、プロセス A の待機中のスレッド 返されたパーセルを受け取った後も実行を続行できます。実際、他のプロセスは現在のプロセスのスレッドのように見えます。 ただし、現在のプロセスでは作成されません。

Binder メカニズムは、プロセス間の再帰呼び出しもサポートしています。たとえば、プロセス A は、プロセス B に対して独自の IBinder の transact() 呼び出しを実行します。 バインダー、プロセス B は Binder.onTransact() で transact() を使用してプロセス A への呼び出しを開始し、次にプロセス A 発行した呼び出しが返されるのを待機している間、Binder.onTransact() を使用してプロセス B の transact() にも応答します。 つまり、Binder の結果として、プロセス間呼び出しとプロセス内呼び出しに違いはないと感じられるようになりました。

リモート オブジェクトを操作する場合、それらが有効かどうかを確認する必要があることがよくあります。使用できるメソッドは次の 3 つです。

  • 1 IBinder が配置されているプロセスが存在しない場合、transact() メソッドは RemoteException をスローします。
  • 2 対象のプロセスが存在しない場合、pingBinder()を呼び出すとfalseが返されます。
  • 3 linkToDeath() メソッドを使用して、IBinder.DeathRecipient を IBinder に登録できます。 IBinder によって表されるプロセスが終了するときに呼び出されます。

PS: 中国語訳は以下から抜粋されています: Android Development: What is IBinder

さて、このリストを読んだ後であなたは混乱しているかもしれませんが、ここに簡単な要約があります:

IBinder は Android です。はプロセス間通信用のインターフェイスを提供しますが、通常はこのインターフェイスを直接実装せず、Binder クラスを継承することでプロセス間通信を実装します。 AndroidでIPC(プロセス間通信)を実装する方法です!

2) Binder メカニズムの簡単な分析

2.png

Android の Binder メカニズムは、一連のシステム コンポーネントで構成されています:
クライアント、サーバー、サービス マネージャー、およびバインダー ドライバー

おおよその呼び出しプロセスは次のとおりです。次のようになります。Service Manager はより複雑なので、ここでは詳しく説明しません。

プロセス分析:

->

クライアントがプロキシ インターフェイスのメソッドを呼び出すと、プロキシ インターフェイスのメソッドはクライアントによって渡されたパラメーターを Parcel オブジェクトにパッケージ化します。 次に、プロキシ インターフェイスが Parcel オブジェクトをカーネル内の Binder ドライバーに送信します。;
-> その後、サーバーは、Binder ドライバー内のリクエスト データを読み取ります。それが自分自身に送信された場合は、Parcel オブジェクトを解凍します。 処理して結果を返します。 追記: プロキシ インターフェイスで定義されたメソッドは、サーバーで定義されたメソッドに 1 対 1 で対応します。 さらに、呼び出しプロセス全体が同期的です。つまり、サーバーが処理している間、クライアントはブロック (ロック) されます。 ここで言うプロキシインターフェースの定義は
AIDL(Android Interface description Language)でお話します!


3) Android はなぜプロセス間通信を実現するために Binder メカニズムを使用するのですか?

  1. 信頼性: モバイル デバイスでは、通常、インターネットとデバイス間の内部通信を実現するためにクライアント/サーバー ベースの通信が使用されます。現在、Linux は従来のパイプを含む IPC、System V IPC、つまりメッセージ キュー/共有メモリ/セマフォをサポートしており、ソケットのみがクライアント/サーバー通信方式をサポートしています。 Android システムは、プロセス間通信、メディア再生、センサー、ワイヤレス伝送のための豊富な機能インターフェイスを開発者に提供します。これらの機能は別のサーバーによって管理されます。開発は、独自のアプリケーションのクライアントとサーバー間の通信を確立して、このサービスを使用できるようにすることだけに関心があります。クライアント/サーバー通信を実装するために一連のプロトコルを下部に設定すると、システムの複雑さが増大することは間違いありません。リソースが限られている携帯電話にこの複雑な環境を実装する場合、信頼性を保証するのは困難です。
  2. 伝送パフォーマンス: ソケットは主にネットワークを介したプロセス間通信とローカルマシン上のプロセス間通信に使用されますが、伝送効率が低く、オーバーヘッドが高くなります。メッセージキューとパイプラインはストアアンドフォワード方式を採用しています。つまり、データはまず送信側のバッファ領域からカーネルによって開かれたバッファ領域にコピーされ、次にカーネルのバッファ領域から受信側のバッファ領域にコピーされます。このプロセスには少なくとも 2 つのコピーが必要です。共有メモリはコピーの必要がありませんが、制御が複雑です。さまざまな IPC 方式のデータ コピー数を比較します。共有メモリ:0回。バインダー:1回。ソケット/パイプライン/メッセージキュー: 2 回。
  3. セキュリティ: Android はオープン プラットフォームであるため、アプリケーションの安全性を確保することが重要です。 Android はインストールされている各アプリケーションに UID/PID を割り当てます。プロセスの UID はプロセス ID を識別するために使用できます。従来、ユーザーはデータ パケットに UID/PID を入力することしかできませんでしたが、これは信頼性が低く、悪意のあるプログラムによって簡単に悪用されます。代わりに、カーネルに信頼できる UID を追加するように要求します。 したがって、信頼性、伝送、セキュリティのために。 Android は、プロセス間通信の新しい方法を確立しました。 ——抜粋: Android の Binder メカニズムの簡単な理解

もちろん、ジュニア開発者として、Binder メカニズムがもたらす最も直接的な利点は次のとおりです。 気にする必要はありません。最下層を実装するには、AIDL のルールに従い、インターフェイス ファイルをカスタマイズし、インターフェイス内のメソッドを呼び出して 2 つのプロセス間の通信を完了するだけです。


2. AIDLの詳しい使い方


1) AIDLとは何ですか?

ねえ、先ほどIPCという用語について話しました、その正式名称は: プロセス間通信、 Android システムでは、各アプリケーションが独自のプロセスで実行されるため、一般にプロセス間でデータを直接交換することはできません。 クロスプロセスを実現するために、Android は前述の Binder メカニズムを提供します。このメカニズムで使用されるインターフェイス言語は AIDL (Android インターフェイス定義言語) です。その構文は非常に単純ですが、このインターフェイス言語は複雑ではありません。本物のプログラミング 言語は 2 つのプロセス間の通信インターフェイスを定義するだけです。通信プロトコルに準拠したJavaコードはAndroid SDKによって生成されます aidl.exe ツールは platform-tools ディレクトリに生成され、対応するインターフェイス ファイルは :gen ディレクトリに生成されます。通常は、Xxx.java! のインターフェイスです。 このインターフェイスには、IBinder インターフェイスとカスタム通信インターフェイスを実装する Stub の内部クラスが含まれています。 このクラスはリモート サービスのコールバック クラスとして使用されます。このクラスは IBinder インターフェイスを実装しているため、サービスの onBind() メソッドの戻り値として使用できます。


2) AIDL は 2 つのプロセス間の単純な通信を実現します

AIDL インターフェイス ファイルの作成を開始する前に、AIDL の作成に関するいくつかの注意事項を理解する必要があります:

AIDL の注意事項:

  • インターフェイス名詞AIDL ファイル名と同じである必要があります
  • インターフェイスやメソッドの前にアクセス修飾子を追加しないでください: public、private、protected など。また、static Final は使用できません!
  • AIDL でデフォルトでサポートされる型には次のものがあります。 Java 基本型StringListMapCharSequence、その他すべての型は カスタム タイプをパラメータまたは戻り値として使用するには、インポート ステートメントが必要です。カスタム タイプは Parcelable インターフェイスを実装する必要があります。 詳細については、後述の「複雑なデータ型の受け渡し」を参照してください
  • AIDL によって生成されたカスタム型とその他のインターフェイス型は、クラスや定義であっても、aidl 記述ファイルに明示的にインポートする必要があります。 同じパッケージ内のパッケージの数。

さらに、aidl の作成に使用するコンパイラが Eclipse の場合は、次の点に注意してください。 新しいファイルを直接作成しないでください。この場合、ファイルを開くことができないため、コードを作成できません。
① 新しいtxtファイルを直接作成し、書き込み後に.aidl形式で保存し、対応するパスにコピーします
② Aidlはインターフェイスに似ているため、直接新しいインターフェイスで、コンテンツを書き込んだ後、ディレクトリに移動します対応する Java ファイルが配置されている場所を変更します。ファイルサフィックス名;

Android Studio を使用している場合は、Eclipse とは異なり、Eclipse と同様に AIDL ファイルを作成すると、 対応する XXX.java ファイルはコンパイルおよび生成されません。AS で AIDL を作成するには、メイン ディレクトリに新しい Aidl フォルダーを作成し、 Aidl パッケージと同じ名前のパッケージの場合は、最後に Aidl ファイルを作成し、Ctrl + F9 を押して再コンパイルするだけです。

3.png

上記 2 つのコンパイルが成功した結果は、次のとおりです。対応するディレクトリに、対応する AIDL ファイルが見つかります

4.png5.png


1. サーバー:

。 AIDL ファイルの作成 :

Iperson.aidl

package com.jay.aidl;

interface Iperson {
String queryperson(int num);
}

Iperson.java を開いて、中のコードを見てみましょう:

Iperson.java

/*
* この ファイルは 自動生成されます。  変更しないでください。
* 元の ファイル: C:\Code\ASCode\AIDLServer\app\src\main\aidl\com\jay\aidl\Iperson.aidl
*/
package com.jay.aidl;
パブリック インターフェイス IPersonal extends android.os.IInterface
{
/** ローカル側の IPC 実装スタブ クラス。 */
public static abstract class Stub extends android.os.Binder implements com.jay.aidl.IPersonal
{
private static final java.lang.String DESCRIPTOR = "com.jay.aidl.Iperson";
/** インターフェースに接続するときにスタブを構築します。 */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* IBinder オブジェクトを com.jay.aidl.IPersonal インターフェイスにキャストし、
* 必要に応じて プロキシ を生成します。
 */
public static com.jay.aidl.Iperson asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.jay.aidl.Iperson))) {
return ((com.jay.aidl.Iperson)iin);
}
return new com.jay.aidl .IPersonal.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_queryperson:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.queryperson(_arg0);
reply.writeNoException();
reply.writeString(_result) ;
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.jay.aidl.Iperson
{
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.lang.String queryperson(int num) 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(num);
mRemote.transact(Stub.TRANSACTION_queryperson, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return_result;
}
}
static final int TRANSACTION_queryperson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.lang.String queryPerson(int num) throws android.os.RemoteException;
}

ここでは、定義したインターフェイスの **asInterface(IBinder)** と **queryperson()** メソッドのみに注目します。

このメソッドは、IBinder 型のオブジェクトを Iperson 型に変換し、Iperson を生成します。必要に応じてタイプを入力します。プロキシ オブジェクトは結果を返します。

残りを読む必要はありません。飛ばして次のステップに進んでください。

ステップ 2: ** Service クラスをカスタマイズし、次の操作を完了します:

1) Service クラスを継承し、 Iperson.Stub クラスを継承するように PersonQueryBinder クラスもカスタマイズします 以上です IPersonal インターフェイスと実装IBinder インターフェイス

2) カスタム Stub クラスをインスタンス化し、Service の onBind メソッドをオーバーライドしてバインダー オブジェクトを返します!

AIDLService.java

package com.jay.aidlserver;

import android.app。サービス;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import com.jay.aidl.Iperson.Stub;

/**
* 2015/8/18 0018 に Jay によって作成されました。
 */
public class AIDLService extends Service {

private IBinder binding = new PersonQueryBinder();
private String[] names = {"B神","艹神","九神","J神","シャンシェン "}; r プライベート文字列クエリ (int Num) {
if (num & gt; 0 && num & lt; 6) {
Return names [num-1]; Overrideb Public iBinder Onbind (Intent Intent) {
Return Null;}}

プライベート Final クラス PersonQuery EXTENDS スタブ {
@v パブリック文字列クエリ パーソン (int Num) が RemoteException をスローする {
クエリを返す
}}}
}

ステップ 3:AndroidManifest.xml 文件中の注釈Service

<service android:name=".AIDLService">
<intent-filter>
<action android:name="android.inテント。 action.AIDLService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>

ここではアクティビティインターフェイスは提供していませんが、アプリケーションによって提供されるサービスは他のアプリから呼び出すことができます。


2. クライアント
はサーバーから Aidl ファイルを直接コピーし、MainActivity で直接それを完了します。これは、ローカル サービスをバインドする操作に似ています。プロセスは次のとおりです。
1) PersonConnection クラスをカスタマイズします
ServiceConnection インターフェイスを実装します 2) PersonConnection オブジェクトをパラメータとして使用して、bindService を呼び出し、リモート サービスをバインドします
bindService(service,conn,BIND_AUTO_CREATE);
ps: 3 番目のパラメータは、サービスが開始されていない場合の自動作成3 ) は、ローカル Service とは異なります。
リモート Service にバインドされた ServiceConnection は、Service の onBind() メソッドによって返される IBinder オブジェクトを直接取得できません
プロキシのみを返すことができます。 onBind()
メソッドによって返された object 次の処理を実行する必要があります: iperson = Iperson.Stub.asInterface(service);
その後、初期化やボタン イベントなどを完了します。 具体的なコードは次のとおりです:

MainActivity.java

package com.jay.aidlclient;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget. TextView;

import com.jay.aidl.Iperson;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

private EditText edit_num;
private Button btn_query;
private TextView txt_name;
private Iperson iperson;
private personConnection conn = new PersonConnection();


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindViews();
//绑定远程Service
Intent service = new Intent("android.intent.action.AIDLService");
service.setPackage("com.jay.aidlserver");

bindService(service, conn, BIND_AUTO_CREATE);
btn_query。 setOnClickListener(this) ;
}

private void bindViews() {
edi​​t_num = (EditText) findViewById(R.id.edit_num);
btn_query = (Button) findViewById(R.id.btn_query);
txt_name = (TextView) findViewById(R .id.txt_name);
}

@Override
public onClick(View v) {
String number = edit_num.getText().toString();
int num = Integer.valueOf(number);
お試しください {
            txt_name.setText(iperson.queryperson(num));
} catch (RemoteException e) {
e.printStackTrace();
}
edi​​t_num.setText("");
}

private final class personConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
iperson = Iperson.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName name) {
iperson = null;
}
}
}

次に、最初に AIDLServivce を起動し、次に AIDLClient を起動し、クエリのシリアル番号を入力すると、対応する名前を取得できます。 もちろん、AIDL クライアントを直接起動することもでき、同じ効果が得られます:

レンダリングは次のとおりです:

6.gif


3) 複雑なデータを配信する AIDL サービス

上の例では、int 型パラメータのみを渡し、サーバーは String 型パラメータを返します。これは次の条件を満たすようです。 基本的なニーズですが、実際の開発では、複雑なデータ型を渡すことを考慮する必要があるかもしれません。次は勉強しましょう 複雑なデータ型のデータをサーバーに渡す方法!始める前に、まず Parcelable インターフェイス について理解しましょう。

——Parcelable インターフェイスの紹介:

シリアル化を使用したことのある人は基本的にこのインターフェイスを知っていると思いますが、これに加えて、シリアル化にも使用される別の Serializable もあります。 Parcelable の方が軽量かつ高速であるというだけです。でも書くのはちょっと面倒ですが、asを使えばもちろん使えます。 シリアル化を完了するためのプラグイン (例: Android Parcelable Code Generator) もちろん、ここでもこのインターフェイスの実装方法を説明します~

まず、次のメソッドを実装する必要があります: writeToParcel メソッドと readFromPacel メソッド write メソッドはオブジェクトをパーセルに書き込み、read メソッドはパーセルからオブジェクトを読み取ります。 属性の書き込み順序は読み取り順序と同じである必要があることに注意してください

その後、CREATORという名前の静的final属性をこのクラスに追加する必要があります プロパティを変更するには、android.os.Parcelable.Creatorインターフェース

を実装する必要があります。次に、インターフェースに 2 つのメソッドを記述する必要があります:

createFromParcel(Parcel ソース) メソッド: JavaBean を作成する関数を実装するソースからのインスタンス newArray(int size): 型 T と長さ size の配列を作成するだけです。単純に new T[size]; を返すだけです (ここでの T は Person クラスです)最後に、describeContents()

: これは何に使うか分かりませんが、0 を返してください!彼を無視してください

- さらに、

String

CharSequenceを除く非プリミティブ型はすべて方向インジケータを必要とします。 方向インジケーターには、inout、および inout が含まれます。 in はクライアントによって設定されることを意味し、out はサーバーによって設定されることを意味し、inout はクライアントとサーバーの両方によって値が設定されることを意味します。 それでは、コードを書いてみましょう (AS のカスタム タイプにはまだ解決されていない問題があるため、Eclipse を使用します~):


コード例:

2 つのオブジェクト タイプをカスタマイズする: Person Salary では、リモート Service を呼び出すためのパラメーターとして person が使用され、戻り値として Salary が使用されます。 次に、最初に行うことは、Person クラスと Salary クラスを作成することです。また、Parcelable インターフェイスを実装する必要もあります

1.——サーバー側

ステップ 1: Person.aidl ファイルと Salary.aidl ファイルを作成します。これらのファイルは Parcelable インターフェイスを実装する必要があるため、次のステートメントだけを記述します:

Person.aidl:     parcelable Person; 
Salary.aidl:     parcelable Salary;
ステップ 2: Person を作成するClass と Salary クラスは、それぞれ Parcelable インターフェイスを実装し、対応するメソッドを書き直す必要があります。


PS: 後から Person オブジェクトに基づいて Map コレクション内のデータを取得するため、Person.java の hashcode と equals を書き換えます。 メソッド; ただし、Salary クラスには必要ありません。

package com.jay.example.aidl;

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

/**
* 2015/8/18 0018 に Jay によって作成されました。
 */
public class Person 実装 Parcelable{

private Integer id ; V プライベート文字列名;

パブリック パーソン () {}

パブリック パーソン (整数 ID、文字列名) {
this.id = id;
戻り値;
} public void setid(integer id){
= id; data into Parcel
@Override
public void writeToParcel(Parcel dest, int flags) {
//オブジェクトに含まれるデータを Parcel に書き込みます
dest.writeInt(id);
dest.writeString(name);
}

//CREATOR という名前の静的な Final 属性を指定する必要があります。この属性は
を実装する必要があります。 //android.os.Parcelable.Creator<T>interface
public static Final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
Public Person CreatefromParcel (パーセル ソース) {
新しい人物を返す (Source.readint (), Source.readString ()); {t 新しい人物を返す [サイズ]; )
{
Final int prime = 31;
int result = 1;
r esult = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public booleanquals(Object obj)
{
if (this == obj)
if(getclass()!= obj.getclass())
;
else if (!name.equals(other.name))戻るreturn false;<pre><p><strong>Salary.java</strong>~照葫芦画瓢</p>

<pre>
package com.jay.example.aidl; 

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

/**
* 2015/8/18 0018 に Jay によって作成されました。
 */
public class Salary implements Parcelable {

private String type;
private Integer salary;

公的 給与( ) {
}

public Salary(String type, Integer salary) {
this.type = type;
this.salary = salary;
}

public String getType() {
return type;
}

public Integer getSalary() {
return salary;
}

public void setType(String type) {
this.type = type;
}

public void setSalary(Integer salary) {
this.salary = 給与;
}

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

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(type);
dest.writeInt(給与);
}

public static final Parcelable.Creator<Salary> CREATOR = new Parcelable.Creator<Salary>() {
//从パーセル中读取数据, 返人对象
@Override
public Salary createFromParcel(Parcel source) {
return new Salary(source.readString(), source. readInt());
}

@Overrideer Public Salary [] News (int Size) {
Return New Salary [size]
}
}



ステップ 3
: ISalary.aidl ファイルを作成します。給与情報を取得するメソッド:
package com.jay.example.aidl;

import com.jay .example.aidl.Salary; interface ISalary

{
// Person オブジェクトを受信パラメータとして定義します
// インターフェースでメソッドを定義するときは、新しいパラメータの転送モードを定式化する必要があります。ここではパラメータが渡されるため、前に in があります
Salary getMsg (個人所有者内)
}

追記:カスタム データ タイプを使用している場合は、それをインポートする必要があることに注意してください。 ! !覚えて! ! !

ステップ 4: コアサービスの作成: Stub を継承する SalaryBinder クラスを定義して、ISalary インターフェイスと IBinder インターフェイスを実装し、情報を格納する Map コレクションを定義します。 onBind メソッドをリセットし、SalaryBinder クラスのオブジェクト インスタンスを返します!

AidlService.java

パッケージcom.jay.example.aidl_complexservice;
インポートjava.util.HashMap;
インポートcom.jay.example.aidl.ISalary.Stub;
import android.os.IBinder;
import com.jay.example.aidl.Salary; public class AidlService extends Service {

private SalaryBinder給与Binder;
private static Map<Person,Salary> ss = new HashMap<Person, Salary>(); // ここで静的コード ブロックで初期化された Map コレクションを初期化します。もちろん、コンストラクター メソッドで初期化を完了することもできます
static
{
ss.put(new Person(1, "Jay"), new Salary("code farmer", 2000)), new Salary ("歌手", 20000));
ss.put(新しい人(3, "XM"), 新しい給与("学生", 20));
ss.put(新しい人 (4, "ミスターワン"), new Salary("Teacher", 2000));
}


@Override
public void onCreate() {
super.onCreate();
saleBinder = newバインダー()
} えー@ @Override
Public iBinder Onbind (Intent インテント) {
Return Salary Binder はスタブを拡張します
{
        @Override
public Salary getMsg(個人所有者) throws RemoteException {
return ss.get(owner);  
}
}

@Override
public void onDestroy() {
System.out.println("服务结束!");  
super.onDestroy();  
}
}

注册下Service:

<service android:name=".AidlService">  
<インテントフィルター>    
<action android:name="android.intent.action.AIDLService" />  
<category android:name="android.intent.category.DEFAULT" />  
</intent-filter>    
</サービス>

2——クライアントの作成

ステップ 1: サーバー側 AIDL ファイルをコピーします。コピーされたディレクトリは次のとおりです:

7.jpg

ステップ 2: 単純なレイアウトを作成し、次にコアを作成します。主な活動実現 ServiceConnection オブジェクトを定義し、前の通常のデータと同様に、対応するメソッドをオーバーライドします。 次に、bindService で、Button のクリック イベントで Salary オブジェクトを取得し、表示します。

MainActivity.java

パッケージ com.jay.example.aidl_complexclient;  

インポート com.jay.example.aidl.ISalary;  
com.jay.example.aidl.person をインポートします。  
com.jay.example.aidl.Salary をインポートします。  

インポート android.app.Activity;  
android.app.Service をインポートします。  
インポート android.content.ComponentName;  
android.content.Intent をインポートします。  
android.content.ServiceConnection をインポートします。  
android.os.Bundle をインポートします。  
android.os.IBinder をインポートします。  
インポート android.os.RemoteException;  
インポート android.view.View;  
android.view.View.OnClickListener をインポートします。  
android.widget.Button をインポートします。  
android.widget.EditText をインポートします。  
インポート android.widget.TextView;  


public class MainActivity extends Activity {

private ISalary salaryService;  
プライベート ボタン btnquery;  
プライベート EditText 編集名;  
プライベート TextView テキストショー;  
private ServiceConnection conn = new ServiceConnection() {

@Override
public void onServiceDisconnected(ComponentName name) {
salaryService = null;  
}

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 返されるのは代理オブジェクト、要调用です个方法哦!  
salaryService = ISalary.Stub.asInterface(service);  
}
};  


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

btnquery = (ボタン) findViewById(R.id.btnquery);  
edi​​tname = (EditText) findViewById(R.id.editname);  
textshow = (TextView) findViewById(R.id.textshow);  

Intent it = new Intent();  
it.setAction("com.jay.aidl.AIDL_SERVICE");  
bindService(it, conn, Service.BIND_AUTO_CREATE);  

btnquery.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
試してみてください
{
String name = editname.getText().toString();
Salary salary = sal aryService.getMsg(新規人(1,名前));
textshow.setText(名前 + salary.toString());
}catch(RemoteException e){e.printStackTrace();}
}
});  

}
@Override
protected void onDestroy() {
        super.onDestroy();  
this.unbindService(conn);  
}

}

実行中のスクリーンショット:

8.jpg

PS: ここのコードは、Eclipse を使用する前に書かれたコードです。 Android Studio でのカスタム タイプに問題があります。 解決策はまだ見つかっていません。知っている人がいたら教えてください。 ! !どうもありがとうございます! ! ! 発生する問題は次のとおりです:

9.png

2 つのインスタンスのコードのダウンロード (Eclipse ベース):
1) AIDL を使用してプロセス間の単純な通信を完了します
2) 複雑なデータを転送する AIDL サービスの実装


3 . Direct Binder の onTransact を介してクロスプロセス通信を完了します

前述したように、Android は Binder の onTrensact メソッドを介して通信を完了できます。 シリアル番号クエリ名の例:

サーバー側実装:

/**
* 2015/8/18 0018 に Jay によって作成されました。
 */
public class IPCService extends Service{

private static final String DESCRIPTOR = "IPCService";
private final String[] names = {"B神","艹神","基神","J神","翔神"};
private MyBinder mBinder = new MyBinder();


private class MyBinder extends Binder {
@Override
protected boolean onTransact(int code, Parcel data,小包の返信、 int flags) throws RemoteException {
switch (code){
case 0x001: {
data.enforceInterface(DESCRIPTOR);
int num = data.readInt();
reply.writeNoException();
reply.writeString(names[num ]);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}

@Override
public IBinder onBind(Intent インテント) {
return mBinder;
}
}

クライアント実装:

public クラス MainActivity extends AppCompatActivity implements View.OnClickListener{

private EditText edit_num;
private Button btn_query;
private TextView txt_result;
private IBinder mIBinder;
private ServiceConnection PersonConnection = new ServiceConnection()
{
@Override
public void onServiceDisconnected(ComponentName name)
{
mIBinder = null;
}

@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
mIBinder = service;
}
};

@Override
protected void onCreate(バンドル savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindViews();

//绑定远程Service
インテントサービス = 新しいインテント("android.intent.action.IPCService ");
service.setPackage("com.jay.ipcserver");
bindService(service, PersonConnection, BIND_AUTO_CREATE);
btn_query.setOnClickListener(this);
}

private void bind Views() {
edi​​t_num = (EditText ) findViewById(R.id.edit_num);
btn_query = (ボタン) findViewById(R.id.btn_query);
txt_result = (TextView) findViewById(R.id.txt_result);
}

@Override
public void onClick (表示 v) {
int Num = Integer.parseint (Edit_num.gettext ().Tostring ());
if (mibinder == null) {
Toast.maketext (この、「接続されていないサーバーまたはサーバーが異常終了しました」、トースト .LENGTH_SHORT).show ();
} else {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain(); String _result = null;
try{
_data.writeInterfaceToken("IPCService");
(0x001, _data, _reply, 0);
_reply.readException(); .readString (); e.printStackTrace(); e.
_data.recycle();

コードは比較的単純なので、あまり説明しません~そのまま使用して自分で修正してください! PS: コードリファレンス: Android Aidl Binder フレームワークの簡単な分析


4. Android 5.0 以降のサービスで注意すべき点:

今日、サービスを暗黙的に開始したときに、このような問題に遭遇しました。

10.png

その後、プログラムは起動するとすぐにクラッシュしてしまい、原因が Android 5.0 にあったことがわかるまでに長い時間がかかりました。 5.0 以降には、次のような新機能があることが判明しました。 サービス インテントは明示的である必要があります。 そうですね、サービスを暗黙的に開始することはできません。解決策は非常に簡単です。 たとえば、StartService:

startService(new Intent(getApplicationContext(), "com.aaa.xxxserver")); 次のようにプログラムを書くと、直接クラッシュします。 startService(new) Intent(getApplicationContext(), LoadContactsService .class));

BindServiceの場合:Intent service = new Intent("android.intent.action.AIDLService");上記を踏まえてパッケージ名を追加します: service.setPackage("com.jay.ipcserver");以上です~

公式ドキュメント:http://developer.android.com/intl/zh-cn/guide/components/intents-filters.html#タイプドキュメントの説明:

11.png


このセクションの概要:

さて、これがサービスに関する最後のセクションです。このセクションでは、Binder の基本概念とプロセス間通信の実装方法について説明します。 2 つの方法: AIDL と Binder.onTransact() を介したクロスプロセス通信!最後に、Android 5.0以降についても説明しました。 Service は暗黙的に開始できないことに注意してください。それはそれで、ありがとう〜