android AIDL服务
這篇文章http://byandby.iteye.com/blog/1026110我們介紹了android的本地服務:它只能由承載它的應用程序使用?,F在我們將介紹如何構建可由其他進程通過 RPC 使用服務。與許多其他基于 RPC 的解決方案一樣,在Android中,需要使用 IDL來定義將向客戶端公開的接口。在Android中,這個 IDL 稱為 AIDL。要構建遠程服務,執行以下步驟。
? ? (1)編寫一個AIDL文件來向客戶端定義接口。AIDL 文件使用 java語法,它的擴展名是點 .aidl 使用的包名稱與Android項目所使用的包相同。
??? (2)將AIDL 文件添加到項目的任意包下。Android Eclipse 插件將調用AIDL編譯器來從 AIDL 文件生成 Java接口。 上邊這2步說白了就是? 你自己隨便定義一個文件 擴展名是 .aidl 然后將這個文件復制到你的包下面,那個包都可以。然后 Android Eclipse 插件會自動幫你 根據這個 AIDL 文件生成 Java文件。
??? (3)實現一個服務并從 onBind()方法返回所生成的接口。
??? (4)將服務配置添加到 AndroidManifest.xml文件中。????
在AIDL中定義服務接口
?? 為了展示遠程服務實例,我們將編寫一個股票報價服務。此服務將提供一種方法來獲取股票代號并返回價格。要在Android 中編寫遠程服務,第一步是在 AID文件中定義服務的接口。
?? 股票報價服務的 AIDL 定義?
//This file is IStockQuoteService.aidl package com.androidbook.stockquoteservice; interface IStockQuoteService {double getQuote(String ticker); }?
???? ??? IStockQuoteService 以字符串的形式接受股票代號,并以雙精度數字形式返回當前的股票價值。當創建AIDL文件時, Android Eclipse插件將運行 AIDL 編譯器來處理 AIDL文件(在構建過程中)。如果AIDL文件成功編譯,編譯器將生成一個適合 RPC 通信的 Java接口。請注意,生成的文件將位于在 AIDL文件中指定的包中,在本例中為 com.androidbook.stockquoteservice。
? 下面是為 IStockQuoteService接口生成的Java文件。生成的文件將放在Eclipse的 gen文件夾中。
/** This file is auto-generated. DO NOT MODIFY.* Original file: F:\\workspace\\StockQuoteService\\src\\com\\androidbook\\stockquoteservice\\IStockQuoteService.aidl*/ package com.androidbook.stockquoteservice; public interface IStockQuoteService extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.androidbook.stockquoteservice.IStockQuoteService { private static final java.lang.String DESCRIPTOR = "com.androidbook.stockquoteservice.IStockQuoteService"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /*** Cast an IBinder object into an com.androidbook.stockquoteservice.IStockQuoteService interface,* generating a proxy if needed.*/ public static com.androidbook.stockquoteservice.IStockQuoteService asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.androidbook.stockquoteservice.IStockQuoteService))) { return ((com.androidbook.stockquoteservice.IStockQuoteService)iin); } return new com.androidbook.stockquoteservice.IStockQuoteService.Stub.Proxy(obj); } 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_getQuote: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); double _result = this.getQuote(_arg0); reply.writeNoException(); reply.writeDouble(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.androidbook.stockquoteservice.IStockQuoteService { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } public double getQuote(java.lang.String ticker) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); double _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(ticker); mRemote.transact(Stub.TRANSACTION_getQuote, _data, _reply, 0); _reply.readException(); _result = _reply.readDouble(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_getQuote = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public double getQuote(java.lang.String ticker) throws android.os.RemoteException; }?
?? 對于所生成的類,請注意以下幾點。 ?? 在 AIDL 文件中定義的接口在生成的代碼中實現為接口 (也就是說,有一個名為IStockQuoteService的接口)。
??????? 名為Stub的 static final 抽象類擴展了 android.os.Binder并實現了 IStockQuoteService。請注意該類是一個抽象類。
?????? ?名為 Proxy的內部類實現了 IStockQuoteService, 后者是Stub類的代理。 ???? AIDL文件必須位于應該包含所生成文件的包中(在AIDL文件的包聲明中指定)。
????? ? 在服務類中實現AIDL接口 ? 上邊我們為股票報價服務定義了 AIDL 文件并生成了綁定文件?,F在我們將提供該服務的實現。要實現服務的接口,需要編寫一個類來擴展 android.app.Service 并實現 IStockQuoteService接口。我們將編寫的類 命名為 StockQuoteService。為了將服務向客戶端公開,StockQuoteService 需要提供onBind()方法 的實現,我們還需要將一些配置信息添加到 AndroidManifest.xml文件中。
? 下面給出 我們服務類得實現。 IStockQuoteService 服務實現。????
package com.androidbook.stockquoteservice;import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log;public class StockQuoteService extends Service {private static final String TAG = "StockQuoteService";public class StockQuoteServiceImpl extends IStockQuoteService.Stub {@Overridepublic double getQuote(String ticker) throws RemoteException {Log.v(TAG, "getQuote() called for " + ticker);return 20.0;}}@Overridepublic void onCreate() {super.onCreate();Log.v(TAG, "onCreate called");}@Overridepublic void onDestroy() {super.onDestroy();Log.v(TAG, "onDestory() called");}@Overridepublic void onStart(Intent intent, int startId) {super.onStart(intent, startId);Log.v(TAG, "onStart() called");}@Overridepublic IBinder onBind(Intent intent) {Log.v(TAG, "onBind() called");return new StockQuoteServiceImpl();}}?
??? 在這個服務類中大家可以看到我們實現了 onBind() 方法。從AIDL文件生成的 Stub類是抽象類并且它實現了 IStockQuoteService接口。在我們的服務實現中,有一個擴展了 Stub類得內部類,名為 StockQuoteServiceImpl。此類充當著遠程服務實現,而且 onBind()方法會返回此類的實例。到此,我們有了一個有效的 ADIL服務,但是外部的客戶端還無法連接到它。 ???? 要將服務向客戶端公開,需要在AndroidManifest.xml文件中添加服務聲明,而這一次我們需要一個Intent 過濾器來公開服務,如下。?
?
?? 從客戶端應用程序調用服務
?? 當客戶端與服務通信時,它們之間必須有一個協議或契約。在Android中,這個契約就是AIDL。所以,使用服務的第一步是,獲取服務的 AIDL文件并將其復制到客戶端項目中。當將AIDL文件復制到客戶端項目時,AIDL 編譯器將創建一個接口定義文件,這個文件與我們在服務端定義的文件相同。這會向客戶端公開所有的方法、參數并返回服務的類型。我們創建一個新項目并復制AIDL文件。
??? (1)創建一個新的Android項目, 將其命名為 StockQuoteClient。使用不同的包名稱比如 com.androidbook.stockquoteclient。在Create Activity字段中使用 MainActivity注意不要把IStockQuoteService.aidl文件放到這包中,這個包只有一個MainActivity 類。
??? (2)在此項目中新建一個包 com.androidbook.stockquoteservice,放在src目錄下。
??? (3)將IStockQuoteService.aidl文件從 StockQuoteService 項目也就是我們服務端得項目復制到新建的包中。復制過來之后,AIDL編譯器會自動生成關聯的java文件。
???? 重新生成的服務接口充當著客戶端與服務之間的契約。下一步是獲取服務的引用,以便調用getQuote()方法。對于遠程服務,必須調用 bindService()方法,而不是 startService()方法。?
???客戶端布局文件
?
?MainActivity類
package com.androidbook.stockquoteclient;import com.androidbook.stockquoteservice.IStockQuoteService; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast;public class MainActivity extends Activity {protected static final String TAG = "StockQuoteClient";private IStockQuoteService stockService = null;private Button bindBtn;private Button callBtn;private Button unbindBtn;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);bindBtn = (Button) findViewById(R.id.bindBtn);bindBtn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {bindService(new Intent(IStockQuoteService.class.getName()),serConn, Context.BIND_AUTO_CREATE);bindBtn.setEnabled(false);callBtn.setEnabled(true);unbindBtn.setEnabled(true);}});callBtn = (Button) findViewById(R.id.callBtn);callBtn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {callService();}});callBtn.setEnabled(false);unbindBtn = (Button) findViewById(R.id.unbindBtn);unbindBtn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {unbindService(serConn);bindBtn.setEnabled(true);callBtn.setEnabled(false);unbindBtn.setEnabled(false);}});}private void callService() {try {double val = stockService.getQuote("SYH");Toast.makeText(this, "Value from service is " + val,Toast.LENGTH_LONG).show();} catch (RemoteException e) {Log.e("MainActivity", e.getMessage(), e);}}private ServiceConnection serConn = new ServiceConnection() {// 此方法在系統建立服務連接時調用@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.v(TAG, "onServiceConnected() called");stockService = IStockQuoteService.Stub.asInterface(service);callService();}// 此方法在銷毀服務連接時調用@Overridepublic void onServiceDisconnected(ComponentName name) {Log.v(TAG, "onServiceDisconnected()");stockService = null;}}; }?
??? MainActivity里邊我們定義了三個按鈕 分別是 Bind、Call Again 和 UnBind。當用戶單擊 Bind 按鈕時,活動調用bindService()方法。類似地,當用戶點擊 UnBind 時,活動調用unbindService()方法。請注意傳遞給 bindService()方法 的3個參數:AIDL 服務的名稱、ServiceConnection 實例和自動創建服務的標志。 ??? 我們在程序了打印了日志,我們可以看到當我們點擊 Bind按鈕的時候 服務端 方法的一個執行順序。 ???
?
? 程序的運行效果如下
??? 對于AIDL 服務,需要提供ServiceConnection接口的實現。此接口定義了兩個方法:一個供系統建立服務連接時調用,另一個在銷毀服務連接時調用。
??? 最后我們在總結一下android中 本地服務和 AIDL服務的區別吧。 ???? 本地服務不支持onBind(),它從onBind()返回null,這種類型的服務只能由承載服務的應用程序組件訪問。可以調用 startService()來調用本地服務。AIDL服務可以同時供 同一進程內的組件和其他應用程序的組件使用。這種類型的服務在AIDL 文件中為自身與其客戶端定義一個契約。服務實現 AIDL契約,而客戶端綁定到 AIDL定義。服務通過從 onBind()方法 返回AIDL接口的實現,來實現契約。客戶端通過調用 bindService()來綁定到AIDL服務,并調用 unBindService()來從服務斷開。??
?? 為了避免大家把aidl文件放錯位置,來2張我們 程序結構的截圖吧,一個是 客戶端的,一個是服務端得。 客戶端。。。。
服務端。。。
?
?
http://byandby.iteye.com/blog/1026193
轉載于:https://www.cnblogs.com/navy-wang/p/3272706.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的android AIDL服务的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: I*k-AK
- 下一篇: 让我们一起Go(十三)