Android Activity View加载与绘制流程深入刨析源码

作者:niuyongzhi 时间:2023-10-02 18:03:48 

1.App的启动流程,从startActivity到Activity被创建。

这个流程主要是ActivityThread和ActivityManagerService之间通过binder进行通信来完成。

ActivityThread可以拿到AMS 的BinderProxy。AMS可以拿到ActivityThread的BinderProxy ApplicationThread。这样双方就可以互相通讯了。

当ApplicationThread 接收到AMS的Binder调用后,会通过handler机制来执行对应的操作。

可以这样说handler和binder是android framework重要的两块基石。

而Activity的创建就是在ActivityThread中调用handleLaunchActivity方法实现。

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //创建一个Activity,并调用生命周期onCreate方法
        Activity a = performLaunchActivity(r, customIntent);
         if (a != null) {
            //如果Activity成功创建,则会调用生命周期onResume方法。
            handleResumeActivity(r.token, false, r.isForward,
                     !r.activity.mFinished && !r.startsNotResumed);
         }
 }

2.接下来看performLaunchActivity。创建一个Activity。

如果Activity创建成功,先调用activity.attach(),在这个方法中,创建了Activity的PhoneWindow实例。

然后mInstrumentation.callActivityOnCreate,调用了Activity的onCreate生命周期方法。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
         Activity activity = null;
               try {
                   // Instrumentation mInstrumentation;
                   //拿到类加载器,最后会通过反射的方式创建Activity对象
                   //(Activity) cl.loadClass(className).newInstance();
                   java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
                   activity = mInstrumentation.newActivity(
                           cl, component.getClassName(), r.intent);
               } catch (Exception e) {
                   if (!mInstrumentation.onException(activity, e)) {
                       throw new RuntimeException(
                           "Unable to instantiate activity " + component
                           + ": " + e.toString(), e);
                   }
               }
               if(activity!=null){
               //调用Activity.attach,
                activity.attach(appContext, this, getInstrumentation(), r.token,
                                       r.ident, app, r.intent, r.activityInfo, title, r.parent,
                                       r.embeddedID, r.lastNonConfigurationInstances, config,
                                       r.voiceInteractor);
               }
               //通过这个方法调用了Activity的onCreate方法
               if (r.isPersistable()) {
                   mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                   // activity.performCreate(icicle, persistentState);
                   // onCreate(icicle, persistentState);
               } else {
                   mInstrumentation.callActivityOnCreate(activity, r.state);
                   //activity.performCreate(icicle);
                   // onCreate(icicle);
               }
         return activity;
}
final void attach(...){
    //初始化了window的子类 初始化了PhoneWindow,PhoneWindow中有一个Decoview对象
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowManager(
                   (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                   mToken, mComponent.flattenToString(),
                   (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    //mWindowManager 是WindowManagerImpl的实例。
    mWindowManager = mWindow.getWindowManager();
}

3.setContentView,布局加载流程:

在Activity.onCreate方法中,我们通过setContentView(R.layout.activity_main);就能在界面显示,那android是怎么做到的?

1)调用installDecor(),初始化Decorview和mContentParent。

2)mLayoutInflater.inflate(),将布局文件,解析成android中对应的View。

//getWindow拿到的是PhoneWindow
  public void setContentView(@LayoutRes int layoutResID) {
       getWindow().setContentView(layoutResID);
  }
@Override
public void setContentView(int layoutResID) {
   if (mContentParent == null) {
         //初始化Decorview和mContentParent
         installDecor();
     }
   //加载并解析传进来的布局文件,并add到mContentParent上。
   //这个操作比较耗时,因为要从xml文件中解析,然后再通过反射的方式生成Java中的View对象。
   //所以在recyclerView和listView中要对这一步进行缓存优化,
   mLayoutInflater.inflate(layoutResID, mContentParent);
}

先看installDecor(),通过generateDecor() new了一个DecoView实例并赋值给了mDecor,

mDecor是PhoneView的一个变量。

//mDecorView是PhoneWindow的一个成员变量
private DecorView mDecor;
private void installDecor() {
    //创建mDecor
    if (mDecor == null) {
        mDecor = generateDecor(-1);
 //new DecorView(context, featureId, this, getAttributes());
    }
   //创建mContentParent,将创建的DecorView作为参数传递
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
    }
}

通过generateLayout(decor) 加载了一个系统的layout文件,在android.jar--res--layout目录下。

在mDecor.onResourcesLoaded方法中加载了这个布局,并添加到了mDecor中。

DecorView继承自FrameLayout,是一个真正的view。

然后通过findViewbyid,找到了一个ViewGoup,可以看下面的布局文件。

ID_ANDROID_CONTENT = com.android.internal.R.id.content; ,并把这个返回出去了。

这个view 就installDecor()方法中的mContentParent()

protected ViewGroup generateLayout(DecorView decor) {
       int layoutResource;
       //这个布局文件就在android.jar--res--layout目录下。
       layoutResource = R.layout.screen_simple;
       mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
      // int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
      //这个R.id.content就是定义在screen_simple中的一个FrameLayout
      //  android:id="@android:id/content"
       ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
       ....
       return contentParent;
 }
  //将布局R.layout.screen_simple 加载成view,并添加到DecorView中
 void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
       ......
        final View root = inflater.inflate(layoutResource, null);
       ......
       // Put it below the color views.
        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
 }
