91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android中Binder詳細學習心得

發布時間:2020-09-06 07:37:32 來源:腳本之家 閱讀:168 作者:忘了12138 欄目:移動開發

該文章是一個系列文章,是本人在Android開發的漫漫長途上的一點感想和記錄,我會盡量按照先易后難的順序進行編寫該系列。該系列引用了《Android開發藝術探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相關知識,另外也借鑒了其他的優質博客,在此向各位大神表示感謝,膜拜!!!另外,本系列文章知識可能需要有一定Android開發基礎和項目經驗的同學才能更好理解,也就是說該系列文章面向的是Android中高級開發工程師。

前言

上一次還不如不說去面試了呢,估計是掛了,數據結構與算法方面雖然面試前突擊了一波,但是時間太短,當時學的也不好。另外Android的一些知識也不是很了解。不過這也加大了我寫博客的動力。許多知識總覺得自己掌握的還挺好,不過一問到比較細節的方面就不太清楚了。所以寫這整個博客的目的也是加深自己的知識,培養自己的溝通能力,和大家一起學習吧。
好了,閑話少說,我們這一篇先解決上一篇中遺留的問題,之后有時間的話,我把這次的面試經歷單寫一篇博客,和大家共勉。
本篇我們來看一下ServiceManager。上一篇中沒怎么說它,ServiceManager作為Android系統服務的大管家。我們還是有必要來看一下它的。

ServiceManager概述

ServiceManager是Android世界中所有重要系統服務的大管家。像前文提到的AMS(ActivityManagerService),還有許多以后可能分析到的PackageManagerService等等服務都需要像ServiceManager中注冊。那么為何需要一個ServiceManager呢,其重要作用何在呢?私認為有以下幾點:

ServiceManager能集中管理系統內的所有服務,它能施加權限控制,并不是任何進程都能注冊服務的。 ServiceManager支持通過字符串名稱來查找對應的Service。這個功能很像DNS。由于各種原因的影響,Server進程可能生死無常。 如果讓每個Client都去檢測,壓力實在太大了。 現在有了統一的管理機構,Client只需要查詢ServiceManager,就能把握動向,得到最新信息。

ServiceManager

[SystemServer.java]

public void setSystemProcess() {
  try {
    //注冊服務,第二個參數為this,這里假設SystemServer通過“socket”與SM交互
    ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
    ..........
  } catch (PackageManager.NameNotFoundException e) {
    ........
  }
}

 

我們SystemServer進程中的AMS通過SM的代理與SM進程交互(讀者也可以把這個過程想象為你所能理解的進程間通信方式,例如管道、Socket等),并把自己注冊在SM中。這個情況下,我們使用ServiceManager的代理與SM進程交互,既然有代理,那么也得有對應的服務端。那么根據我們之前博客的思路分析的話,就是如下的流程:

ServiceManager是如何啟動的?

按照我們之前博客的思路,我們在SystemServer端有了個ServiceManager的代理,那么Android系統中應該提供類似AMS這樣的繼承或間接繼承自java層Binder然后重寫onTransact方法以處理請求。但是并沒有,ServiceManager并沒有使用如AMS這樣復雜的Binder類結構。而是直接與Binder驅動設備打交道。所以我們上一篇說了ServiceManager不一樣。我們來看具體看一下。

ServiceManager在init.rc配置文件中配置啟動,是一個以c/c++語言編寫的程序。init進程、SM進程等關系如下圖

Android中Binder詳細學習心得

我們來看它的main方法。

int main(int argc, char **argv)
{
  struct binder_state *bs;
  //①應該是打開binder設備吧?
  bs = binder_open(128*1024);
  if (!bs) {
    ALOGE("failed to open binder driver\n");
    return -1;
  }
  //②成為manager
  if (binder_become_context_manager(bs)) {
    ALOGE("cannot become context manager (%s)\n", strerror(errno));
    return -1;
  }
  ......
  //③處理客戶端發過來的請求
  binder_loop(bs, svcmgr_handler);
  return 0;
}

①打開Binder設備

[binder.c]

struct binder_state*binder_open(unsigned mapsize)
{
  struct binder_state*bs;
  bs=malloc(sizeof(*bs));
  ......
  //打開Binder設備
  bs->fd=open("/dev/binder",O_RDWR);
  ......
  bs->mapsize=mapsize;
  //進行內存映射
  bs->mapped=mmap(NULL,mapsize,PROT_READ,MAP_PRIVATE,bs->
  fd,0);
}

 

這一步的目的是把內核層的binder驅動映射到用戶空間。我們知道進程之間是獨立的,進程呢運行在用戶空間內,內核層的Binder驅動可以看成是一個文件(實際上它也是,Linux上都是文件)。這一步呢,可以看成把一個文件映射到用戶空間,我們的進程呢通過這個文件進行交互。

