Android 使用WindowManager实现悬浮窗及源码解析
2017-08-16 16:48
886 查看
本文已授权微信公众号《鸿洋》原创首发,转载请务必注明出处。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201908/15/f0585c682fd894cc974ccbf6ff17b56a)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201908/15/9b920e564293e7f8b53e6ef6577e580a)
一个
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
[/code]
在设置各种属性之后,直接向
2
3
4
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
[/code]
现在点击
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
[/code]
这里在
2
3
4
5
6
7
8
9
10
11
12
13
14
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[/code]
至此为止。悬浮窗已经显示出来,点击拖动事件也已经搞定。虽然和360悬浮窗差距还蛮大,但是剩下的只剩具体实现。像
源码位置:frameworks/base/core/Java/Android/app/Service.java
Service#getApplication()
2
3
![](https://oscdn.geek-share.com/Uploads/Images/Content/201702/b303364dc8d788bc830b1e2a21d6c01b.png)
1
2
3
[/code]
首先获取应用程序的
源码位置:frameworks/base/core/java/android/content/ContextWrapper.java
ContextWrapper#getSystemServiceName()
2
3
4
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
[/code]
成员变量
源码位置:frameworks/base/core/java/Android/content/Context.java
Context#getSystemServiceName()
2
3
4
5
6
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
[/code]
源码位置:frameworks/base/core/java/android/app/SystemServiceRegistry.java
2
3
4
5
6
7
8
9
10
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
[/code]
这里返回了
WindowManagerImpl#addView()
2
3
4
5
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
[/code]
首先验证
源码位置:frameworks/base/core/Java/Android/view/WindowManagerGlobal.java
WindowManagerGlobal#addView()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[/code]
给我们的
源码位置:frameworks/base/core/Java/Android/view/ViewRootImpl.java
ViewRootImpl#setView()
2
3
4
5
6
7
8
9
10
11
12
13
14
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[/code]
2
3
4
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
[/code]
跟进。
源码位置:frameworks/base/core/Java/Android/view/WindowManagerGlobal.java
WindowManagerGlobal#getWindowSession()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[/code]
这里
源码位置:frameworks/base/services/java/com/android/server/wm/WindowManagerService.java
WindowManagerService#getWindowSession()
2
3
4
5
6
7
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
[/code]
返回一个
源码位置:frameworks/base/services/java/com/android/server/wm/Session.java
Session#addToDisplay()
2
3
4
5
6
7
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
[/code]
这里的
源码位置:frameworks/base/services/java/com/android/server/wm/WindowManagerService.java
WindowManagerService#addWindow()
2
3
4
5
6
7
8
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
[/code]
源码位置:frameworks/base/core/Java/Android/view/WindowManagerGlobal.java
WindowManagerGlobal#getWindowSession()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[/code]
将传入的
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[/code]
这个过程要稍微麻烦点,首先调用
源码位置:frameworks/base/core/java/android/view/ViewRootImpl.java
ViewRootImpl#die()
2
3
4
5
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
[/code]
这里的参数
2
3
4
5
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
[/code]
跟进。
2
3
4
5
6
7
8
9
10
11
12
13
14
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[/code]
经过一圈效验最终还是回到
至此,本文已经全部结束,感谢耐心阅读到最后~
更多Framework源码解析,请移步 Framework源码解析系列[目录]
使用
效果预览
Demo结构
一个
Activity、一个
Service和两个布局文件。布局十分简单,这里就不贴了,大概描述下。
activity_main.xml中俩按钮,
layout_window.xml中一个
TextView。ok,首先看下
MainActivity。
MainActivity中只有俩按钮,点击启动
WindowService,点击停止
WindowService。没啥好说的。直接看
WindowService。
/** * @author CSDN 一口仨馍 */ public class WindowService extends Service { private final String TAG = this.getClass().getSimpleName(); private WindowManager.LayoutParams wmParams; private WindowManager mWindowManager; private View mWindowView; private TextView mPercentTv; private int mStartX; private int mStartY; private int mEndX; private int mEndY; @Override public void onCreate() { super.onCreate(); Log.i(TAG, "onCreate"); initWindowParams(); initView(); addWindowView2Window(); initClick(); } private void initWindowParams() { mWindowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE); wmParams = new WindowManager.LayoutParams(); // 更多type:https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#TYPE_PHONE wmParams.type = WindowManager.LayoutParams.TYPE_PHONE; wmParams.format = PixelFormat.TRANSLUCENT; // 更多falgs:https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_NOT_FOCUSABLE wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; wmParams.gravity = Gravity.LEFT | Gravity.TOP; wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT; wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT; } private void initView() { mWindowView = LayoutInflater.from(getApplication()).inflate(R.layout.layout_window, null); mPercentTv = (TextView) mWindowView.findViewById(R.id.percentTv); } private void addWindowView2Window() { mWindowManager.addView(mWindowView, wmParams); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); if (mWindowView != null) { //移除悬浮窗口 Log.i(TAG, "removeView"); mWindowManager.removeView(mWindowView); } Log.i(TAG, "onDestroy"); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
[/code]
在设置各种属性之后,直接向
WindowManager中添加
mWindowView(也就是我们自己的布局
layout_window.xml)。在此之前需要在
AndroidManifest。xml中注册
Service和添加相应的限权。
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.GET_TASKS" /> <service android:name=".WindowService"/>1
2
3
4
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
[/code]
现在点击
startBtn,桌面上已经可以出现悬浮窗。但是没有拖动啦点击啦这些动作。小意思,重写点击事件。根据拖动距离,判断是点击还是滑动。由于
onTouchEvent()的优先级比
onClick高,拖动时在需要的拦截的地方,
return true就ok了。具体如下:
private void initClick() { mPercentTv.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mStartX = (int) event.getRawX(); mStartY = (int) event.getRawY(); break; case MotionEvent.ACTION_MOVE: mEndX = (int) event.getRawX(); mEndY = (int) event.getRawY(); if (needIntercept()) { //getRawX是触摸位置相对于屏幕的坐标,getX是相对于按钮的坐标 wmParams.x = (int) event.getRawX() - mWindowView.getMeasuredWidth() / 2; wmParams.y = (int) event.getRawY() - mWindowView.getMeasuredHeight() / 2; mWindowManager.updateViewLayout(mWindowView, wmParams); return true; } break; case MotionEvent.ACTION_UP: if (needIntercept()) { return true; } break; default: break; } return false; } }); mPercentTv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (isAppAtBackground(WindowService.this)) { Intent intent = new Intent(WindowService.this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } } }); } /** * 是否拦截 * @return true:拦截;false:不拦截. */ private boolean needIntercept() { if (Math.abs(mStartX - mEndX) > 30 || Math.abs(mStartY - mEndY) > 30) { return true; } return false; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
[/code]
这里在
onClick中进行了一个程序前后台的判断操作,方法如下:
/** *判断当前应用程序处于前台还是后台 */ private boolean isAppAtBackground(final Context context) { ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1); if (!tasks.isEmpty()) { ComponentName topActivity = tasks.get(0).topActivity; if (!topActivity.getPackageName().equals(context.getPackageName())) { return true; } } return false; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[/code]
至此为止。悬浮窗已经显示出来,点击拖动事件也已经搞定。虽然和360悬浮窗差距还蛮大,但是剩下的只剩具体实现。像
addView(),
removeView()和动画等等,这里就不再具体实现。本着知其然知其所以然的精神,下文是整个流程的源码解析。
源码解析
初始化解析
在WindowService中通过
getApplication().getSystemService(getApplication().WINDOW_SERVICE)获取到一个
WindowManager,姑且称这么过程为初始化。
源码位置:frameworks/base/core/Java/Android/app/Service.java
Service#getApplication()
public final Application getApplication() { return mApplication; }1
2
3
![](https://oscdn.geek-share.com/Uploads/Images/Content/201702/b303364dc8d788bc830b1e2a21d6c01b.png)
1
2
3
[/code]
首先获取应用程序的
Application对象,然后调用
Application#getSystemService()。但是,在
Application中并没有
getSystemService()这个方法,那么这个方法肯定在父类中或在某个接口中。追踪发现在其父类
ContextWrapper中。跟进。
源码位置:frameworks/base/core/java/android/content/ContextWrapper.java
ContextWrapper#getSystemServiceName()
@Override public String getSystemServiceName(Class<?> serviceClass) { return mBase.getSystemServiceName(serviceClass); }1
2
3
4
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
[/code]
成员变量
mBase为
Context对象,跟进。
源码位置:frameworks/base/core/java/Android/content/Context.java
Context#getSystemServiceName()
public final <T> T getSystemService(Class<T> serviceClass) { String serviceName = getSystemServiceName(serviceClass); return serviceName != null ? (T)getSystemService(serviceName) : null; } public abstract Object getSystemService(@ServiceName @NonNull String name);1
2
3
4
5
6
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
[/code]
Context的实现类是
ContextImpl,接下来获取服务的方式和Android XML布局文件解析过程源码解析中一样,为了节省篇幅,直接进入到
SystemServiceRegistry中的静态代码快
源码位置:frameworks/base/core/java/android/app/SystemServiceRegistry.java
static { ... registerService(Context.WINDOW_SERVICE, WindowManager.class, new CachedServiceFetcher<WindowManager>() { @Override public WindowManager createService(ContextImpl ctx) { return new WindowManagerImpl(ctx.getDisplay()); }}); ... }1
2
3
4
5
6
7
8
9
10
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
[/code]
这里返回了
WindowManagerImpl对象,不过最后强转称了父类
WindowManager。目前为止,已经获取到了
WindowManager对象,各种参数也已经初始化完成。接下来只有一行
WindowManager.addView()。真可谓简单到极致。极度的简单往往是繁琐的假象。接下来,才是本文真正的开始。
WindowManager.addView()解析
源码位置:frameworks/base/core/Java/Android/view/WindowManagerImpl.javaWindowManagerImpl#addView()
@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mDisplay, mParentWindow); }1
2
3
4
5
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
[/code]
首先验证
Token,这里不作为重点。接下来还有个
addView()跟进。
源码位置:frameworks/base/core/Java/Android/view/WindowManagerGlobal.java
WindowManagerGlobal#addView()
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { // 参数效验 ... ViewRootImpl root; synchronized (mLock) { // 查找缓存,类型效验 ... root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); } try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // who care? } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[/code]
给我们的
View设置参数并添加到
mRoots中,由
WindowManagerGlobal进行管理,之后的事情就和View没什么关系了。接着调用
ViewRootImpl#setView()。跟进。下面是个关键点,同学们注意力要集中。
源码位置:frameworks/base/core/Java/Android/view/ViewRootImpl.java
ViewRootImpl#setView()
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { // 各种属性读取,赋值及效验 ... try { ... res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); } catch (RemoteException e) { ... }1
2
3
4
5
6
7
8
9
10
11
12
13
14
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[/code]
mWindowSession是
IWindowSession对象。在创建
ViewRootImpl对象时被实例化。
public ViewRootImpl(Context context, Display display) { mWindowSession = WindowManagerGlobal.getWindowSession(); ... }1
2
3
4
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
[/code]
跟进。
源码位置:frameworks/base/core/Java/Android/view/WindowManagerGlobal.java
WindowManagerGlobal#getWindowSession()
public static IWindowSession getWindowSession() { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { InputMethodManager imm = InputMethodManager.getInstance(); IWindowManager windowManager = getWindowManagerService(); sWindowSession = windowManager.openSession( new IWindowSessionCallback.Stub() { @Override public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); } }, imm.getClient(), imm.getInputContext()); } catch (RemoteException e) { Log.e(TAG, "Failed to open window session", e); } } return sWindowSession; } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[/code]
这里
getWindowManagerService()通过
AIDL返回
WindowManagerService实例。之后调用
WindowManagerService#openSession()。跟进。
源码位置:frameworks/base/services/java/com/android/server/wm/WindowManagerService.java
WindowManagerService#getWindowSession()
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client, IInputContext inputContext) { if (client == null) throw new IllegalArgumentException("null client"); if (inputContext == null) throw new IllegalArgumentException("null inputContext"); Session session = new Session(this, callback, client, inputContext); return session; }1
2
3
4
5
6
7
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
[/code]
返回一个
Session对象。也就是说在
ViewRootImpl#setView()中调用的是
Session#addToDisplay()。跟进。
源码位置:frameworks/base/services/java/com/android/server/wm/Session.java
Session#addToDisplay()
@Override public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outStableInsets, outOutsets, outInputChannel); }1
2
3
4
5
6
7
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
[/code]
这里的
mService是个
WindowManagerService对象,也就是说最后调用的是
WindowManagerService#addWindow()。
源码位置:frameworks/base/services/java/com/android/server/wm/WindowManagerService.java
WindowManagerService#addWindow()
public int addWindow(...) { ... WindowState win = new WindowState(this, session, client, token, attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent); win.attach(); mWindowMap.put(client.asBinder(), win); ... }1
2
3
4
5
6
7
8
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
[/code]
mWindowMap是个
Map实例,将
WindowManager添加进
WindowManagerService统一管理。至此,整个添加视图操作解析完毕。
WindowManager.updateViewLayout()解析
和addView()过程一样,最终会进入到
WindowManagerGlobal#updateViewLayout()。
源码位置:frameworks/base/core/Java/Android/view/WindowManagerGlobal.java
WindowManagerGlobal#getWindowSession()
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); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[/code]
将传入的
View设置参数之后,更新
mRoot中View的参数。没撒好说的。next one。
WindowManager.removeView()解析
和上两个过程一样,最终会进入到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); } } private void removeViewLocked(int index, boolean immediate) { ViewRootImpl root = mRoots.get(index); View view = root.getView(); ... boolean deferred = root.die(immediate); if (view != null) { view.assignParent(null); if (deferred) { mDyingViews.add(view); } } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[/code]
这个过程要稍微麻烦点,首先调用
root.die(),接着将
View添加进
mDyingViews。跟进。
源码位置:frameworks/base/core/java/android/view/ViewRootImpl.java
ViewRootImpl#die()
boolean die(boolean immediate) { ... mHandler.sendEmptyMessage(MSG_DIE); return true; }1
2
3
4
5
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
[/code]
这里的参数
immediate默认为
false,也就是说这里只是发送了一个
what=MSG_DIE的空消息。
ViewRootHandler收到这条消息会执行
doDie()。
void doDie() { checkThread(); ... WindowManagerGlobal.getInstance().doRemoveView(this); }1
2
3
4
5
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
[/code]
跟进。
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(); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
![](https://oscdn.geek-share.com/Uploads/Images/Content/201701/9cc493f1e15b23d0f4eaea0d0f8b35d0.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[/code]
经过一圈效验最终还是回到
WindowManagerGlobal中移除
View。
至此,本文已经全部结束,感谢耐心阅读到最后~
更多Framework源码解析,请移步 Framework源码解析系列[目录]
相关文章推荐
- Android 使用WindowManager实现悬浮窗及源码解析
- Android 使用WindowManager实现悬浮窗及源码解析
- 使用Android自带Gallery组件实现CoverFlow,源码+解析
- 使用Android自带Gallery组件实现CoverFlow,源码+解析
- 使用Android自带Gallery组件实现CoverFlow,源码+解析
- 使用Android自带Gallery组件实现CoverFlow,源码+解析
- 在Android中使用WindowManager实现悬浮窗口
- 在Android中使用WindowManager实现悬浮窗口
- 【转】android源码分析之windowmanager (android悬浮窗口的实现)
- Android -- 使用WindowManager实现悬浮框效果
- 【转】android源码分析之windowmanager (android悬浮窗口的实现)
- 使用Android自带Gallery组件实现CoverFlow,源码+解析
- 在Android中使用WindowManager实现悬浮窗口
- 在Android中使用WindowManager实现悬浮窗口
- Android中通过使用WindowManager实现悬浮窗口
- 在Android中使用WindowManager实现悬浮窗口
- Android中使用WindowManager实现悬浮窗口
- 在Android中使用WindowManager实现悬浮窗口
- android源码分析之windowmanager (android悬浮窗口的实现)
- 在Android中使用WindowManager实现悬浮窗口