//R.layout.screen_simple
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         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" />
</LinearLayout>

总结:至此 installDecor();已经完成。主要是创建了mDecorView,并加载了一个系统的布局,R.layout.screen_simple,

将加载得到的View添加到了mDecorView中,并findViewById(R.id.content)的到的View赋值给了mParentContent。

回到setContentView中看第二行代码:

layoutResID 就是传入的布局文件id,mContentParent就是加载的系统的布局文件中id为&ldquo;content&rdquo;的view

mLayoutInflater.inflate(layoutResID, mContentParent);

//加载并解析传进来的布局文件,并add到mContentParent上。
   mLayoutInflater.inflate(layoutResID, mContentParent);
 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
  }
  public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
      final Resources res = getContext().getResources();
       //通过 Resource 得到了一个XML解析器。
      final XmlResourceParser parser = res.getLayout(resource);
      try {
          //解析我们自定义的layout,并添加到mParentContent上。
          //将xml中定义的ViewGroup和View解析成Java对象。
          //这块代码会单独写文章讲解
          return inflate(parser, root, attachToRoot);
      } finally {
          parser.close();
      }
  }

至此上面的关系可以总结为:

Activity-->PhoneWindow-->mDecorView-->addView(R.layout.screen.simple)-->

R.id.content-->mParentContent-->addView(R.layout.activity.main)

我们自己写的layout已经添加到了系统的DecorView中。

4.我们知道View有三个重要的方法onMeasure,onLayout,onDraw,

那这些方法是在哪里调用的?我们创建的View是如何添加到屏幕上的呢?

回到handleLaunchActivity方法中,还有一个handleResumeActivity,通过performResumeActivity 会执行Activity的onResume生命周期方法。

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
          //创建一个Activity,并调用生命周期onCreate方法
          Activity a = performLaunchActivity(r, customIntent);
           if (a != null) {
              //如果Activity成功创建,则会调用生命周期onResume方法。
              handleResumeActivity(r.token, false, r.isForward,
                       !r.activity.mFinished && !r.startsNotResumed);
           }
  }
 final void handleResumeActivity(IBinder token,
              boolean clearHide, boolean isForward, boolean reallyResume) {
   //执行Activity onResume生命周期方法
  ActivityClientRecord r = performResumeActivity(token, clearHide);
   if(r!=null){
               final Activity a = r.activity;
               //通过上面代码我们知道 window 是PhoneWindow
               r.window = r.activity.getWindow();
               //拿到DecorView
               View decor = r.window.getDecorView();
               //wm 是 WindowManagerImpl
               ViewManager wm = a.getWindowManager();
                  if (a.mVisibleFromClient) {
                        a.mWindowAdded = true;
                        //关键代码,将decorView 添加到wm中
                        wm.addView(decor, l);
                  }
          }
}
public final ActivityClientRecord performResumeActivity(IBinder token,
            boolean clearHide) {
       //执行Activity onStart onResume方法
       ActivityClientRecord r = mActivities.get(token);
       r.activity.performResume();
    return r;
}
//Activity中 onStart onResume 生命周期方法
final void performResume(boolean followedByPause, String reason) {
        performRestart(true /* start */, reason);
        mInstrumentation.callActivityOnResume(this);
}
 final void performRestart(boolean start, String reason) {
    if (start) {
        performStart(reason);
     }
 }
public void callActivityOnResume(Activity activity) {
    activity.mResumed = true;
    activity.onResume();
}

执行完onResume方法后:

wm.addView(decor, l);将Activity的DecorView添加到了wm中。

ViewManager wm = a.getWindowManager();

ViewManager 是一个抽象类,实例是WindowManagerImpl。

在WindowManagerImpl中通过单例模式获取了一个WindowManagerGlobal对象。

既然是单例模式获取的对象,也就在一个进程,ActivityThread 主进程中 只有一个实例。

//WindowManagerImpl.java addView 方法
//WindowManagerGlobal 是一个单例模式,在ActivityThread 主进程中 只有一个实例。
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
   @Override
   public void addView(View view, ViewGroup.LayoutParams params) {
       mGlobal.addView(view, params, mDisplay, mParentWindow);
}
WindowManagerGlobal.java
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
public void addView(View view, ViewGroup.LayoutParams params,
           Display display, Window parentWindow) {
      final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
        ViewRootImpl root;
           ........
             root = new ViewRootImpl(view.getContext(), display);
           .........
          view.setLayoutParams(wparams);
        //将decorView ViewRootImpl 存到集合中
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
        root.setView(view, wparams, panelParentView);
}

在WindowManagerGlobal中创建了一个ViewRootImpl对象。这是很重要的一个对象。

将传进来的DecorView设置在了root中,root.setView(view, wparams, panelParentView);

