android IPC通訊(上)-sharedUserId&&Messenger
android IPC通訊(中)-ContentProvider&&Socket
這篇咱們將會着重介紹AIDL的使用方式和原理,要介紹AIDL先要簡單介紹一下Binder,並且Messenger,ContentProvider和AIDL的最底層都是使用的Binder。javascript
直觀來講,Binder是Android中的一個類,它實現了IBinder接口。從IPC角度來講,Binder是Android中的一種跨進程通訊方式,Binder還能夠理解爲一種虛擬的物理設備,它的設備驅動是 /dev/binder,該通訊方式在Linux中沒有;從Android Framework 角度來講,Binder 是 ServiceManager 鏈接各類Manager (ActivityManager,WindowManager,等等)和相應 ManagerService 的橋樑;從Android 應用層來講,Binder 是客戶端和服務端進行通訊的媒介,當 bindService 的時候,服務端會返回一個包含了服務端業務調用的 Binder 對象,經過這個Binder對象,客戶端就能夠獲取服務端提供的服務或者數據,這裏的服務包括普通服務和基於AIDL的服務。
還有兩點須要提到,第一點就是當客戶端發起遠程請求時,因爲當前線程會被掛起直至服務端進程返回數據,因此若是一個遠程方法是耗時的,那麼不能在UI線程中發起此遠程請求;其次,因爲服務端的Binder方法運行在Binder的線程池中,因此Binder方法無論是否耗時都應該採用同步的方法去實現,由於他已經運行在一個線程中了。下圖爲Binder的工做機制圖:
能夠看到Client客戶端會block直到方法返回,從圖中咱們能夠看到 Binder 的調用中有四個角色:Client, Proxy, Binder Driver 和 Server ,他們的關係以下圖所示:
這四者的關係相似於網絡訪問,html
IBinder b = ServiceManager.getService("activity");複製代碼
經過 ServiceManager 去獲取 AMS 的 Binder 對象,ServiceManager 類的代碼很簡單:
public final class ServiceManager {
private static final String TAG = "ServiceManager";
private static IServiceManager sServiceManager;
private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
/
Returns a reference to a service with the given name.
@param name the name of the service to get @return a reference to the service, or <code>null</code> if the service doesn't exist
*/
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
public static void addService(String name, IBinder service) {
...
}
....
}複製代碼
public interface IServiceManager extends IInterface
{
/
Retrieve an existing service called @a name from the service manager. Blocks for a few seconds waiting for it to be
published if it does not already exist. /
public IBinder getService(String name) throws RemoteException;
/
Retrieve an existing service called @a name from the service manager. Non-blocking.
*/
public IBinder checkService(String name) throws RemoteException;
/
Place a new @a service called @a name into the service manager.
/
public void addService(String name, IBinder service, boolean allowIsolated)
throws RemoteException;
/** Return a list of all currently running services.
/
public String[] listServices() throws RemoteException;
/** Assign a permission controller to the service manager. After set, this
interface is checked before any services are added. /
public void setPermissionController(IPermissionController controller)
throws RemoteException;
static final String descriptor = "android.os.IServiceManager";
int GET_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
int CHECK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1;
int ADD_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2;
int LIST_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3;
int CHECK_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4;
int SET_PERMISSION_CONTROLLER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+5;
}複製代碼
// IWeatherManager.aidl
package com.android.aidl;
import com.android.aidl.Weather;
import com.android.aidl.listener.IWeatherChangeListener;
interface IWeatherManager {
List<Weather> getWeather();
void addWeather(in Weather weather);
void addListener(in IWeatherChangeListener listener);
void removeListener(in IWeatherChangeListener listener);
}複製代碼
//Weather.aidl
package com.android.aidl;
parcelable Weather;複製代碼
// IWeatherChangeListener.aidl
package com.android.aidl.listener;
import com.android.aidl.Weather;
interface IWeatherChangeListener {
void onWeatherChange(in Weather newWeather);
}複製代碼
public class Weather implements Parcelable{
public String cityName;
public double temperature;
public double humidity;
public AllWeather weather;
protected Weather(Parcel in) {
temperature = in.readDouble();
humidity = in.readDouble();
//使用該方式來寫入枚舉
weather = AllWeather.values()[in.readInt()];
cityName = in.readString();
}
public Weather() {
}
public static final Creator<Weather> CREATOR = new Creator<Weather>() {
@Override
public Weather createFromParcel(Parcel in) {
return new Weather(in);
}
@Override
public Weather[] newArray(int size) {
return new Weather[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeDouble(temperature);
dest.writeDouble(humidity);
dest.writeInt(weather.ordinal());
dest.writeString(cityName);
}
public enum AllWeather{
sunny,cloudy,rain,snowy
}
}複製代碼
public class WeatherManagerService extends Service{
//支持併發讀寫的list
public CopyOnWriteArrayList<Weather> weathers = new CopyOnWriteArrayList<>();
public RemoteCallbackList<IWeatherChangeListener> listeners = new RemoteCallbackList<>();
@Override
public void onCreate() {
super.onCreate();
Weather nanshan = new Weather();
nanshan.cityName = "南山";
nanshan.temperature = 20.5;
nanshan.humidity = 45;
nanshan.weather = Weather.AllWeather.cloudy;
Weather futian = new Weather();
futian.cityName = "福田";
futian.temperature = 21.5;
futian.humidity = 48;
futian.weather = Weather.AllWeather.rain;
weathers.add(nanshan);
weathers.add(futian);
}
private Binder mBinder = new IWeatherManager.Stub() {
@Override
public List<Weather> getWeather() throws RemoteException {
L.i("server returns all of the weathers");
return weathers;
}
@Override
public void addWeather(Weather weather) throws RemoteException {
weathers.add(weather);
L.i("server add new Weather:" + weather.cityName);
int N = listeners.beginBroadcast();
for (int i=0; i<N; i++){
IWeatherChangeListener listener = listeners.getBroadcastItem(i);
listener.onWeatherChange(weather);
}
L.i("server notify the listener that weathers have been changed");
listeners.finishBroadcast();
}
@Override
public void addListener(IWeatherChangeListener listener) throws RemoteException {
L.i("server adding listener");
listeners.register(listener);
}
@Override
public void removeListener(IWeatherChangeListener listener) throws RemoteException {
L.i("server removing listener");
listeners.unregister(listener);
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
int permission = checkCallingPermission("com.android.permission.WRITEWEATHERPERMISSION");
//檢測客戶端是否聲明權限
if (permission == PackageManager.PERMISSION_DENIED){
L.e("permission denied");
return false;
}
L.i("permission granted");
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0){
String packageName = packages[0];
if (!packageName.startsWith("com.android")){
L.e("package name not accept");
return false;
}
L.i("package name accept");
}
return super.onTransact(code, data, reply, flags);
}
};
@Override
public IBinder onBind(Intent intent) {
// int permission = checkCallingPermission("com.android.permission.WRITEWEATHERPERMISSION");
// //檢測客戶端是否聲明權限
// if (permission == PackageManager.PERMISSION_DENIED){
// L.e("permission denied");
// return null;
// }
return mBinder;
}
}複製代碼
ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>()
IBinder binder = callback.asBinder();
Callback cb = new Callback(callback, cookie);
public class ClientActivity extends BaseActivity implements View.OnClickListener{
private ServiceConnection serviceConnection = null;
private IBinder.DeathRecipient deathRecipient = null;
private IWeatherChangeListener listener = null;
private IWeatherManager weatherManager;
private TextView tv_content;
private TextView tv_add;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
findViewById(R.id.btn_add).setOnClickListener(this);
findViewById(R.id.btn_query).setOnClickListener(this);
findViewById(R.id.btn_remove_listener).setOnClickListener(this);
tv_content = (TextView) findViewById(R.id.tv_content);
tv_add = (TextView) findViewById(R.id.tv_add);
listener = new IWeatherChangeListener.Stub(){
@Override
public void onWeatherChange(Weather newWeather) throws RemoteException {
L.i("client has been notified that "+newWeather.cityName+" has been added");
tv_add.setText(newWeather.cityName + "has been added");
}
};
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
weatherManager = IWeatherManager.Stub.asInterface(service);
try {
weatherManager.asBinder().linkToDeath(deathRecipient, 0);
weatherManager.addListener(listener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
weatherManager = null;
}
};
deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
//移出以前的死亡容器
weatherManager.asBinder().unlinkToDeath(deathRecipient, 0);
weatherManager = null;
//從新鏈接
bindServer();
}
};
bindServer();
}
private void bindServer(){
Intent intent = new Intent(this, WeatherManagerService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_query){
try {
//調用遠程服務端接口時,客戶端進程會掛起,勿在主線程中調用耗時遠程操做
L.i("client is getting weather");
List<Weather> weathers = weatherManager.getWeather();
L.i("client has gotten weather");
StringBuilder sb = new StringBuilder();
for (Weather weather : weathers){
sb.append(weather.cityName).append("\n");
sb.append("humidity:").append(weather.humidity)
.append("temperature").append(weather.temperature)
.append("weather").append(weather.weather).append("\n");
}
tv_content.setText(sb);
} catch (RemoteException e) {
e.printStackTrace();
}
}else if (v.getId() == R.id.btn_add){
Weather weather = new Weather();
weather.weather = Weather.AllWeather.cloudy;
weather.humidity = 25.5;
weather.temperature = 19.5;
weather.cityName = "羅湖";
try {
//調用遠程服務端接口時,客戶端進程會掛起,勿在主線程中調用耗時遠程操做
L.i("client is adding weather " + weather.cityName);
weatherManager.addWeather(weather);
L.i("client has added weather " + weather.cityName);
} catch (RemoteException e) {
e.printStackTrace();
}
}else if (v.getId() == R.id.btn_remove_listener){
try {
weatherManager.removeListener(listener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
try {
weatherManager.asBinder().linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}複製代碼
I/PID:28533: [TID:7035] 1.onTransact(line:90): permission granted
I/PID:28533: [TID:7035] 1.onTransact(line:99): package name accept
I/PID:28533: [TID:7035] 1.addListener(line:72): server adding listener
//client add click
I/PID:28502: [TID:1] ClientActivity.onClick(line:115): client is adding weather 羅湖
I/PID:28533: [TID:7036] 1.onTransact(line:90): permission granted
I/PID:28533: [TID:7036] 1.onTransact(line:99): package name accept
I/PID:28533: [TID:7036] 1.addWeather(line:59): server add new Weather:羅湖
I/PID:28502: [TID:1] 1.onWeatherChange(line:47): client has been notified that 羅湖 has been added
I/PID:28533: [TID:7036] 1.addWeather(line:66): server has notified the listener that weathers have been changed
I/PID:28502: [TID:1] ClientActivity.onClick(line:117): client has added weather 羅湖
//client remove listener click
I/PID:28533: [TID:7035] 1.onTransact(line:90): permission granted
I/PID:28533: [TID:7035] 1.onTransact(line:99): package name accept
I/PID:28533: [TID:7035] 1.removeListener(line:78): server removing listener
//*client get click
I/PID:28502: [TID:1] ClientActivity.onClick(line:93): client is getting weather
I/PID:28533: [TID:7036] 1.onTransact(line:90): permission granted
I/PID:28533: [TID:7036] 1.onTransact(line:99): package name accept
I/PID:28533: [TID:7036] 1.getWeather(line:52): server returns all of the weathers
I/PID:28502: [TID:1] ClientActivity.onClick(line:95): client has gotten weather複製代碼
上面差很少就把AIDL的用法詳細介紹完了,可是有的時候咱們可能須要不止一個業務模塊,也就是不僅僅須要一個天氣模塊,咱們還須要一個計算溫度平均值的模塊(雖然能夠寫在一個模塊中,可是咱們仍是假設要用兩個模塊吧~),是否是須要爲每一個模塊都單獨創建一個Service呢?固然不是,會很耗資源的好嗎,解決方法就是先爲每個模塊創建一個單獨的aidl文件,最後再創建一個總體的aidl文件用來管理這些單獨的aidl。
看看這三個文件,IWeatherManager.aidl,IComputerManager.aidl和IBinderPoolManager.aidl:java
// IWeatherManager.aidl
package com.android.binderpool;
import com.android.binderpool.Weather;
interface IWeatherManager {
List<Weather> getWeather();
void addWeather(in Weather weather);
}複製代碼
// IComputerManager.aidl
package com.android.binderpool;
import com.android.binderpool.Weather;
interface IComputerManager {
double computeAverageTemperature(in List<Weather> weathers);
}複製代碼
// IBinderPoolManager.aidl
package com.android.binderpool;複製代碼
interface IBinderPoolManager {
IBinder queryCode(int code);
}IBinderPoolManager.aidl文件用來統一管理全部的AIDL接口,queryCode函數經過code值來肯定須要返回給客戶端的IBinder對象。
來看看服務端的代碼的變更:node
public class BinderPoolService extends Service{
public static final int CODE_WEATHER = 1;
public static final int CODE_COMPUTER = 2;複製代碼
<span class="hljs-keyword">private</span> IBinderPoolManager iBinderPoolManager;
<span class="hljs-comment">//支持併發讀寫的list</span>
<span class="hljs-keyword">public</span> CopyOnWriteArrayList<Weather> weathers = <span class="hljs-keyword">new</span> CopyOnWriteArrayList<>();
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>() {
<span class="hljs-keyword">super</span>.onCreate();
Weather nanshan = <span class="hljs-keyword">new</span> Weather();
nanshan.cityName = <span class="hljs-string">"南山"</span>;
nanshan.temperature = <span class="hljs-number">20.5</span>;
nanshan.humidity = <span class="hljs-number">45</span>;
nanshan.weather = Weather.AllWeather.cloudy;
Weather futian = <span class="hljs-keyword">new</span> Weather();
futian.cityName = <span class="hljs-string">"福田"</span>;
futian.temperature = <span class="hljs-number">21.5</span>;
futian.humidity = <span class="hljs-number">48</span>;
futian.weather = Weather.AllWeather.rain;
weathers.add(nanshan);
weathers.add(futian);
iBinderPoolManager = <span class="hljs-keyword">new</span> IBinderPoolManager.Stub(){
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> IBinder <span class="hljs-title">queryCode</span>(<span class="hljs-keyword">int</span> code) <span class="hljs-keyword">throws</span> RemoteException {
<span class="hljs-keyword">switch</span> (code){
<span class="hljs-keyword">case</span> CODE_WEATHER:
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> IWeatherManager.Stub(){
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> List<Weather> <span class="hljs-title">getWeather</span>() <span class="hljs-keyword">throws</span> RemoteException {
<span class="hljs-keyword">return</span> weathers;
}
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addWeather</span>(Weather weather) <span class="hljs-keyword">throws</span> RemoteException {
weathers.add(weather);
}
};
<span class="hljs-keyword">case</span> CODE_COMPUTER:
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> IComputerManager.Stub() {
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">computeAverageTemperature</span>(List<Weather> weathers) <span class="hljs-keyword">throws</span> RemoteException {
<span class="hljs-keyword">double</span> sum = <span class="hljs-number">0</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>; i<weathers.size(); i++){
sum += weathers.get(i).temperature;
}
<span class="hljs-keyword">return</span> sum/weathers.size();
}
};
<span class="hljs-keyword">default</span>:
<span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
}
}
};
}
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> IBinder <span class="hljs-title">onBind</span>(Intent intent) {
<span class="hljs-keyword">return</span> iBinderPoolManager.asBinder();
}複製代碼
} 根據code的不一樣返回不一樣的IBinder對象,這樣在客戶端中就可以獲取對應AIDL接口的IBinder對象,最終就能在客戶端調用不一樣AIDL模塊中的方法。客戶端代碼很簡單,在這裏就不介紹了,感興趣的能夠去看看源碼:
github.com/zhaozepeng/…
關於IPC相關知識的介紹就到這了,若是有什麼疑問,你們能夠多多交流啊,謝謝~android