当前位置: 移动技术网 > IT编程>移动开发>Android > Android中WindowManager与WMS的解析

Android中WindowManager与WMS的解析

2019年07月30日  | 移动技术网IT编程  | 我要评论

博马娱乐城,县长什么级别,联合早报 薄逼宫

最近在改bug的时候发现在windowmanager.addview的时候会发生莫名其妙的崩溃,那个崩溃真的是让你心态爆炸,潜心研究了两天window相关的东西,虽然不是很深奥的东西,本人也只是弄清楚了window的添加逻辑,在此分享给大家:

一、悬浮窗的概念

在android中,无论我们的app界面,还是系统桌面,再或者是手机下方的几个虚拟按键和最上方的状态栏,又或者是一个吐司。。。我们所看到的所有界面,都是由一个个悬浮窗口组成的。

但是这些窗口有不同的级别:

  1. 系统的是老大,是最高级别,你没见过你下载的什么app把你的下拉菜单盖住了吧-。=
  2. 其次是每一个应用,都有自己的一个应用级别窗口。
  3. 在应用之内能创建好多的界面,所以还有一种是应用内的窗口。

基于上述三种,android把悬浮窗划分成三个级别,并通过静态int型变量来表示:

    /**
     * start of system-specific window types. these are not normally
     * created by applications.
     **/
    public static final int first_system_window   = 2000;
    /**
     * end of types of system windows.
     **/
    public static final int last_system_window   = 2999;

2000~2999:在系统级别的悬浮窗范围内,一般我们要想创建是需要申请权限。

    public static final int first_sub_window = 1000;
    /**
     * end of types of sub-windows.
     **/
    public static final int last_sub_window = 1999;

1000~1999:子窗口级别的悬浮窗,他如果想要创建必须在一个父窗口下。

public static final int type_base_application  = 1;
public static final int last_application_window = 99;

1~99:应用程序级别的悬浮窗,作为每个应用程序的基窗口。

在每段的范围内都有众多个窗口类型,这个具体就不说了,因为太多了根本说不完。。

但是说了这么半天,悬浮窗到底是个啥东西,可能这个名词听得很多,但是仔细想想android中用到的哪个控件还是哪个类叫悬浮窗?没有吧,那么view总该知道吧(不知道别说你是做android的)

其实说白了悬浮窗就是一个被包裹的view。因为除了一个view他还有很多的属性:长宽深度,类型,证书等等东西,只是属性很多而且属性之间的依赖关系有一些复杂而已。简单的来说可以这么理解。

二、windowmanager介绍

上面简单介绍了悬浮窗的概念,而windowmanager是对悬浮窗进行操作的一个媒介。

windowmanager是一个接口,他是继承了viewmanager接口中的三个方法:

public interface viewmanager
{
  public void addview(view view, viewgroup.layoutparams params);
  public void updateviewlayout(view view, viewgroup.layoutparams params);
  public void removeview(view view);
}

windowmanage暴露给我们的只是这个三个方法,真的是简单粗暴,但是很实用。

这三个方法看名字就知道含义了,增删改嘛,就不多说啦。

而在上面提到的对于悬浮窗的三种分类,也是windowmanager的内部类:windowmanager.layoutparams,关于layoutparams是什么在这里就不多说了。这不是我们的重点。

我们平时想要添加一个悬浮窗,就会使用第一个方法:

  windowmanager windowmanager = getwindowmanager();
  windowmanager.addview(.....);

我们在getwindowmanager获取的类,实际上是windowmanager的是windowmanager的实现类:windowmanagerimpl。接下来我们走一下添加悬浮窗的流程。

三、悬浮窗添加流程

入口肯定是从自己的addview中,上面说到了windowmanager的实现类是windowmanagerimpl,来看一下:

    @override
    public void addview(@nonnull view view, @nonnull viewgroup.layoutparams params) {
    applydefaulttoken(params);
    mglobal.addview(view, params, mcontext.getdisplay(), mparentwindow);
  }

