现在的位置: 首页 > 综合 > 正文

应用窗口activity的启动过程

2013年10月18日 ⁄ 综合 ⁄ 共 7848字 ⁄ 字号 评论关闭

每一个应用窗口对应于一个activity对象,因此创建应用类窗口需要先创建activity对象,ams通过ipc通知客户端进程启动一个activity,而一个客户端进程对应一个activityThread。

创建应用窗口的步骤

  一.构建activity对象

从ams调用ActivityThread中的 handleLaunchActivity开始讲起。

 handleLaunchActivity方法中调用 performLaunchActivity()来实例化

performLaunchActivity()部分代码如下

Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            r.intent.setExtrasClassLoader(cl);
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        };

可以看出源码是通过类加载器来实例化activity.

 

 二.为activity中的重要参数初始化(包括mwindow属性),为以后activity调度做准备,也是在方法performLaunchActivity()中实现的

部分代码如下

    if (activity != null) {
                //创建contextimpl,找到函数init的第一个参数的来源
                ContextImpl appContext = new ContextImpl();
                appContext.init(r.packageInfo, r.token, this);
                appContext.setOuterContext(activity);

                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mConfiguration);
                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.lastNonConfigurationInstance,
                        r.lastNonConfigurationChildInstances, config);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }

大家都知道activity的本质其实是context,从红色语句可以看出真正的"context"是contextimpl.粗体部分attach()函数就是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,
            Object lastNonConfigurationInstance,
            HashMap<String,Object> lastNonConfigurationChildInstances,
            Configuration config) {
        attachBaseContext(context);

        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setCallback(this);
       .........

      
       
    }
   重要参数有:appContext,作为Activity的BaseContext; this: 当前activityThread,后面activity对象内部要用到主进程(ActivityTread)的引用等。

在该方法中也为activity创建了为windos对象,PolicyManager.makeNewWindow(this);表面上是返回window接口,事实上是返回phonewindow对象,这里利用java多态性,是程序具有灵活性。我们可以追溯代码可以知道

PolicyManager静态模块中 实例化了sPolicy,代码如下

try {
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
            sPolicy = (IPolicy)policyClass.newInstance();
        }
也是通过类加载器实例化的,forname中参数POLICY_IMPL_CLASS_NAME,值为com.android.internal.policy.impl.Policy,可以猜到实例化的对象应该为Policy

在Policy(实现了IPolicy接口)中makeNewWindow的方法

public PhoneWindow makeNewWindow(Context context) {
        return new PhoneWindow(context);
    }

在弄清  mWindow真实值为Phonewindow,我花一些时间才找到,所以在这里啰嗦了些,呵呵。

 

三.创建好window对象后,需要给window对象中mwindowManager(windowManager接口)变量赋值,实现在代码attach()方法中。

mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;

实现windowManager接口的类有WindowManagerImpl和LocalWindowManager,而LocalWindowManager像contextwrap一样只是壳,真正实现windowmanager功能的是壳里面的WindowManagerImp,这两个实现类,在后面把新建好的窗口使用用到这两实现类,这里一定要弄清楚activity,window,localwindowmanager类中都变量mWindowManager,

我们来分析下各个mWindowManager的值是什么

从windos 的方法setWindowManager代码

 public void setWindowManager(WindowManager wm,
            IBinder appToken, String appName) {
        mAppToken = appToken;
        mAppName = appName;
        if (wm == null) {
            wm = WindowManagerImpl.getDefault();
        }
        mWindowManager = new LocalWindowManager(wm);
    }

window的mWindowManager变量值为LocalWindowManager对象,localwindowmanager的mWindowManager为WindowManagerImpl,从attach()方法中的代码mWindowManager = mWindow.getWindowManager();可以知道activity中mWindowManager和window的一样。

四.配置好window和activity对象后,则需要给该窗口添加真正的视图view或者viewgroup,从performLaunchActivity方法内部调用mInstrumentation.callActivityOnCreate(activity, r.state);转到activity.oncreat()方法,我们也最熟悉不过的。我们开发一般都是重写oncreate方法,其中添加setContentView()方法;.

从activity中setContentView()代码

public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID);
    }

还是转到了phonewindow(在步骤二已经分析mWindow真实值为Phonewindow对象。所以getwindow的返回真实值为phonewindow)对象的setContentView中,

public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();//为window类安装了一个窗口修饰,
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);//布局加载器,我们时曾相识。呵呵呵,layou.xml界面被包含在窗口修饰中,成为窗口内容。
        final Callback cb = getCallback();
        if (cb != null) {
            cb.onContentChanged();
        }

    }

private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();//创建了一个decorview对象,初始化了变量mDecor,而窗口修饰为mDecor的唯一子窗口。
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);//调用decor.addView()即添加了窗口修饰

            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
           。。。。。。
    }
粗体语句是关键的,generateLayout方法代码如下:

       mDecor.startChanging();

       View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//添加了窗口修饰,

       ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//ID_ANDROID_CONTENT正是id=content的framelayout,这就是为可以进行窗口叠加的原因吧。窗口内容被包含在该framelayout中。

最后通过phonewindow.setcontentview方法mLayoutInflater.inflate(layoutResID, mContentParent);把用户界面安装到窗口修饰中去。

五.给window图像设置完视图元素后,就得通知wms了。

(1)现在回到activityTjread.handleLaunchActivity中去handleResumeActivity(r.token, false, r.isForward);//该方法中调用了activity.makeVisible()通知ams

void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();//wm的真实值为localwindowManager类型,这里上行转化了,前面已经分析过了。
            wm.addView(mDecor, getWindow().getAttributes());//这里是调用localwindowManager.addview方法,做了些判断给token赋值,内部在调用windowmanagerimpl.addview方法。
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

(2)在应用程序中有且只有一个windowmanagerimpl对象(因为获取到是是个静态属性windowmanagerimpl,在该类源码可以发现),windowmanagerimpl类中维护着三个数组

View[] mViews,每一个view对象都将成为wms所认为的一个窗口

ViewRoot[] mRoots,每一view对象都对应有个viewRoot对象

windowManager.LayoutParams[] mParams,每一view 也对应一个LayoutParams,来保存view的相关参数。

windowmanagerimpl.add方法内部最后调用 viewRoot.setview()真正的通知wms.

viewRoot.setview()部分代码:

              // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();//重绘
                mInputChannel = new InputChannel();
                try { //通知wms添加新窗口 mwindow 为w内部类 binder,sWindowSession为binder的索引
                    res = sWindowSession.add(mWindow, mWindowAttributes,
                            getHostVisibility(), mAttachInfo.mContentInsets,
                            mInputChannel);
                }

sWindowSession在viewroot中是静态属性,在整个应用apk程序中只有一个,本想进入其add方法中去,结果发现该类代码是隐藏的,再从该对象是怎样获取的,获取代码如下

    public static IWindowSession getWindowSession(Looper mainLooper) {
        synchronized (mStaticInit) {
            if (!mInitialized) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
                    sWindowSession = IWindowManager.Stub.asInterface(
                            ServiceManager.getService("window"))

                            .openSession(imm.getClient(), imm.getInputContext());
                    mInitialized = true;
                } catch (RemoteException e) {
                }
            }
            return sWindowSession;
        }
    }

IWindowManager.Stub.asInterface这种类型,提供了与服务端进行ipc通信的统一接口IWindowManager,是不是很熟悉了,我在使用aidl中也是通过这样来获取服务端的引用的。

抱歉!评论已关闭.