package cn.com.remoteServiceClient;import com.cn.aidl.IRemoteQuery;import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;//用戶端步驟://1 自訂ServiceConnection類實現ServiceConnection介面//即RemoteQueryServiceConnection implements ServiceConnection//重寫其方法public void onServiceConnected(ComponentName name, IBinder service)//主要目的就是接受服務端傳過來的Binder對象,即方法的參數IBinder service.//但是因為存在AIDL協議的轉換,所以IBinder service是一個代理對象,我們要強轉,將其轉換為介面類型對象.代碼如下://public void onServiceConnected(ComponentName name, IBinder service) {//remoteQueryBinder=IRemoteQuery.Stub.asInterface(service);//強轉!!//}////2 利用隱式意圖啟用遠程服務////3 利用remoteQueryBinder調用服務裡面的方法////////AIDL的用戶端的總結:// (1) 採用的是隱式意圖去啟用服務service,因為服務和用戶端不在同一個應用!// 彼此看不見,所以是"隱"的,那麼就要採用隱式意圖去啟用,所以在檔案清單裡// 聲明服務元件的時候,還要給其配置意圖過濾器!// (2) 在onServiceConnected()方法裡接收服務端返回來的Binder對象時一定要注意!!!// 這裡和本地服務的情況不一樣!!!因為存在AIDL協議的轉換,所以IBinder service是一個代理對象// 我們要強轉將其轉換為介面類型對象remoteQueryBinder// 即remoteQueryBinder=IRemoteQuery.Stub.asInterface(service);實現轉換// (3) 核心總結:// 現在把兩個例子都寫完了,再往回看,那麼可以發現其實本地服務和遠程服務// 都在圍繞一個核心:得到一個Binder對象--->這個Binder對象有一個顯著的// 特點:可以實現與服務的綁定,且可以完成一些業務// // 在本地服務裡面的實現是:Binder對象的類繼承自Binder且實現了業務的介面// 那麼在接收此Binder對象的時候,當然可以用此介面來接收(父類引用指向子類對象嘛)// // 在遠程服務裡面實現是:我們要寫一個aidl檔案.// 然後由AIDL自動產生了一個很大的介面,在此介面中最核心是Stub類!它繼承自Binder而且// 實現了我們定義的業務介面!所以返回來的Binder代理對象(注意是代理對象!)// 既有"綁定"到服務的能力,也有完成業務方法的能力// // 所以在本地和遠程中我們都可以用業務介面來接受返回來的Binder對象或Binder代理對象 public class MainActivity extends Activity { TextView numberTextView; TextView resultTextView; Button button; RemoteQueryServiceConnection conn=new RemoteQueryServiceConnection(); IRemoteQuery remoteQueryBinder; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); numberTextView=(TextView) findViewById(R.id.number); resultTextView=(TextView) findViewById(R.id.result); button=(Button) findViewById(R.id.button); button.setOnClickListener(new ButtonOnClickListener()); Intent service=new Intent(); service.setAction("com.cn.remoteService.RemoteQueryService"); bindService(service, conn, this.BIND_AUTO_CREATE);//綁定服務 } private class ButtonOnClickListener implements OnClickListener{public void onClick(View v) {String number=numberTextView.getText().toString();String result;try {result = remoteQueryBinder.queryByNum(Integer.valueOf(number));resultTextView.setText(result);} catch (Exception e) {e.printStackTrace();}} } //接收綁定的服務和解除服務 private final class RemoteQueryServiceConnection implements ServiceConnection{public void onServiceConnected(ComponentName name, IBinder service) {remoteQueryBinder=IRemoteQuery.Stub.asInterface(service);}public void onServiceDisconnected(ComponentName name) {remoteQueryBinder=null;} }protected void onDestroy() {unbindService(conn);super.onDestroy();} }
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/number" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/number" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/button" android:id="@+id/button" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/result" /></LinearLayout>
package com.cn.remoteService;import com.cn.aidl.IRemoteQuery;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;//服務端步驟://1 產生aidl檔案//建立一個介面,裡面可以有各種方法.//將此介面稍作修改,讓"介面名"和"方法名"都沒有修飾(如public)!!代碼如下://interface IRemoteQuery {// String queryByNum(int number);//}// 然後找到這個被修改後介面的檔案路徑,按照此路徑在硬碟上將其尾碼改為.aidl//這樣就產生了aidl檔案.在此過程中注意:介面名和aidl檔案名稱必須相同.//2 重新整理工程,系統會自動產生用於用於遠程通訊的IRemoteQuery.java// 分析此IRemoteQuery.java//(1)IRemoteQuery.java內部實際是一個名叫IRemoteQuery的介面//(2)該IRemoteQuery介面內部最重要的是一個Stub類(即IRemoteQuery.Stub),此類繼承自Binder類且實現了IRemoteQuery業務介面// 所以該類的對象具有遠端存取的能力// //3在用戶端建立一個包,包的名稱與aidl檔案所在的包名一致!然後將服務端的aidl檔案拷貝到此包下,然後重新整理//發現在用戶端的gen下產生了IRemoteQuery.java// //4 自訂遠程服務類(RemoteQueryService),其繼承自service////5 在RemoteQueryService裡面寫一個內部類RemoteQueryBinder繼承自IRemoteQuery.Stub//即RemoteQueryBinder extends IRemoteQuery.Stub//6 重寫服務的public IBinder onBind(Intent intent)方法,返回一個Binder對象即RemoteQueryBinder類對象給用戶端//////關於AIDL的服務端的總結://(1) 自動產生的Stub是核心重點,從產生的程式碼可以看出:它繼承自Binder而且實現了我們定義的業務介面// 所以它既可以有綁定的能力也有調用業務的能力(這點和剛才寫的調用本地服務的例子有異曲同工之妙)//(2) AIDL的定義和介面很類似,但是"介面名"和"方法名"都沒有修飾!!!!比如public//(3) 在用戶端和服務端都要此包!!因為這相當於一個通訊協定!!!雙方都必須遵守,所以一式兩份!!!public class RemoteQueryService extends Service {@Overridepublic IBinder onBind(Intent intent) {return remoteQueryBinder;}RemoteQueryBinder remoteQueryBinder=new RemoteQueryBinder();private String [] names=new String [] {"小明","小王","小楊","小李"};private final class RemoteQueryBinder extends IRemoteQuery.Stub{@Overridepublic String queryByNum(int number) throws RemoteException {return query(number);}}public String query(int i){if(i>0&&i<5){return names[i-1];}return "查詢錯誤,請再次輸入";}}
/* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\workspace\\queryByRemoteService\\src\\com\\cn\\aidl\\IRemoteQuery.aidl */package com.cn.aidl;/** *注意: *此AIDL的定義和介面很類似,但是"介面名"和"方法名"都沒有修飾!!!!比如public */public interface IRemoteQuery extends android.os.IInterface{/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.cn.aidl.IRemoteQuery{private static final java.lang.String DESCRIPTOR = "com.cn.aidl.IRemoteQuery";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an com.cn.aidl.IRemoteQuery interface, * generating a proxy if needed. */public static com.cn.aidl.IRemoteQuery asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.cn.aidl.IRemoteQuery))) {return ((com.cn.aidl.IRemoteQuery)iin);}return new com.cn.aidl.IRemoteQuery.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_queryByNum:{data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();java.lang.String _result = this.queryByNum(_arg0);reply.writeNoException();reply.writeString(_result);return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.cn.aidl.IRemoteQuery{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 queryByNum(int number) 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(number);mRemote.transact(Stub.TRANSACTION_queryByNum, _data, _reply, 0);_reply.readException();_result = _reply.readString();}finally {_reply.recycle();_data.recycle();}return _result;}}static final int TRANSACTION_queryByNum = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);}public java.lang.String queryByNum(int number) throws android.os.RemoteException;}