Android源码笔记--应用窗口创建

应用窗口的的创建

           这一节主要来记录Android应用窗口的创建,大体步骤如下:

    1  每个应用窗口都对应一个Activity对象。因此创建应用窗口首先需要创建一个Activity对象。而每个客户端进程都对应一个ActivityThread类,启动Activity的任务最终交由ActivityThread完成。启动某个Activity的代码本质是构造一个Activity对象。

ActivityThread.java

Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

分析:使用ClassLoader从程序文件中装载指定的Activity对应的class文件。

    2    构造好指定的Activity对象,接着调用Activity的attach()方法,为Activity设置内部变量,这些变量为以后Activity的调度做准备。

ActivityThread.java
  private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  ...  
  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);
  ...
 }

分析:appContext:Activity的BaseContext,Activity本质是一个Context,而同时Activity继承于ContextWrapper,该类中需要一个真正的Context对象,就是这个appContext,该对象使用new ContextImpl()方法创建;  
     this:ActivityThread; 
     r.token:r是ActivityRecord对象,内部变量token的含义是Ams中的一个HistoryRecord对象。
     r.parent:一个Activity可以有一个父Activity.
       除了为重要变量赋值,另一个就是为Activity创建Window对象,通过调用PhoneWindow的构造方法来完成。代码归根结底是创建了一个PhoneWindow对象。注意:创建好Window对象后,将其赋值给Activity的内部变量mWindow并设置该Window的Callback接口为当前的Activity对象,这就是为什么用户消息能够传递到Activity中的原因。

Activity.java
 
  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,
            Window window) {
			...
		  mWindow = new PhoneWindow(this, window);
          mWindow.setWindowControllerCallback(this);
          mWindow.setCallback(this);
          mWindow.setOnWindowDismissedCallback(this);
          mWindow.getLayoutInflater().setPrivateFactory(this);
			...
			}

  3   创建好Window对象后,需要给Window对象的mWindowManager变量赋值,该变量的类型是WindowManager类, 每一个Window中内部都有一个WindowManager(WM)对象,WM是一个接口,它的实现类是WindowMangerImpl类。

 Activity.java

  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,
            Window window) {
			...
 //先将WindowManager set进去
  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());
        }
	//将WindowManager get出来	
        mWindowManager = mWindow.getWindowManager();
		...
}

     分析:setWindowManager()方法的第一个参数如果为null,在Window类该方法实现中,在其内部就会创建一个LocalWindowManager对象, 第二个参数是Ams中Activity对应的ActivityRecord的Binder引用,该变量将作为Window中的mAppToken的值。

Window.java
 
 /**
     * Set the window manager for use by this Window to, for example,
     * display panels.  This is <em>not</em> used for displaying the
     * Window itself -- that must be done by the client.
     *
     * @param wm The window manager for adding new windows.
     */
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }	 
 
  public WindowManager getWindowManager() {
        return mWindowManager;
    }

	WindowManagerImpl.java
	
	private final Context mContext;
    private final Window mParentWindow;
	
	 public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }
	
	 private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }
	

    4 配置好了Activity和Window对象后,接下来就需要给该窗口中添加真正的显示元素View或ViewGroup。这是从performLaunchActivity()内部调用callActivityOnCreate()开始的,并会辗转调用到Activity的onCreate()方法。注意:给Activity添加界面是在onCreate()方法中调用setContentView()方法,该方法实际又调用了其所对应的Window对象的setContentView()方法。如下:

ActivityThread.java
	 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
	  ...	
	  if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
	  ...
	}
	
	
	Instrumentation.java
	
	 public void callActivityOnCreate(Activity activity, Bundle icicle,
            PersistableBundle persistentState) {
       ...
        activity.performCreate(icicle, persistentState);
       ...
    }
	}
	
	Activity.java
	
	 final void performCreate(Bundle icicle) {
      ...
        onCreate(icicle);
      ...
    }
	
	 protected void onCreate(@Nullable Bundle savedInstanceState) {
		...
	 }

      分析:大家都知道给Activity添加界面是在onCreate()方法中调用setContentView()方法,该方法实际又调用了其所对应的Window对象的setContentView()方法。

Activity.java
	  public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
	
	
Window.java

public abstract void setContentView(@LayoutRes int layoutResID);

