編輯:關於android開發
IPC是Inter-Process Communication的縮寫,即跨進程通信。Android中跨進程通信有多種方式,如文件共享、使用ContentProvider、Broadcast、和Socket等。比較復雜的情況下,常用的兩種方式為Messenger和AIDL,而Messenger的底層實現又是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對象,調用服務端的接口方法。這其中我們屏蔽了很多細節,只談了使用方法,下一節婷子會把更具體的部分再貼出來,第一篇技術博客,有什麼不對的地方還請留言哦,麼麼哒~
在Android Studio中安裝OpenCV mac環境/Linux環境 在Android Studio中安裝OpenCV 對於女程序猿來說,每次安裝個什麼軟件,或者
Android視圖控件架構分析之View、ViewGroup,androidviewgroup 在Android中,視圖控件大致被分為兩類,即ViewGroup和Vie
Remote Displayer for Android,displayerandroid應用截圖: 作者:sunrain_hjb  
Android Log,androidlog1、Android Log簡介; 在程序中輸出日志,使用 android.util.Log 類。該類提供了若干靜態方法: