欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android中Window添加View的底层原理

程序员文章站 2024-02-26 21:36:34
一、window和windowmanager window是一个抽象类,它的具体实现是phonewindow,创建一个window很简单,只需要创建一个windowman...

一、window和windowmanager
window是一个抽象类,它的具体实现是phonewindow,创建一个window很简单,只需要创建一个windowmanager即可,window具体实现在windowmanagerservice中,windowmanager和windowmanagerservice的交互是一个ipc的过程。
下面是用windowmanager的例子:

mfloatingbutton = new button(this); 
      mfloatingbutton.settext( "window"); 
      mlayoutparams = new windowmanager.layoutparams( 
          layoutparams. wrap_content, layoutparams.wrap_content, 0, 0, 
          pixelformat. transparent); 
      mlayoutparams. flags = layoutparams.flag_not_touch_modal 
          | layoutparams. flag_not_focusable 
          | layoutparams. flag_show_when_locked; 
      mlayoutparams. type = layoutparams. type_system_error; 
      mlayoutparams. gravity = gravity. left | gravity. top; 
      mlayoutparams. x = 100; 
      mlayoutparams. y = 300; 
      mfloatingbutton.setontouchlistener( this); 
      mwindowmanager.addview( mfloatingbutton, mlayoutparams);  

flags和type两个属性很重要,下面对一些属性进行介绍,首先是flags:
flag_not_touch_modal表示不需要获取焦点,也不需要接收各种输入,最终事件直接传递给下层具有焦点的window。
flag_not_focusable:在此window外的区域单击事件传递到底层window中。当前的区域则自己处理,这个一般都要设置,很重要。
flag_show_when_locked :开启可以让window显示在锁屏界面上。
再来看下type这个参数:
window有三种类型:应用window,子window,系统window。应用类对应一个activity,子window不能单独存在,需要附属在父window上,比如常用的dialog。系统window是需要声明权限再创建的window,如toast等。
window有z-ordered属性,层级越大,越在顶层。应用window层级1-99,子window1000-1999,系统2000-2999。这此层级对应着windowmanager的type参数。系统层级常用的有两个type_system_overlay或者type_system_error。比如想用type_system_error,只需
mlayoutparams.type = layoutparams.type_system_error。还要添加权限<uses-permission andorid:name="android.permission.system_alert_window"/>。
有了对window的基本认识之后,我们来看下它底层如何实现加载view的。
二、window的创建
其实window的创建跟之前我写的一篇博客layoutinflater源码分析有点相似。window的创建是在activity创建的attach方法中,通过policymanager的makenewwindow方法。activity中实现了window的callback接口,因此当window状态改变时就会回调activity方法。如onattachedtowindow等。policymanager的真正实现类是policy,看下它的代码:

public window makenewwindow(context context) { 
    return new phonewindow(context); 
  } 

到此window创建完成。
下面分析view是如何附属到window上的。看activity的setcontentview方法。

public void setcontentview(int layoutresid) { 
    getwindow().setcontentview(layoutresid); 
    initwindowdecoractionbar(); 
  } 

两部分,设置内容和设置actionbar。window的具体实现是phonewindow,看它的setcontent。

public void setcontentview(int layoutresid) { 
    // note: feature_content_transitions may be set in the process of installing the window 
    // decor, when theme attributes and the like are crystalized. do not check the feature 
    // before this happens. 
    if (mcontentparent == null) { 
      installdecor(); 
    } else if (!hasfeature(feature_content_transitions)) { 
      mcontentparent.removeallviews(); 
    } 
 
    if (hasfeature(feature_content_transitions)) { 
      final scene newscene = scene.getsceneforlayout(mcontentparent, layoutresid, 
          getcontext()); 
      transitionto(newscene); 
    } else { 
      mlayoutinflater.inflate(layoutresid, mcontentparent); 
    } 
    final callback cb = getcallback(); 
    if (cb != null && !isdestroyed()) { 
      cb.oncontentchanged(); 
    } 
  }  