PhoneWindow.java
	
	@Override
    public void setContentView(int layoutResID) {
       ...
        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);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
       ...
    }

分析:首先调用installDecor()方法为Window类安装一个窗口修饰,所谓窗口修饰就是界面上常见的标题栏,程序中指定的layout.xml界面将被包含在一个窗口修饰中,称为窗口内容。窗口修饰也是一个ViewGroup,窗口修饰及其内部的窗口内容加起来就是我们所说的窗口,或叫做Window的界面。
      再看installDecor()方法。如下:

 private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
		
		 if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);

            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeOptionalFitsSystemWindows();
			
		}

     分析:1) 使用generateDecor()创建一个DecorView对象,并赋值给mDecor变量。该变量并不完全等同于窗口修饰,窗口修饰是mDecor内部的唯一一个子视图。
          2) 根据用户指定的参数选择不同的窗口修饰,并把该窗口修饰作为mDecor的子窗口,这是在generateLayout()中调用mDecor.addView()完成的。          
          3)给mContentParent变量赋值,其值是通过调用ViewGroup contentParent=
 (ViewGroup)findViewById(ID_ANDROID_CONTENT)获得的ID_ANDROID_CONTENT正是id=content的FrameLayout.不同的窗口装饰的区别不大,共同的特点是其内部必须包含一个id=content的FrameLayout.因为窗口正是被包含在FrameLayout。     
          4)安装完窗口修饰后,通过setContentView()中调用inflate()来将layout.xml文件添加到窗口修饰中。
         5)最后,回调cb.onContentChanged()方法,通知应用程序窗口发生改变,cb是Activity,因为Activity实现了Window.Callback接口,并且在attach()方法中将自身作为Window对象的Callback接口实现。
           注意:所谓的“根据用户指定的参数”中“用户指定”有两个地方可以指定。第一个地方是在Activity的onCreate()方法中调用得到当前Window,然后调用requestFeature()来指定。另一个是在清单文件中使用android:theme="XXX"指定。

     5 给Window设置视图后,就需要告诉Wms把窗口显示在屏幕上。Ams经过判断最终会调用到Activity的makeVisiable()方法。如下:

ActivityThread.java
final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {			
         ...
		  ActivityClientRecord r = mActivities.get(token);
          r = performResumeActivity(token, clearHide, reason);
			
		   r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                if (r.activity.mVisibleFromClient) {
                    r.activity.makeVisible();
                }
         ...				
			}
			

Activity.java

void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

        分析:在makeVisiable()方法中 首先获得Activity内部的WidowManager,这实际就是Window.LocalWindowManager对象,然后调用addView()方法。注意:这不是WindowManagerImp类的addView()方法。注意第二个参数是在构造Window对象时默认构造的WindowManager.LayoutParams对象,如下:

Window.java
 private final WindowManager.LayoutParams mWindowAttributes =
        new WindowManager.LayoutParams();
 public final WindowManager.LayoutParams getAttributes() {
        return mWindowAttributes;
    }

WindowManager.java	
	public LayoutParams() {
            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            type = TYPE_APPLICATION;
            format = PixelFormat.OPAQUE;
        }

分析:默认情况下窗口参数的类型是一个TYPE_APPLICATION类型,即应用程序类型的窗口。

WindowManagerImpl.java

 private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
  @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
		
	   applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
	
	  private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
        // Only use the default token if we don't have a parent window.
        if (mDefaultToken != null && mParentWindow == null) {
            if (!(params instanceof WindowManager.LayoutParams)) {
                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
            }

            // Only use the default token if we don't already have a token.
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
            if (wparams.token == null) {
                wparams.token = mDefaultToken;
            }
        }
    }
 WindowManagerGlobal.java
   
    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
			
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
			...
			  final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
			  // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        // do this last because it fires off messages to start doing things
        try {
			//完成窗口的添加工作
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }

       分析: mView:这里的每一个View对象都将成为Wms所认为的一个窗口。mRoots:所有的ViewRootImpl对象,mViews中每一个View对应一个ViewRootImpl。mParams:当把mViews中的View对象当作一个窗口添加进Wms中,Wms要求每个被添加的窗口都要对应一个LayoutParams对象,mParams正是保存了每一个窗口对应的参数对象。到此应用窗口分析告一段落。    

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值