这里有两步:第一步是给layoutparams设置一个默认的令牌(就是token这个属性,至于这个干什么的等会再说)

  private void applydefaulttoken(@nonnull viewgroup.layoutparams params) {
    // 设置条件:有默认令牌,而且不是子窗口级别的悬浮窗
    if (mdefaulttoken != null && mparentwindow == null) {
      if (!(params instanceof windowmanager.layoutparams)) {
        throw new illegalargumentexception("params must be windowmanager.layoutparams");
      }
      // 如果没有令牌就设置默认令牌
      final windowmanager.layoutparams wparams = (windowmanager.layoutparams) params;
      if (wparams.token == null) {
        wparams.token = mdefaulttoken;
      }
    }
  }

然后调用了mglobal的addview:

  public void addview(view view, viewgroup.layoutparams params,
      display display, window parentwindow) {
    /**进行一系列判空操作。。。**/
    if (parentwindow != null) {
      parentwindow.adjustlayoutparamsforsubwindow(wparams);
    } else {
      // if there's no parent, then hardware acceleration for this view is
      // set from the application's hardware acceleration setting.
      final context context = view.getcontext();
      if (context != null
          && (context.getapplicationinfo().flags
              & applicationinfo.flag_hardware_accelerated) != 0) {
        wparams.flags |= windowmanager.layoutparams.flag_hardware_accelerated;
      }
    }
    viewrootimpl root;
      root = new viewrootimpl(view.getcontext(), display);
      view.setlayoutparams(wparams);
      mviews.add(view);
      mroots.add(root);
      mparams.add(wparams);
      // 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.
        if (index >= 0) {
          removeviewlocked(index, true);
        }
        throw e;
      }
    }
  }

看到windowmanagerglobal中有三个属性: mviews、mroots、mparams,可以大胆猜测这个类中保存了我们进程中的所有视图以及相关属性。在这里主要关注一下viewrootimpl的这个实例对象root,接下来的会走进root的setview中。

viewrootimpl的setview方法内容有点多,我这里就截取关键的两部分:

1.

  int res; /** = windowmanagerimpl.add_okay; **/
  try {
    morigwindowtype = mwindowattributes.type;
    mattachinfo.mrecomputeglobalattributes = true;
    collectviewattributes();
    res = mwindowsession.addtodisplay(mwindow, mseq, mwindowattributes,
        gethostvisibility(), mdisplay.getdisplayid(), mwinframe,
        mattachinfo.mcontentinsets, mattachinfo.mstableinsets,
        mattachinfo.moutsets, mattachinfo.mdisplaycutout, minputchannel);

创建了一个名为res的int类型变量,他要获取到的是悬浮窗添加的结果:成功或者失败。

2.

    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 typ
                  + 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);
      }

第二部分是res返回失败的所有情况,在添加成功的时候res为okay,而非okay的情况就是上述情况。

接下来来看一下添加悬浮窗的操作,就是1中mwindowsession.addtodisplay。mwindowsession类型如下:

  final iwindowsession mwindowsession;

在这里其实用到了aidl跨进程通信,最终执行该方法的类是session:

  @override
  public int addtodisplay(iwindow window, int seq, windowmanager.layoutparams attrs,
      int viewvisibility, int displayid, rect outframe, rect outcontentinsets,
      rect outstableinsets, rect outoutsets,
      displaycutout.parcelablewrapper outdisplaycutout, inputchannel outinputchannel) {
    return mservice.addwindow(this, window, seq, attrs, viewvisibility, displayid, outframe,
        outcontentinsets, outstableinsets, outoutsets, outdisplaycutout, outinputchannel);
  }

这个mservice就是一个关键了系统类——windowmamagerservice(wms)。到了这里我们简单过一下思路:在addview之后,通过windowmanagerglobal进行一些相关配置,传入viewrootimpl,再通过aidl方式发送给wms系统服务。