Android中Binder詳細學習心得

②成為manager

[Binder.c]

int binder_become_context_manager(struct binder_state*bs)
{
  //實現太簡單了!這個有個0,什么鬼?
  return ioctl(bs->fd,BINDER_SET_CONTEXT_MGR,0);
}

 

③處理客戶端發過來的請求

[Binder.c]

void binder_loop(struct binder_state *bs, binder_handler func)
{
  int res;
  struct binder_write_read bwr;
  uint32_t readbuf[32];
  bwr.write_size = 0;
  bwr.write_consumed = 0;
  bwr.write_buffer = 0;
  readbuf[0] = BC_ENTER_LOOPER;
  binder_write(bs, readbuf, sizeof(uint32_t));
  for (;;) {//果然是循環
    bwr.read_size = sizeof(readbuf);
    bwr.read_consumed = 0;
    bwr.read_buffer = (uintptr_t) readbuf;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
      ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
      break;
    }
    //接收到請求交給binder_parse,最終會調用func來處理這些請求
    res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
    if (res == 0) {
      ALOGE("binder_loop: unexpected reply?!\n");
      break;
    }
    if (res < 0) {
      ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
      break;
    }
  }
}

上面傳入func的是svcmgr_ handler函數指針,所以會在svcmgr_handler中進行集中處理客戶端的請求。

[service_manager.c]

ServiceManager的代理是如何獲得的?

我們來回到最初的調用

[SystemServer.java]

public void setSystemProcess() {
  try {
    //注冊服務,第二個參數為this,這里假設SystemServer通過“socket”與SM交互
    ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
    ..........
  } catch (PackageManager.NameNotFoundException e) {
    ........
  }
}

上面的請求最終是通過SM服務代理發送的,那這個代理是怎么來的呢?我們來看

[ServiceManager.java]

public static void addService(String name, IBinder service) {
  try {
    getIServiceManager().addService(name, service, false);
  } catch (RemoteException e) {
    Log.e(TAG, "error in addService", e);
  }
}
private static IServiceManager getIServiceManager() {
  if (sServiceManager != null) {
    return sServiceManager;
  }
  // 是這里,沒錯了
  sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
  return sServiceManager;
}

我們先來看BinderInternal.getContextObject()

[BinderInternal.java]

//好吧,它還是個native函數
public static final native IBinder getContextObject();

跟進[android_ util_Binder.cpp]

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
  sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
  return javaObjectForIBinder(env, b);
}

跟進[ProcessState.cpp]

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
  return getStrongProxyForHandle(0);
}
/*這個函數是不是我們之前見過*/
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
  sp<IBinder> result;
  AutoMutex _l(mLock);
  handle_entry* e = lookupHandleLocked(handle);
  if (e != NULL) {
    IBinder* b = e->binder;
    if (b == NULL || !e->refs->attemptIncWeak(this)) {
      if (handle == 0) {//這里我們的handle為0
        Parcel data;
      //在handle對應的BpBinder第一次創建時
      //會執行一次虛擬的事務請求,以確保ServiceManager已經注冊
        status_t status = IPCThreadState::self()->transact(
            0, IBinder::PING_TRANSACTION, data, NULL, 0);
        if (status == DEAD_OBJECT)
          return NULL;//如果ServiceManager沒有注冊,直接返回
      }
      //這里還是以handle參數創建了BpBinder
      b = new BpBinder(handle); 
      e->binder = b;
      if (b) e->refs = b->getWeakRefs();
      result = b;
    } else {
      
      result.force_set(b);
      e->refs->decWeak(this);
    }
  }
  return result;
}

我們再一步步返回

