Android View显示过程

      之前,我们研究了Android应用的启动过程和退出过程,以及布局文件的加载过程,今天我们仍然照着这条线路,继续研究在布局文件加载之后,View是如何被显示到屏幕上来的,也就是研究View的显示过程。下面将从以下两个方面做介绍:1、从加载layout文件到view的测量、布局和绘制的全过程。2、surface、surfaceFlinger与view是如何关联起来的。
      在Activity的onCreate方法中,我们通过setContentView(view)来初始化定义好的一个view,而此时view还没有显示出来,在Activity执行了onResume方法后,界面才显示完成,这说明了view的显示经历了Activity的这两个过程。下面是启动Activity与加载view的一个简单过程图。
这里写图片描述

一、过程分析
      下面将会以ActivityThread类为起点,一步步跟踪view的显示过程。
在ActivityThread类中有一个Handler对象,它负责处理服务端传过来的消息。在接收到LAUNCH_ACTIVITY消息后,执行handleLaunchActivity方法,也就是启动一个Activity。

case LAUNCH_ACTIVITY: {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
    ActivityClientRecord r = (ActivityClientRecord)msg.obj;
    r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
    handleLaunchActivity(r, null);
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;

在此方法中,首先解析package信息,然后调用handleLaunchActivity方法来执行启动Activity。
我们继续跟踪handleLaunchActivity方法。

// 上面代码省略
// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);

if (localLOGV) Slog.v(TAG, "Handling launch of " + r);
Activity a = performLaunchActivity(r, customIntent);

if (a != null) {
    r.createdConfig = new Configuration(mConfiguration);
    Bundle oldState = r.state;
    handleResumeActivity(r.token, false, r.isForward,
            !r.activity.mFinished && !r.startsNotResumed);

    if (!r.activity.mFinished && r.startsNotResumed) {
        // The activity manager actually wants this one to start out
        // paused, because it needs to be visible but isn't in the
        // foreground.  We accomplish this by going through the
        // normal startup (because activities expect to go through
        // onResume() the first time they run, before their window
        // is displayed), and then pausing it.  However, in this case
        // we do -not- need to do the full pause cycle (of freezing
        // and such) because the activity manager assumes it can just
        // retain the current state it has.
        try {
            r.activity.mCalled = false;
            mInstrumentation.callActivityOnPause(r.activity);
            // We need to keep around the original state, in case
            // we need to be created again.  But we only do this
            // for pre-Honeycomb apps, which always save their state
            // when pausing, so we can not have them save their state
            // when restarting from a paused state.  For HC and later,
            // we want to (and can) let the state be saved as the normal
            // part of stopping the activity.
            if (r.isPreHoneycomb()) {
                r.state = oldState;
            }
            if (!r.activity.mCalled) {
                throw new SuperNotCalledException(
                    "Activity " + r.intent.getComponent().toShortString() +
                    " did not call through to super.onPause()");
            }
        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {
            if (!mInstrumentation.onException(r.activity, e)) {
                throw new RuntimeException(
                        "Unable to pause activity "
                        + r.intent.getComponent().toShortString()
                        + ": " + e.toString(), e);
            }
        }
        r.paused = true;
    }
} else {
    // If there was an error, for any reason, tell the activity
    // manager to stop us.
    try {
        ActivityManagerNative.getDefault()
            .finishActivity(r.token, Activity.RESULT_CANCELED, null);
    } catch (RemoteException ex) {
        // Ignore
    }
}

此方法注释比较多,过程非常简单,教容易理解,主要是执行performLaunchActivity和handleResumeActivity方法,他们分别执行Activity的onCreate,onStart和onResume生命周期。
我们继续跟踪performLaunchActivity方法

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);
    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 app = r.packageInfo.makeApplication(false, mInstrumentation);

    if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
    if (localLOGV) Slog.v(
            TAG, r + ": app=" + app
            + ", appName=" + app.getPackageName()
            + ", pkg=" + r.packageInfo.getPackageName()
            + ", comp=" + r.intent.getComponent().toShortString()
            + ", dir=" + r.packageInfo.getAppDir());

    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);
        if (!activity.mCalled) {
            throw new SuperNotCalledException(
                "Activity " + r.intent.getComponent().toShortString() +
                " did not call through to super.onCreate()");
        }
        r.activity = activity;
        r.stopped = true;
        if (!r.activity.mFinished) {
            activity.performStart();
            r.stopped = false;
        }
        if (!r.activity.mFinished) {
            if (r.state != null) {
                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
            }
        }
        if (!r.activity.mFinished) {
            activity.mCalled = false;
            mInstrumentation.callActivityOnPostCreate(activity, r.state);
            if (!activity.mCalled) {
                throw new SuperNotCalledException(
                    "Activity " + r.intent.getComponent().toShortString() +
                    " did not call through to super.onPostCreate()");
            }
        }
    }
    r.paused = true;

    mActivities.put(r.token, r);

}

