(一)Binder核心原理
1,先看一張圖,這張圖是我從網上下載下來的
從圖中可以看出,上層的Binder通信是依賴於底層的。服務端提供了一個Binder對象,這個Binder對象是需要我們自定義的 。我們自定義的Binder指向的是native層的
BBinder。指向BBinder的時候需要在Service Manager中註冊服務。然後調用ioctl進行數據方面的操作。這是服務端。
再看下客戶端,客戶端提供了一個BinderProxy。通過ioctl進行數據的交互
2,Binder是什麼?
從IPC角度來說:
定義:Binder是Android中的一種跨進程通信方式,該通信方式在linux中沒有,是Android獨有,作用是在Android中實現跨進程通信
從Android Driver層來說
定義:Binder還可以理解爲一種虛擬的物理設備,它的設備驅動是/dev/binder
備註:驅動層位於Linux內核中,它提供了最底層的數據傳遞,對象標識,線程管理,調用過程控制等功能。驅動層是整個Binder機制的核心
從Android Native層
定義:Binder是創建Service Manager以及BpBinder/BBinder模型,搭建與binder驅動的橋樑
從Android Framework層
定義:Binder是各種Manager(ActivityManager、WindowManager等)和相應xxxManagerService的橋樑
從Android APP層
定義:Binder是客戶端和服務端進行通信的媒介,當bindService的時候,服務端會返回一個包含了服務端業務調用的 Binder對象,通過這個Binder對象,客戶端就可以獲取服務端提供的服務或者數據,這裏的服務包括普通服務和基於AIDL的服務
(二)Binder源碼分析
1,Binder驅動
主要是四個核心函數,這裏全部是C層代碼,我就不貼代碼了。
binder_init():主要是驅動設備的初始化。
binder_open():打開binder驅動設備
binder_mmap():首先在內核虛擬地址空間,申請一塊與用戶虛擬內存相同大小的內存;然後再申請1個page大小的物理內存,再將同一塊物理內存分別映射到內核虛擬地址空間和用戶虛擬內存空間,從而實現了用戶空間的Buffer和內核空間的Buffer同步操作的功能。
binder_ioctl():數據操作
Binder在進程間數據通信的流程圖:當Client端與Server端發送數據時,Client(作爲數據發送端)先從自己的進程空間把IPC通信數據copy_from_user拷貝到內核空間,而Server端(作爲數據接收端)與內核共享數據,不再需要拷貝數據,而是通過內存地址空間的偏移量,即可獲悉內存地址,整個過程只發生一次內存拷貝
2,ServiceManager
我們看兩張時序圖
獲取ServiceManager時序圖
啓動ServiceManager時序圖
3,framework層的分析
ServiceManager 類有一個addService方法
public static void addService(String name, IBinder service, boolean allowIsolated,
182 int dumpPriority) {
183 try {
184 getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
185 } catch (RemoteException e) {
186 Log.e(TAG, "error in addService", e);
187 }
188 }
我們看下getServiceManager();
private static IServiceManager getIServiceManager() {
//採用的是單例模式
102 if (sServiceManager != null) {
103 return sServiceManager;
104 }
105
106 // Find the service manager
107 sServiceManager = ServiceManagerNative
108 .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
109 return sServiceManager;
110 }
(三)手寫Binder C/S架構
服務端代碼:
Person實體類
package com.dn_alan.service;
import android.os.Parcel;
import android.os.Parcelable;
//進程間通信需要序列化
public class Person implements Parcelable {
private String name;
private int grade;
protected Person(Parcel in) {
name = in.readString();
grade = in.readInt();
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(grade);
}
}
Person.aidl文件
// DNAIdl.aidl
package com.dn_alan.service;
// Declare any non-default types here with import statements
parcelable Person;
DNAIdl.aidl文件
// DNAIdl.aidl
package com.dn_alan.service;
// Declare any non-default types here with import statements
import com.dn_alan.service.Person;
interface DNAIdl {
void addPerson(in Person person);
List<Person> getPersonList();
}
AIDL中的定向 tag 表示了在跨進程通信中數據的流向,其中 in 表示數據只能由客戶端流向服務端, out 表示數據只能由服務端流向客戶端,而 inout 則表示數據可在服務端與客戶端之間雙向流通。
DNAidlService類
package com.dn_alan.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import java.util.ArrayList;
import java.util.List;
public class DNAidlService extends Service {
private ArrayList<Person> personArrayList;
@Override
public IBinder onBind(Intent intent) {
personArrayList = new ArrayList<>();
return iBinder;
}
private IBinder iBinder = new DNAIdl.Stub() {
@Override
public void addPerson(Person person) throws RemoteException {
personArrayList.add(person);
}
@Override
public List<Person> getPersonList() throws RemoteException {
return personArrayList;
}
};
}
在文件清單中註冊
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dn_alan.service">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--exported表示跨進程-->
<service android:name=".DNAidlService"
android:exported="true"/>
</application>
</manifest>
在MainActivity中開啓服務
package com.dn_alan.service;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this, DNAidlService.class));
}
}
客戶端
注意包名和服務端保持一致,兩個aidl文件和person類(注意:person類必須在service文件夾下)和服務端相同,這裏不再贅述。
我們看下MainActivity類
package com.dn_alan.myapplication;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.dn_alan.service.DNAIdl;
import com.dn_alan.service.Person;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private DNAIdl dnaIdl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService();
}
private void bindService() {
//開啓服務
Intent intent = new Intent();
//第一個參數是DNAIDL.aidl文件的包名,第二個參數是服務端service的路徑
intent.setComponent(new ComponentName(
"com.dn_alan.service",
"com.dn_alan.service.DNAidlService"));
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
dnaIdl = DNAIdl.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
public void click(View view) {
try {
dnaIdl.addPerson(new Person("dn", 10));
List<Person> people = dnaIdl.getPersonList();
Toast.makeText(this, people.toString(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}