看到了吧,又是分析它。
这里分三步执行:
1.如果没有decorview,在installdecor中的generatedecor()创建decorview。之前就分析过,这次就不再分析它了。
2.将view添加到decorview中的mcontentparent中。
3.回调activity的oncontentchanged接口。
经过以上操作,decorview创建了,但还没有正式添加到window中。在activityresumeactivity中首先会调用activity的onresume,再调用activity的makevisible,makevisible中真正添加view ,代码如下:

void makevisible() { 
   if (!mwindowadded) { 
     viewmanager wm = getwindowmanager(); 
     wm.addview(mdecor, getwindow().getattributes()); 
     mwindowadded = true; 
   } 
   mdecor.setvisibility(view.visible); 
 } 

通过上面的addview方法将view添加到window。
三、window操作view内部机制
1.window的添加
一个window对应一个view和一个viewrootimpl,window和view通过viewrootimpl来建立联系,它并不存在,实体是view。只能通过 windowmanager来操作它。
windowmanager的实现类是windowmanagerimpl。它并没有直接实现三大操作,而是委托给windowmanagerglobal。addview的实现分为以下几步:
1).检查参数是否合法。

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"); 
    } 
 
    final windowmanager.layoutparams wparams = (windowmanager.layoutparams)params; 
    if (parentwindow != null) { 
      parentwindow.adjustlayoutparamsforsubwindow(wparams); 
    } else { 
      // if there's no parent and we're running on l or above (or in the 
      // system context), assume we want hardware acceleration. 
      final context context = view.getcontext(); 
      if (context != null 
          && context.getapplicationinfo().targetsdkversion >= build.version_codes.lollipop) { 
        wparams.flags |= windowmanager.layoutparams.flag_hardware_accelerated; 
      } 
    } 

2).创建viewrootimpl并将view添加到列表中。

root = new viewrootimpl(view.getcontext(), display); 
 
      view.setlayoutparams(wparams); 
 
      mviews.add(view); 
      mroots.add(root); 
      mparams.add(wparams); 

3).通过viewrootimpl来更新界面并完成window的添加过程 。
root.setview(view, wparams, panelparentview); 
上面的root就是viewrootimpl,setview中通过requestlayout()来完成异步刷新,看下requestlayout:

public void requestlayout() { 
    if (!mhandlinglayoutinlayoutrequest) { 
      checkthread(); 
      mlayoutrequested = true; 
      scheduletraversals(); 
    } 
  } 

接下来通过windowsession来完成window添加过程,windowsession是一个binder对象,真正的实现类是 session,window的添加是一次ipc调用。

 try { 
          morigwindowtype = mwindowattributes.type; 
          mattachinfo.mrecomputeglobalattributes = true; 
          collectviewattributes(); 
          res = mwindowsession.addtodisplay(mwindow, mseq, mwindowattributes, 
              gethostvisibility(), mdisplay.getdisplayid(), 
              mattachinfo.mcontentinsets, mattachinfo.mstableinsets, 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); 
} 

 在session内部会通过windowmanagerservice来实现window的添加。

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内部会为每一个应用保留一个单独的session。
2.window的删除
看下windowmanagerglobal的removeview:

public void removeview(view view, boolean immediate) { 
    if (view == null) { 
      throw new illegalargumentexception("view must not be null"); 
    } 
 
    synchronized (mlock) { 
      int index = findviewlocked(view, true); 
      view curview = mroots.get(index).getview(); 
      removeviewlocked(index, immediate); 
      if (curview == view) { 
        return; 
      } 
 
      throw new illegalstateexception("calling with view " + view 
          + " but the viewancestor is attached to " + curview); 
    } 
  } 

首先调用findviewlocked来查找删除view的索引,这个过程就是建立数组遍历。然后再调用removeviewlocked来做进一步的删除。

private void removeviewlocked(int index, boolean immediate) { 
    viewrootimpl root = mroots.get(index); 
    view view = root.getview(); 
 
    if (view != null) { 
      inputmethodmanager imm = inputmethodmanager.getinstance(); 
      if (imm != null) { 
        imm.windowdismissed(mviews.get(index).getwindowtoken()); 
      } 
    } 
    boolean deferred = root.die(immediate); 
    if (view != null) { 
      view.assignparent(null); 
      if (deferred) { 
        mdyingviews.add(view); 
      } 
    } 
  } 