可能有小伙伴会疑惑。好端端的为什么要用aidl实现?最开始本人也有这个疑惑,但是后来想了想所有的窗口无论系统窗口还是第三方app,窗口都是要通过一个类去进行添加允许判断,这里使用aidl是在合适不过的了。我们接着看一下wms的addwindow方法:

这个addwindow方法又是一段超长的代码,所以也就不全粘,说一下他的简单流程吧,主要是分为三步:权限判断、条件筛选、添加窗口

wms的addwindow方法:

  int res = mpolicy.checkaddpermission(attrs, appop);
  if (res != windowmanagerglobal.add_okay) {
    return res;
  }

首先进行一个权限判断,

final windowmanagerpolicy mpolicy;

windowmanagerpolicy的实现类是phonewindowmanagerpolicy,看一下他的实现:

又是小一百行的代码,我们拆开来看:

  //排除不属于三种类型悬浮窗范围内的type
  //很明显的三段排除。
  if (!((type >= first_application_window && type <= last_application_window)
      || (type >= first_sub_window && type <= last_sub_window)
      || (type >= first_system_window && type <= last_system_window))) {
    return windowmanagerglobal.add_invalid_type;
  }
  //不是系统级别的悬浮窗直接满足条件
  if (type < first_system_window || type > last_system_window) {
    return add_okay;
  }
    //以下几种不是系统警告类型的系统弹窗,会满足条件,除此之外的使用默认判断的方式
    if (!issystemalertwindowtype(type)) {
      switch (type) {
        case type_toast:
          outappop[0] = op_toast_window;
          return add_okay;
        case type_dream:
        case type_input_method:
        case type_wallpaper:
        case type_presentation:
        case type_private_presentation:
        case type_voice_interaction:
        case type_accessibility_overlay:
        case type_qs_dialog:
          // the window manager will check these.
          return add_okay;
      }
      return mcontext.checkcallingorselfpermission(internal_system_window)
          == permission_granted ? add_okay : add_permission_denied;
    }

后面的几段代码会频繁出现最后的这段代码:mcontext.checkcallingorselfpermission,具体实现的类是contextfixture:

    @override
    public int checkcallingorselfpermission(string permission) {
      if (mpermissiontable.contains(permission)
          || mpermissiontable.contains(permission_enable_all)) {
        logd("checkcallingorselfpermission: " + permission + " return granted");
        return packagemanager.permission_granted;
      } else {
        logd("checkcallingorselfpermission: " + permission + " return denied");
        return packagemanager.permission_denied;
      }
    }

这里会使用默认权限判断的方式,要么允许对应权限,要么就是拥有全部权限,否则就会返回denied。

这个说完接着回到checkpermission方法。

    //对于系统进程直接满足允许
    final int callinguid = binder.getcallinguid();
    if (userhandle.getappid(callinguid) == process.system_uid) {
      return add_okay;
    }

说实话下面这一段代码我看的不是很明白,只是看到了这里对8.0之后做了版本限制,直接使用默认检查方式。

    applicationinfo appinfo;
    try {
      appinfo = mcontext.getpackagemanager().getapplicationinfoasuser(
              attrs.packagename,
              0 /* flags */,
              userhandle.getuserid(callinguid));
    } catch (packagemanager.namenotfoundexception e) {
      appinfo = null;
    }
    if (appinfo == null || (type != type_application_overlay && appinfo.targetsdkversion >= o)) {
      
      return (mcontext.checkcallingorselfpermission(internal_system_window)
          == permission_granted) ? add_okay : add_permission_denied;
    }

这段是要从packagemanager中获取applicationinfo,如果获取失败会抛出namenotfound异常。所以下面的判断是在异常的时候使用默认权限处理方式。

最后还以一步检查操作,关系不大就不看了。到这里checkpermission方法就结束了。

权限检查的步骤已经结束,接着就是根据上述获取到的结果进行条件筛选。

  if (res != windowmanagerglobal.add_okay) {
    return res;
  }

