當(dāng)一個類存在兩個獨(dú)立變化的緯度谬莹,且這兩個緯度都需要進(jìn)行擴(kuò)展,我們可以使用橋接模式帅涂。下面來看看橋接模式的UML
Abstraction和Implementor就是兩個獨(dú)立緯度變化的類田绑,Implementor相對于Abstraction是一個聚合的關(guān)系,也就是Abstraction可能擁有多個Implementor彼念,下面我們來舉個例子.
咖啡一般分為4種,大杯加糖浅萧,大杯不加糖逐沙,小杯加糖和小杯不加糖。對于大杯和小杯洼畅,加糖和不加糖其實(shí)是兩個相對獨(dú)立緯度的變化.下面先定一個咖啡類
public abstract class Coffee {
protected CoffeeAdditives impl;
public Coffee(CoffeeAdditives impl) {
this.impl = impl;
}
/**
* 咖啡具體是什么樣的由子類決定
*/
public abstract void makeCoffee();
}
CoffeeAdditives是一種橋接的方式吩案,咖啡分為大杯和小杯,下面繼續(xù)看看大杯咖啡和小杯咖啡的定義
public class LargeCoffee extends Coffee{
public LargeCoffee(CoffeeAdditives impl) {
super(impl);
}
@Override
public void makeCoffee() {
System.out.println("大杯的"+impl.addSomething()+"咖啡");
}
}
public class SmallCoffee extends Coffee{
public SmallCoffee(CoffeeAdditives impl) {
super(impl);
}
@Override
public void makeCoffee() {
System.out.println("小杯的"+impl.addSomething()+"咖啡");
}
}
至于加糖不加糖我們通過CoffeeAdditives這種橋接方式定義
public abstract class CoffeeAdditives {
public abstract String addSomething();
}
public class Ordinary extends CoffeeAdditives {
@Override
public String addSomething() {
return "原味";
}
}
public class Sugar extends CoffeeAdditives {
@Override
public String addSomething() {
return "加糖";
}
}
最終調(diào)用:
public class Test {
public static void main(String[] args){
//原汁原味
Ordinary ordinary = new Ordinary();
//準(zhǔn)備糖類
Sugar sugar = new Sugar();
//大杯咖啡 原味
LargeCoffee largeCoffeeOrdinary = new LargeCoffee(ordinary);
largeCoffeeOrdinary.makeCoffee();
//小杯咖啡 原味
SmallCoffee smallCoffeeOrdinary = new SmallCoffee(ordinary);
smallCoffeeOrdinary.makeCoffee();
//大杯咖啡 加糖
LargeCoffee largeCoffeeSugar = new LargeCoffee(sugar);
largeCoffeeSugar.makeCoffee();
//小杯咖啡 加糖
SmallCoffee smallCoffeeSugar = new SmallCoffee(sugar);
smallCoffeeSugar.makeCoffee();
}
}
總結(jié)
這里Coffee對應(yīng)uml圖中的Abstraction, CoffeeAdditives對應(yīng)的是Implementor這個類帝簇,這種橋接模式很好的獨(dú)立了大杯和小杯徘郭,加糖和不加糖兩個緯度靠益,你也可以添加另外一個緯度,比如說加奶不加奶残揉,這樣就是3個緯度進(jìn)行橋接胧后,當(dāng)然你也可以使用繼承來實(shí)現(xiàn),不過總的來說橋接模式更加靈活抱环。
Android源碼中的橋接模式
比較典型的是Window與WindowManager之間的關(guān)系壳快,它們就用到了橋接這種模式
在framework中Window和PhoneWindow構(gòu)成窗口的抽象部分,其中Window類為該抽象部分的抽象接口镇草,PhoneWindow為抽象部分具體的實(shí)現(xiàn)及擴(kuò)展眶痰。而WindowManager則為實(shí)現(xiàn)部分的基類,WindowManagerImpl為實(shí)現(xiàn)部分具體的邏輯實(shí)現(xiàn)陶夜,其使用WindowManagerGlobal通過IWindowManager接口與WindowManagerService進(jìn)行交互(簡稱WMS)凛驮,并由WMS完成具體的窗口管理工作. 如下是Window與WindowManager橋梁搭建的主要代碼.
public abstract class Window {
//代碼省略...
public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
setWindowManager(wm, appToken, appName, false);
}
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
//代碼省略...
}
關(guān)于WindowManagerService
毫不夸張的說Android中的framework層主要就是由它與另外一個系統(tǒng)服務(wù)AMS還有View構(gòu)成裆站,這三個模塊穿插交互在整個framework中条辟。
wms是由SystemServer啟動
private void startOtherServices() {
//通過wms的靜態(tài)方法main獲取一個WindowManagerService對象
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());
//將wms添加到ServiceManager中
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
}
在WindowManagerService的main中通過runWithScissors執(zhí)行一個同步的task構(gòu)造wms實(shí)例
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
WindowManagerPolicy policy) {
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
onlyCore, policy), 0);
return sInstance;
}
WindowManagerService的構(gòu)造方法大部分是一些窗口管理使用到的成員變量進(jìn)行初始化.
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
WindowManagerPolicy policy) {
installLock(this, INDEX_WINDOW);
mRoot = new RootWindowContainer(this);
mContext = context;
mHaveInputMethods = haveInputMethods;
mAllowBootMessages = showBootMsgs;
mOnlyCore = onlyCore;
mLimitedAlphaCompositing = context.getResources().getBoolean(
com.android.internal.R.bool.config_sf_limitedAlpha);
mHasPermanentDpad = context.getResources().getBoolean(
com.android.internal.R.bool.config_hasPermanentDpad);
mInTouchMode = context.getResources().getBoolean(
com.android.internal.R.bool.config_defaultInTouchMode);
mDrawLockTimeoutMillis = context.getResources().getInteger(
com.android.internal.R.integer.config_drawLockTimeoutMillis);
mAllowAnimationsInLowPowerMode = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowAnimationsInLowPowerMode);
mMaxUiWidth = context.getResources().getInteger(
com.android.internal.R.integer.config_maxUiWidth);
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mDisplaySettings = new DisplaySettings();
mDisplaySettings.readSettingsLocked();
mWindowPlacerLocked = new WindowSurfacePlacer(this);
mPolicy = policy;
mTaskSnapshotController = new TaskSnapshotController(this);
LocalServices.addService(WindowManagerPolicy.class, mPolicy);
if(mInputManager != null) {
final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
mPointerEventDispatcher = inputChannel != null
? new PointerEventDispatcher(inputChannel) : null;
} else {
mPointerEventDispatcher = null;
}
mFxSession = new SurfaceSession();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mDisplays = mDisplayManager.getDisplays();
for (Display display : mDisplays) {
createDisplayContentLocked(display);
}
mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
if (mPowerManagerInternal != null) {
mPowerManagerInternal.registerLowPowerModeObserver(
new PowerManagerInternal.LowPowerModeListener() {
@Override
public int getServiceType() {
return ServiceType.ANIMATION;
}
@Override
public void onLowPowerModeChanged(PowerSaveState result) {
synchronized (mWindowMap) {
final boolean enabled = result.batterySaverEnabled;
if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) {
mAnimationsDisabled = enabled;
dispatchNewAnimatorScaleLocked(null);
}
}
}
});
mAnimationsDisabled = mPowerManagerInternal
.getLowPowerState(ServiceType.ANIMATION).batterySaverEnabled;
}
mScreenFrozenLock = mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN");
mScreenFrozenLock.setReferenceCounted(false);
mAppTransition = new AppTransition(context, this);
mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
final AnimationHandler animationHandler = new AnimationHandler();
animationHandler.setProvider(new SfVsyncFrameCallbackProvider());
mBoundsAnimationController = new BoundsAnimationController(context, mAppTransition,
AnimationThread.getHandler(), animationHandler);
mActivityManager = ActivityManager.getService();
mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
AppOpsManager.OnOpChangedInternalListener opListener =
new AppOpsManager.OnOpChangedInternalListener() {
@Override public void onOpChanged(int op, String packageName) {
updateAppOpsState();
}
};
mAppOps.startWatchingMode(OP_SYSTEM_ALERT_WINDOW, null, opListener);
mAppOps.startWatchingMode(AppOpsManager.OP_TOAST_WINDOW, null, opListener);
// Get persisted window scale setting
mWindowAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(),
Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting);
mTransitionAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(),
Settings.Global.TRANSITION_ANIMATION_SCALE,
context.getResources().getFloat(
R.dimen.config_appTransitionAnimationDurationScaleDefault));
setAnimatorDurationScale(Settings.Global.getFloat(context.getContentResolver(),
Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting));
IntentFilter filter = new IntentFilter();
// Track changes to DevicePolicyManager state so we can enable/disable keyguard.
filter.addAction(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
// Listen to user removal broadcasts so that we can remove the user-specific data.
filter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(mBroadcastReceiver, filter);
mSettingsObserver = new SettingsObserver();
mHoldingScreenWakeLock = mPowerManager.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
mHoldingScreenWakeLock.setReferenceCounted(false);
mAnimator = new WindowAnimator(this);
mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
LocalServices.addService(WindowManagerInternal.class, new LocalService());
initPolicy();
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
openSurfaceTransaction();
try {
createWatermarkInTransaction();
} finally {
closeSurfaceTransaction();
}
showEmulatorDisplayOverlayIfNeeded();
}
WMS主要功能分為兩方面滓侍,一是對窗口的管理;二是對事件的管理和分發(fā)。其接口方法以AIDL的方式定義在IWindowManager.aidl文件中歹鱼,編譯后會生成一個IWindowManager.java接口文件,這個接口文件定義了WMS絕大部分的功能方法铃绒,大家有興趣可以自行查看定血。作為窗口的管理承擔(dān)者灾票,WMS中定義了許多各種不同的窗口濒析,它們被定義在WMS的成員變量中.
下面列出一部分相關(guān)的成員變量
/**
* List of window tokens that have finished starting their application,
* and now need to have the policy remove their windows.
*/
final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
/**
* List of window tokens that have finished drawing their own windows and
* no longer need to show any saved surfaces. Windows that's still showing
* saved surfaces will be cleaned up after next animation pass.
*/
final ArrayList<AppWindowToken> mFinishedEarlyAnim = new ArrayList<>();
/**
* List of app window tokens that are waiting for replacing windows. If the
* replacement doesn't come in time the stale windows needs to be disposed of.
*/
final ArrayList<AppWindowToken> mWindowReplacementTimeouts = new ArrayList<>();
/**
* Windows that are being resized. Used so we can tell the client about
* the resize after closing the transaction in which we resized the
* underlying surface.
*/
final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
/**
* Windows whose animations have ended and now must be removed.
*/
final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
/**
* Used when processing mPendingRemove to avoid working on the original array.
*/
WindowState[] mPendingRemoveTmp = new WindowState[20];
/**
* Windows whose surface should be destroyed.
*/
final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
/**
* Windows with a preserved surface waiting to be destroyed. These windows
* are going through a surface change. We keep the old surface around until
* the first frame on the new surface finishes drawing.
*/
final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
/**
* Windows that have lost input focus and are waiting for the new
* focus window to be displayed before they are told about this.
*/
ArrayList<WindowState> mLosingFocus = new ArrayList<>();
WMS維護(hù)上述的各個成員的變量值庭惜,可以看到大量線性表的應(yīng)用,不同的窗口或同一個窗口在不同的狀態(tài)階段有可能位于不同的表中。雖然窗口的狀態(tài)種類繁多,但是雏胃,對于Android來講窗口的類型主要只有兩種固棚,一種是應(yīng)用窗口,我們常見的Activity所處的窗口,應(yīng)用對話框窗口,應(yīng)用彈出的窗口等都屬于該類,與該應(yīng)用相關(guān)的Window類主要是PhoneWindow, 其主要應(yīng)用于手機(jī),PhoneWindow繼承于Window,其核心是DecorView, 應(yīng)用窗口的添加主要就是通過WindowManager的addView方法將一個DecorView添加到WindowManager中暖释。另一種是系統(tǒng)窗口袭厂,常見的屏幕頂部狀態(tài)欄纹磺,底部的導(dǎo)航欄橄杨,桌面窗口等都是系統(tǒng)窗口瞬痘,系統(tǒng)窗口沒有針對性的封裝類,只需要直接通過WindowManager的addView方法將一個View添加到WindowManager中即可.
wms和其它系統(tǒng)服務(wù)一樣由SystemServer啟動拆撼,其運(yùn)行在系統(tǒng)進(jìn)程里筒严,當(dāng)一個應(yīng)用需要創(chuàng)建窗口時通過IPC請求wms生成一個窗口,爾后再由wms向應(yīng)用返回和窗口交互的消息筋岛。addView的實(shí)質(zhì)上是由WindowManagerGlobal的addView方法實(shí)現(xiàn)具體的邏輯娶视,輾轉(zhuǎn)多次后最終調(diào)用到ViewRootImpl的setView方法,在該方法通過addToDisplay方法向wms發(fā)起一個Session請求睁宰,這里要注意的是肪获,IWindowSession方法也是一個AIDL接口文件,需要將其編譯后才生成IWindowSession.java接口柒傻,這里addToDisplay方法最終會調(diào)用到Session中對應(yīng)方法.
public class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient {
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
}
最終我們回到了wms的addWindow方法.