編輯:關於Android編程
最常見的aidl的使用就是Service的跨進程通信了,那麼我們就寫一個Activity和Service的跨進程通信吧。
首先,我們就在AS裡面新建一個aidl文件(ps:現在AS建aidl不要求和java包名相同了):
package aidl;
interface IMyInterface {
String getInfor(String s);
}
可以看到,在這裡面我們就一個方法getInfor(String s),接受一個字符串參數,然後返回一個字符串,恩,相當的簡單。
接著你sync project一下就可以在app/generated/source/aidl/debug/aidl裡面發現由aidl文件生成的java文件了。點進去一看,可能你也被這個貌似’龐大’的類給嚇住了,那現在我們就先不管它了。
然後就看看Service:
public class MyService extends Service {
public final static String TAG = "MyService";
private IBinder binder = new IMyInterface.Stub() {
@Override
public String getInfor(String s) throws RemoteException {
Log.i(TAG, s);
return "我是 Service 返回的字符串";
}
};
@Overrid
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreat");
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
這裡我們寫了一個Service,看一下也比較簡單。先new了一IMyInterface.Stub()並把它向上轉型成了IBinder,最後在onBind方法中返回回去。可能你注意到了,在IMyInterface.Stub()的內部我們重寫getInfor(String s) 方法,沒錯這就是我們 aidl文件中定義的接口。
對了,因為我們希望看到的是跨進程通信,所以我們把MyService定義成新啟動一個進程:
定義為啟動在新進程中,只需要在AndroidMainfest.xml中聲明是加上一個process屬性即可,不過這裡有兩個地方值得注意:
1.組件默認的進程名就是包名;
2.定義新的進程名的時候需要以包的形式(eg: com.xu.aidl)。
接著,我們繼續向下看:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "MainActivity";
private IMyInterface myInterface;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myInterface = IMyInterface.Stub.asInterface(service);
Log.i(TAG, "連接Service 成功");
try {
String s = myInterface.getInfor("我是Activity傳來的字符串");
Log.i(TAG, "從Service得到的字符串:" + s);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "連接Service失敗");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startAndBindService();
}
private void startAndBindService() {
Intent service = new Intent(MainActivity.this, MyService.class);
//startService(service);
bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
由於主要是Service和Activity間的通信,所以為了讓代碼整潔就沒有寫UI了。
在onCreate(Bundle savedInstanceState)中,我們調用了自己定義的一個方法startAndBindService(),這個方法裡面我們生成了一個Intent,然後 bindService了這個Intent傳入了三個參數分別是Intent、ServiceConnection、Flag。
Intent我們就不用說了,我們看看後面兩個參數:
在Activity中,我們new了一個ServiceConnection並實現了他的兩個方法onServiceConnected、onServiceDisconnected。在onServiceConnected中我們通過IMyInterface.Stub.asInterface(service)把傳來的IBinder轉換成了我們定義的IMyInterface。然後我們調用了getInfor方法,傳遞了個字符串和獲取從MyService傳來的字符串,並且打印了Log。
對於我們傳的Context.BIND_AUTO_CREATE的意思就是說:如果綁定Service的時候,Service還沒有被創建,就創建它。當然,這裡還有很多Flag,我也不一一說了。
然後,我們的編碼就完成了,開始運行測試一下把!
vcHLztLDx8/r0qq1xL3hufujrMv50tSjrNK7uPbTw2FpZGzKtc/WtcS/5734s8zNqNDFvs3V4tH5zeqzycHLoaO1sci7ztLDx7XEZGVtbyjV4rTOsrvE3Ma0tO3ByynW0KOsZ2V0SW5mb3LDu9PQyM66zsLfvK2jrMTj0rK/ydLUvNPSu9Cpwt+8rcil1rTQ0NK70Km4tNTTtcSy2df3oaM8L3A+DQo8aDIgaWQ9"aidl的理解">AIDL的理解:
現在我們會使用aidl了,那我們還不去掀開它神秘的面紗。
Service中的IBinder
還記得我們在MyService中利用new IMyInterface.Stub()向上轉型成了IBinder然後在onBind方法中返回的。那我們就看看IMyInterface.Stub吧:
public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {
..........
}
可以看到,Stub是IMyInterface中的一個靜態抽象類,繼承了Binder,並且實現了IMyInterface接口。這也就解釋了我們定義IMyInterface.Stub的時候為什麼需要實現IMyInterface中的方法了,也說明了為什麼我們可以把IMyInterface.Stub向上轉型成IBinder了。
Activity中的IMyInterface
在Activity中,通過ServiceConnection連接MyService並成功回調onServiceConnected中我們把傳回來的IBinder通過IMyInterface.Stub.asInterface(service)轉換成為IMyInterface,那就來看看這裡是如何轉換的吧:
public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {
..........
public static aidl.IMyInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//檢查Binder是不是在當前進程
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof aidl.IMyInterface))) {
return ((aidl.IMyInterface) iin);
}
return new aidl.IMyInterface.Stub.Proxy(obj);
}
}
首先,我們因該明白的是,傳回來的IBinder就是我們在Service的onBind( )方法所return的IBinder,然後我們調用Stub中的靜態方法asInterface並把返回來的IBinder當參數傳進去。
在asInterface方法中,首先判斷了傳進來的IBinder是不是null,如果為null就返回一個null;接著就判斷傳進來的IBinder是不是就在當前進程裡面,如果是的話就直接返回IMyInterface,不是的話就返回IMyInterface.Stub.Proxy(obj)。這裡我覺得需要明白的是:直接返回的IMyInterface是實現了定義的接口方法getInfor的。因為在IMyInterface.Stub中所實現的。當然如果是在同一進程中,那麼我們調用IMyInterface的方法時就是在本地調用方法,直接調用就可以了。
如果沒在同一進程,就會返回IMyInterface.Stub.Proxy(obj):
private static class Proxy implements aidl.IMyInterface {
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 getInfor(java.lang.String s) 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.writeString(s);
//傳送數據到遠程的
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
_reply.readException();
//接受從遠端傳回的數據
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getInfor = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
在Proxy中,我們首先把Service連接成功返回的IBinder它的內部變量mRemote,這裡在提一下,這裡得IBinder還是是MyService中onBind所返回的。然後,當我們調用IMyInterface的方法的時候,其實就是調用的Proxy的方法了,這也是為什麼這個類叫做Porxy的原因了。
當調用IMyInterface.getInfor(String s) ,我們就看Proxy中的getInfor,先獲取了兩個Parcel對象 _data、_data,從變量名就可以看出,一個是傳送數據的,另一個則是接受返回數據的。接著,向_data中寫入了DESCRIPTOR(也就是這個類的全名),再寫入了方法參數。然後就到了最重要的一步了,
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
這裡我們調用了IBinder的transact方法,來把數據傳給遠端的服務器。然後在我們遠程的MyService中,裡面的Stub中就會回調onTransact()(因為你把數據傳個遠程的服務,遠端的服務收到數據也就回調了)
注意:這裡是在遠程的服務裡調用的。
@Overridepublic
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_getInfor: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
//取出參數
_arg0 = data.readString();
// 遠程服務調用自己本地實現的方法獲取返回值
java.lang.String _result = this.getInfor(_arg0);
reply.writeNoException();
//寫入返回值
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
onTransact方法是在Stub的內部實現的。
先看一下它的四個參數:
code:每個方法都有一個int類型的數字用來區分(後面中的swicth),在我們例子中也就是我們Proxy中的Stub.TRANSACTION_getInfor。
data:傳過來的數據,其中包含我們的參數,以及類的描述。
reply:傳回的數據,我們要寫入是否發生了Exception,以及返回值
flags:該方法是否有返回值 ,0表示有返回值。
調用onTransact就表示有數據傳來,首先就會通過swicth判斷是哪個方法,然後取出方法參數,調用本地實現的方法獲取返回值,寫入返回值到reply。最後,返回true,才會把數據發送出去,發揮false就不會把結果返回給Activity了。這裡也就是說,只有返回true,我們Proxy中才能接受從遠端傳回的數據。
//傳送數據到遠程的
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
_reply.readException();
//接受從遠端傳回的數據
_result = _reply.readString();
注意:Service也是把數據發送出來,讓客戶端接受的。
Service發出了數據,客戶端接收到了,就會一層一層返回去。所以,當我們簡單的調用IMyInterface的getInfor時候,先是Proxy的transact發送出數據,然後服務端的onTransact接受並處理傳來的數據,再把處理得到的數據寫入返回值並發送給客戶端,客戶端讀取值後就成為調用方法的返回值返回了。
然後AIDL的基本原理就是這樣了,看明白了AIDL,才發現原來AIDL不過就是幫我們生成了那些數據寫入,傳送,讀取的方法而已。所以,我們自己寫一個不要AIDL的跨進程通信也是挺簡單的,快動手試試吧!
Android中動畫分為三種,逐幀動畫,補間動畫,屬性動畫,這篇先總結逐幀動畫和補間動畫。逐幀動畫1, 是什麼字面上理解,幀之間追逐,幀動畫是順序的播放一系列圖片,從而產
相信對於手機的時間日期設置大家一定都不陌生吧,今天舉一個關於時間日期設置的示例,其中有些許不完善之處,例如如何使設置的時間日期和手機系統同步等。感興趣的讀者可以根據自身經
第一次做這樣的設備,原來如此簡單,記錄一下。 用公告程序替代launcher 其實小米盒子,樂視盒子等類似設備都是這樣做的。 一:顯示器嵌入系統。 買一個主
搞軟件開發的都知道項目中各種需求都有,而有時候各種奇葩的需求真是讓人大跌眼鏡,為了實現這些奇葩的需求我們往往苦逼的廢寢忘食,我現在的項目中就有一個應該算得上奇葩的需求吧,