本文來自 http://blog.csdn.net/liuxian13183/
,引用必須注明出處!
關于Framework,就是應用層底下的控制層,離應用層最近,總想找個機會,寫寫WindowMangerService和ActivityManagerService(注意非控件,而是指一類服務)以及其他一些東西,對底層做一個更為全面的認識。而很早以前,寫過一篇文章,來簡述Android系統-"
Android進階之系統介紹",同樣今天我們在講Framework層時也會再對系統做一個回顧;下圖是我對本節内容的一個基本介紹。
PS:W類是ViewRoot的一個内部類,ViewRoot最大作用就是把IPC調用轉為本地調用。
HistoryRecord-每個Acitivty都會有一個,用來管理和記錄Activity,是一個Binder對象
ViewRoot-實作View和WindowManger之間的協定,是View Hierarchy的最頂層
PhoneWindow-其中有autoManger和keyguardManager的實作對象
一、視窗
就着上圖,我們會對每一條做進一步的解說(注意上圖大多是包含關系,少數是關聯關系,請差別對待),上圖對Framework簡單做了描述;同時科普一下什麼叫視窗,視窗非指window類,而是指所有使用windowmanger将
其展示在使用者面前的控件,如toast、activity、menu等,而這些界面通過設定window的callback來監聽到wms傳給view對象的資訊,如手勢操作。而視窗類型基本可以分為3種:
1、系統視窗,不需要父視窗-可以指定2000-2999層
2、子視窗,依賴父視窗-1000-1999層 如Toast
3、應用視窗,對應activity-小于99層 如Activity
視窗可以說是View,而wms不直接跟view溝通,而是通過實作IWindow的ViewRoot.W類,然後再傳給view。
關于Context,上下文引用,項目中使用的還比較多,是一個場景,用來配合上下文操作;一個應用中context的數量=service個數+activity個數+application個數,原因它們都繼承自ContextWraper,而它繼承自context。
二、linux檔案系統
由于android系統基于linux,就先講一下linux基礎,檔案系統通常有3個特點:
1、由檔案和目錄組成,占據一定存儲空間
2、具有讀、寫、執行權限,可以複制和移動
3、多個檔案系統可以并列,且類型可以不同,如FAT16和NTFS
主要的檔案目錄有以下幾種-與android系統類比:
1、bin,存放使用者級二進制工具-相當于android系統的acct目錄
2、boot,核心鏡像檔案,由bootloader裝載-firmware
3、dev,各種檔案系統如列印機等-相當于android系統的dev目錄+storage+mnt/sdcard
4、etc,配置檔案區-相當于android系統的config目錄
5、home,使用者工作目錄-data/user
6、lib,系統運作時庫的存放地-data/app-lib
7、opt,存放系統程式-data/app-private
8、proc,系統級如核心和程序所在檔案-proc目錄
9、root,管理者工作目錄-root目錄
10、sbin,管理者的二進制工具-sbin目錄
11、sys,驅動對應的系統檔案如固件、核心、電量等-sys目錄+system目錄
12、usr,應用程式安裝區-data/app
13、var,調試資訊等-data/anr等
是以從上面來以看出,其實作業系統都是由檔案組成,外加一些硬體感應裝置。但上面介紹的依然不全面,因為android是一層套一層,資源是總體一緻,大體分散的結構。同時上面會涉及到程序pid,值為100以内是系統程序,1000以内是root程序,1000以上是使用者程序。講完目錄,咱們再講講指令:
1、man,查詢某指令的意思
2、ls,列出目前目錄下所有檔案及檔案夾資訊
3、find,用名字查找檔案資訊
4、grep,查詢檔案中的字元串資訊
5、cat,打開檔案
6、chmod,指定權限,ugo指user(自身)、group(組)、other(其他),權限有r(讀1)w(寫2)x(執行4),指定權限有兩種方式如chmod ug+x(給予目前使用者和某群組執行的權限),chmod 777(給予三者所有權限,原因請看上一行)
7、ps和kill,ps列出目前所有程序,kill殺死某程序
8、export,用于設定變量作用于全局
9、mount和unmount,加載和解除安裝檔案系統
好在用過linux作業系統工作過一段時間,對後來做Android開發,起到很大的幫助,上面介紹的是一些常用指令,有興趣的可以安裝一個linux系統來用,之前有一個同僚使用ubuntu來編譯so,而我當年用的是小紅帽rethat。
三、linux啟動過程
下面簡單講一個linux啟動過程,其實Dalvik虛拟機也是類似
上圖少說一點,在CPU運作之前,要把磁盤和其他記憶體啟動,這樣才能保證系統正式開始,因為所有系統均為檔案,加載檔案的硬碟不啟,系統如何能被啟動?
四、異步資訊系統
在Android系統中,用的最多的設計模式就是handler+looper+messageQueue,無論在系統層還是在應用層,
之後我們會再講到都被用爛了,異步線程表現為:開啟後無限循環,周遊資料,如果為空則等待,直到下次資料不
為空時,使用場景有二
1、任務要常駐
2、任務需要根據消息來做不同操作
使用方法如上,解釋幾點,上面的資料指messegeQueue屬于隊列LILO,讀/寫資料時會加鎖;如何應用呢,
Handler handler =new Handler(); 首先建立handler時,一般需要在UI線程中(一般你也沒必要在子線程重寫
handler),獲得一個UI線程的looper對象;looper對象通過prepare方法(僅一次)準備一個MessageQueue
(系統唯一),調用loop方法一直分發消息;MessageQueue可以定義消息處理時刻,否則先進先出,
通過next和enquenceMessage方法來取和加入消息,處于wait狀态則喚醒,若無消息則挂起。
五、Binder
再講一個知識點Binder,然後結束本篇。
Binder工作在linux層面,是一小段記憶體,屬于驅動,主要用來解決IPC調用,應用架構包括3部分
1、服務端接口-Binder對象,收到消息即啟動隐藏線程,執行onTransact函數初始化;向用戶端發送消息時,
挂起目前線程。
2、Binder驅動-創造mRemote的Binder對象,重載以下方法
1)以線程間消息通信模式,向服務端發送參數
2)挂起用戶端線程,等待服務端傳回處理結果,并通知用戶端
3)接收服務端通知,繼續執行用戶端線程
3、用戶端接口-向用戶端發送消息,挂起目前線程。
Binder服務的設計原則是,開放ServiceManger接口給外界,關于具體的操作由底層去統一執行,不暴露出來,
這也是保證架構穩定、邏輯正确的重要方式,使不同管理類與底層實作可以分離解耦。
另一篇關于Binder介紹,最下面:
http://blog.csdn.net/reboot123/article/details/52370416 下面這篇文章底層就是使用binder進行通信 Android進階第十講之AIDL與JNI六、token
token原意指密碼,在這裡指“身份證”,代表唯一性和代理性;一般token為IBind的實作類,即使View.Attach
Info中也是用IBind實作類來進行IPC調用。應用視窗一般都有token,而window可能沒有,為什麼?window可以不
對應此視窗存在。
應用視窗的token:一般最初跟window一樣最終指向DecorView的W類,初始值是window的mAppToken,指應用
HistoryRecord。Activity的token起始是HistoryRecord,最終指向此W類即mAppToken,與window相同。
子視窗的token:是父視窗view的W類,即mPanelParentWindowToken
系統視窗無token
是以view中會有window的token,也會有父視窗的token,而每個應用視窗随activity誕生,均會有一個Activity
Thread陪伴。
PopupWindow上套PopupWindow,報錯:unable to add window ,is your activity running ?
經查在windowManager執行addView操作時出錯
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
WindowManagerGlobal
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
ViewRootImpl出錯出現ADD_BAD_APP_TOKEN或ADD_BAD_SUBWINDOW_TOKEN,要查WindowSession.addToDisplay
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
int res; /* = WindowManagerImpl.ADD_OKAY; */
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
}
mPendingOverscanInsets.set(0, 0, 0, 0);
mPendingContentInsets.set(mAttachInfo.mContentInsets);
mPendingStableInsets.set(mAttachInfo.mStableInsets);
mPendingVisibleInsets.set(0, 0, 0, 0);
mAttachInfo.mAlwaysConsumeNavBar =
(res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar;
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
mAdded = false;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
switch (res) {
case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not valid; is your activity running?");
case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not for an application");
case WindowManagerGlobal.ADD_APP_EXITING:
throw new WindowManager.BadTokenException(
"Unable to add window -- app for token " + attrs.token
+ " is exiting");
case WindowManagerGlobal.ADD_DUPLICATE_ADD:
throw new WindowManager.BadTokenException(
"Unable to add window -- window " + mWindow
+ " has already been added");
case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
// Silently ignore -- we would have just removed it
// right away, anyway.
return;
case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- another window of type "
+ mWindowAttributes.type + " already exists");
case WindowManagerGlobal.ADD_PERMISSION_DENIED:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- permission denied for window type "
+ mWindowAttributes.type);
case WindowManagerGlobal.ADD_INVALID_DISPLAY:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified display can not be found");
case WindowManagerGlobal.ADD_INVALID_TYPE:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified window type "
+ mWindowAttributes.type + " is not valid");
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
}
}
WindowSession
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outInputChannel);
}
WindowManagerService
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
boolean reportNewConfig = false;
WindowState attachedWindow = null;
long origId;
final int type = attrs.type;
synchronized(mWindowMap) {
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent == null) {
Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
+ displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
if (!displayContent.hasAccess(session.mUid)) {
Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
+ "does not have access: " + displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
if (mWindowMap.containsKey(client.asBinder())) {
Slog.w(TAG_WM, "Window " + client + " is already added");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
attachedWindow = windowForClientLocked(null, attrs.token, false);
if (attachedWindow == null) {
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display. Aborting.");
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
}
boolean addToken = false;
WindowToken token = mTokenMap.get(attrs.token);
AppWindowToken atoken = null;
if (token == null) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_DREAM) {
Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
token = new WindowToken(this, attrs.token, -1, false);
addToken = true;
} else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
atoken = token.appWindowToken;
if (atoken == null) {
Slog.w(TAG_WM, "Attempted to add window with non-application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
} else if (atoken.removed) {
Slog.w(TAG_WM, "Attempted to add window with exiting application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {
// No need for this guy!
if (DEBUG_STARTING_WINDOW || localLOGV) Slog.v(
TAG_WM, "**** NO NEED TO START: " + attrs.getTitle());
return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED;
}
} else if (type == TYPE_INPUT_METHOD) {
if (token.windowType != TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_VOICE_INTERACTION) {
if (token.windowType != TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_WALLPAPER) {
if (token.windowType != TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_DREAM) {
if (token.windowType != TYPE_DREAM) {
Slog.w(TAG_WM, "Attempted to add Dream window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_ACCESSIBILITY_OVERLAY) {
if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_QS_DIALOG) {
if (token.windowType != TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (token.appWindowToken != null) {
Slog.w(TAG_WM, "Non-null appWindowToken for system window of type=" + type);
// It is not valid to use an app token with other system types; we will
// instead make a new token for it (as if null had been passed in for the token).
attrs.token = null;
token = new WindowToken(this, null, -1, false);
addToken = true;
}
if (addToken) {
mTokenMap.put(attrs.token, token);
}
win.attach();
mWindowMap.put(client.asBinder(), win);
if (win.mAppOp != AppOpsManager.OP_NONE) {
int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
win.getOwningPackage());
if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
(startOpResult != AppOpsManager.MODE_DEFAULT)) {
win.setAppOpVisibilityLw(false);
}
}
Binder.restoreCallingIdentity(origId);
return res;
}
如果加入成功(mWindowMap.put(attr.token,token)),則子View與父View建立連接配接關系。
查到mTokenMap.get(attr.token)為null,同時又是子視窗,是以報錯。
至于為什麼是null,繼續
回到PopupWindow
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
if (isShowing() || mContentView == null) {
return;
}
TransitionManager.endTransitions(mDecorView);
attachToAnchor(anchor, xoff, yoff, gravity);
mIsShowing = true;
mIsDropdown = true;
final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
preparePopup(p);
final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,
p.width, p.height, gravity);
updateAboveAnchor(aboveAnchor);
p.accessibilityIdOfAnchor = (anchor != null) ? anchor.getAccessibilityViewId() : -1;
invokePopup(p);
}
可以看到,二次添加的PopupWidow,使用一次添加的PopupWindow對象的token,而此token為
private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
private WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
// These gravity settings put the view at the top left corner of the
// screen. The view is then positioned to the appropriate location by
// setting the x and y offsets to match the anchor's bottom-left
// corner.
p.gravity = computeGravity();
p.flags = computeFlags(p.flags);
p.type = mWindowLayoutType;
p.token = token;
p.softInputMode = mSoftInputMode;
p.windowAnimations = computeAnimationResource();
return p;
}
但在WindowManagerService裡,PopupWindow依賴show的控件是傳過來的,而一次PopupWindow的type剛好等于
/**
* Window type: a panel on top of an application window. These windows
* appear on top of their attached window.
*/
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
所在WindowManagerService不允許在子視窗上建立子視窗。
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
attachedWindow = windowForClientLocked(null, attrs.token, false);
if (attachedWindow == null) {
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
完。
補充:Dialog則無此限制
public void show() {
if (mShowing) {
if (mDecor != null) {
if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
}
mDecor.setVisibility(View.VISIBLE);
}
return;
}
WindowManager.LayoutParams l = mWindow.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
nl.copyFrom(l);
nl.softInputMode |=
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
l = nl;
}
mWindowManager.addView(mDecor, l);
mShowing = true;
sendShowMessage();
}
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
final Window w = new PhoneWindow(mContext);
mWindow = w;
}
PhoneWindow
public final WindowManager.LayoutParams getAttributes() {
return mWindowAttributes;
}
private final WindowManager.LayoutParams mWindowAttributes =
new WindowManager.LayoutParams();
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = TYPE_APPLICATION;
format = PixelFormat.OPAQUE;
}
public static final int TYPE_APPLICATION = 2;
WindowManagerService要求大于等1000,小于等于1999才報ADD_BAD_SUB_WINDOW。
是以Dialog無此限制。
Android的安裝過程:
http://www.2cto.com/kf/201402/280725.htmlBinder能支援跨程序通信的原因,是它實作IBinder接口,系統定義實作此接口即賦予程序通信的功能。
優勢:做資料拷貝隻用一次,則Pipe、Socket都需要兩次;其次安全性高,不會像Socket會暴露位址,被人替換;
通信機制:Service向ServiceManager注冊,得到虛拟的Uid和Pid,使用Binder通信;Client向ServiceManager請求,得到虛拟的Uid和Pid,以及目标對象的Proxy,底層通過硬體協定傳輸,使用Binder通訊。
通信時Client手持Proxy,ServiceManger進行轉換,調用到Service。三者運作在三個獨立程序中。Client/Sever全雙工,互為Sever/Client
實際案例:
Vivo7.0以上手機,無法彈出自定義Toast(WindowManager通過addView方式,再配合動畫),由于Android手機系統權限逐漸收緊,導緻無法成功申請的“SYSTEM_ALERT_WINDOW”權限。
解決思路:先看權限是否被收回,其次看使用系統Toast是否可行,最後嘗試降級使用WindowManager。
解決辦法,降低WindowManager.LayoutParam的flag等級,使用2000以下的等級,比如LAST_SUB_WINDOW(次級window1999)