您的位置:首页 > 其它

Activity启动及窗口创建过程详解

2015-01-27 19:08 387 查看
有这么一个网站,能通过web方式查看Android源码.在你需要的时候,本地又没有源码的时候,通过这个网站可以很方便的查看到源码

网站地址:http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android


1. 应用的入口。

以前一直都说Activity的人口是onCreate方法。其实android上一个应用的入口,应该是ActivityThread。和普通的java类一样,入口是一个main方法。

任何的apk有且仅有一个ActivityThread类,该类为应用程序的主线程类。
public static final void main(String[] args) {
SamplingProfilerIntegration.start();

Process.setArgV0("<pre-initialized>");

Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}

ActivityThread thread = new ActivityThread();
thread.attach(false);

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}

Looper.loop();

if (Process.supportsProcesses()) {
throw new RuntimeException("Main thread loop unexpectedly exited");
}

thread.detach();
String name = (thread.mInitialApplication != null)
? thread.mInitialApplication.getPackageName()
: "<unknown>";
Slog.i(TAG, "Main thread of " + name + " is now exiting");
}
下面仔细分析一下这个main方法。


1.1.Looper.prepareMainLooper();

ActivityThread其实就是我们经常说的UI thread,也就是主线程。我们都知道主线程可以使用Handler进行异步通信,因为主线程中已经创建了Looper,而这个Looper就是在这里创建的。如果其他线程需要使用Handler通信,就要自己去创建Looper。


1.2. sMainThreadHandler = new Handler();

创建一个Handler。

ActivityThread thread = new ActivityThread();
创建ActivityThread 对象。

ActivityThread 有几个比较重要的成员变量,会在创建ActivityThread对象时初始化。

(1)final ApplicationThread mAppThread = new ApplicationThread();

ApplicationThread继承自ApplicationThreadNative, 而ApplicationThreadNative又继承自Binder并实现了IApplicationThread接口。IApplicationThread继承自IInterface。这是一个很明显的binder结构,用于于Ams通信。IApplicationThread接口定义了对一个程序(linux的进程)操作的接口。ApplicationThread通过binder与Ams通信,并将Ams的调用,通过下面的H类(也就是Hnalder)将消息发送到消息队列,然后进行相应的操作,入activity的start,
stop。

(2)final H mH = new H();

private final class H extends Handler

mH负责处理ApplicationThread发送到消息队列的消息,例如:

public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what);
switch (msg.what) {
case LAUNCH_ACTIVITY: {
ActivityClientRecord r = (ActivityClientRecord)msg.obj;

r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo);
handleLaunchActivity(r, null);
} break;
。。。



1.3. handleLaunchActivity(r, null);

从名字中就可以看出,这里就将进行启动activity的工作了。

方法中主要调用了这一句:

Activity a = performLaunchActivity(r, customIntent);


1.4. performLaunchActivity()

(1)进行了一些初始化和赋值操作后,创建activity。

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);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}


以上代码中使用ClassLoader从程序中加载指定的activity对应的class文件

(2)然后调用:

activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstance,
r.lastNonConfigurationChildInstances, config);


attach的作用就是为刚构造好的Activity设置内部变量,还有一个主要的工作就是创建Window对象

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);
。。。


(3)创建好Window对象后,给Window对象的mWindowManager对象赋值

接下来看setWindowManager在 Window.java里面

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

public static WindowManagerImpl getDefault()
{
return mWindowManager;
}
private static WindowManagerImpl mWindowManager = new WindowManagerImpl();


搞清楚了wm参数之后再看LocalWindowManager在Window.java中

private class LocalWindowManager implements WindowManager {
LocalWindowManager(WindowManager wm) {
mWindowManager = wm;
mDefaultDisplay = mContext.getResources().getDefaultDisplay(
mWindowManager.getDefaultDisplay());
}
。。。


实现WindowManager这个interface的有两个类,一个是LocalWindowManager,一个是WindowManagerImpl,

而LocalWindowManager只是一个壳而已,真正实现功能的是实现WindowManager这个interface的WindowManagerImpl类。


2、配置好Activity和Window之后,接下来给窗口添加显示视图View或ViewGroup,

还是在ActivityThread.java中performLaunchActivity方法里面

mInstrumentation.callActivityOnCreate(activity, r.state);


这一句就会调用到acitivity的onCreate方法了,就进入了大多数应用开发的入口了。


3、在activity的onCreate方法里面实际上又调用了其Window的方法

在Activity.java中可以看到

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


getWindow()返回的是一个PhoneWindow类,接下来看PhoneWindow.java

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


关键是在下面

private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);

mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
if (mTitleView != null) {
if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
View titleContainer = findViewById(com.android.internal.R.id.title_container);
if (titleContainer != null) {
titleContainer.setVisibility(View.GONE);
} else {
mTitleView.setVisibility(View.GONE);
}
if (mContentParent instanceof FrameLayout) {
((FrameLayout)mContentParent).setForeground(null);
}
} else {
mTitleView.setText(mTitle);
}
}
}
}


protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}


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);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}


该方法会做如下事情:

根据窗口的风格修饰类型为该窗口选择不同的窗口布局文件(根视图),具体体现为代码中的代码中的layoutResource。这些窗口修饰布局文件指定一个用来存放Activity自定义布局文件的ViewGroup视图,一般为FrameLayout 其id 为: android:id="@android:id/content"。

例如窗口修饰类型包括FullScreen(全屏)、NoTitleBar(不含标题栏)等。选定窗口修饰类型有两种:

①、指定requestFeature()指定窗口修饰符,PhoneWindow对象调用getLocalFeature()方法获取值;

②、为我们的Activity配置相应属性,即android:theme=“”,PhoneWindow对象调用getWindowStyle()方法

获取值。

举例如下,隐藏标题栏有如下方法:requestWindowFeature(Window.FEATURE_NO_TITLE);

或者 为Activity配置xml属性:android:theme=”@android:style/Theme.NoTitleBar”。

PS:因此,在Activity中必须在setContentView之前调用requestFeature()方法。

确定好窗口风格之后,选定该风格对应的布局文件,这些布局文件位于 frameworks/base/core/res/layout/ ,

典型的窗口布局文件有:

R.layout.dialog_titile_icons

R.layout.screen_title_icons

R.layout.screen_progress

R.layout.dialog_custom_title

R.layout.dialog_title

R.layout.screen_title // 最常用的Activity窗口修饰布局文件

R.layout.screen_simple //全屏的Activity窗口布局文件

这样layoutResource的值确定了,contentParent即为@android:id/content对应的View,接下来就可以把应用中的layout.xml添加到mDecor中。具体是通过setContentView方法中 mLayoutInflater.inflate(layoutResID, mContentParent)这一句来实现的。

最后调用cb.onContentChanged()通知窗口内容发生改变。cb即为Activity本身,Activity实现了Window.CallBack接口,因为在之前Activity的attach()方法中mWindow.setCallback(this)


4、Window类设置完视图之后,告诉Wms,Wms就把窗口显示在屏幕上了

首先Activity准备好了会通知Ams,Ams经过一系列判断最终调用Activity的makeVisible方法

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


在上面的方法中,首先获得一个WindowManager对象,wm实际上就是Window.LocalWindowManager对象,该对象的addView方法第一个参数是DecroView对象即为一个Activity全部看的到的视图内容,第二个参数为Window初始化时默认构造的WindowManager.LayoutParams对象,具体代码在Window.java中如下

public final WindowManager.LayoutParams getAttributes() {
return mWindowAttributes;
}


// The current window attributes.
private final WindowManager.LayoutParams mWindowAttributes =
new WindowManager.LayoutParams();


而WindowManager中的构造方法
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = TYPE_APPLICATION;
format = PixelFormat.OPAQUE;
}


综上可见创建的窗口类型为TYPE_APPLICATION,为应用窗口类型。而在LocalWindowManager的addView方法中会先检查第二个参数(WindowManager.LayoutParams类型,以下用params替代,即为代码中的wp)的值并给其内部变量token赋值,以便于添加

if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
if (wp.token == null) {
View decor = peekDecorView();
if (decor != null) {
wp.token = decor.getWindowToken();
}
}


以上表示添加的为子窗口,如果token为空则把activity对应的窗口token赋值给params。

如果添加的不是子窗口则把mAppToken值赋值给params,若被父activity包含则把父activity的mAppToken赋值给params。

if (wp.token == null) {
wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
}
检查完params参数后,接下来就调用WindowManagerImpl的addView方法。

由此可以看出前面创建Window时,为什么mWindowManager对象是一个Window.LocalWindowManager对象而不是一个WindowManagerImpl对象。

前者比后者多了检查params.token的关卡。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