如下添加一个Button到屏幕(100,300)的位置
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Button mButton;
private WindowManager.LayoutParams mLayoutParams;
private WindowManager mWindowManager;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkAndRequestPermissions();
}
private void showButton() {
mButton = new Button(this);
mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
mLayoutParams = new WindowManager.LayoutParams();
mButton.setText("test");
mButton.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
float rawX = event.getRawX();
float rawY = event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
mLayoutParams.x = (int) rawX;
mLayoutParams.y = (int) rawY;
mWindowManager.updateViewLayout(mButton, mLayoutParams);
break;
}
return false;
}
});
mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
mLayoutParams.x = 100;
mLayoutParams.y = 300;
mWindowManager.addView(mButton, mLayoutParams);
}
private void checkAndRequestPermissions() {
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1);
} else {
showButton();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "权限授予失败,无法开启悬浮窗", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "权限授予成功,开启悬浮窗", Toast.LENGTH_SHORT).show();
showButton();
}
}
}
}
Window有三种,由type决定层级,大的会覆盖在小的上面:
WindowManager继承ViewManager,但其本身是接口
public interface WindowManager extends 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);
}
具体实现类是WindowManagerImpl,传递给WindowManagerGlobal
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
WindowManagerGlobal中的如下域存储Window所对应的View、ViewRootImpl、LayoutParams以及调用removeView但还未被删除的对象
private final ArrayList mViews = new ArrayList();
private final ArrayList mRoots = new ArrayList();
private final ArrayList mParams =
new ArrayList();
private final ArraySet mDyingViews = new ArraySet();
WindowManagerGlobal中的addView()方法中,将View、ViewRootImpl、LayoutParams对象添加到列表
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow, int userId){
.....
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView, userId);
.....
}
ViewRootImpl中的setView()方法会调用requestLayout()异步刷新View
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
ViewRootImpl中的scheduleTraversals()通过postCallback()运行TraversalRunnable线程
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
ViewRootImpl中的doTraversal()会调用performTraversals()完成measure、layout、draw
void doTraversal() {
if (mTraversalScheduled) {
......
performTraversals();
......
}
}
ViewRootImpl中的setView()方法会通过mWindowSession的addToDisplayAsUser()添加Window
try {
......
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
setFrame(mTmpFrame);
} catch (RemoteException e) {
......
} finally {
......
}
mWindowSession类型为IWindowSession,其是Binder对象,添加过程是一次IPC调用,实现类是Session,内部通过WindowManagerService实现添加
@Override
public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
outInsetsState, outActiveControls, userId);
}
WindowManagerGlobal中的removeView()通过findViewLocked()找到待删除View的索引
@UnsupportedAppUsage
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 int findViewLocked(View view, boolean required) {
final int index = mViews.indexOf(view);
if (required && index < 0) {
throw new IllegalArgumentException("View=" + view + " not attached to window manager");
}
return index;
}
再调用WindowManagerGlobal中的removeViewLocked()
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (root != null) {
root.getImeFocusController().onWindowDismissed();
}
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
WindowManager提供了removeView() / removeViewImmediate() 异步 / 同步删除,具体实现在ViewRootImpl的die(),异步删除只发送消息,并将其添加到mDyingViews列表
boolean die(boolean immediate) {
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
if (!mIsDrawing) {
destroyHardwareRenderer();
} else {
Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
" window=" + this + ", title=" + mWindowAttributes.getTitle());
}
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
ViewRootImpl的doDie()调用dispatchDetachedFromWindow(),主要做
void doDie() {
checkThread();
if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {
return;
}
mRemoved = true;
if (mAdded) {
dispatchDetachedFromWindow();
}
......
}
WindowManagerGlobal.getInstance().doRemoveView(this);
}
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);
}
}
ActivityThread中的performLaunchActivity()通过ClassLoader创建activity,调用attach()传入运行过程中所需要的上下文环境变量
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
......
}......
try {
......
if (activity != null) {
......
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
......
}
......
}......
return activity;
}
Activity的attach()创建Window的唯一实现类PhoneWindow,将自身作为回调
final void attach(......) {
......
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
......
}
Activity在setContentView()将布局ID传给了PhoneWindow
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
PhoneWindow的setContentView()如下
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor(); // 1. 创建mDecor 、mContentParent
} 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); // 2.将Activity的布局加载到DecorView中的mContentParent(即R.id.content)
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged(); // 3. 回调Activity的onContentChanged()
}
mContentParentExplicitlySet = true;
}
PhoneWindow的installDecor()通过generateDecor()、generateLayout()创建mDecor 、mContentParent
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(-1);
......
} ......
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
......
}
}
protected DecorView generateDecor(int featureId) {
......
return new DecorView(context, featureId, this, getAttributes());
}
protected ViewGroup generateLayout(DecorView decor) {
......
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
......
return contentParent;
}
上面的ID_ANDROID_CONTENT,在Window中定义为R.id.content
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
在ActivityThread中的handleResumeActivity()
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
......
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
....
r.activity.makeVisible();
.....
}
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
在构造函数中创建Window
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
......
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
......
}
在setContentView()中通过Window将布局添加到DecorView
public void setContentView(@LayoutRes int layoutResID) {
mWindow.setContentView(layoutResID);
}
在show()中通过addView()将DecorView添加到Window,在dismiss()中通过removeViewImmediate()移除
public void show() {
......
onStart();
mDecor = mWindow.getDecorView();
......
mWindowManager.addView(mDecor, l);
......
}
Dialog必须采用Activity的Context,否则会报错,但可以通过设置type让其成为系统Window
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkAndRequestPermissions();
}
private void showDialog(){
Dialog dialog = new Dialog(getApplicationContext());
TextView textView = new TextView(this);
textView.setText("test");
dialog.setContentView(textView);
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
dialog.show();
}
private void checkAndRequestPermissions() {
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1);
} else {
showDialog();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "permission denied", Toast.LENGTH_SHORT).show();
} else {
showDialog();
}
}
}
}
Toast基于Handler,且内部有两类IPC
Toast的视图可采用系统默认,或通过setView()自定义View,其show()方法如下
public void show() {
......
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
final int displayId = mContext.getDisplayId();
try {
if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
if (mNextView != null) {
service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
} else {
ITransientNotificationCallback callback =
new CallbackBinder(mCallbacks, mHandler);
service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback);
}
} else {
service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
}
} catch (RemoteException e) {
}
}
NotificationManagerService的enqueueToast方法()会将Toast封装成ToastRecord再添加到mToastQueue,同一包名应用最多同时存在50个,防止一直弹Toast导致其他应用没有机会弹
private void enqueueToast(......) {
synchronized (mToastQueue) {
......
try {
ToastRecord record;
int index = indexOfToastLocked(pkg, token);
if (index >= 0) {
record = mToastQueue.get(index);
record.update(duration);
} else {
int count = 0;
final int N = mToastQueue.size();
for (int i = 0; i < N; i++) {
final ToastRecord r = mToastQueue.get(i);
if (r.pkg.equals(pkg)) {
count++;
if (count >= MAX_PACKAGE_NOTIFICATIONS) {
Slog.e(TAG, "Package has already posted " + count
+ " toasts. Not showing more. Package=" + pkg);
return;
}
}
}
......
record = getToastRecord(callingUid, callingPid, pkg, token, text, callback,
duration, windowToken, displayId, textCallback);
mToastQueue.add(record);
index = mToastQueue.size() - 1;
.....
}
if (index == 0) {
showNextToastLocked();
}
}.....
}
}
具体显示Toast的方法在showNextToastLocked(),调用ToastRecord的show()显示,并从mToastQueue移除
void showNextToastLocked() {
ToastRecord record = mToastQueue.get(0);
while (record != null) {
if (record.show()) {
scheduleDurationReachedLocked(record);
return;
}
int index = mToastQueue.indexOf(record);
if (index >= 0) {
mToastQueue.remove(index);
}
record = (mToastQueue.size() > 0) ? mToastQueue.get(0) : null;
}
}
scheduleDurationReachedLocked()通过Handler发送一个MESSAGE_DURATION_REACHED控制其显示时间,这里也可知LENGTH_LONG是3.5s,LENGTH_SHORT是2s
private void scheduleDurationReachedLocked(ToastRecord r) {
mHandler.removeCallbacksAndMessages(r);
Message m = Message.obtain(mHandler, MESSAGE_DURATION_REACHED, r);
int delay = r.getDuration() == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
delay = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
AccessibilityManager.FLAG_CONTENT_TEXT);
mHandler.sendMessageDelayed(m, delay);
}
收到MESSAGE_DURATION_REACHED后,通过cancelToastLocked()调用ToastRecord的hide(),再次调用showNextToastLocked()显示下一个Toast
void cancelToastLocked(int index) {
ToastRecord record = mToastQueue.get(index);
record.hide();
ToastRecord lastToast = mToastQueue.remove(index);
......
if (mToastQueue.size() > 0) {
showNextToastLocked();
}
}
ToastRecord的show()会回调到Toast中TN的handleShow(),将View添加到Window
public void handleShow(IBinder windowToken) {
......
if (mView != mNextView) {
......
mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
......
try {
mWM.addView(mView, mParams);
trySendAccessibilityEvent();
} catch (WindowManager.BadTokenException e) {
}
}
}