真正删除操作是viewrootimpl来完成的。windowmanager提供了两种删除接口,removeviewimmediate,removeview。它们分别表示异步删除和同步删除。具体的删除操作由viewrootimpl的die来完成。

boolean die(boolean immediate) { 
    // make sure we do execute immediately if we are in the middle of a traversal or the damage 
    // done by dispatchdetachedfromwindow will cause havoc on return. 
    if (immediate && !misintraversal) { 
      dodie(); 
      return false; 
    } 
 
    if (!misdrawing) { 
      destroyhardwarerenderer(); 
    } else { 
      log.e(tag, "attempting to destroy the window while drawing!\n" + 
          " window=" + this + ", title=" + mwindowattributes.gettitle()); 
    } 
    mhandler.sendemptymessage(msg_die); 
    return true; 
  } 

由上可知如果是removeviewimmediate,立即调用dodie,如果是removeview,用handler发送消息,viewrootimpl中的handler会处理消息并调用dodie。重点看下dodie:

void dodie() { 
    checkthread(); 
    if (local_logv) log.v(tag, "die in " + this + " of " + msurface); 
    synchronized (this) { 
      if (mremoved) { 
        return; 
      } 
      mremoved = true; 
      if (madded) { 
        dispatchdetachedfromwindow(); 
      } 
 
      if (madded && !mfirst) { 
        destroyhardwarerenderer(); 
 
        if (mview != null) { 
          int viewvisibility = mview.getvisibility(); 
          boolean viewvisibilitychanged = mviewvisibility != viewvisibility; 
          if (mwindowattributeschanged || viewvisibilitychanged) { 
            // if layout params have been changed, first give them 
            // to the window manager to make sure it has the correct 
            // animation info. 
            try { 
              if ((relayoutwindow(mwindowattributes, viewvisibility, false) 
                  & windowmanagerglobal.relayout_res_first_time) != 0) { 
                mwindowsession.finishdrawing(mwindow); 
              } 
            } catch (remoteexception e) { 
            } 
          } 
 
          msurface.release(); 
        } 
      } 
 
      madded = false; 
    } 
    windowmanagerglobal.getinstance().doremoveview(this); 
  } 

主要做四件事:
1.垃圾回收相关工作,比如清数据,回调等。
2.通过session的remove方法删除window,最终调用windowmanagerservice的removewindow

3.调用dispathdetachedfromwindow,在内部会调用ondetachedfromwindow()和ondetachedfromwindowinternal()。当view移除时会调用ondetachedfromwindow,它用于作一些资源回收。
4.通过doremoveview刷新数据,删除相关数据,如在mroot,mdyingviews中删除对象等。

void doremoveview(viewrootimpl root) { 
    synchronized (mlock) { 
      final int index = mroots.indexof(root); 
      if (index >= 0) { 
        mroots.remove(index); 
        mparams.remove(index); 
        final view view = mviews.remove(index); 
        mdyingviews.remove(view); 
      } 
    } 
    if (hardwarerenderer.strimforeground && hardwarerenderer.isavailable()) { 
      dotrimforeground(); 
    } 
  } 

3.更新window
看下windowmanagerglobal中的updateviewlayout。

public void updateviewlayout(view view, viewgroup.layoutparams params) { 
    if (view == null) { 
      throw new illegalargumentexception("view must not be null"); 
    } 
    if (!(params instanceof windowmanager.layoutparams)) { 
      throw new illegalargumentexception("params must be windowmanager.layoutparams"); 
    } 
 
    final windowmanager.layoutparams wparams = (windowmanager.layoutparams)params; 
 
    view.setlayoutparams(wparams); 
 
    synchronized (mlock) { 
      int index = findviewlocked(view, true); 
      viewrootimpl root = mroots.get(index); 
      mparams.remove(index); 
      mparams.add(index, wparams); 
      root.setlayoutparams(wparams, false); 
    } 
  } 

通过viewrootimpl的setlayoutparams更新viewrootimpl的layoutparams,接着scheduletraversals对view重新布局,包括测量,布局,重绘,此外它还会通过windowsession来更新window。这个过程由windowmanagerservice实现。这跟上面类似,就不再重复,到此window底层源码就分析完啦。

以上就是本文的全部内容,希望对大家的学习有所帮助。