Android源码剖析之Framework层基础版(窗口、linux、token、Binder)



  本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处!


关于Framework,就是应用层底下的控制层,离应用层最近,总想找个机会,写写WindowMangerService和ActivityManagerService(注意非控件,而是指一类服务)以及其他一些东西,对底层做一个更为全面的认识。而很早以前,写过一篇文章,来简述Android系统-" Android高级之系统介绍",同样今天我们在讲Framework层时也会再对系统做一个回顾;下图是我对本节内容的一个基本介绍。

Android源码剖析之Framework层基础版(窗口、linux、token、Binder)

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虚拟机也是类似

Android源码剖析之Framework层基础版(窗口、linux、token、Binder)

上图少说一点,在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 ?

Android源码剖析之Framework层基础版(窗口、linux、token、Binder)

经查在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.html

Binder能支持跨进程通信的原因,是它实现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)

上一篇:五:分布式事务一致性协议paxos的应用场景


下一篇:静态编译Qt4.4.3