[android_ util_Binder.cpp]

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
  sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
  //這里的b = new BpBinder(0);
  return javaObjectForIBinder(env, b);
}
/*這個函數我們上一篇是不是也見過*/
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) {
  if (val == NULL) return NULL;
  //如果val是Binder對象,進入下面分支,此時val是BpBinder
  if (val->checkSubclass(&gBinderOffsets)) {
    // One of our own!
    jobject object = static_cast<JavaBBinder*>(val.get())->object();
    LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
    return object;
  }
  .........
  //調用BpBinder的findObject函數
  //在Native層的BpBinder中有一個ObjectManager,它用來管理在Native BpBinder上創建的Java BinderProxy對象
  //findObject用于判斷gBinderProxyOffsets中,是否存儲了已經被ObjectManager管理的Java BinderProxy對象
  jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
  if (object != NULL) {
    jobject res = jniGetReferent(env, object);
    ............
    //如果該Java BinderProxy已經被管理,則刪除這個舊的BinderProxy
    android_atomic_dec(&gNumProxyRefs);
    val->detachObject(&gBinderProxyOffsets);
    env->DeleteGlobalRef(object);
  }
  //創建一個新的BinderProxy對象
  object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
  if (object != NULL) {
    env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
    val->incStrong((void*)javaObjectForIBinder);
    jobject refObject = env->NewGlobalRef(
        env->GetObjectField(object, gBinderProxyOffsets.mSelf));
    //新創建的BinderProxy對象注冊到BpBinder的ObjectManager中,同時注冊一個回收函數proxy_cleanup
    //當BinderProxy對象detach時,proxy_cleanup函數將被調用,以釋放一些資源
    val->attachObject(&gBinderProxyOffsets, refObject,
        jnienv_to_javavm(env), proxy_cleanup);
    // Also remember the death recipients registered on this proxy
    sp<DeathRecipientList> drl = new DeathRecipientList;
    drl->incStrong((void*)javaObjectForIBinder);
    //將死亡通知list和BinderProxy聯系起來
    env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get()));
    // Note that a new object reference has been created.
    android_atomic_inc(&gNumProxyRefs);
    //垃圾回收相關;利用gNumRefsCreated記錄創建出的BinderProxy數量
    //當創建出的BinderProxy數量大于200時,該函數將利用BinderInternal的ForceGc函數進行一個垃圾回收
    incRefsCreated(env);
    return object;
  }
}

接著返回到[ServiceManager.java]

private static IServiceManager getIServiceManager() {
  if (sServiceManager != null) {
    return sServiceManager;
  }
  // 是這里,沒錯了BinderInternal.getContextObject()是BinderProxy對象
  sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
  return sServiceManager;
}

跟進[[ServiceManagerNative.java]]

static public IServiceManager asInterface(IBinder obj)
{
  if (obj == null) {
    return null;
  }
  我們知道這里的obj指向的是BinderProxy對象
  IServiceManager in =
    (IServiceManager)obj.queryLocalInterface(descriptor);
  if (in != null) {
    return in;
  }
  return new ServiceManagerProxy(obj);
}

跟進[Binder.java]

final class BinderProxy implements IBinder {
  public IInterface queryLocalInterface(String descriptor) {
    return null;
  }
}

跟進[ServiceManagerNative.java]

class ServiceManagerProxy implements IServiceManager {
  public ServiceManagerProxy(IBinder remote) {
    //這里的mRemote指向了BinderProxy,與我們上一篇博客中講述的遙相呼應
    mRemote = remote;
  }
}

本節小結

我們詳盡講述了SM進程的啟動以及它作為服務大管家的意義。結合上一篇的內容我們總算是把Binder講述的比較清楚了。

Binder補充說明 AIDL

經過上面的介紹,你應該明白Java層Binder的架構中,Bp端可以通過BinderProxy的transact()方法與Bn端發送請求,而Bn端通過集成Binder重寫onTransact()接收并處理來自Bp端的請求。這個結構非常清晰簡單,在Android6.0,我們可以處處看到這樣的設計,比如我們的ActivityManagerNavtive這個類,涉及到Binder通信的基本上都是這種設計。不過如果我們想要自己來定義一些遠程服務。那這樣的寫法就比較繁瑣,還好Android提供了AIDL,并且在Android8.0之后,我們可以看到與ActivityManagerNavtive相似的許多類已經被標注過時,因為Android系統也使用AIDL了。

AIDL的簡單例子

AIDL的語法與定義一個java接口非常類似。下面我就定以一個非常簡單的aidl

IMyAidlInterface.aidl

interface IMyAidlInterface {
  int getTest();
}

然后基本上就行了,我們重新build之后會得到一個
IMyAidlInterface.java文件,這個文件由aidl工具生成,我們現在使用的基本是AndroidStudio,即使你使用的是Eclipse也沒關系,這個文件會自動生成,不需要你操心。但是我們還是得來看看我們生成的這個文件