ViewRootImpl.java
 public ViewRootImpl(Context context, Display display) {
       mWindowSession = WindowManagerGlobal.getWindowSession();
       mThread = Thread.currentThread();
 }
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
     // Schedule the first layout -before- adding to the windowmanager,
       //to make sure we do the relayout before receiving
       // any other events from the system.
      //在添加到windowmanager之前进行布局,确保在收到系统的event之前进行relayout
      // 出发布局的绘制流程,measure,layout,view 的绘制流程,就是从这来的。
      //这个方法保证了,在添加的屏幕前已经完成了测量、绘制。
       requestLayout();
       try {
           //通过binder和WindowManagerService进行通信,将view添加到屏幕上。
           // mWindow = new W(this);
           // static class W extends IWindow.Stub {}
           //添加到屏幕的逻辑,稍后写文章详细分析。
            res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                       getHostVisibility(), mDisplay.getDisplayId(),
                                       mAttachInfo.mContentInsets, mInputChannel);
       }catch (RemoteException e) {
           throw new RuntimeException("Adding window failed", e);
       }
}

在setView()方法中有两句很重要的代码。

requestLayout();

res = mWindowSession.addToDisplay()

1) requestLayout()请求布局,调用者行代码会执行view的measue,layou,draw方法。

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

checkThread这有一个很重要的知识点,为啥子线程不能修改主线程创建的view?

//mThread是在ViewRootImp初始初始化是所在的线程。
//在requestLayout时,会获取当前请求布局的线程。
//如果两个线程不一致就会抛异常,只有原始创建的线程,可以修改views
 void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

在scheduleTraversals方法中。通过mChoreographer编舞者对象,最后执行了mTraversalRunnable中的方法。这块代码在消息屏障文章中,详细分解。

//开始遍历
void scheduleTraversals() {
  if (!mTraversalScheduled) {
    mTraversalScheduled = true;
       //发送一个同步消息,在handler机制分析中提过一下,同步屏障。关于这个知识点还会做详细的分析。
           mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
           //mChoreographer 编舞者类。
           //通过编舞者,执行 runnable中 doTraversal 方法
           mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
  }
}
final class TraversalRunnable implements Runnable {
          @Override
          public void run() {
              doTraversal();
          }
   }
void doTraversal() {
       if (mTraversalScheduled) {
           mTraversalScheduled = false;
           mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
           performTraversals();
       }
 }

在TraversalRunnable中执行了doTraversal()方法。在这里调用了三个重要的方法

performMeasure(),performLayout(),performDraw()。

这也就是为什么View的绘制流程是先调用onMeasure,onLayout,后调用onDraw的原因。

并且这些写方法都是在onResume执行才调用的。所以,这就是我们想拿到View的宽高,在onResume之前拿不到的原因。

private void performTraversals() {
      //mView DecorView
      final View host = mView;
     // Ask host how big it wants to be
      performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
       if (didLayout) {
         performLayout(lp, desiredWindowWidth, desiredWindowHeight);
      }
     performDraw();
}
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
 }
  private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
             int desiredWindowHeight) {
       final View host = mView;
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
 }
  private void performDraw() {
      draw(fullRedrawNeeded);
      //
 }

来源:https://blog.csdn.net/niuyongzhi/article/details/125922833

标签:Android,Activity,View,加载,绘制
0
投稿

猜你喜欢

  • springboot-mongodb的多数据源配置的方法步骤

    2022-05-06 12:04:48
  • Java之JFrame输出Helloworld实例

    2022-01-19 16:40:52
  • java使用计算md5校验码方式比较两个文件是否相同

    2023-05-10 23:43:16
  • C#单例模式与多线程用法介绍

    2023-03-25 09:36:07
  • Android实现简单画图画板

    2022-04-02 12:52:56
  • Android studio报: java.lang.ExceptionInInitializerError 错误

    2022-08-14 14:21:30
  • Android 6.0 扫描不到 Ble 设备需开启位置权限的方法

    2021-09-15 04:59:25
  • Android 动画之TranslateAnimation应用详解

    2023-06-27 06:17:59
  • Java Spring @Lazy延迟注入源码案例详解

    2023-06-24 05:21:07
  • android端微信支付V3版本地签名统一下单详解

    2023-05-18 02:37:44
  • Java Config下的Spring Test几种方式实例详解

    2022-12-17 00:31:36
  • Android UI 实现老虎机详解及实例代码

    2022-06-09 05:01:30
  • Maven的生命周期与自定义插件实现方法

    2022-04-06 20:13:48
  • DevExpress SplitContainerControl用法总结

    2022-06-13 22:10:24
  • java反射技术与类使用示例

    2021-09-14 06:34:34
  • Java基础之数组详解

    2021-08-06 08:15:28
  • 浅谈Mybatis传参类型如何确定

    2023-11-12 12:13:21
  • java WSDL接口webService实现方式

    2022-06-15 21:03:29
  • JAVA字符串占位符使用方法实例

    2021-09-20 17:30:27
  • MyBatis源码解析——获取SqlSessionFactory方式

    2023-04-03 15:09:14
  • asp之家 软件编程 m.aspxhome.com