1、Activity的Window创建 Activity的启动最终是由ActivityThead的performLaunchActivity()完成启动,通过类加载器创建Activity对象。 在performLaunchActivity()中调用activity的attach方法 #ActivityThread private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ...... if (activity != null) { Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); 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); ...... } (1)#Activity final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor) { attachBaseContext(context); mFragments.attachHost(null /*parent*/); mWindow = new PhoneWindow(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); } if (info.uiOptions != 0) { mWindow.setUiOptions(info.uiOptions); } ...... } 创建Activity的Window对象PhoneWindow,设置回调接口。 Activity 实现了Window的Callback接口,所以window接收到外界状态改变会调用Activity中的方法。 常见的方法例如:onWndowFouseChanged、onAttachedToWindow、onDetachFromWindow、dispatchTouchEvent。 (2)setContentView #Activity public void setContentView(int layoutResID) { //调用Window的setContView方法 getWindow().setContentView(layoutResID); initActionBar(); } #PhoneWindow(Window实现类) @Override public void setContentView(int layoutResID) { if (mContentParent == null) { //创建DecorView 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 { //将acvivity的视图添加到DecorView的mContentParent中 mLayoutInflater .inflate(layoutResID, mContentParent); } mContentParent .requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); //通知Activity,通过onContentChanged方法 } } DecorView是一个FrameLayout,是Activity的顶级view,包含标题栏和内部栏。 通过generateDecor()方法创建DecorView。 #PhoneWindow protected DecorView generateDecor() { return new DecorView(getContext(), -1); } PhoneWindow 在 installDecor方法中调用generateLayout加载具体的布局文件到DecorView中。 #PhoneWindow protected ViewGroup generateLayout(DecorView decor) { ...... View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); mContentRoot = (ViewGroup) in; ViewGroup contentParent = (ViewGroup) findViewById(ID_ANDROID_CONTENT); if (contentParent == null) { throw new RuntimeException("Window couldn't find content container view"); } ...... } 如果没有DecorView,则调用installDecor创建DecorView 调用generateLayout,将View添加到DecorView的mConentParent中。 回调Activity的onContentChanged的方法通知Activity视图已经改变。 (因为Activity是实现了Window的Callback接口,当布局文件添加到DecorView上时,会回调Activity的onContentChanged方法) 总结:通过前三步DecorView初始化完成,Activity的布局文件添加到了DecorView中。 但是DecorView还没有被WindowManager正式添加到Window中 (3)在ActivityThread的handleResumeActivity中,调用Actiivty的onResume方法,调用Activity的makeVisible()。DecorView 完成添加和显示的过程。 #Activity void makeVisible() { if (!mWindowAdded ) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); //将DecorView添加到Window上 mWindowAdded = true; } mDecor .setVisibility(View.VISIBLE); } Activity的Window创建过程: .......ActivityThread(调用performLaunchActivity)—>Activity(调用attach,创建Window)—>Activity(调用setContentView,创建DecorView,将布局文件添加到DecorView上)—>ActivityThread(调用hanldeResumeActivity)—>Activity(调用onResume,makeVisible)—>调用WindowManager.addView将DecorView添加到Window上并显示 2、Dialog的Window的创建 (1)和Activity一样,先完成Window的创建,创建后的对象实际是PhoneWindow #Dialog Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { … mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); final Window w = new PhoneWindow(mContext); mWindow = w; w.setCallback(this); w.setOnWindowDismissedCallback(this); w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); mListenersHandler = new ListenersHandler(this); } (2)初始化DecorView,并将Dialog添加到DecorView中。 调用setContentView,内部也是调用PhoneWindow的setContentView方法完成。 public void setContentView(@LayoutRes int layoutResID) { mWindow.setContentView(layoutResID); } (3)在show方法中通过WindowManager将DecorView添加到Window中并显示。 (当Dialog被关闭时,通过WindowManager移除DecorView) #Dialog public void show() { ..... try { mWindowManager.addView(mDecor, l); mShowing = true; sendShowMessage(); } finally { } } #Dialog dismiss调用dismissDialog方法 void dismissDialog() { if (mDecor == null || !mShowing) { return; } if (mWindow.isDestroyed()) { Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!"); return; } try { mWindowManager.removeViewImmediate(mDecor); } finally { if (mActionMode != null) { mActionMode.finish(); } mDecor = null; mWindow.closeAllPanels(); onStop(); mShowing = false; sendDismissMessage(); } } 注:Dialog必须引用Activity的context,因为需要token,token只有Activity中拥有。系统Window不需要token Dialog的Window创建过程: Dialog(构造方法中创建PhoneWindow)—>Dialog(调用setContentView)—>PhoneWindow(调用setContentView,创建DecorView,将视图添加到DecorView中)—>Dialog(调用show方法,WindowManager.addView将DecorView添加到Window并显示) 3、Toast的window的创建 Toast属于系统Window,它内部的视图由两种方式指定:一种是系统默认的样式,另一种是通过setView方法来指定一个自定义的View。 在Toast的内部有两类IPC过程,第一类是Toast访问NotificationManagerService,第二类是NotificationManagerService回调Toast里的TN接口。 #Toast: 显示 public void show() { if (mNextView == null) { throw new RuntimeException("setView must have been called"); } INotificationManager service = getService(); String pkg = mContext .getOpPackageName(); TN tn = mTN ; tn.mNextView = mNextView; try { service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty } } 取消 public void cancel() { mTN.hide(); try { getService().cancelToast( mContext.getPackageName(), mTN); } catch (RemoteException e) { // Empty } } Toast的显示和隐藏是IPC过程,都需要NotificationManagerService(NMS)来实现,因为NMS运行在系统进程中,只能通过远程调用的方式显示和隐藏Toast。 在Toast和NMS进行IPC过程时,NMS会跨进程回调Toast中的TN类中的方法,TN类是一个Binder类,运行在Binder线程池中,所以需要通过Handler将其切换到当前发送Toast请求所在的线程,所以Toast无法在没有Looper的线程中弹出。 #NotificationManagerService private final IBinder mService = new INotificationManager.Stub() { @Override public void enqueueToast(String pkg, ITransientNotification callback, int duration) { …… synchronized (mToastQueue) { int callingPid = Binder. getCallingPid(); long callingId = Binder. clearCallingIdentity(); try { ToastRecord record; int index = indexOfToastLocked(pkg, callback); // If it's already in the queue, we update it in place, we don't // move it to the end of the queue. if (index >= 0) { record = mToastQueue.get(index); record.update(duration); } else { // Limit the number of toasts that any given package except the android // package can enqueue. Prevents DOS attacks and deals with leaks. if (!isSystemToast) { 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 = new ToastRecord(callingPid, pkg, callback, duration); mToastQueue.add(record); index = mToastQueue.size() - 1; keepProcessAliveLocked(callingPid); } if (index == 0) { showNextToastLocked(); //显示当前的Toast } } finally { Binder. restoreCallingIdentity(callingId); } } ..... } @Override public void cancelToast(String pkg, ITransientNotification callback) { ..... synchronized (mToastQueue) { long callingId = Binder. clearCallingIdentity(); try { int index = indexOfToastLocked(pkg, callback); if (index >= 0) { cancelToastLocked(index); } else { Slog.w( TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback); } } finally { Binder. restoreCallingIdentity(callingId); } } } (1)enquequeToast将Toast封装成ToastRecord,并放入mToastQueue的列队中。对于非系统应用来说,mToastQueue最多能同时存在50个ToastRecord。因为如果某个应用弹出太多的Toast会导致其他应用没有机会弹出Toast。 #NotificationManagerService void showNextToastLocked() { ToastRecord record = mToastQueue.get(0); while (record != null) { if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); try { record.callback.show(); scheduleTimeoutLocked(record); return; } catch (RemoteException e) { Slog.w(TAG, "Object died trying to show notification " + record.callback + " in package " + record.pkg); // remove it from the list and let the process die int index = mToastQueue.indexOf(record); if (index >= 0) { mToastQueue.remove(index); } keepProcessAliveLocked(record.pid); if (mToastQueue.size() > 0) { record = mToastQueue.get(0); } else { record = null; } } } } (2)NMS内部通过调用showNextToastLocked显示当前Toast。Toast由ToastRecord的callback完成,callback类是ITransientNotification,具体实现是Toast中的TN对象,是一个远程的Binder。所以最终被调用的TN会运行 在发起Toast应用的Binder线程池中。 (3)Toast显示完后,NMS调用scheduleTimeoutLocked发送延时消息,然后NotificationManagerService调用cancelToastLocked掩藏Toast,并从队列中删除。 Toast的隐藏也是通过ToastRecord的callback完成。也是一次IPC过程。 (4)Toast的显示和隐藏是通过Binder(TN)调用其中的show和hide方法,最后show和hide内部调用了TN中的handleShow和handleHide方法,将Toast视图添加到window上,或者在window上移除 #Toast public void handleShow() { ..... mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); ...... mWM.addView(mView, mParams); ...... } public void handleHide() { if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView); if (mView != null) { // note: checking parent() just to make sure the view has // been added... i have seen cases where we get here when // the view isn't yet added, so let's try not to crash. if (mView.getParent() != null) { if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this); mWM.removeView(mView); } mView = null; } }
Toast的Window创建过程:
Toast(调用show)—>INotificationManager—>NotificationManagerService(调用enquequeToast,添加到队列中)—>NotificationManagerService(调用showNextToastLocked显示Toast)—>Toast.TN(跨进程调用show方法)—>Toast.TN(调用handleShow,将Toast添加到Window中)