Activity的启动最终会通过ActivityThread#performLaunchActivity()来完成。
ActivityThread#performLaunchActivity()
PRivate Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { //... //通过类加载器创建Activity实例 java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); //... application app = r.packageInfo.makeApplication(false, mInstrumentation); Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); //关联运行过程中所依赖的一系列上下文环境变量 activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.voiceInteractor); mActivities.put(r.token, r); return activity;}在attach方法里,系统会创建Activity所属的Window对象并为其设置回调接口。
Activity#attach()
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, IVoiceInteractor voiceInteractor) { attachBaseContext(context); mFragments.attachActivity(this, mContainer, null); //创建Window对象 mWindow = PolicyManager.makeNewWindow(this); //设置回调 比如我们熟悉的onAttachedToWindow、onDetachedFromWindow、dispatchTouchEvent //[重要] 当Window接收到外界的状态改变时就会调用Activity实现的回调 mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); //赋值 if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); } if (info.uiOptions != 0) { mWindow.setUiOptions(info.uiOptions); } mUiThread = Thread.currentThread(); mMainThread = aThread; mInstrumentation = instr; mToken = token; mIdent = ident; mApplication = application; mIntent = intent; mComponent = intent.getComponent(); mActivityInfo = info; mTitle = title; mParent = parent; mEmbeddedID = id; mLastNonConfigurationInstances = lastNonConfigurationInstances; //... //设置WindowManager mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); if (mParent != null) { mWindow.setContainer(mParent.getWindow()); } mWindowManager = mWindow.getWindowManager(); mCurrentConfig = config;}再来看 PolicyManager.makeNewWindow(this),PilicyManager的真正实现类是Policy类。 位于source/frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java
public Window makeNewWindow(Context context) { return new PhoneWindow(context);}可以发现Window的实现类确实是PhoneWindow。
Window初始完毕后,再来看Activity的视图是如何依附在Window上的。Activity的视图通过setContent方法提供。
public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar();}可以发现,Activity的setContentView将具体实现交由Window处理。
phoneWindow#setContentView()
@Overridepublic 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. //1.创建DecorView (如果没有创建的话) 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); //2.将View添加到DecorView的mContentParent中 mLayoutInflater.inflate(layoutResID, mContentParent); } //3.回调Activity的onContentChanged()通知Activity视图已经发生改变 final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); }}经过setContentView方法,DecorView已经被创建并初始化完毕,Activity的布局文件也已经成功添加到DecorView的mContentParent中,但这个时候DecorView还没有被WindowManager添加到Window中。 (在添加之前)
Dialog的Window创建过程和Activity类似
Dialog#构造方法
Dialog(Context context, int theme, boolean createContextThemeWrapper) { //... mContext = context; mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); //创建Window对象 Window w = PolicyManager.makeNewWindow(mContext); mWindow = w; w.setCallback(this); w.setOnWindowDismissedCallback(this); w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); mListenersHandler = new ListenersHandler(this);}再来看Dialog#setContentView()
public void setContentView(int layoutResID) { mWindow.setContentView(layoutResID);}同样是交由Window处理。
当Dialog dismiss时,会通过WindowManager来移除DecorView
@Overridepublic void dismiss() { if (Looper.myLooper() == mHandler.getLooper()) { dismissDialog(); } else { mHandler.post(mDismissAction); }}void dismissDialog() { //... //移除DecorView mWindowManager.removeViewImmediate(mDecor); //...}普通Dialog需要使用Activity的Content 因为Window需要应用token,而应用token一般只有Activity才拥有。 系统Window比较特殊,它可以不需要token。
Toast属于系统Window Toast内部有两类ipC过程
Toast访问NotifationManagerServiceNotificationManagerService回调Toast的TN接口Toast提供了show和cancel分别用于显示和隐藏Toast [都是IPC过程]
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 }}主要来看enqueueToast()
//参数一:当前应用的包名//参数二:远程回调//参数三:Toast的时长//enqueueToast首先将Toast封装为ToastRecord对象并将其添加到一个名为mToastQueue的队列中service.enqueueToast(pkg, tn, mDuration);当ToastRecord被添加到mToastQueue中后,Inotifacationmanager就会通过showNextToastLacked方法来显示当前的Toast。
NotificationManagerService#showNextToastLocked()
void showNextToastLocked() { //获取下一个ToastRecord ToastRecord record = mToastQueue.get(0); while (record != null) { if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); try { //record.callback就是Toast.java类中的TN (Binder) 对象 record.callback.show(); //发送延迟消息来移除toast //scheduleTimeoutLocked -> mHandler.sendMessageDelayed -> cancelToastLocked -> record.callback.hide(); 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; } } }}可以发现,Toast的显示和移除都是通过Toast的TN类(Binder对象)来完成的。
Toast内部类TN
private static class TN extends ITransientNotification.Stub { final Runnable mShow = new Runnable() { @Override public void run() { handleShow(); } }; final Runnable mHide = new Runnable() { @Override public void run() { handleHide(); // Don't do this in handleHide() because it is also invoked by handleShow() mNextView = null; } }; @Override public void show() { if (localLOGV) Log.v(TAG, "SHOW: " + this); mHandler.post(mShow); } @Override public void hide() { if (localLOGV) Log.v(TAG, "HIDE: " + this); mHandler.post(mHide); } //...}真正的显示
public void handleShow() { //... mWM = (WindowManager)context.getSystemService //... if (mView.getParent() != null) { mWM.removeView(mView); } mWM.addView(mView, mParams); //... }}真正的隐藏
public void handleHide() { if (mView != null) { if (mView.getParent() != null) { mWM.removeView(mView); } mView = null; }}PhoneWindow伪代码
新闻热点
疑难解答