Android 深入了解Surface

来源:互联网 发布:浙江省网络图书馆账号 编辑:程序博客网 时间:2024/05/02 02:04
Activity是如何显示的

  最初的想法就是,Activity获得一块显存,然后在上面绘图,最后交给设备去显示.这个道理是没错,但是Android的 SurfaceFlinger是在System Server进程中创建的,Activity一般另有线程,这之间是如何…如何挂上关系的呢?我可以先提前告诉大家,这个过程还比较复杂.呵呵.
  好吧,我们从Activity最初的启动开始.代码在
  framework/base/core/java/android/app/ActivityThread.java中,这里有个函数叫handleLaunchActivity
  [---->ActivityThread:: handleLaunchActivity()]

java代码:
  1. private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {

  2. Activity a = performLaunchActivity(r, customIntent);
  3. if (a != null) {
  4. r.createdConfig = new Configuration(mConfiguration);
  5. Bundle ldState = r.state;
  6. handleResumeActivity(r.token, false, r.isForward);
  7. ---->调用handleResumeActivity
  8. }
复制代码

        handleLaunchActivity中会调用handleResumeActivity。

java代码:
  1. [--->ActivityThread:: handleResumeActivity]

  2. final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
  3. boolean willBeVisible = !a.mStartedActivity;


  4. if (r.window == null && !a.mFinished && willBeVisible) {
  5. r.window = r.activity.getWindow();
  6. View decor = r.window.getDecorView();
  7. decor.setVisibility(View.INVISIBLE);

  8. ViewManager wm = a.getWindowManager();
  9. WindowManager.LayoutParams l = r.window.getAttributes();
  10. a.mDecor = decor;
  11. l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

  12. if (a.mVisibleFromClient) {
  13. a.mWindowAdded = true;
  14. wm.addView(decor, l); //这个很关键。
  15. }
复制代码

       上面addView那几行非常关键,它关系到咱们在Activity中setContentView后,整个Window到底都包含了些什么。我先告诉大家。所有你创建的View之上,还有一个DecorView,这是一个FrameLayout,另外还有一个PhoneWindow。上面这些东西的代码在

  framework/Policies/Base/Phone/com/android/Internal/policy/impl。这些隐藏的View的创建都是由你在Acitivty的onCreate中调用setContentView导致的。

  [---->PhoneWindow:: addContentView]

java代码:
  1. public void addContentView(View view, ViewGroup.LayoutParams params) {

  2. if (mContentParent == null) { //刚创建的时候mContentParent为空
  3. installDecor();
  4. }

  5. mContentParent.addView(view, params);
  6. final Callback cb = getCallback();
  7. if (cb != null) {
  8. cb.onContentChanged();
  9. }
  10. }
复制代码

        installDecor将创建mDecor和mContentParent。mDecor是DecorView类型,mContentParent是ViewGroup类型

java代码:
  1. private void installDecor() {

  2. if (mDecor == null) {
  3. mDecor = generateDecor();
  4. mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
  5. mDecor.setIsRootNamespace(true);
  6. }

  7. if (mContentParent == null) {
  8. mContentParent = generateLayout(mDecor);
复制代码

       那么,ViewManager wm = a.getWindowManager()又返回什么呢?
  PhoneWindow从Window中派生,Acitivity创建的时候会调用它的setWindowManager.而这个函数由Window类实现.
  代码在framework/base/core/java/android/view/Window.java中

java代码:
  1. public void setWindowManager(WindowManager wm,IBinder appToken, String appName) {

  2. mAppToken = appToken;
  3. mAppName = appName;

  4. if (wm == null) {
  5. wm = WindowManagerImpl.getDefault();
  6. }
  7. mWindowManager = new LocalWindowManager(wm);
  8. }
复制代码

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

java代码:
  1. private void addView(View view, ViewGroup.LayoutParams params, boolean nest){

  2. ViewRoot root; //ViewRoot,我们的主人公终于登场!
  3. synchronized (this) {

  4. root = new ViewRoot(view.getContext());
  5. root.mAddNesting = 1;
  6. view.setLayoutParams(wparams);

  7. if (mViews == null) {
  8. index = 1;
  9. mViews = new View[1];
  10. mRoots = new ViewRoot[1];
  11. mParams = new WindowManager.LayoutParams[1];
  12. } else {
  13. }
  14. index--;
  15. mViews[index] = view;
  16. mRoots[index] = root;
  17. mParams[index] = wparams;
  18. }
  19. root.setView(view, wparams, panelParentView);
  20. }
复制代码
ViewRoot是整个显示系统中最为关键的东西,看起来这个东西好像和View有那么点关系,其实它根本和View等UI关系不大,它不过是一个Handler罢了,唯一有关系的就是它其中有一个变量为Surface类型.我们看看它的定义.ViewRoot代码在framework/base/core/java/android/view/ViewRoot.java中

java代码:
  1. public final class ViewRoot extends Handler implements ViewParent,
  2. View.AttachInfo.Callbacks{
  3. private final Surface mSurface = new Surface();
  4. }
复制代码

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

java代码:
  1. public Surface() {
  2. mCanvas = new CompatibleCanvas(); //就是创建一个Canvas!
  3. }
复制代码

     如果你有兴趣的话,看看Surface其他构造函数,最终都会调用native的实现,而这些native的实现将和SurfaceFlinger 建立关系,但我们这里ViewRoot中的mSurface显然还没有到这一步。那它到底是怎么和SurfaceFlinger搞上的呢?这一切待会就会水落石出的。

java代码:
  1. public ViewRoot(Context context) {
  2. super();
  3. ....
  4. getWindowSession(context.getMainLooper());
  5. }
复制代码

        getWindowsession将建立和WindowManagerService的关系。

java代码:
  1. ublic static IWindowSession getWindowSession(Looper mainLooper) {

  2. synchronized (mStaticInit) {
  3. if (!mInitialized) {
  4. try {
  5. //sWindowSession是通过Binder机制创建的。终于让我们看到点希望了
  6. InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
  7. sWindowSession = IWindowManager.Stub.asInterface(

  8. ServiceManager.getService("window")).openSession(imm.getClient(), imm.getInputContext());
  9. mInitialized = true;
  10. } catch (RemoteException e) {
  11. }
  12. }
  13. return sWindowSession;
  14. }
  15. }
复制代码

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

java代码:
  1. public void setView(View view, WindowManager.LayoutParams attrs,

  2. View panelParentView) {
  3. synchronized (this) {
  4. requestLayout();
  5. try {
  6. res = sWindowSession.add(mWindow, mWindowAttributes,
  7. getHostVisibility(), mAttachInfo.mContentInsets);
  8. }
  9. }
复制代码

        requestLayout实现很简单,就是往handler中发送了一个消息。

java代码:
  1. public void requestLayout() {
  2. checkThread();
  3. mLayoutRequested = true;
  4. scheduleTraversals(); //发送DO_TRAVERSAL消息
  5. }

  6. public void scheduleTraversals() {
  7. if (!mTraversalScheduled) {
  8. mTraversalScheduled = true;
  9. sendEmptyMessage(DO_TRAVERSAL);
  10. }
  11. }
复制代码

        我们看看跨进程的那个调用.sWindowSession.add.它的最终实现在WindowManagerService中.
   [--->WindowSession::add()]

java代码:
  1. public int add(IWindow window, WindowManager.LayoutParams attrs,int viewVisibility, Rect outContentInsets) {
  2. return addWindow(this, window, attrs, viewVisibility, outContentInsets);
  3. }
复制代码

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

java代码:
  1. public int addWindow(Session session, IWindow client,WindowManager.LayoutParams attrs, int viewVisibility,Rect outContentInsets) {

  2. //创建一个WindowState,这个又是什么玩意儿呢?
  3. win = new WindowState(session, client, token,
  4. attachedWindow, attrs, viewVisibility);
  5. win.attach();
  6. return res;
  7. }
复制代码
WindowState类中有一个和Surface相关的成员变量,叫SurfaceSession。它会在
  attach函数中被创建。SurfaceSession嘛,就和SurfaceFlinger有关系了。我们待会看。
  好,我们知道ViewRoot创建及调用add后,我们客户端的View系统就和WindowManagerService建立了牢不可破的关系。
  另外,我们知道ViewRoot是一个handler,而且刚才我们调用了requestLayout,所以接下来消息循环下一个将调用的就是ViewRoot的handleMessage。

java代码:
  1. public void handleMessage(Message msg) {
  2. switch (msg.what) {
  3. case DO_TRAVERSAL:
  4. performTraversals();
复制代码

       performTraversals更加复杂无比,经过我仔细挑选,目标锁定为下面几个函数。当然,后面我们还会回到performTraversals,不过我们现在更感兴趣的是Surface是如何创建的。

java代码:
  1. private void performTraversals() {
  2. // cache mView since it is used so much below...
  3. final View host = mView;
  4. boolean initialized = false;
  5. boolean contentInsetsChanged = false;
  6. boolean visibleInsetsChanged;

  7. try {
  8. //ViewRoot也有一个Surface成员变量,叫mSurface,这个就是代表SurfaceFlinger的客户端
  9. //ViewRoot在这个Surface上作画,最后将由SurfaceFlinger来合成显示。刚才说了mSurface还没有什么内容。

  10. relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
复制代码

java代码:
  1. private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
  2. boolean insetsPending) throws RemoteException {
  3. //relayOut是跨进程调用,mSurface做为参数传进去了,看来离真相越来越近了呀!

  4. int relayoutResult = sWindowSession.relayout(
  5. mWindow, params,
  6. (int) (mView.mMeasuredWidth * appScale + 0.5f),
  7. (int) (mView.mMeasuredHeight * appScale + 0.5f),
  8. viewVisibility, insetsPending, mWinFrame,
  9. mPendingContentInsets, mPendingVisibleInsets,
  10. mPendingConfiguration, mSurface); mSurface做为参数传进去了。
  11. }
复制代码

        我们赶紧转到WindowManagerService去看看吧。

java代码:
  1. public Surface(SurfaceSession s,int pid, String name, int display, int w, int h, int format, int flags)throws OutOfResourcesException {
  2. mCanvas = new CompatibleCanvas();
  3. init(s,pid,name,display,w,h,format,flags); ---->调用了native的init函数。
  4. mName = name;
  5. }
复制代码

       到这里,不进入JNI是不可能说清楚了.不过我们要先回顾下之前的关键步骤.
  add中,new了一个SurfaceSession
  创建new了一个Surface
  调用copyFrom,把本地Surface信息传到outSurface中
  JNI层
  上面两个类的JNI实现都在framework/base/core/jni/android_view_Surface.cpp中.
  [---->SurfaceSession:: SurfaceSession()]

java代码:
  1. public class SurfaceSession {

  2. /** Create a new connection with the surface flinger. */
  3. public SurfaceSession() {
  4. init();
  5. }
复制代码

       它的init函数对应为:
       [--->SurfaceSession_init]

java代码:
  1. static void SurfaceSession_init(JNIEnv* env, jobject clazz){

  2. //SurfaceSession对应为SurfaceComposerClient
  3. sp<SurfaceComposerClient> client = new SurfaceComposerClient;
  4. client->incStrong(clazz);

  5. //Google常用做法,在JAVA对象中保存C++对象的指针。
  6. env->SetIntField(clazz, sso.client, (int)client.get());
  7. }
复制代码
Surface的init对应为:

       [--->Surface_init]

java代码:
  1. static void Surface_init(

  2. JNIEnv* env, jobject clazz,

  3. jobject session,

  4. jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)

  5. {

  6. SurfaceComposerClient* client =

  7. (SurfaceComposerClient*)env->GetIntField(session, sso.client);



  8. sp<SurfaceControl> surface;

  9. if (jname == NULL) {

  10. //client是SurfaceComposerClient,返回的surface是一个SurfaceControl

  11. //真得很复杂!

  12. surface = client->createSurface(pid, dpy, w, h, format, flags);

  13. } else {

  14. const jchar* str = env->GetStringCritical(jname, 0);

  15. const String8 name(str, env->GetStringLength(jname));

  16. env->ReleaseStringCritical(jname, str);

  17. surface = client->createSurface(pid, name, dpy, w, h, format, flags);

  18. }

  19. //把surfaceControl信息设置到Surface对象中

  20. setSurfaceControl(env, clazz, surface);

  21. }



  22. static void setSurfaceControl(JNIEnv* env, jobject clazz,

  23. const sp<SurfaceControl>& surface)

  24. {

  25. SurfaceControl* const p =

  26. (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);

  27. if (surface.get()) {

  28. surface->incStrong(clazz);
  29. }
  30. if (p) {
  31. p->decStrong(clazz);
  32. }
  33. env->SetIntField(clazz, so.surfaceControl, (int)surface.get());
  34. }
复制代码

        [--->Surface_copyFrom]

java代码:
  1. static void Surface_copyFrom(
  2. JNIEnv* env, jobject clazz, jobject other)
  3. {
  4. const sp<SurfaceControl>& surface = getSurfaceControl(env, clazz);
  5. const sp<SurfaceControl>& rhs = getSurfaceControl(env, other);
  6. if (!SurfaceControl::isSameSurface(surface, rhs)) {
  7. setSurfaceControl(env, clazz, rhs);
  8. //把本地那个surface的surfaceControl对象转移到outSurface上
  9. }
  10. }
复制代码

        这里仅仅是surfaceControl的转移,但是并没有看到Surface相关的信息。
  那么Surface在哪里创建的呢?为了解释这个问题,我使用了终极武器,aidl。
  1 终极武器AIDL
  aidl可以把XXX。aidl文件转换成对应的java文件。我们刚才调用的是WindowSession的
  relayOut函数。如下:

java代码:
  1. sWindowSession.relayout(
  2. mWindow, params,(int) (mView.mMeasuredWidth * appScale + 0.5f),(int) (mView.mMeasuredHeight * appScale + 0.5f),viewVisibility, insetsPending, mWinFrame,
  3. mPendingContentInsets, mPendingVisibleInsets,
  4. mPendingConfiguration, mSurface);
复制代码

       它的aidl文件在framework/base/core/java/android/view/IWindowSession.aidl中

java代码:
  1. interface IWindowSession {
  2. int add(IWindow window, in WindowManager.LayoutParams attrs,in int viewVisibility, out Rect outContentInsets);
  3. void remove(IWindow window);
  4. //注意喔,这个outSurface前面的是out,表示输出参数,这个类似于C++的引用。
  5. int relayout(IWindow window, in WindowManager.LayoutParams attrs,
  6. int requestedWidth, int requestedHeight, int viewVisibility,
  7. boolean insetsPending, out Rect outFrame, out Rect outContentInsets,
  8. out Rect outVisibleInsets, out Configuration outConfig,
  9. out Surface outSurface);
复制代码

       刚才说了,JNI及其JAVA调用只是copyFrom了SurfaceControl对象到outSurface中,但是没看到哪里创建Surface.这其中的奥秘就在aidl文件编译后生成的java文件中.

  你在命令行下可以输入:
  aidl -Id:\android-2.2-froyo-20100625-source\source\frameworks\base\core\java-Id:\android-2.2-froyo-20100625-source\source\frameworks\base\Graphics\java d:\android-2.2-froyo-20100625-source\source\frameworks\base\core\java\android\view\IWindowSession.aidl test.java
  以生成test.java文件.-I参数指定include目录,例如aidl有些参数是在别的java文件中指定的,那么这个-I就需要把这些目录包含进来.

  先看看ViewRoot这个客户端生成的代码是什么.

java代码:

  1. public int relayout(

  2. android.view.IWindow window,

  3. android.view.WindowManager.LayoutParams attrs,

  4. int requestedWidth, int requestedHeight,
  5. int viewVisibility, boolean insetsPending,
  6. android.graphics.Rect outFrame,
  7. android.graphics.Rect outContentInsets,
  8. android.graphics.Rect outVisibleInsets,
  9. android.content.res.Configuration outConfig,
  10. android.view.Surface outSurface) ---->outSurface是第11个参数

  11. throws android.os.RemoteException{

  12. android.os.Parcel _data = android.os.Parcel.obtain();
  13. android.os.Parcel _reply = android.os.Parcel.obtain();
  14. int _result;
  15. try {
  16. _data.writeInterfaceToken(DESCRIPTOR);
  17. _data.writeStrongBinder((((window!=null))?(window.asBinder()):(null)));

  18. if ((attrs!=null)) {
  19. _data.writeInt(1);
  20. attrs.writeToParcel(_data, 0);
  21. }else {
  22. _data.writeInt(0);
  23. }

  24. _data.writeInt(requestedWidth);
  25. _data.writeInt(requestedHeight);
  26. _data.writeInt(viewVisibility);
  27. _data.writeInt(((insetsPending)?(1):(0)));

  28. //奇怪,outSurface的信息没有写到_data中。那.....

  29. mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0);
  30. _reply.readException();
  31. _result = _reply.readInt();

  32. if ((0!=_reply.readInt())) {
  33. outFrame.readFromParcel(_reply);
  34. }

  35. ....
  36. if ((0!=_reply.readInt())) {
  37. outSurface.readFromParcel(_reply); //从Parcel中读取信息来填充outSurface
  38. }
  39. }
  40. finally {

  41. _reply.recycle();
  42. _data.recycle();
  43. }
  44. return _result;

  45. }
复制代码

       真奇怪啊,Binder客户端这头竟然没有把outSurface的信息发过去。我们赶紧看看服务端。
  服务端这边处理是在onTranscat函数中。

java代码:
  1. @Override
  2. public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{

  3. switch (code){
  4. case TRANSACTION_relayout:{
  5. data.enforceInterface(DESCRIPTOR);
  6. android.view.IWindow _arg0;
  7. android.view.Surface _arg10;
  8. //刚才说了,Surface信息并没有传过来,那么我们在relayOut中看到的outSurface是怎么
  9. //出来的呢?看下面这句,原来在服务端这边竟然new了一个新的Surface!!!

  10. _arg10 = new android.view.Surface();
  11. int _result = this.relayout(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8, _arg9, _arg10);
  12. reply.writeNoException();
  13. reply.writeInt(_result);
  14. //_arg10是copyFrom了,那怎么传到客户端呢?
  15. if ((_arg10!=null)) {
  16. reply.writeInt(1);//调用Surface的writeToParcel,把信息加入reply
  17. _arg10.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);

  18. }

  19. return true;

  20. }
复制代码

太诡异了!竟然有这么多花花肠子。我相信如果没有aidl的帮助,我无论如何也不会知道这其中的奥妙。
  那好,我们的流程明白了。
  客户端虽然传了一个surface,但其实没传递给服务端
  服务端调用writeToParcel,把信息写到Parcel中,然后数据传回客户端
  客户端调用Surface的readFromParcel,获得surface信息。
  那就去看看writeToParcel吧。
  [---->Surface_writeToParcel]

java代码:
  1. static void Surface_writeToParcel(

  2. JNIEnv* env, jobject clazz, jobject argParcel, jint flags){

  3. Parcel* parcel = (Parcel*)env->GetIntField(
  4. argParcel, no.native_parcel);

  5. const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));
  6. //还好,只是把数据序列化到Parcel中
  7. SurfaceControl::writeSurfaceToParcel(control, parcel);
  8. if (flags & PARCELABLE_WRITE_RETURN_VALUE) {
  9. setSurfaceControl(env, clazz, 0);
  10. }
  11. }
复制代码

        那看看客户端的Surface_readFromParcel吧。
        [----->Surface_readFromParcel]

java代码:
  1. static void Surface_readFromParcel(

  2. JNIEnv* env, jobject clazz, jobject argParcel){
  3. Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);
  4. //客户端这边还没有surface呢

  5. const sp<Surface>& control(getSurface(env, clazz));
  6. //不过我们看到希望了,根据服务端那边Parcel信息来构造一个新的surface

  7. sp<Surface> rhs = new Surface(*parcel);

  8. if (!Surface::isSameSurface(control, rhs)) {
  9. setSurface(env, clazz, rhs); //把这个新surface赋给客户端。终于我们有了surface!
  10. }

  11. }
复制代码

       到此,我们终于七拐八绕的得到了surface,这其中经历太多曲折了。下一节,我们将精简这其中复杂的操作,统一归到Native层,以这样为切入点来了解Surface的工作流程和原理。
  好,反正你知道ViewRoot调用了relayout后,Surface就真正从WindowManagerService那得到了。继续回到ViewRoot,其中还有一个重要地方是我们知道却不了解的。

java代码:
  1. private void performTraversals() {

  2. // cache mView since it is used so much below...
  3. final View host = mView;
  4. boolean initialized = false;
  5. boolean contentInsetsChanged = false;
  6. boolean visibleInsetsChanged;

  7. try {

  8. relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
  9. // relayoutWindow完后,我们得到了一个无比宝贵的Surface
  10. //那我们画界面的地方在哪里?就在这个函数中,离relayoutWindow不远处。

  11. ....
  12. boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();

  13. if (!cancelDraw && !newSurface) {
  14. mFullRedrawNeeded = false;
  15. draw(fullRedrawNeeded); //draw?draw什么呀?
  16. }
复制代码

       [--->ViewRoot::draw()]

java代码:
  1. private void draw(boolean fullRedrawNeeded) {

  2. Surface surface = mSurface; //嘿嘿,不担心了,surface资源都齐全了
  3. if (surface == null || !surface.isValid()) {
  4. return;
  5. }

  6. if (mAttachInfo.mViewScrollChanged) {
  7. mAttachInfo.mViewScrollChanged = false;
  8. mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
  9. }

  10. int yoff;
  11. final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();
  12. if (scrolling) {
  13. yoff = mScroller.getCurrY();
  14. } else {
  15. yoff = mScrollY;
  16. }

  17. if (mCurScrollY != yoff) {
  18. mCurScrollY = yoff;
  19. fullRedrawNeeded = true;
  20. }

  21. float appScale = mAttachInfo.mApplicationScale;
  22. boolean scalingRequired = mAttachInfo.mScalingRequired;
  23. Rect dirty = mDirty;

  24. if (mUseGL) { //我们不用OPENGL
  25. ...
  26. }

  27. Canvas canvas;
  28. try {
  29. int left = dirty.left;
  30. int top = dirty.top;
  31. int right = dirty.right;

  32. int bottom = dirty.bottom;
  33. //从Surface中锁定一块区域,这块区域是我们认为的需要重绘的区域
  34. canvas = surface.lockCanvas(dirty);
  35. // TODO: Do this in native
  36. canvas.setDensity(mDensity);
  37. }

  38. try {
  39. if (!dirty.isEmpty() || mIsAnimating) {
  40. long startTime = 0L;
  41. try {
  42. canvas.translate(0, -yoff);

  43. if (mTranslator != null) {

  44. mTranslator.translateCanvas(canvas);

  45. }

  46. canvas.setScreenDensity(scalingRequired? DisplayMetrics.DENSITY_DEVICE : 0);

  47. //mView就是之前的decoreView,
  48. mView.draw(canvas);
  49. }
  50. } finally {
  51. //我们的图画完了,告诉surface释放这块区域

  52. surface.unlockCanvasAndPost(canvas);
  53. }
  54. if (scrolling) {
  55. mFullRedrawNeeded = true;
  56. scheduleTraversals();
  57. }
  58. }
复制代码

原创粉丝点击