Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 698|回复: 0

开发交流:深入浅出 详解Android Surface系统(1)

[复制链接]

该用户从未签到

发表于 2011-10-24 10:49:22 | 显示全部楼层 |阅读模式
一 目的
本篇文章的目的就是为了讲清楚Android中的Surface系统,大家耳熟能详的SurfaceFlinger到底是个什么东西,它的工作流程又是怎样的。当然,鉴于SurfaceFlinger的复杂性,我们依然将采用情景分析的办法,找到合适的切入点。
一个Activity是怎么在屏幕上显示出来的呢?我将首先把这个说清楚。
接着我们把其中的关键调用抽象在Native层,以这些函数调用为切入点来研究SurfaceFlinger。好了,开始我们的征途吧。
二 Activity是如何显示的
最初的想法就是,Activity获得一块显存,然后在上面绘图,最后交给设备去显示。这个道理是没错,但是Android的SurfaceFlinger是在System Server进程中创建的,Activity一般另有线程,这之间是如何...如何挂上关系的呢?我可以先提前告诉大家,这个过程还比较复杂。
好吧,我们从Activity最初的启动开始。代码在framework/base/core/java/android/app/ActivityThread.java中,这里有个函数叫handleLaunchActivity。
[---->ActivityThread:: handleLaunchActivity()]
private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {  



      Activity a = performLaunchActivity(r, customIntent);  



   



        if (a != null) {  



            r.createdConfig = new Configuration(mConfiguration);  



            Bundle oldState = r.state;  



            handleResumeActivity(r.token, false, r.isForward);  



---->调用handleResumeActivity  



}
复制代码
handleLaunchActivity中会调用handleResumeActivity。
[--->ActivityThread:: handleResumeActivity]
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {  



         boolean willBeVisible = !a.mStartedActivity;  



            



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;  



                if (a.mVisibleFromClient) {  



                    a.mWindowAdded = true;  



                    wm.addView(decor, l); //这个很关键。  



                }
复制代码
上面addView那几行非常关键,它关系到咱们在Activity中setContentView后,整个Window到底都包含了些什么。我先告诉大家。所有你创建的View之上,还有一个DecorView,这是一个FrameLayout,另外还有一个PhoneWindow。上面这些东西的代码在framework/Policies/Base/Phone/com/android/Internal/policy/impl。这些隐藏的View的创建都是由你在Acitivty的onCreate中调用setContentView导致的。
[----&gthoneWindow:: addContentView]
public void addContentView(View view, ViewGroup.LayoutParams params) {  



if (mContentParent == null) { //刚创建的时候mContentParent为空  



installDecor();  



}  



mContentParent.addView(view, params);  



final Callback cb = getCallback();  



if (cb != null) {  



cb.onContentChanged();  



}  



}
复制代码installDecor将创建mDecor和mContentParent。mDecor是DecorView类型,ContentParent是ViewGroup类型 private void installDecor() {  



if (mDecor == null) {  



mDecor = generateDecor();  



mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);  



mDecor.setIsRootNamespace(true);  



}  



if (mContentParent == null) {  



mContentParent = generateLayout(mDecor);
复制代码
那么,ViewManager wm = a.getWindowManager()又返回什么呢?
PhoneWindow从Window中派生,Acitivity创建的时候会调用它的setWindowManager。而这个函数由Window类实现。
代码在framework/base/core/java/android/view/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);  



}
复制代码
你看见没,分析JAVA代码这个东西真的很复杂。mWindowManager的实现是LocalWindowManager,但由通过Bridge模式把功能交给WindowManagerImpl去实现了。
真的很复杂!
好了,我们回到wm.addView(decor, l)。最终会由WindowManagerImpl来完成addView操作,我们直接看它的实现好了。
代码在framework/base/core/java/android/view/WindowManagerImpl.java:
[---->addView]
private void addView(View view, ViewGroup.LayoutParams params, boolean nest)  



{  



ViewRoot root; //ViewRoot,我们的主人公终于登场!  



synchronized (this) {  



root = new ViewRoot(view.getContext());  



root.mAddNesting = 1;  



view.setLayoutParams(wparams);  



if (mViews == null) {  



index = 1;  



mViews = new View[1];  



mRoots = new ViewRoot[1];  



mParams = new WindowManager.LayoutParams[1];  



} else {  



}  



index--;  



mViews[index] = view;  



mRoots[index] = root;  



mParams[index] = wparams;  



}  



root.setView(view, wparams, panelParentView);  



}
复制代码ViewRoot是整个显示系统中最为关键的东西,看起来这个东西好像和View有那么点关系,其实它根本和View等UI关系不大,它不过是一个Handler罢了,唯一有关系的就是它其中有一个变量为Surface类型。我们看看它的定义。ViewRoot代码在framework/base/core/java/android/view/ViewRoot.java中: public final class ViewRoot extends Handler implements ViewParent,  



View.AttachInfo.Callbacks  



{  



private final Surface mSurface = new Surface();  



}
复制代码
它竟然从handler派生,而ViewParent不过定义了一些接口函数罢了。
看到Surface直觉上感到它和SurfaceFlinger有点关系。要不先去看看?
Surface代码在framework/base/core/java/android/view/Surface.java中,我们调用的是无参构造函数。
public Surface() {  



mCanvas = new CompatibleCanvas(); //就是创建一个Canvas!  



}
复制代码
如果你有兴趣的话,看看Surface其他构造函数,最终都会调用native的实现,而这些native的实现将和SurfaceFlinger建立关系,但我们这里ViewRoot中的mSurface显然还没有到这一步。那它到底是怎么和SurfaceFlinger搞上的呢?这一切待会就会水落石出的。
另外,为什么ViewRoot是主人公呢?因为ViewRoot建立了客户端和SystemServer的关系。我们看看它的构造函数。
public ViewRoot(Context context) {  



super();  



....  



getWindowSession(context.getMainLooper());  



}
复制代码getWindowsession将建立和WindowManagerService的关系。 public static IWindowSession getWindowSession(Looper mainLooper) {  



        synchronized (mStaticInit) {  



            if (!mInitialized) {  



                try {  



                //sWindowSession是通过Binder机制创建的。终于让我们看到点希望了  



                    InputMethodManager imm = InputMethodManager.getInstance(mainLooper);  



                    sWindowSession = IWindowManager.Stub.asInterface(  



                            ServiceManager.getService("window"))  



                            .openSession(imm.getClient(), imm.getInputContext());  



                    mInitialized = true;  



                } catch (RemoteException e) {  



                }  



            }  



            return sWindowSession;  



        }  



    }
复制代码
上面跨Binder的进程调用另一端是WindowManagerService,代码在framework/base/services/java/com/android/server/WindowManagerService.java中。我们先不说这个。
回过头来看看ViewRoot接下来的调用。
[-->ViewRoot::setView()],这个函数很复杂,我们看其中关键几句。
public void setView(View view, WindowManager.LayoutParams attrs,  



            View panelParentView) {  



        synchronized (this) {  



            requestLayout();   



                try {  



                    res = sWindowSession.add(mWindow, mWindowAttributes,  



                            getHostVisibility(), mAttachInfo.mContentInsets);  



                }   



}
复制代码requestLayout实现很简单,就是往handler中发送了一个消息。 public void requestLayout() {  



        checkThread();  



        mLayoutRequested = true;  



        scheduleTraversals(); //发送DO_TRAVERSAL消息  



}   



public void scheduleTraversals() {  



        if (!mTraversalScheduled) {  



            mTraversalScheduled = true;  



            sendEmptyMessage(DO_TRAVERSAL);  



        }  



}
复制代码
我们看看跨进程的那个调用。sWindowSession.add。它的最终实现在WindowManagerService中。
[--->WindowSession::add()]
public int add(IWindow window, WindowManager.LayoutParams attrs,  



                int viewVisibility, Rect outContentInsets) {  



            return addWindow(this, window, attrs, viewVisibility, outContentInsets);  



        }
复制代码
WindowSession是个内部类,会调用外部类的addWindow。
这个函数巨复杂无比,但是我们的核心目标是找到创建显示相关的部分。所以,最后精简的话就简单了。
[--->WindowManagerService:: addWindow]
public int addWindow(Session session, IWindow client,  



            WindowManager.LayoutParams attrs, int viewVisibility,  



            Rect outContentInsets) {  



        //创建一个WindowState,这个又是什么玩意儿呢?  



              win = new WindowState(session, client, token,  



                    attachedWindow, attrs, viewVisibility);  



           win.attach();  



           return res;  



}
复制代码
WindowState类中有一个和Surface相关的成员变量,叫SurfaceSession。它会在attach函数中被创建。SurfaceSession嘛,就和SurfaceFlinger有关系了。我们待会看。
好,我们知道ViewRoot创建及调用add后,我们客户端的View系统就和WindowManagerService建立了牢不可破的关系。
另外,我们知道ViewRoot是一个handler,而且刚才我们调用了requestLayout,所以接下来消息循环下一个将调用的就是ViewRoot的handleMessage。
public void handleMessage(Message msg) {  



        switch (msg.what) {  



       case DO_TRAVERSAL:  



            performTraversals();
复制代码performTraversals更加复杂无比,经过我仔细挑选,目标锁定为下面几个函数。当然,后面我们还会回到performTraversals,不过我们现在更感兴趣的是Surface是如何创建的。 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 {  



//ViewRoot也有一个Surface成员变量,叫mSurface,这个就是代表SurfaceFlinger的客户端  



//ViewRoot在这个Surface上作画,最后将由SurfaceFlinger来合成显示。刚才说了mSurface还没有什么内容。  



          relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
复制代码[---->ViewRoot:: relayoutWindow()] 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去看看吧。 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,然后把传入的参数outSurface copyFrom一下  



                        outSurface.copyFrom(surface);  



                        win.mReportDestroySurface = false;  



                        win.mSurfacePendingDestroy = false;  



                       } else {  



                       outSurface.release();  



                    }  



                }  



}
复制代码[--->WindowState::createSurfaceLocked] Surface createSurfaceLocked() {  



            



                try {  



                    mSurface = new Surface(  



                            mSession.mSurfaceSession, mSession.mPid,  



                            mAttrs.getTitle().toString(),  



                            0, w, h, mAttrs.format, flags);  



                  }   



                Surface.openTransaction();
复制代码这里使用了Surface的另外一个构造函数。   public Surface(SurfaceSession s,  



            int pid, String name, int display, int w, int h, int format, int flags)  



        throws OutOfResourcesException {  



        mCanvas = new CompatibleCanvas();  



        init(s,pid,name,display,w,h,format,flags); ---->调用了native的init函数。  



        mName = name;  



}
复制代码
到这里,不进入JNI是不可能说清楚了。不过我们要先回顾下之前的关键步骤。
◆ add中,new了一个SurfaceSession
◆创建new了一个Surface
◆调用copyFrom,把本地Surface信息传到outSurface中
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

GMT+8, 2025-1-11 07:50 , Processed in 0.378650 second(s), 46 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表