public interface IMyAidlInterface extends android.os.IInterface {
  //抽象的Stub類,繼承自Binder并實現我們定義的IMyAidlInterface接口
  //繼承自Binder,重寫onTransact方法,是不是感覺跟我們的XXXNative很像
  public static abstract class Stub extends android.os.Binder implements com.mafeibiao.testapplication.IMyAidlInterface {
    private static final java.lang.String DESCRIPTOR = "com.mafeibiao.testapplication.IMyAidlInterface";
    /**
     * Construct the stub at attach it to the interface.
     */
    public Stub() {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.mafeibiao.testapplication.IMyAidlInterface interface,
     * generating a proxy if needed.
     */
    public static com.mafeibiao.testapplication.IMyAidlInterface asInterface(android.os.IBinder obj) {
      if ((obj == null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin != null) && (iin instanceof com.mafeibiao.testapplication.IMyAidlInterface))) {
        return ((com.mafeibiao.testapplication.IMyAidlInterface) iin);
      }
      return new com.mafeibiao.testapplication.IMyAidlInterface.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_getTest: {
          data.enforceInterface(DESCRIPTOR);
          int _result = this.getTest();
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
      }
      return super.onTransact(code, data, reply, flags);
    }
    /*這個Proxy不用說肯定是代理了,其內部還有個mRemote對象*/
    private static class Proxy implements com.mafeibiao.testapplication.IMyAidlInterface {
      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 getTest() 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);
          mRemote.transact(Stub.TRANSACTION_getTest, _data, _reply, 0);
          _reply.readException();
          _result = _reply.readInt();
        } finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
    }
    static final int TRANSACTION_getTest = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
  }
  public int getTest() throws android.os.RemoteException;
}

可見,AIDL的本質與XXXNative之類的類并沒有什么本質的不同,不過他的出現使得構建一個Binder服務的工作大大簡化了。

AIDL的使用詳解

上面用一個非常簡單的小例子來解密AIDL的本質,但是在實際使用AIDL的時候還有許多地方需要注意。

AIDL支持的數據類型 基本數據類型(int,long,charmboolean,double等) String和CharSequence List:只支持ArrrayList,并且里面每個元素的類型必須是AIDL支持的 Map:只支持HashMap,t,并且里面每個元素的類型必須是AIDL支持的 Parcelable:所有實現Parcelable接口的對象 AIDL:所有的AIDL接口本身也可以在AIDL文件中使用

以上6種數據類型就是AIDL所支持的所有類型,其中自定義的Parcel對象和AIDL對象必須要顯示import進來,不管他們是否和當前的AIDL文件位于同一個包內。

另外一個需要注意的地方是如果我們在AIDL中使用了自定義的Parcelable接口的對象,那么我們必須新建一個和它同名的AIDL文件,并在其中聲明它為Parcelable類型。

如下例

[IBookManager.aidl]

package com.ryg.chapter_2.aidl;
/*這里顯示import*/
import com.ryg.chapter_2.aidl.Book;
interface IBookManager {
   //這里我們使用了自定義的Parcelable對象
   List<Book> getBookList();
   void addBook(in Book book);
}

這里我們新建一個與Book同名的AIDL文件并聲明

[Book.aidl]

package com.ryg.chapter_2.aidl;
parcelable Book;

定向tag

定向tag:這是一個極易被忽略的點——這里的“被忽略”指的不是大家都不知道,而是很少人會正確的使用它。
AIDL中的定向 tag 表示了在跨進程通信中數據的流向,其中 in 表示數據只能由客戶端流向服務端, out 表示數據只能由服務端流向客戶端,而 inout 則表示數據可在服務端與客戶端之間雙向流通。其中,數據流向是針對在客戶端中的那個傳入方法的對象而言的。in 為定向 tag 的話表現為服務端將會接收到一個那個對象的完整數據,但是客戶端的那個對象不會因為服務端對傳參的修改而發生變動;out 的話表現為服務端將會接收到那個對象的的空對象,但是在服務端對接收到的空對象有任何修改之后客戶端將會同步變動;inout 為定向 tag 的情況下,服務端將會接收到客戶端傳來對象的完整信息,并且客戶端將會同步服務端對該對象的任何變動。

另外,Java 中的基本類型和 String ,CharSequence 的定向 tag 默認且只能是 in 。還有,請注意,請不要濫用定向 tag ,而是要根據需要選取合適的——要是不管三七二十一,全都一上來就用 inout ,等工程大了系統的開銷就會大很多——因為排列整理參數的開銷是很昂貴的。

所有的非基本參數都需要一個定向tag來指出數據的流向,不管是 in , out , 還是 inout 。基本參數的定向tag默認是并且只能是 in 。

本篇總結

我們本篇詳細分析了ServiceManager,ServiceManager并沒有使用復雜的類結構,他直接與Binder驅動設備交互達到IPC通信的目的。感謝你對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

克拉玛依市| 时尚| 新平| 宁海县| 出国| 奈曼旗| 卢氏县| 翁源县| 余庆县| 上犹县| 蛟河市| 万全县| 当雄县| 陆川县| 南安市| 马关县| 桦南县| 措勤县| 峡江县| 甘洛县| 焦作市| 曲周县| 上犹县| 云南省| 霍城县| 启东市| 依安县| 咸宁市| 获嘉县| 南华县| 犍为县| 台湾省| 礼泉县| 蓝山县| 讷河市| 沙雅县| 沐川县| 藁城市| 永福县| 陈巴尔虎旗| 都兰县|