在此方法中,首先调用mInstrumentation.newActivity来创建一个Activity的实例,然后调用makeApplication创建application对象,接着通过activity.attach来调用activity对象的attach方法,然后执行mInstrumentation.callActivityOnCreate,也就是调用activity对象的onCreate方法。在onCreate里我们调用setContentView设置我们预定义好的View,我们便接着跟踪Activity的setContentView方法。

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

方法中getWindow()返回的是一个android.app.Window对象,这个对象就是刚刚在attach方法中赋值的mWindow成员变量,它的类型是PhoneWindow,继续查看PhoneWindow的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();
    }
}

PhoneWindow中有两个和视图相关的成员变量,一个是DecorView mDecor,另一个是ViewGroup mContentParent。mDecor是一个FrameLayout,而mContentParent是mDecor中的一个子视图,我们继续跟踪下面的代码便会很清晰了。
继续查看installDecor方法

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);
        // 下面代码省略
    }
}

继续查看generateLayout方法的代码
这里写图片描述
该方法会做如下事情:
      根据窗口的风格修饰类型为该窗口选择不同的窗口布局文件(根视图)。这些窗口修饰布局文件指定一个用来存放Activity自定义布局文件的ViewGroup视图,一般为FrameLayout 其id 为: android:id=”@android:id/content”。例如窗口修饰类型包括FullScreen(全屏)、NoTitleBar(不含标题栏)等。选定窗口修饰类型有两种:1、指定requestFeature()指定窗口修饰符,PhoneWindow对象调用getLocalFeature()方法获取值;2、为我们的Activity配置相应属性,即android:theme=“”,PhoneWindow对象调用getWindowStyle()方法。
      确定好窗口风格之后,选定该风格对应的布局文件,这些布局文件位于 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窗口布局文件)。

下面是Activity最常用的一种窗口布局文件,R.layout.screen_title

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:fitsSystemWindows="true">  

    <FrameLayout  
        android:layout_width="match_parent"   
        android:layout_height="?android:attr/windowTitleSize"  
        style="?android:attr/windowTitleBackgroundStyle">  

        <TextView android:id="@android:id/title"   
            style="?android:attr/windowTitleStyle"  
            android:background="@null"  
            android:fadingEdge="horizontal"  
            android:gravity="center_vertical"  
            android:layout_width="match_parent"  
            android:layout_height="match_parent" />  

    </FrameLayout>  

    <FrameLayout android:id="@android:id/content"  
        android:layout_width="match_parent"   
        android:layout_height="0dip"  
        android:layout_weight="1"  
        android:foregroundGravity="fill_horizontal|top"  
        android:foreground="?android:attr/windowContentOverlay" />  

</LinearLayout>

布局中上面部分,id为title的TextView显示窗口的标题,下面部分id为content的FrameLayout是装载上层应用的layout的容器,也就是代码中的mContentParent对象。
在这儿小结一下Activity的层级结构
这里写图片描述
1.一个Activity对应着一个PhoneWindow对象,是一对一的关系,如果从Activity A启动到Activity B,那么Activity B会创建一个自己的PhoneWindow对象。
2.PhoneWindow管理着整个屏幕的内容,不包括屏幕最顶部的系统状态条。所以,PhoneWindow或者Window是与应用的一个页面相关联。
3.PhoneWindow同时管理着ActionBar和下面的内容主题,setContentView()方法是用来设置内容主体的,而setTitle()等其他方法就是操作ActionBar的,Window中定义的requestFeature()等方法,有很多与ActionBar属性相关的设置。
4.PhoneWindow自己并不是一个视图(View),它的成员变量mDecor才是整个界面的视图,mDecor是在generateLayout()的时候被填充出来的,而actionBar和contentParent两个视图都是通过findViewById()直接从mDecor中获取出来的。

第一部分performLaunchActivity就执行完成了,下面继续执行android.app.ActivityThread.handleResumeActivity方法。在此方法中,首先调用performResumeActivity执行Activity的onResume方法。下面接着执行这段代码非常关键

if (r.window == null && !a.mFinished && willBeVisible) {
    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);
    }

// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
    if (localLOGV) Slog.v(
        TAG, "Launch " + r + " mStartedActivity set");
    r.hideForNow = true;
}

其中wm是a.getWindowManager();获取到的,a是Activity,getWindowManager()返回它的mWindowManager对象,而这个对象是WindowManagerImpl类型,它的内部方法大部分是代理的WindowManagerGlobal。通过跟踪wm.addView(decor, l)这句代码,下面我们继续查看WindowManagerGlobal类中的addView方法。

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    // 省略代码

    ViewRootImpl root;
    View panelParentView = null;

    synchronized (mLock) {
        // Start watching for system property changes.
        // 省略代码

        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;
    }
}

其中root从名称可以看出,它是根节点,它是ViewRootImpl类型,也就是说把传进来的DecorView设置给根节点,继续查看ViewRootImpl的setView方法。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

    synchronized (this) {
     if (mView == null) {
         mView = view;
         ...


         // Schedule the first layout -before- adding to the window
         // manager, to make sure we do the relayout before receiving
         // any other events from the system.
         requestLayout();
         ...

         view.assignParent(this);
         ...

     }
    }
 }

首先将传进来的参数view赋值给mView,这里要说明一下,ViewRootImpl其实并不是一个View的子类,mView将是这个对象所认识的root节点,也是整个Activity的root节点。现在整个view的树形结构中有了根节点,也就是ViewRootImpl,那么requestLayout()就有效了,就可以进行后面的measure,layout,draw三步操作了。
下面继续查看requestLayout方法

public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

首先检查了是否在主线程,然后就执行scheduleTraversals()方法。

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        scheduleConsumeBatchedInput();
    }
}

这里主要是post一个mTraversalRunnable成员变量。

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

不多说了,接着执行doTraversal()方法,在doTraversal方法中只是简单的调用performTraversals方法,这个方法非常长,可以研究一下源码,依次调用了performMeasure(),performLayout(),performDraw()三个方法,终于开始了控件层的测量,布局,绘制三个步骤。
这里写图片描述
完成draw后整个view就显示出来了,在View的onDraw里实际操作的是Canvas对象,那么Canvas对象是哪儿来的?它是怎么通知SurfaceFlinger来显示图形的呢? 下面将介绍surface与surfaceFlinger。

二、Surface与SurfaceFlinger
1、什么是Surface、SurfaceFlinger?
Surface:surface相当于一个容器,存放需要显示的内容。surface是双缓冲,一个buffer用于绘制,一个buffer用于显示。
SurfaceFlinger:SurfaceFlinger是一个系统服务,主要是负责合成各窗口的Surface,然后通过OpenGLES显示到硬件帧缓冲区上。
2、为什么需要SurfaceFlinger?
系统的硬件帧缓冲区一般只有一个,并且不应该是随便访问的,需要一个服务来统一管理访问它,surfaceFlinger就是这个角色。
3、canvas如何和surface关联起来的?
我们查看ViewRootImpl类的源码

// These can be accessed by any thread, must be protected with a lock.
// Surface can never be reassigned or cleared (use Surface.clear()).
private final Surface mSurface = new Surface();

// 在drawSoftware方法里:
Canvas canvas;
canvas = mSurface.lockCanvas(dirty);
....
mView.draw(canvas);
// mView 就是DecoView,执行这个view树的绘制。Canvas是Surface的一个成员变量

4、surface如何和surfaceFlinger联系起来的?
我们首先看一下ViewRootImpl的构造函数

public ViewRootImpl(Context context, Display display) {
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        mBasePackageName = context.getBasePackageName();
        ...

其中,getWindowsession将建立和WindowManagerService的关系,我们简单看一下getWindowSession方法的源码

public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
    if (sWindowSession == null) {
        try {
            InputMethodManager imm = InputMethodManager.getInstance();
            IWindowManager windowManager = getWindowManagerService();
            sWindowSession = windowManager.openSession(
                    imm.getClient(), imm.getInputContext());
            float animatorScale = windowManager.getAnimationScale(2);
            ValueAnimator.setDurationScale(animatorScale);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to open window session", e);
        }
    }
    return sWindowSession;
}

我们接着上面的流程中doTraversal之后执行performTraversals方法

private void performTraversals() {

    // cache mView since it is used so much below...
    final View host = mView;
    boolean initialized = false;
    boolean contentInsetsChanged = false;
    boolean visibleInsetsChanged;
    try {
        relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
        // 以下代码省略

ViewRoot也有一个Surface成员变量,叫mSurface,这个就是代表SurfaceFlinger的客户端。ViewRoot在这个Surface上作画,最后将由SurfaceFlinger来合成显示。

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
  boolean insetsPending) throws RemoteException {

  //relayOut是跨进程调用,mSurface做为参数传进去了,离真相越来越近了!
       int relayoutResult = sWindowSession.relayout(

            mWindow, params,
            (int) (mView.mMeasuredWidth * appScale + 0.5f),
            (int) (mView.mMeasuredHeight * appScale + 0.5f),
            viewVisibility, insetsPending, mWinFrame,
            mPendingContentInsets, mPendingVisibleInsets,
            mPendingConfiguration, mSurface);  // mSurface做为参数传进去了。

}

下面就进入到服务端查看WindowManagerService的relayoutWindow方法。

public int relayoutWindow(Session session, IWindow client,
    WindowManager.LayoutParams attrs, int requestedWidth,
    int requestedHeight, int viewVisibility, boolean insetsPending,
    Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
    Configuration outConfig, Surface outSurface){

    .....
  try {
    //其中win是最初创建的WindowState!
        Surface surface = win.createSurfaceLocked();
        if (surface != null) {
         //先创建一个本地surface,然后把这个surface复制给传进来的客户端surface
            outSurface.copyFrom(surface);
            win.mReportDestroySurface = false;
            win.mSurfacePendingDestroy = false;
           } else {
           outSurface.release();
        }
    }
}

客户端的surface终于和服务端分配的surface联系起来了,接下来就是view使用surface的mCanvas绘制了。
好了,view在上层的显示过程就串联起来了,其余的工作就交给底层的Display系统展示出来即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值