那个...嗯...曾记否上篇的2.Android Binder 中留下的疑问?就是在Context的实现类中使用
getSystemService(String serviceName)方法获取一个系统服务,那么这些系统服务的Binder
引用时如何传递给客户端的呢?其实这里有个关键点,就是系统服务并不是通过startService()启
动的。
getSystemService函数的实现是在Context的实现类中,该函数返回的Service比较多。那
么就让我们看一下Activity、ContextThemeWrapper的部分源代码吧:
Activity:
@Override public Object getSystemService(@ServiceName @NonNull String name) { if (getBaseContext() == null) { throw new IllegalStateException( "System services not available to Activities before onCreate()"); } if (WINDOW_SERVICE.equals(name)) { return mWindowManager; } else if (SEARCH_SERVICE.equals(name)) { ensureSearchManager(); return mSearchManager; } return super.getSystemService(name); }
1.首先, if (getBaseContext() == null)判断 mBase(Context类型)是否为空。如果为空则 ,提
示Context没创建前调用了getSystemService的错误。至于,我为什么能准确地mBase这个名字呢?让
我们再看一段关于ContextWrapper(Activity的父类、Context的子类)的源代码:
Context mBase; public ContextWrapper(Context base) { mBase = base; } /** * Set the base context for this ContextWrapper. All calls will then be * delegated to the base context. Throws * IllegalStateException if a base context has already been set. * * @param base The new base context for this wrapper. */ protected void attachBaseContext(Context base) { if (mBase != null) { throw new IllegalStateException("Base context already set"); } mBase = base; } /** * @return the base context as set by the constructor or setBaseContext */ public Context getBaseContext() { return mBase; }
2.if (WINDOW_SERVICE.equals(name)) {......}elseif(SEARCH_SERVICE.equals(name)){......}。这个
就是判断要取的服务是Window类型还是Search类型的。这个在Activity里有对应的WindowManager类
型和SearchManager类型属性,并且做了对应的实例化。
3.return super.getSystemService(name):当要取的服务不是Window类型也不是Search类型的时候,就
调用了父类(ContextThemeWrapper)的getSystemService(String name)帮你找服务。那么就让我们顺便看看
ContextThemeWrapper的源代码:
@Override public Object getSystemService(String name) { if (LAYOUT_INFLATER_SERVICE.equals(name)) { if (mInflater == null) { mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this); } return mInflater; } return getBaseContext().getSystemService(name); }
4.这里也做了个Activity里类似的判断是否为 layout Inflater 服务,然后返回相应的layout Inflater 服务。
5.return getBaseContext().getSystemService(name):这里的话就回到了Context 这个抽象的基类。 我
们就再看看Context的源代码:
/** * Return the handle to a system-level service by name. The class of the * returned object varies by the requested name. Currently available names * are: * * <dl> * <dt> {@link #WINDOW_SERVICE} ("window") * <dd> The top-level window manager in which you can place custom * windows. The returned object is a {@link android.view.WindowManager}. * <dt> {@link #LAYOUT_INFLATER_SERVICE} ("layout_inflater") * <dd> A {@link android.view.LayoutInflater} for inflating layout resources * in this context. * <dt> {@link #ACTIVITY_SERVICE} ("activity") * <dd> A {@link android.app.ActivityManager} for interacting with the * global activity state of the system. * <dt> {@link #POWER_SERVICE} ("power") * <dd> A {@link android.os.PowerManager} for controlling power * management. * <dt> {@link #ALARM_SERVICE} ("alarm") * <dd> A {@link android.app.AlarmManager} for receiving intents at the * time of your choosing. * <dt> {@link #NOTIFICATION_SERVICE} ("notification") * <dd> A {@link android.app.NotificationManager} for informing the user * of background events. * <dt> {@link #KEYGUARD_SERVICE} ("keyguard") * <dd> A {@link android.app.KeyguardManager} for controlling keyguard. * <dt> {@link #LOCATION_SERVICE} ("location") * <dd> A {@link android.location.LocationManager} for controlling location * (e.g., GPS) updates. * <dt> {@link #SEARCH_SERVICE} ("search") * <dd> A {@link android.app.SearchManager} for handling search. * <dt> {@link #VIBRATOR_SERVICE} ("vibrator") * <dd> A {@link android.os.Vibrator} for interacting with the vibrator * hardware. * <dt> {@link #CONNECTIVITY_SERVICE} ("connection") * <dd> A {@link android.net.ConnectivityManager ConnectivityManager} for * handling management of network connections. * <dt> {@link #WIFI_SERVICE} ("wifi") * <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of * Wi-Fi connectivity. * <dt> {@link #WIFI_P2P_SERVICE} ("wifip2p") * <dd> A {@link android.net.wifi.p2p.WifiP2pManager WifiP2pManager} for management of * Wi-Fi Direct connectivity. * <dt> {@link #INPUT_METHOD_SERVICE} ("input_method") * <dd> An {@link android.view.inputmethod.InputMethodManager InputMethodManager} * for management of input methods. * <dt> {@link #UI_MODE_SERVICE} ("uimode") * <dd> An {@link android.app.UiModeManager} for controlling UI modes. * <dt> {@link #DOWNLOAD_SERVICE} ("download") * <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads * <dt> {@link #BATTERY_SERVICE} ("batterymanager") * <dd> A {@link android.os.BatteryManager} for managing battery state * <dt> {@link #JOB_SCHEDULER_SERVICE} ("taskmanager") * <dd> A {@link android.app.job.JobScheduler} for managing scheduled tasks * </dl> * * <p>Note: System services obtained via this API may be closely associated with * the Context in which they are obtained from. In general, do not share the * service objects between various different contexts (Activities, Applications, * Services, Providers, etc.) * * @param name The name of the desired service. * * @return The service or null if the name does not exist. * * @see #WINDOW_SERVICE * @see android.view.WindowManager * @see #LAYOUT_INFLATER_SERVICE * @see android.view.LayoutInflater * @see #ACTIVITY_SERVICE * @see android.app.ActivityManager * @see #POWER_SERVICE * @see android.os.PowerManager * @see #ALARM_SERVICE * @see android.app.AlarmManager * @see #NOTIFICATION_SERVICE * @see android.app.NotificationManager * @see #KEYGUARD_SERVICE * @see android.app.KeyguardManager * @see #LOCATION_SERVICE * @see android.location.LocationManager * @see #SEARCH_SERVICE * @see android.app.SearchManager * @see #SENSOR_SERVICE * @see android.hardware.SensorManager * @see #STORAGE_SERVICE * @see android.os.storage.StorageManager * @see #VIBRATOR_SERVICE * @see android.os.Vibrator * @see #CONNECTIVITY_SERVICE * @see android.net.ConnectivityManager * @see #WIFI_SERVICE * @see android.net.wifi.WifiManager * @see #AUDIO_SERVICE * @see android.media.AudioManager * @see #MEDIA_ROUTER_SERVICE * @see android.media.MediaRouter * @see #TELEPHONY_SERVICE * @see android.telephony.TelephonyManager * @see #INPUT_METHOD_SERVICE * @see android.view.inputmethod.InputMethodManager * @see #UI_MODE_SERVICE * @see android.app.UiModeManager * @see #DOWNLOAD_SERVICE * @see android.app.DownloadManager * @see #BATTERY_SERVICE * @see android.os.BatteryManager * @see #JOB_SCHEDULER_SERVICE * @see android.app.job.JobScheduler */ public abstract Object getSystemService(@ServiceName @NonNull String name); /******************************以下是服务的名称***************************/ /** * Use with {@link #getSystemService} to retrieve a * {@link android.os.PowerManager} for controlling power management, * including "wake locks," which let you keep the device on while * you're running long tasks. */ public static final String POWER_SERVICE = "power"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.view.WindowManager} for accessing the system's window * manager. * * @see #getSystemService * @see android.view.WindowManager */ public static final String WINDOW_SERVICE = "window"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.view.LayoutInflater} for inflating layout resources in this * context. * * @see #getSystemService * @see android.view.LayoutInflater */ public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater"; /** * Use with {@link #getSystemService} to retrieve a * {@link android.accounts.AccountManager} for receiving intents at a * time of your choosing. * * @see #getSystemService * @see android.accounts.AccountManager */ /*******************************下面还有很多很多*************************/
6.以上可以看出,如果在Activity和ContextThemeWrapper不能帮你取到服务的话,最终会回到Context来一个"大搜查"。
ServiceManager是一个独立的进程!这里特别强调,它是一个进程!作用就跟它的名称一样,管理各种系统服务,如下图所示:
其实ServiceManager本身也是一个Service,Framework提供了一个系统函数,可以获取ServiceManager
对应的Binder引用,那就是BinderInternal.getContextObject()。该静态函数返回ServiceManager后,就
可以通过ServiceManger提供的方法获取其他服务Service的Binder引用。这种设计模式就好比:ServiceManager
是一个公司的总机,这个总计的号码是公开的,系统中任何进程都可以使用BinderInternal.getContextObject()
获取该总机的Binder对象,而当前用户想联系公司的其他人(服务)的时候,则要经过总机再获取到分机的号码。
这种设计模式的优点就是:系统中仅仅只暴漏一个全局Binder引用,那就是ServiceManager,而其他服务则可以
隐藏起来,从而有助于系统服务的扩展,以及调用系统服务的安全检查。其他系统服务在启动的时候,首先把自己
的Binder对象传递给ServiceManager,就是所谓的注册(addService)。
BinderInternal.getContextObject() ->ServiceManager ->其它Service的Binder引用
那么我们来看一看ServiceManager.getService()的源码:
public static IBinder getService(String name){ try{ IBinder service = sCache.get(name); if(servie != null){ return service }else{ return getIServiceManager().getService(name); } }catch (RemoteException e) { Log.e(TAG, "error in getService",e); } return null; }
以上片段源码,sCache.get(name) 缓存中查看是否有对应的 Binder 对象,有则返回,没有则调用
return getIServiceManager.getService(name);第一个getIServiceManager()即用于返回系统中
唯一的ServiceManager对应的Binder。
接着看看getIServiceManager():
private static IServiceManager getIServiceManager() { if (sServiceManager != null) { return sServiceManager; } // Find the service manager sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject()); return sServiceManager; }
以上源码中,BinderInternal.getContextObject()静态函数用于返回ServiceManager对应的全局Binder
对象,该函数不需要任何参数,因为它的作用所致是固定的。
其他所有通过ServiceManager获取的系统服务的过程与以上基本相似,所不同的就是传递给ServiceManager
的服务名称不同,因为ServiceManager正是按照服务的名称(String)来保存不同的Binder。
在Android中,Manager的含义应该翻译为“经纪人”,Manager所管理的对象时服务本身,因为每一
个具体的服务一般都会提供多个API接口,而Manager所管理的就是这些API。客户端一般不能直接通过
Binder访问的服务,而是要经过一个Manager,对应的Manager类对客户端是可见的,而远程的服务类
对客户端是隐藏的。
而这些Manager的类内部都会存在一个远程服务Binder的变量,而且在一般情况下,这些Manager的构造
函数中都会包含这个Binder对象。即先通过ServiceManager获取远程服务的Binder引用,然后使用这个Binder
引用构造一个客户端本地可以访问的经纪人,然后客户端就可以通过经纪人间接访问远程的服务了。
这种设计的作用是为了屏蔽客户端直接访问远程服务,从而可以给应用程序提供灵活的、可控的API接口,比
如Ams。系统不希望用户直接去访问Ams,而是经过ActivityManager类去访问,而ActivityManager内部则
提供一些更具可操作性的数据结构,比如RecentTaskInfo数据结构封装了最近访问过的Task列表;MemoryInfo
数据类封装了和内存相关的信息。如下(RecentTaskInfo和MemoryInfo的源代码):
ActivityManager 中的 RecentTaskInfo 类 源码:
/** * Information you can retrieve about tasks that the user has most recently * started or visited. */ public static class RecentTaskInfo implements Parcelable { /** * If this task is currently running, this is the identifier for it. * If it is not running, this will be -1. */ public int id; /** * The true identifier of this task, valid even if it is not running. */ public int persistentId; /** * The original Intent used to launch the task. You can use this * Intent to re-launch the task (if it is no longer running) or bring * the current task to the front. */ public Intent baseIntent; /** * If this task was started from an alias, this is the actual * activity component that was initially started; the component of * the baseIntent in this case is the name of the actual activity * implementation that the alias referred to. Otherwise, this is null. */ public ComponentName origActivity; /** * Description of the task's last state. */ public CharSequence description; /** * The id of the ActivityStack this Task was on most recently. * @hide */ public int stackId; /** * The id of the user the task was running as. * @hide */ public int userId; /** * The first time this task was active. * @hide */ public long firstActiveTime; /** * The last time this task was active. * @hide */ public long lastActiveTime; /** * The recent activity values for the highest activity in the stack to have set the values. * {@link Activity#setTaskDescription(android.app.ActivityManager.TaskDescription)}. */ public TaskDescription taskDescription; /** * Task affiliation for grouping with other tasks. */ public int affiliatedTaskId; /** * Task affiliation color of the source task with the affiliated task id. * * @hide */ public int affiliatedTaskColor; public RecentTaskInfo() { } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); dest.writeInt(persistentId); if (baseIntent != null) { dest.writeInt(1); baseIntent.writeToParcel(dest, 0); } else { dest.writeInt(0); } ComponentName.writeToParcel(origActivity, dest); TextUtils.writeToParcel(description, dest, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); if (taskDescription != null) { dest.writeInt(1); taskDescription.writeToParcel(dest, 0); } else { dest.writeInt(0); } dest.writeInt(stackId); dest.writeInt(userId); dest.writeLong(firstActiveTime); dest.writeLong(lastActiveTime); dest.writeInt(affiliatedTaskId); dest.writeInt(affiliatedTaskColor); } public void readFromParcel(Parcel source) { id = source.readInt(); persistentId = source.readInt(); baseIntent = source.readInt() > 0 ? Intent.CREATOR.createFromParcel(source) : null; origActivity = ComponentName.readFromParcel(source); description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); taskDescription = source.readInt() > 0 ? TaskDescription.CREATOR.createFromParcel(source) : null; stackId = source.readInt(); userId = source.readInt(); firstActiveTime = source.readLong(); lastActiveTime = source.readLong(); affiliatedTaskId = source.readInt(); affiliatedTaskColor = source.readInt(); } public static final Creator<RecentTaskInfo> CREATOR = new Creator<RecentTaskInfo>() { public RecentTaskInfo createFromParcel(Parcel source) { return new RecentTaskInfo(source); } public RecentTaskInfo[] newArray(int size) { return new RecentTaskInfo[size]; } }; private RecentTaskInfo(Parcel source) { readFromParcel(source); } }
ActivityManager 的 MemoryInfo类 源代码 :
/** * Information you can retrieve about the available memory through * {@link ActivityManager#getMemoryInfo}. */ public static class MemoryInfo implements Parcelable { /** * The available memory on the system. This number should not * be considered absolute: due to the nature of the kernel, a significant * portion of this memory is actually in use and needed for the overall * system to run well. */ public long availMem; /** * The total memory accessible by the kernel. This is basically the * RAM size of the device, not including below-kernel fixed allocations * like DMA buffers, RAM for the baseband CPU, etc. */ public long totalMem; /** * The threshold of {@link #availMem} at which we consider memory to be * low and start killing background services and other non-extraneous * processes. */ public long threshold; /** * Set to true if the system considers itself to currently be in a low * memory situation. */ public boolean lowMemory; /** @hide */ public long hiddenAppThreshold; /** @hide */ public long secondaryServerThreshold; /** @hide */ public long visibleAppThreshold; /** @hide */ public long foregroundAppThreshold; public MemoryInfo() { } public int describeContents() { return 0; } public void writeToParcel(Parcel dest, int flags) { dest.writeLong(availMem); dest.writeLong(totalMem); dest.writeLong(threshold); dest.writeInt(lowMemory ? 1 : 0); dest.writeLong(hiddenAppThreshold); dest.writeLong(secondaryServerThreshold); dest.writeLong(visibleAppThreshold); dest.writeLong(foregroundAppThreshold); } public void readFromParcel(Parcel source) { availMem = source.readLong(); totalMem = source.readLong(); threshold = source.readLong(); lowMemory = source.readInt() != 0; hiddenAppThreshold = source.readLong(); secondaryServerThreshold = source.readLong(); visibleAppThreshold = source.readLong(); foregroundAppThreshold = source.readLong(); } public static final Creator<MemoryInfo> CREATOR = new Creator<MemoryInfo>() { public MemoryInfo createFromParcel(Parcel source) { return new MemoryInfo(source); } public MemoryInfo[] newArray(int size) { return new MemoryInfo[size]; } }; private MemoryInfo(Parcel source) { readFromParcel(source); } }
这种通过Manager 访问远程服务的模型如下图: