一、window和windowmanager
window是一个抽象类,它的具体实现是phonewindow,创建一个window很简单,只需要创建一个windowmanager即可,window具体实现在windowmanagerservice中,windowmanager和windowmanagerservice的交互是一个ipc的过程。
下面是用windowmanager的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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,看下它的代码:
1
2
3
|
public window makenewwindow(context context) {
return new phonewindow(context);
}
|
到此window创建完成。
下面分析view是如何附属到window上的。看activity的setcontentview方法。
1
2
3
4
|
public void setcontentview( int layoutresid) {
getwindow().setcontentview(layoutresid);
initwindowdecoractionbar();
}
|
两部分,设置内容和设置actionbar。window的具体实现是phonewindow,看它的setcontent。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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 ,代码如下:
1
2
3
4
5
6
7
8
|
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).检查参数是否合法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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添加到列表中。
1
2
3
4
5
6
7
|
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:
1
2
3
4
5
6
7
|
public void requestlayout() {
if (!mhandlinglayoutinlayoutrequest) {
checkthread();
mlayoutrequested = true ;
scheduletraversals();
}
}
|
接下来通过windowsession来完成window添加过程,windowsession是一个binder对象,真正的实现类是 session,window的添加是一次ipc调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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的添加。
1
2
3
4
5
6
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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来做进一步的删除。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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来完成。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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 + ", java" id="highlighter_694217">
主要做四件事:
3.调用dispathdetachedfromwindow,在内部会调用ondetachedfromwindow()和ondetachedfromwindowinternal()。当view移除时会调用ondetachedfromwindow,它用于作一些资源回收。
3.更新window
通过viewrootimpl的setlayoutparams更新viewrootimpl的layoutparams,接着scheduletraversals对view重新布局,包括测量,布局,重绘,此外它还会通过windowsession来更新window。这个过程由windowmanagerservice实现。这跟上面类似,就不再重复,到此window底层源码就分析完啦。 以上就是本文的全部内容,希望对大家的学习有所帮助。 延伸 · 阅读
精彩推荐
|