上次咱們從源碼的角度分析了Service的兩種狀態下的工做過程,具體分析可查看Service的工做過程。在文章的最後引出了兩個問題,其中一個是: Activity和Service的交互,Service是本地的或者遠程的,二者之間有什麼區別? 爲了分析上述二者的區別,咱們新建了一個工程,增長了兩個aidl文件,分別是Book.aidl、IBookManager.aidl。 Book.aidl:java
// IBook.aidl
package com.hwldzh.myapplication.aidl;
// Declare any non-default types here with import statements
parcelable Book;
複製代碼
IBookManager.aidl:android
// IBookManager.aidl
package com.hwldzh.myapplication.aidl;
import com.hwldzh.myapplication.aidl.Book;
// Declare any non-default types here with import statements
interface IBookManager {
List<Book> getBookList();
void addBookWithIn(in Book book);
}
複製代碼
而且增長了BookService.java和BookActivity.java。 BookService.java:bash
package com.hwldzh.myapplication.aidl;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* Created by hwldzh on 2018/11/21
*/
public class BookService extends Service {
private List<Book> bookList = new ArrayList<>();
public BookService() {
Book bookAndroid = new Book("Android", 10);
bookList.add(bookAndroid);
}
IBookManager.Stub binder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return bookList;
}
@Override
public void addBookWithIn(Book book) throws RemoteException {
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
複製代碼
BookActivity.java:app
package com.hwldzh.myapplication.aidl;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.mucfc.myapplication.R;
import java.util.List;
/**
* Created by hwldzh on 2018/11/21
*/
public class BookActivity extends Activity {
private IBookManager bookManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.book_layout);
Intent intent = new Intent(this, BookService.class);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bookManager = IBookManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
bookManager = null;
}
}, BIND_AUTO_CREATE);
findViewById(R.id.add_book_in).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bookManager != null) {
try {
Book book = new Book("JavaScript", 40);
bookManager.addBookWithIn(book);
Toast.makeText(BookActivity.this, "增長成功", Toast.LENGTH_SHORT).show();
Log.d("ABC", "book.Info=書名:" + book.getmBookName() + ", 價格:" + book.getmBookPrice());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
findViewById(R.id.get_books).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bookManager != null) {
List<Book> bookList = null;
try {
bookList = bookManager.getBookList();
} catch (RemoteException e) {
e.printStackTrace();
}
if (bookList != null) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < bookList.size(); i++) {
Book book = bookList.get(i);
builder.append("第" + (i + 1) + "本書,書名爲:" + book.getmBookName() + ", 價格爲:" + book.getmBookPrice() + "\n");
}
((TextView) findViewById(R.id.book_list)).setText(builder.toString());
}
}
}
});
}
}
複製代碼
所謂本地Service,即Activity和Service處於同一個進程中。咱們直接看Activity和Service綁定成功後,Service的onBind方法返回的IBinder對象:ide
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bookManager = IBookManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
bookManager = null;
}
}, BIND_AUTO_CREATE);
複製代碼
在ServiceConnection的onServiceConnected方法中,調用了IBookManager.Stub對象的asInterface方法。 **IBookManager.Stub對象是什麼???**它就是上面的IBookManager.aidl通過build命令後生成的IBookManager的內部類,其中的asInterface方法以下:函數
public static com.mucfc.myapplication.aidl.IBookManager asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.mucfc.myapplication.aidl.IBookManager))) {
return ((com.mucfc.myapplication.aidl.IBookManager)iin);
}
return new com.mucfc.myapplication.aidl.IBookManager.Stub.Proxy(obj);
}
複製代碼
上面的代碼中,調用了obj. queryLocalInterface方法,而obj就是Service傳進來的IBinder對象。因爲IBinder是一個接口,因此其中的queryLocalInterface方法由其實現類Binder來實現:ui
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
複製代碼
上面代碼傳進來的參數DESCRIPTOR是IBookManager的一個靜態不可變字符串,其內容爲IBookManager類的包路徑,保證了工程內的惟一性。this
從BookService中可知,當Service爲本地Service時,該Service的onBind方法返回的對象就是IBookManager.Stub對象,該對象的構造函數以下所示。spa
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
複製代碼
IBookManager.Stub對象調用了Binder類的attachInterface方法,並將本身和DESCRIPTOR字符串傳遞進去,以下所示。3d
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
複製代碼
上面的代碼邏輯將IBookManager的DESCRIPTOR字符串賦值給了mDescriptor對象,並將IBookManager.Stub對象賦值給mOwner對象。
這個時候,咱們再回頭看上面說到的queryLocalInterface方法,就知道了其返還的mOwer對象就是IBookManager.Stub對象,也就是在Service服務中建立的IBookManager.Stub對象。
這樣,咱們就可以在Activity中獲取到Service建立的IBookManager.Stub對象,經過該對象就能夠和Service進行交互。
所謂遠程Service,即該Service和Activity不在同一個進程中。這個時候Activity和Service的交互通訊就是進程間的通訊(IPC)。 Activity和Service綁定成功後,Service的onBind方法給咱們返回了一個IBinder對象:
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bookManager = IBookManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
bookManager = null;
}
}, BIND_AUTO_CREATE);
複製代碼
在ServiceConnection的onServiceConnected方法中,調用了IBookManager.Stub對象的asInterface方法。這一步和上面本地Service的分析是一致的,其中IBookManager.Stub對象的asInterface方法以下:
public static com.mucfc.myapplication.aidl.IBookManager asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.mucfc.myapplication.aidl.IBookManager))) {
return ((com.mucfc.myapplication.aidl.IBookManager)iin);
}
return new com.mucfc.myapplication.aidl.IBookManager.Stub.Proxy(obj);
}
複製代碼
從本地Service的分析可得知,obj因爲是一個遠程進程傳遞過來的IBinder對象,因此其中的mDescriptor和本地的IBookManager的DESCRIPTOR是不相等的,即asInterface方法會走到最後一步,也就是new了一個IBookManager.Stub.Proxy對象,並將obj傳遞了過去,IBookManager.Stub.Proxy對象的構造函數以下:
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
複製代碼
很簡單的賦值操做,將傳進來的IBinder對象進行了存儲。 這樣就完了嗎???所謂的交互呢???
咱們接着往下看,BookActivity中有一個獲取書單的按鈕,其邏輯爲:
findViewById(R.id.get_books).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bookManager != null) {
List<Book> bookList = null;
try {
bookList = bookManager.getBookList();
} catch (RemoteException e) {
e.printStackTrace();
}
if (bookList != null) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < bookList.size(); i++) {
Book book = bookList.get(i);
builder.append("第" + (i + 1) + "本書,書名爲:" + book.getmBookName() + ", 價格爲:" + book.getmBookPrice() + "\n");
}
((TextView) findViewById(R.id.book_list)).setText(builder.toString());
}
}
}
});
複製代碼
onClick方法中的bookManager對象是什麼?就是上面分析的IBookManager.Stub.Proxy對象。好,它接着調用了getBookList方法,也就是IBookManager.Stub.Proxy對象的getBookList方法,以下所示。
@Override
public java.util.List<com.hwldzh.myapplication.aidl.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.hwldzh.myapplication.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.hwldzh.myapplication.aidl.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
複製代碼
上面的代碼中建立了兩個Parcel對象,分別是_data、_reply,建立了一個List<com.hwldzh.myapplication.aidl.Book>對象——_result,而後調用了mRemote對象的transact方法,並將_data、_reply對象傳遞過去。上面分析到mRemote對象就是遠程的IBinder對象,這裏調用transact方法,會回調到遠程IBinder對象的onTransact方法:
@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_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.mucfc.myapplication.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBookWithIn:
{
data.enforceInterface(DESCRIPTOR);
com.mucfc.myapplication.aidl.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.mucfc.myapplication.aidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBookWithIn(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
複製代碼
而onTransact方法會根據不一樣方法的code值,將計算結果計算出來後,並回寫到reply(Parcel類型)對象中。因此上面的getBookList方法經過遠程Service返回的_reply參數就能獲取到遠程Service的BookList,從而實現了本地Activity和遠程Service的交互。
上面咱們只分析了Activity從遠程Service獲取數據的過程,而Activity傳遞數據給遠程Service數據的過程是相相似的,因此就再也不做分析。