上一節中我們講解了本地Service,這一節主要是講解遠程Service,這裡涉及到了AIDL。好吧,老規矩,先來點基礎知識:
一.基礎知識
AIDL的作用
在Android平台,每個應用程式App都運行在自己的進程空間。通常一 個進程不能訪問另一個進程的記憶體空間(一個應用不能訪問另一個應用),如果想溝通,需要將對象分解成作業系統可以理解的基本單元,Android提供了AIDL來處理。
AIDL (Android Interface Definition Language) 是一種IDL 語言,用於產生可以在Android裝置上兩個進程之間進行處理序間通訊(interprocess communication, IPC)的代碼。如果在一個進程中(例如Activity)要調用另一個進程中(例如Service)對象的操作,就可以使用AIDL產生可序列化的參 數。換句比較淺顯的話來說,就是我這個App應用的activity,需要調用其他App應用的Service
.當然同一App應用的activity 與service也可以在不同進程間,這可以設定Service配置中,android:process=":remote"
AIDL的使用 官方文檔特別提醒我們何時使用AIDL是必要的:只有你允許用戶端從不同的應用程式為了進程間的通訊而去訪問你的service,以及想在你的service處理多線程。(太生硬了,不同進程的組件調用吧。)
那麼怎麼製作AIDL呢?下面步驟
1:建立.aidl檔案。建立立個檔案並且以.aidl作為尾碼儲存,在這個檔案中編寫介面以及方法,這個我們普通java介面聲明是一樣的,不同的是要顯示import 複雜類型,即便複雜類型對象在同一個包內。Java基礎資料型別 (Elementary Data Type) (int, long, char, boolean等),String和CharSequence,集合介面類型List和Map,不需要import 。
比如:
package com.dongzi;
interface IStockQuoteService{
double getPrice(String ticker);
}
2:建立好AIDL檔案後,重新整理下工程,你會發現在gen包下,對應的包下面出現一個與AIDL檔案相同的java檔案。如:
View Code
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\mywordspace\\MyPhone\\src\\com\\dongzi\\IStockQuoteService.aidl
*/
package com.dongzi;
public interface IStockQuoteService extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.dongzi.IStockQuoteService {
private static final java.lang.String DESCRIPTOR = "com.dongzi.IStockQuoteService";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.dongzi.IStockQuoteService
* interface, generating a proxy if needed.
*/
public static com.dongzi.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.dongzi.IStockQuoteService))) {
return ((com.dongzi.IStockQuoteService) iin);
}
return new com.dongzi.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_getPrice: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
double _result = this.getPrice(_arg0);
reply.writeNoException();
reply.writeDouble(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.dongzi.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 getPrice(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_getPrice, _data, _reply, 0);
_reply.readException();
_result = _reply.readDouble();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getPrice = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public double getPrice(java.lang.String ticker) throws android.os.RemoteException;
}
AIDL工具自動產生了那麼多代碼,其實我們只需要知道3個就夠了。
public static abstract class Stub extends android.os.Binder implements com.dongzi.IStockQuoteService 靜態抽象內部類Stub
private static class Proxy implements com.dongzi.IStockQuoteService AIDL服務代理類
public double getPrice(java.lang.String ticker) throws android.os.RemoteException; AIDL公布出的介面,就是我們定義的介面方法
3:把AIDL檔案存放在其他用戶端應用中,我們這個作為服務端。當然我們也可以方便的把這個應用作為用戶端以及服務端。其實根本區別就是為了使調用者與被調用者在不同進程中,於是在服務中添加android:process=":remote"即可。省去了再建立應用用戶端調用。
4:AIDL只是請定義一個契約,我們這裡需要一個服務來提供服務。於是建立服務MyService.
二.實戰
既然我們瞭解一些基礎知識後,現在我們開始來代碼吧。假設我們需要在一個進程中調用其他應用的服務,這個服務提供一個股票價格查詢,或者GPS定位的服務。
並且定義一個類,繼承我們AIDL產生的Stub內部類,並實現我們AIDL定義的方法
代碼如下:
View Code
package com.dongzi;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class MyService extends Service {
static final String TAG="MyService";
//定義內部類MyServiceImpl繼承我們的AIDL檔案自動產生的內部類,
//並且實現我們AIDL檔案定義的介面方法
private class MyServiceImpl extends IStockQuoteService.Stub{
@Override
public double getPrice(String ticker) throws RemoteException {
Log.e(TAG, "getPrice");
return 10.5;
}
}
@Override
public IBinder onBind(Intent arg0) {
//返回AIDL實現
return new MyServiceImpl();
}
@Override
public void onDestroy(){
Log.e(TAG, "Release MyService");
super.onDestroy();
}
}
我們需要在onBind方法中返回我們的AIDL介面實現對象,以便其他進程調用。
當然了,現在AIDL,以及Service都定義好了,就需要在mainfest.xml中設定
<service android:name=".MyService"
android:process=":remote"
>
<intent-filter>
<action android:name="com.dongzi.IStockQuoteService"/>
</intent-filter>
</service>
在用戶端服務端在同個App中,android:process=":remote",代表在應用程式裡,當需要該service時,會自動建立新的進程。而如果是android:process="remote",沒有“:”分號的,則建立全域進程,不同的應用程式共用該進程。
那麼現在用戶端來調用我們的服務了,代碼如下:
View Code
package com.dongzi;
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;
public class MYyActivity extends Activity {
static final String TAG="MYyActivity";
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btnCall=(Button)findViewById(R.id.btnCall);
if(btnCall!=null)
btnCall.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
//綁定一個服務
bindMyService();
}
});
}
IStockQuoteService iService=null;
private ServiceConnection conn=new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//返回AIDL介面對象,然後可以調用AIDL方法
iService=IStockQuoteService.Stub.asInterface(service);
double value=0.0;
try {
value=iService.getPrice("");
}
catch (RemoteException e) {
Log.e(TAG,"調用出錯!");
e.printStackTrace();
}
Log.e(TAG, "返回數值為:"+value);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "釋放Service");
}
};
private void bindMyService(){
// Intent intent=new Intent("com.dongzi.IStockQuoteService");
Intent intent=new Intent(this,MyService.class);
startService(intent);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
}
在按鈕點擊時候啟動service,然後再綁定這個Service.在串連到服務後,我們會發現,調用AIDL中定義的方法成了,列印如下:
項目結構為: