本文将带你了解Android应用开发andriod开发之Activity的渲染机制,希望本文对大家学Android有所帮助。

一切从setContentView说起。安卓中最常用的代码可能就是setContentView了,但大家有没有想过这个方法的背后到底做了些什么?

public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle   savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

直接跳转到Activity的源码我们可以看到,Activity.setContentView实际上调用了PhoneWindow.setContentView:

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);
...
}
public Window getWindow() {
return mWindow;
}
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}

我们继续跟踪PhoneWindow的源码,可以发现最终layoutResID被inflate出来之后是成为了mDecor这个DecorView的子view。而DecorView实际上是一个FrameLayout:

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();
}
}
private void installDecor() {
if (mDecor == null)   {
mDecor =   generateDecor();
...
}
if (mContentParent == null)   {
//mContentParent   实际上是mDecor的一个子view
mContentParent =   generateLayout(mDecor);
...
}
...
}
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
private   final class DecorView extends FrameLayout implements RootViewSurfaceTaker   {
...
}

这里的generateLayout比较重要,它实际上是根据window的各种属性inflate出不同的layout挂到DecorView下面,而mContentParent是这个layout中的一个子ViewGroup。如果我们没有对window的属性进行设置就会使用默认的com.android.internal.R.layout.screen_simple这个layout:

protected ViewGroup generateLayout(DecorView decor) {
...
if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) !=   0) {
...
layoutResource = com.android.internal.R.layout.screen_title_icons;
...
} else   if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) !=   0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
layoutResource =   com.android.internal.R.layout.screen_progress;
} else   if ((features & (1 << FEATURE_CUSTOM_TITLE)) !=   0) {
...
layoutResource =   com.android.internal.R.layout.screen_custom_title;
...
} ... else{
layoutResource =   com.android.internal.R.layout.screen_simple;
}
...
View in = mLayoutInflater.inflate(layoutResource,   null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT,   MATCH_PARENT));
ViewGroup contentParent =   (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...
return contentParent;
}

我们可以在AndroidSdk根目录/platforms/android-19/data/res/layout/下面找到这些layout

xml,例如screen_simple,这是个竖直的LinearLayout,由上方的ActionBar和下方的content

FrameLayout组成。它就是我们最常见的带ActionBar的activity样式:

android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"   />
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay"   />

我们可以用一张图片来总结下Activity是如何管理布局的(这里假设DecorView里面添加了screen_simple这个布局):

Activity的布局是怎样被系统渲染的

在上一节中我们已经知道了Activity是怎样管理布局的。接着我们来看看Activity中的布局是如何渲染到系统的。

ActivityThread用于管理Activity的声明周期,之后我会专门写一篇文章来讲它。我们直接看ActivityThread.handleResumeActivity方法:

final   void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume)   {
...
//performResumeActivity方法会调用Activity.onResume
ActivityClientRecord r = performResumeActivity(token,   clearHide);
...
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm =   a.getWindowManager();
WindowManager.LayoutParams l =   r.window.getAttributes();
a.mDecor = decor;
l.type =   WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
...
}

可以看到它在Activity.onResume之后从Activity中获取了Window,然后又从window中获取了DecorView。最后使用WindowManager.addView将DecorView添加到了WindowManager中。这样就将DecorView在手机上渲染了出来。

WindowManager.addView方法可以将一个view渲染到手机界面上。不知道大家有没有做过类似悬浮球的应用,就是用WindowManager.addView去实现的。这里就不再展开了,大家有兴趣的话可以自己去搜索一下。

为什么不能在子线程中操作view

我们都知道,在安卓中必须在ui线程中操作ui,不能在子线程中对view进行操作,否则或抛出CalledFromWrongThreadException异常。但是在子线程中操作view是不是真的就一定会出现异常呢?让我们运行下面的代码:

public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState)   {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
((TextView)findViewById(R.id.textView)).setText("子线程中操作view");