Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android IPC 之 AIDL(一)

Android IPC 之 AIDL(一)

編輯:關於Android編程

IPC是Inter-Process Communication的縮寫,即跨進程通信。Android中跨進程通信有多種方式,如文件共享、使用ContentProviderBroadcast、和Socket等。比較復雜的情況下,常用的兩種方式為MessengerAIDL,而Messenger的底層實現又是AIDL。

首先不看別的,先來看一下AIDL是如何使用的。

假設我們現在有一個兩數相加的任務,客戶端沒辦法完成(別問我它為什麼完不成==,咱舉栗子簡單點哈~),需要將任務交給另一個進程中的服務端完成,再從服務端獲取到該任務的結果。

我們首先如下圖方式創建一個AIDL接口:

新建文件,選擇AIDL

Android Studio會自動為它生成一個路徑,如下圖:

這裡寫圖片描述

在該文件中聲明一個接口以及一個我們想讓服務端實現的接口方法。如下:

package com.vera.aidltest;

interface IMyAdd {
   int myAdd(int num_a,int num_b);
}

注意,並不是所有數據類型都能在AIDL文件中使用,AIDL文件只支持以下幾種數據類型:

Java 中的基本數據類型 String 和CharSequence List 和 Map ,且List和Map 對象的元素必須是AIDL支持的數據類型 AIDL 自動生成的接口 ,需要導入(import,即使同處於一個包中) 實現android.os.Parcelable 接口的類的對象. 需要導入(import,即使同處於一個包中),且必須新建一個與其同名的AIDL文件,並在文件中聲明該類為Parcelable

在接口定義好後,系統將為我們生成一個Java文件,AS下是在app\build\generated\source\aidl\debug目錄下,生成的代碼如下:

package com.vera.aidltest;
public interface IMyAdd extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.vera.aidltest.IMyAdd
{
private static final java.lang.String DESCRIPTOR = "com.vera.aidltest.IMyAdd";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.vera.aidltest.IMyAdd interface,
 * generating a proxy if needed.
 */
public static com.vera.aidltest.IMyAdd asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.vera.aidltest.IMyAdd))) {
return ((com.vera.aidltest.IMyAdd)iin);
}
return new com.vera.aidltest.IMyAdd.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_myAdd:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.myAdd(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.vera.aidltest.IMyAdd
{
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 int myAdd(int num_a, int num_b) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num_a);
_data.writeInt(num_b);
mRemote.transact(Stub.TRANSACTION_myAdd, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_myAdd = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int myAdd(int num_a, int num_b) throws android.os.RemoteException;
}

代碼很長很凌亂的樣子……嗯,本節我們先不看它,只需要知道它是根據AIDL文件生成的一個接口IMyAdd,包含一個繼承自Binder的靜態內部抽象類Stub(咦,這麼連起來說總有哪裡怪怪的……),並且聲明了myAdd()方法。為什麼是抽象類呢,因為它實現了IMyAdd接口卻並沒有真正實現,那放到哪裡實現呢?當然是我們的服務端咯。

接下來,我們可以就可以來寫客戶端和服務端的代碼了,那麼,我們這裡的客戶端和服務端指的是什麼呢?就本例來說,它們分別是一個Activity和一個Service,這裡我們將它們放在了同一個應用中,只不過通過某種方法使其運行在不同的進程。更多情況下它們並不運行在同一個應用中,這時候我們需要將整個aidl文件夾的內容復制一份,這是因為客戶端和服務端的AIDL包結構需要保持一致,否則將會出現反序列化不成功的結果,那麼跨進程通信將無法進行。

那麼我們現在開始寫服務端的代碼,新建一個Service名為ServerService如下:

package com.vera.aidltest;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class ServerService extends Service {

   private Binder mBinder=new IMyAdd.Stub(){
       @Override
       public int myAdd(int num_a, int num_b) throws RemoteException {
           int result=num_a+num_b;
           Log.d("ServerService","the result of "+num_a+" and "+num_b+" is "+result);
           return result;
       }
   };

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

ServerService很簡單,它只是創建了一個Binder對象並在onBinder()方法中將其返回。該Binder對象就是我們實現了接口方法的Stub對象。好啦,現在我們可以在myAdd()方法中愉快地進行我們的操作啦。在這裡我們只是得到num_a和num_b的和並將其打印,最後再返回結果。

另外,我們需要將該Service設置在一個獨立的進程中,不然還怎麼玩跨進程通信~
更改AndroidManifest.xml如下:


好了,服務端的創建已經完成啦,我們現在來看客戶端。客戶端要做些什麼呢?,我們來看代碼:

package com.vera.aidltest;

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.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class ClientActivity extends AppCompatActivity {

    Button mButton;

    private ServiceConnection myConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            IMyAdd mMyAdd=IMyAdd.Stub.asInterface(iBinder);
            try {
                int result=mMyAdd.myAdd(1,2);
                Log.d("ClientActivity","get the result is "+result);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);
        mButton=(Button)findViewById(R.id.activity_client_mbutton);

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent=new Intent(ClientActivity.this,ServerService.class);
                bindService(intent,myConnection, Context.BIND_AUTO_CREATE);
            }
        });
    }
    @Override
    protected void onDestroy(){
        unbindService(myConnection);
        super.onDestroy();
    }
}

首先,我們創建了一個ServiceConnection的匿名類並實例化一個對象,然後,我們又設置了在Button點擊之後通過bindService()方法綁定服務。bindService()方法接收三個參數,第一個是在這之前我們創建的Intent對象,第二個是ServiceConnection的實例對象,第三個參數是一個標志位,BIND_AUTO_CREATE表示在Activity和Service進行綁定後,服務將自動創建。然後我們重寫了onDestroy()方法,將Activity和Service解除綁定。

重點是在創建的ServiceConnection匿名類裡!在這個類裡我們重寫了onServiceConnected()方法和onServiceDisconnected()方法,這兩個方法分別會在Activity與Service成功綁定和解除綁定的時候調用。在onServiceConnected()方法裡,我們調用了Stub()的asInterface()方法,該方法返回一個Binder代理對象,並向上轉型成為客戶端接受的AIDL接口類型的對象!

好啦,拿到了這個對象,現在只差一步調用方法的事啦,我們來試一試1+2等於多少叭~

運行程序,在點擊Button之後,查看日志打印信息,如下圖:

這裡寫圖片描述

這裡寫圖片描述

在com.vera.aidltest進程中,客戶端打印出得到的結果為3,在com.vera.aidltest:remote進程中,服務端打印出1+2等於3(我英語不好……),通信成功啦!

嗯好,那我們來總結一下,具體的步驟吧:

首先建立一個AIDL文件,在其中定義一個接口,接口中應包含我們希望服務端實現的方法的聲明。 其次,在服務端中將實現好方法的Stub對象通過onBind()方法返回 最後,在客戶端中將服務綁定,並重寫ServiceConnection類中的方法,在其中獲得Binder對象,調用服務端的接口方法。

這其中我們屏蔽了很多細節,只談了使用方法,下一節婷子會把更具體的部分再貼出來,第一篇技術博客,有什麼不對的地方還請留言哦,麼麼哒~

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved