Android源码:Activity的创建和显示

请尊重原创,转载请注明出处【tianyl_Melodie】的博客,http://blog.csdn.net/tianyl_melodie/article/details/53469700

1、说明

在前一篇【Android源码学习笔记:Context、ActivityThread和Activity的生命周期】中,已经基本学习了Android中,对于一个Activity创建所处理的逻辑,当然对于细节的地方,当时我是一笔略过了。不过对于我们这种钻研的猿类来说,这显然是有些不合适的,所以在这一篇博客中,就来对于创建Activity和将Activity显示到界面上,做一些仔细的学习。


2、创建Activity的实现细节

我们已经说了,Activity的创建,是在ActivityThread中的handleLaunchActivity完成的。在handleLaunchActivity方法中,有一个performLaunchActivity,完成了对Activity的创建,和onCreate的调用,然后在handleLaunchActivity方法中,完成了接下来的onResume和OnPause调用。只是当时我们并没有真正的看到调用Activity的这两个方法,都是根据方法名,例如“callActivityOnPause”进行的猜测。那么较真的同学肯定会有疑惑,单凭一个方法名,怎么就能肯定它是真的调用了这些生命周期的方法了。就算是真的调用了生命周期的方法,那么Activity创建的细节是怎样的呢?所以,我们今天要从performLaunchActivity这个方法看起。


3、performLaunchActivity

之前我们已经知道了Activity是通过这个performLaunchActivity进行创建的,首先看这个方法中的代码片段:

//首先拿到packageInfo中的类加载器
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
//调用mInstrumentation中的newActivity创建activity 
activity = mInstrumentation.newActivity(cl,component.getClassName(), r.intent);
//创建Application 
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
//调用activity的onCreate方法
mInstrumentation.callActivityOnCreate(activity, r.state);


上面代码就是通过这个performLaunchActivity创建activity的过程,还记得之前我们学习的,在handleLaunchActivity方法中调用Activity的onResume和OnPause也是通过mInstrumentation完成的。例如:

mInstrumentation.callActivityOnPause(r.activity); 

当时我们只是说了它调用了Activity的onPause方法。看到这里,可能就会有同学开始好奇了,既然Activity的创建和生命周期方法的调用,有通过mInstrumentation这个对象来进行,那么它到底是一个什么呢?通过看ActivityThread的源码可以发现,它其实是一个Instrumentation类的实例,ActivityThread将它定义成了一个成员变量。

Instrumentation mInstrumentation;

4、Instrumentation

现在,我们就开始学习Activity创建时具体的细节。首先是Activity的创建:newActivity方法。通过查看Instrumentation的源码,可以看到,newActivity方法,实际上也只是通过调用Activity自己的newInstance方法而已:

public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
}

在Instrumentation中,只是通过ClassLoader拿到Activity的对象,然后通过调用Activity自己的newInstance,就创建出了我们需要的Activity。


完成了创建之后,就是对Activity生命周期的调用了,可能有的同学已经猜到了,既然创建不过是这样通过拿到Activity的对象,调用一个它自己的newInstance而已,那么对于Activity生命周期的管理,是不是也没有我们想象中那么神秘呢。接下来,我们就看一下生命周期管理的细节:

mInstrumentation.callActivityOnPostCreate(activity, r.state);

首先,我们已经知道了,它是通过调用mInstrumentation的callActivityOnPostCreate的方法,完成对Activity中onCreate的调用的。接下来,我们就看一下在callActivityOnPostCreate中做了哪些事情。

public void callActivityOnCreate(Activity activity, Bundle icicle) {
        if (mWaitingActivities != null) {
            synchronized (mSync) {
                final int N = mWaitingActivities.size();
                for (int i=0; i<N; i++) {
                    final ActivityWaiter aw = mWaitingActivities.get(i);
                    final Intent intent = aw.intent;
                    if (intent.filterEquals(activity.getIntent())) {
                        aw.activity = activity;
                        mMessageQueue.addIdleHandler(new ActivityGoing(aw));
                    }
                }
            }
        }
        //调用activity的performCreate方法
        activity.performCreate(icicle);
        
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    am.match(activity, activity, activity.getIntent());
                }
            }
        }
    }

我们可以看到,在Instrumentation的代码中,它是做了一件事,就是调用Activity自己的方法performCreate,也就是说Instrumentation更像是一个指挥官,在创建Activity的时候,指挥Activity需要调用什么方法。接下来我们就需要看Activity中的performCreate方法了:

final void performCreate(Bundle icicle) {
        onCreate(icicle);
        mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
                com.android.internal.R.styleable.Window_windowNoDisplay, false);
        mFragments.dispatchActivityCreated();
    }

这是Activity中的performCreate方法,我们可以看到在第一行就调用了onCreate方法,到这里,我们基本就已经完成了对Activity创建和生命周期源码的追踪。既然Activity已经创建了,那么它又是如何显示在界面上的呢?接下来,我们就来学习activity是如何显示的。


5、Activity是如何显示的

如果我们要让一个Activity显示一个布局,那么我们需要调用setContentView这个方法,那么这个方法是如何让我们的Activity把这个布局显示在屏幕上的呢。首先,我们需要看Activity中的setContentView方法:

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

当然,Activity中其实还有几个重载的setContentView方法,不过实现方式基本一样,都是通过getWindow().setContentView进行实现的。那么getWindow()拿到的是哪个对象呢?

public Window getWindow() {
        return mWindow;
    }

我们查看Activity中的getWindow方法,发现它返回的是一个成员变量,也就是说在我们调用setContentView这个方法之前,它已经被创建出来了,那么是在哪里创建的这个mWindow对象的呢?


这时我们需要回到performLaunchActivity这个方法,之前我们学习的是其中对Activity创建和生命周期的调用,我们仔细看这个方法,在创建完Activity,还没有调用onCreate方法之前,它还调用了一个Activity的attach方法,我们都是在在onCreate方法中调用的setContentView方法,那么想必这个写在调用onCreate方法前的一个方法,应该就是创建mWindow对象的逻辑了。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
	......
        Activity activity = null;
        try {
            //创建Activity 
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            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);
            }
        }
        try {
            //创建Application 
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            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);
                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }
                activity.mCalled = false;
                mInstrumentation.callActivityOnCreate(activity, r.state);
               
			   ......
            }
            r.paused = true;
            mActivities.put(r.token, r);
        } catch 
			......
        return activity;
    }

我们跳转到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) {
        attachBaseContext(context);
        mFragments.attachActivity(this, mContainer, null);
        
        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setCallback(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);
        }
        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;
        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;
    }

可以看到attach方法中基本就是对一些成员变量的初始化,其中就有mWindow对象的创建。我们接着看PolicyManager.makeNewWindow(this)创建的实现逻辑:

public static Window makeNewWindow(Context context) {
        return sPolicy.makeNewWindow(context);
    }

其中,sPolicy是PolicyManager的一个成员对象,它是IPolicy接口实现类,在Android中,IPolicy主要提供的就是处理Window的一些方法。它只有一个唯一的实现类Policy。我们看实现类中的makeNewWindow方法:

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

它创建了一个PhoneWindow对象。看到这里,我们算是基本理清了把Activity显示在界面上的逻辑,就是通过拿到PhoneWindow 对象,然后调用它的setContentView完成的。


6、PhoneWindow

接下来,我们再学习PhoneWindow中的setContentView是如何把Activity显示到界面上的。首先,我们看setContentView的代码:

public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mContentParent.addView(view, params);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

首先,它会判断一个mContentParent是否为空。mContentParent 其实就是一个ViewGroup,它就是当前Activity的根布局。如果它为空,我们就会初始化一个根布局。
我们接着看根布局初始化逻辑,因为初始化根布局的逻辑非常长,所以这里只列举出其中主要的一些代码:

private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
        //对一些属性的设置
		......
        }
}

首先,系统会判断mDecor这个对象是否为null,如果为null,就创建一个mDecor对象。mDecor它是DecorView这个类的实例对象,它是继承自FrameLayout的,也是Android显示中,最外层的布局。系统在创建完这个最外层的DecorView之后,还做一个判断mContentParent是否为null,如果为null,就创建一个mContentParent对象。我们接下来看mContentParent = generateLayout(mDecor)这行代码做了那些事情。

protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.
        ......
        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);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
            ProgressBar progress = getCircularProgressBar(false);
            if (progress != null) {
                progress.setIndeterminate(true);
            }
        }
        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
        if (getContainer() == null) {
            Drawable drawable = mBackgroundDrawable;
            if (mBackgroundResource != 0) {
                drawable = getContext().getResources().getDrawable(mBackgroundResource);
            }
            mDecor.setWindowBackground(drawable);
            drawable = null;
            if (mFrameResource != 0) {
                drawable = getContext().getResources().getDrawable(mFrameResource);
            }
            mDecor.setWindowFrame(drawable);
            // System.out.println("Text=" + Integer.toHexString(mTextColor) +
            // " Sel=" + Integer.toHexString(mTextSelectedColor) +
            // " Title=" + Integer.toHexString(mTitleColor));
            if (mTitleColor == 0) {
                mTitleColor = mTextColor;
            }
            if (mTitle != null) {
                setTitle(mTitle);
            }
            setTitleColor(mTitleColor);
        }
        mDecor.finishChanging();
        return contentParent;
    }

因为创建mContentParent对象的代码较多,所以这里也只列出创建的主要代码。
我们可以看到,系统会inflate出一个View,然后将它添加到DecorView中,然后查找view中id为"ID_ANDROID_CONTENT"的view,当然它是一个字符变量。

public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content

最后将它return出去。看到这里,我们mDecor和mContentParent的创建也已经完成了,想必机智的同学已经猜到了。mContentParent这个变量的名字和setContentView方法这么像,根据Google工程师的命名,它们之间肯定有这某种联系。
最后我们再回到我们最开始的setContentView方法:

public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

没错,看到熟悉的mContentParent对象。正如同我们猜测的那样,这个方法所做的,就是把我们设置的view添加到mContentParent对象中而已。


7、总结

好了,Activity的创建和显示的学习一算是告一段落了。
①Activity的创建是在performLaunchActivity这个方法中完成的。不过它并非直接new一个Activity对象,而已通过Instrumentation这个类的newActivity的方法创建。
②在Instrumentation中,创建Activity是通过调用Activity自己的newInstance完成的。
③在创建完Activity之后,会调用Activity的attach方法,在这个方法中会初始化mWindow对象,它其实是一个PhoneWindow对象的实例。
④创建PhoneWindow对象是通过PolicyManager.makeNewWindow(this)方法完成的,PolicyManager中持有接口IPolicy唯一的实现类Policy的实例对象,接口IPolicy主要提供的就是处理Window的一些方法。
⑤在完成了activity的attach方法后,会通过Instrumentation调用callActivityOnCreate方法,最后调用到activity的performCreate方法,在这个方法中调用了activity的onCreate方法。
⑥当我们在onCreate方法中调用setContentView方法设置界面的时候,其实是在向mContentParent中addView。⑦ mContentParent其实是DecorView中的一个id是“com.android.internal.R.id.content”的布局,DecorView是Android界面的根布局,它继承自FrameLayout。
到此,Activity的创建和显示也算是学习完了,当然Android的源码远比本博客多核复杂,一篇博客也不可能面面俱到,对象activity细节中的一些东西,还是需要同学去自己研究咯。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值