系统服务与 ServiceManager

  • 2017-04-11
  • 8,681
  • 4

Android Binder 机制是安卓应用运行的基础,一个应用的运行需要无法避免地和系统提供的Binder做交互。Android 系统以服务(Service)的方式暴露出很多Binder对象,准确的说我们拿到的是Binder代理对象(BinderProxy),真正的Binder对象运行于安卓系统进程中(system_process)。我们的应用以夸进程的方式调用系统提供的各种服务,通常以Context.getSystemService()的方式获取系统服务,常见的有 ActivityManager, AlarmManager, InputMethodManager, ConnectivityManager, LayoutInflater等等,其中有一部分是普通对象,大部分是对系统Binder对象的封装。那应用又是如何拿到系统的Binder对象的呢?如果我们要拿到其他应用进程的Binder对象一般会使用ServiceConnection连接其他进程的Service拿到IBinder。然而系统的IBinder是用ServiceManager暴露给应用进程的。下面以获取InputMethodManager为例分析应用是如何获取系统IBinder对象的。

通过下面的代码可以拿到InputMethodManager:

InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);

调用的是Context的实现类ContextImpl:


@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}

然后看SystemServiceRegistry的getSystemService方法:


/**
* Gets a system service from a given context.
*/
public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

可以看到是在SYSTEM_SERVICE_FETCHERS中取到ServiceFetcher,然后调用fetcher.getService(ctx)拿到的,事实上SYSTEM_SERVICE_FETCHERS是一个Map:HashMap<String, ServiceFetcher<?>>
并且是SystemServiceRegistry类的静态变量,在类首次加载的时候初始化好:


static {
    registerService(Context.INPUT_METHOD_SERVICE, InputMethodManager.class,
    new StaticServiceFetcher() {
    @Override
    public InputMethodManager createService() {
    return InputMethodManager.getInstance();
}});
//省略其他。。。。。
}

这样我们就可以通过Context.INPUT_METHOD_SERVICE拿到对应的ServiceFetcher,看一下ServiceFetcher是啥:


/**
* Base interface for classes that fetch services.
* These objects must only be created during static initialization.
*/
static abstract interface ServiceFetcher {
    T getService(ContextImpl ctx);
}

以及他的子类StaticServiceFetcher:


static abstract class StaticServiceFetcher implements ServiceFetcher {
    private T mCachedInstance;

    @Override
    public final T getService(ContextImpl unused) {
        synchronized (StaticServiceFetcher.this) {
        if (mCachedInstance == null) {
            mCachedInstance = createService();
        }
        return mCachedInstance;
    }
}

public abstract T createService();
}

以及CachedServiceFetcher


static abstract class CachedServiceFetcher implements ServiceFetcher {
    private final int mCacheIndex;

    public CachedServiceFetcher() {
        mCacheIndex = sServiceCacheSize++;
    }

@Override
@SuppressWarnings("unchecked")
public final T getService(ContextImpl ctx) {
    final Object[] cache = ctx.mServiceCache;
    synchronized (cache) {
        // Fetch or create the service.
        Object service = cache[mCacheIndex];
        if (service == null) {
              service = createService(ctx);
              cache[mCacheIndex] = service;
        }
    return (T)service;
    }
}

public abstract T createService(ContextImpl ctx);
}

结合看来最终会调用createService拿到对应的服务类:InputMethodManager.getInstance(),接下来看InputMethodManager的getInstance方法:


public static InputMethodManager getInstance() {
    synchronized (InputMethodManager.class) {
        if (sInstance == null) {
            IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
            IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
            sInstance = new InputMethodManager(service, Looper.getMainLooper());
        }
        return sInstance;
    }
}

这里可以看到,通过ServiceManager.getService(Context.INPUT_METHOD_SERVICE)获取了一个系统提供的Binder对象并转化成远程调用接口(IInterface),我们在操作InputMethodManager的时候最终都会通过这个Binder对象远程过程调用(RPC)系统的Binder对象,最终实现与系统的交互。了解过Binder的同学应该很熟悉这段代码,进程间远程调用也是通过这种方式实现的。下面逐行分析:
1.ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
ServiceManager类getService方法:


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;
}

这里的sCache是一个Map,如果cache中有这个Binder对象就直接返回了,如果没有就调用getIServiceManager().getService(name)来获取


private static IServiceManager getIServiceManager() {
    if (sServiceManager != null) {
    return sServiceManager;
    }

    // Find the service manager
    sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
    return sServiceManager;
}

可以看到sServiceManager本身是一个Binder对象,对应的是系统服务ServiceManagerService,通过它可以拿到所有系统注册的服务,然而这个Binder对象是通过BinderInternal.getContextObject()获取的:


/**
* Return the global "context object" of the system. This is usually
* an implementation of IServiceManager, which you can use to find
* other services.
*/
public static final native IBinder getContextObject();

注释已经解释的很清楚了:
返回系统的全局“上下文对象”。这通常是IServiceManager的实现类,可以使用它来查找其他服务。
拿到Binder对象后转换为远程调用接口:service = IInputMethodManager.Stub.asInterface(b);
和普通的Binder对象一样需要转换成IInterface对象才能正常调用远程方法,


 public static com.android.internal.view.IInputMethodManager asInterface(android.os.IBinder obj){
    if ((obj==null)) {
    return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof com.android.internal.view.IInputMethodManager))) {
        return ((com.android.internal.view.IInputMethodManager)iin);
    }
    return new com.android.internal.view.IInputMethodManager.Stub.Proxy(obj);
}

上面的代码也很简单,如果是在当前进程(system_process)直接返回本身,如果是应用进程则返回一个代理对象。
然后使用获取的IInputMethodManager对象构造InputMethodManager:


sInstance = new InputMethodManager(service, Looper.getMainLooper())

最后再看一下InputMethodManager的showSoftInput方法:


public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
        checkFocus();
        synchronized (mH) {
            if (mServedView != view && (mServedView == null
                    || !mServedView.checkInputConnectionProxy(view))) {
                return false;
            }

            try {
                return mService.showSoftInput(mClient, flags, resultReceiver);
            } catch (RemoteException e) {
            }
            
            return false;
        }
}

这里的mService就是IInputMethodManager,一个Binder对象,可以看到,启动软键盘最终是一次远程过程调用。不仅如此,启动Activity等等都需要远程调用。

>> 转载请注明来源:系统服务与 ServiceManager

评论

  • OnClickListener回复

    老哥您好 请问背景里的动画是如何实现的

  • paozhuanyinyu回复

    大神,你的开源项目InputmethodHolder中issues5中说到的监听键盘收起,不知道现在hook IInputConnectionWrapper成功了吗?

    • pqpo回复

      现在很多方案都是通过对布局的监听来实现软键盘的监听,其实已经可以满足大部分场景需求了。反而通过 hook 容易出现问题

回复给 Linmin Qiu 点击这里取消回复。