首先在权限检查的步骤获取权限失败,那么会直接返回,不会执行条件筛选的步骤。而真正的条件筛选步骤代码也是很多,我这里直接粘过来然后说了。

      //111111111111111
      if (!mdisplayready) {
        throw new illegalstateexception("display has not been initialialized");
      }
      final displaycontent displaycontent = getdisplaycontentorcreate(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)
          && !mdisplaymanagerinternal.isuidpresentondisplay(session.muid, displayid)) {
        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;
      }
      //22222222222222
      if (type >= first_sub_window && type <= last_sub_window) {
        parentwindow = windowforclientlocked(null, attrs.token, false);
        if (parentwindow == 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 (parentwindow.mattrs.type >= first_sub_window
            && parentwindow.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;
        }
      }
      //333333333333333
      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;
      }
      //444444444444444
      appwindowtoken atoken = null;
      final boolean hasparent = parentwindow != null;
      // use existing parent window token for child windows since they go in the same token
      // as there parent window so we can apply the same policy on them.
      windowtoken token = displaycontent.getwindowtoken(
          hasparent ? parentwindow.mattrs.token : attrs.token);
      // if this is a child window, we want to apply the same type checking rules as the
      // parent window type.
      final int roottype = hasparent ? parentwindow.mattrs.type : type;
      boolean addtoastwindowrequirestoken = false;
      if (token == null) {
        if (roottype >= first_application_window && roottype <= 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 (roottype == 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 (roottype == 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 (roottype == type_wallpaper) {
          slog.w(tag_wm, "attempted to add wallpaper window with unknown token "
             + attrs.token + ". aborting.");
          return windowmanagerglobal.add_bad_app_token;
        }
        if (roottype == type_dream) {
          slog.w(tag_wm, "attempted to add dream window with unknown token "
             + attrs.token + ". aborting.");
          return windowmanagerglobal.add_bad_app_token;
        }
        if (roottype == 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 (roottype == 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;
        }
        if (type == type_toast) {
          // apps targeting sdk above n mr1 cannot arbitrary add toast windows.
          if (doesaddtoastwindowrequiretoken(attrs.packagename, callinguid,
              parentwindow)) {
            slog.w(tag_wm, "attempted to add a toast window with unknown token "
                + attrs.token + ". aborting.");
            return windowmanagerglobal.add_bad_app_token;
          }
        }
        final ibinder binder = attrs.token != null ? attrs.token : client.asbinder();
        final boolean isroundedcorneroverlay =
            (attrs.privateflags & private_flag_is_rounded_corners_overlay) != 0;
        token = new windowtoken(this, binder, type, false, displaycontent,
            session.mcanaddinternalsystemwindow, isroundedcorneroverlay);
      } else if (roottype >= first_application_window && roottype <= last_application_window) {
        atoken = token.asappwindowtoken();
        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;
        } else if (type == type_application_starting && atoken.startingwindow != null) {
          slog.w(tag_wm, "attempted to add starting window to token with already existing"
              + " starting window");
          return windowmanagerglobal.add_duplicate_add;
        }
      } else if (roottype == 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 (roottype == 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 (roottype == 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 (roottype == 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 (roottype == 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_toast) {
        // apps targeting sdk above n mr1 cannot arbitrary add toast windows.
        addtoastwindowrequirestoken = doesaddtoastwindowrequiretoken(attrs.packagename,
            callinguid, parentwindow);
        if (addtoastwindowrequirestoken && token.windowtype != type_toast) {
          slog.w(tag_wm, "attempted to add a toast 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.asappwindowtoken() != null) {
        slog.w(tag_wm, "non-null appwindowtoken for system window of roottype=" + roottype);
        // 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, client.asbinder(), type, false, displaycontent,
            session.mcanaddinternalsystemwindow);
      }
      //5555555555555
      final windowstate win = new windowstate(this, session, client, token, parentwindow,
          appop[0], seq, attrs, viewvisibility, session.muid,
          session.mcanaddinternalsystemwindow);
      if (win.mdeathrecipient == null) {
        // client has apparently died, so there is no reason to
        // continue.
        slog.w(tag_wm, "adding window client " + client.asbinder()
            + " that is dead, aborting.");
        return windowmanagerglobal.add_app_exiting;
      }
      if (win.getdisplaycontent() == null) {
        slog.w(tag_wm, "adding window to display that has been removed.");
        return windowmanagerglobal.add_invalid_display;
      }
      final boolean hasstatusbarservicepermission =
          mcontext.checkcallingorselfpermission(permission.status_bar_service)
              == packagemanager.permission_granted;
      mpolicy.adjustwindowparamslw(win, win.mattrs, hasstatusbarservicepermission);
      win.setshowtoowneronlylocked(mpolicy.checkshowtoowneronly(attrs));
      res = mpolicy.prepareaddwindowlw(win, attrs);
      if (res != windowmanagerglobal.add_okay) {
        return res;
      }
      final boolean openinputchannels = (outinputchannel != null
          && (attrs.inputfeatures & input_feature_no_input_channel) == 0);
      if (openinputchannels) {
        win.openinputchannel(outinputchannel);
      }
      //666666666666666
      if (type == type_toast) {
        if (!getdefaultdisplaycontentlocked().canaddtoastwindowforuid(callinguid)) {
          slog.w(tag_wm, "adding more than one toast window for uid at a time.");
          return windowmanagerglobal.add_duplicate_add;
        }
        
        if (addtoastwindowrequirestoken
            || (attrs.flags & layoutparams.flag_not_focusable) == 0
            || mcurrentfocus == null
            || mcurrentfocus.mowneruid != callinguid) {
          mh.sendmessagedelayed(
              mh.obtainmessage(h.window_hide_timeout, win),
              win.mattrs.hidetimeoutmilliseconds);
        }
      }

这里讲筛选部分大体分成这么几个步骤:

  1. 系统以及初始化的一些判断:就像最开始的四个判断。
  2. 子窗口类型时候的对父窗口的相关筛选(父是否为空,以及父亲的类型判断)
  3. 一种特殊的私有类型条件筛选,该类型属于系统类型
  4. 涉及证书(token)的窗口类型条件筛选。
  5. 状态栏权限条件筛选
  6. 吐司类型的条件筛选

在代码中对应的步骤有明确的标注,而具体的代码大多只是一些判断,所以在感觉没有细说的必要了。

在条件筛选完成之后,剩下的类型都是符合添加的类型,从现在开始就开始对不同的type进行不同的添加。经过多到加工后,将okay返回。

如果能从添加窗口的步骤返回,就说明一定是okay的。那么我们可以一步步跳回层层调用的代码,最终在viewrootimpl中,对没有添加成功的抛出异常。

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

对于okay的,在viewrootimpl中会做一些其他的操作,反正我是没看懂-。=、

四、小结

到这里wms的添加悬浮窗口的流程差不多就过了一遍了。可能有些地方说的不是很细,大家下来可以关注一下个别几个点。整个过程有这么几个需要强调的地方。

  • 函数循环嵌套,共同消费返回值。
  • 异常循环嵌套
  • 个别地方对m和o以上的系统进行了限制

如果在添加悬浮窗的时候使用了不同的type,可能会发生异常:本人拿了一个8.0的手机,分别对窗口type设置为overlay和error。因为error类型是被弃用的,我发现使用error会抛出异常,而overlay不会。同样的拿了一个6.0的手机添加error类型就没有异常抛出,肯定是上述的问题导致的,但是具体在哪一块我还没有找到,因为整个流程的出口太多了-。=。

此外在windowmanagerglobal.addview方法中,有一个地方:

  if (parentwindow != null) {
    parentwindow.adjustlayoutparamsforsubwindow(wparams);
  } else {

这个方法是对于有子窗口类型的证书处理,网上查了一下该方法在四点几、六点几和8.0是不同的,也就是说对证书的处理方式变化了,这里本人还没有细看,有兴趣的盆友可以研究一下然后评论交流一番。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对移动技术网的支持。如果你想了解更多相关内容请查看下面相关链接

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网