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

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为“content”的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);
       //
  }

到此这篇关于Android Activity View加载与绘制流程深入刨析源码的文章就介绍到这了,更多相关Android Activity View内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android实现在一个activity中添加多个listview的方法

    本文实例讲述了Android实现在一个activity中添加多个listview的方法.分享给大家供大家参考,具体如下: listview的id一般是这样的android:id="@id/android:list".请注意,这时的Activity是ListActivity,在这样的Activity中到目前为止,我没有发现添加多个activity的方法.要添加多个listview,所用的Activity要是一般的Activity,listview的id也要自己定义,改成android:i

  • Android activity和view判断滑动

    Android activity和view判断滑动 实例代码: //手指按下的点为(x1, y1)手指离开屏幕的点为(x2, y2) float x1 = 0; float x2 = 0; float y1 = 0; float y2 = 0; @Override public boolean onTouchEvent(MotionEvent event) { //继承了Activity的onTouchEvent方法,直接监听点击事件 if(event.getAction() == Motion

  • Android中通过view方式获取当前Activity的屏幕截图实现方法

    此方法是通过view的方式获取当前activity的屏幕截图,并不是framebuffer的方式,所以有一定的局限性.但是这种方法相对简单,容易理解. 首先通过下面的函数获取Bitmap格式的屏幕截图: 复制代码 代码如下: public Bitmap myShot(Activity activity) { // 获取windows中最顶层的view View view = activity.getWindow().getDecorView(); view.buildDrawingCache()

  • Android Activity通用悬浮可拖拽View封装的思路详解

    目录 1,背景 2,思路 2.1,封装通用的基础悬浮View 2.1,继承通用View 2.3,设计view的控制器 2.4,view的添加和使用 1,背景 在开发中总会遇到一个可拖拽的悬浮View,不管是在开发中,还是在线上,都时长有这样的控件,我们通常遇到这种情况,经常需要自己封装,需要耗费时间,我这边封装了一个可以通用的悬浮可拖拽View,这样使用的时候,只需要传入自己要设计的样式和位置既可 2,思路 2.1,封装通用的基础悬浮View 设计通用的父View 1,传入的childView是

  • Android编程开发之TextView单击链接弹出Activity的方法

    本文实例讲述了Android编程开发之TextView单击链接弹出Activity的方法.分享给大家供大家参考,具体如下: 话不多说直接上码: 核心源码: package com.example.textview4; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.text.SpannableString; import android.tex

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

    1.App的启动流程,从startActivity到Activity被创建. 这个流程主要是ActivityThread和ActivityManagerService之间通过binder进行通信来完成. ActivityThread可以拿到AMS 的BinderProxy.AMS可以拿到ActivityThread的BinderProxy ApplicationThread.这样双方就可以互相通讯了. 当ApplicationThread 接收到AMS的Binder调用后,会通过handler机

  • SpringCloud Gateway加载断言predicates与过滤器filters的源码分析

    我们今天的主角是Gateway网关,一听名字就知道它基本的任务就是去分发路由.根据不同的指定名称去请求各个服务,下面是Gateway官方的解释: https://spring.io/projects/spring-cloud-gateway,其他的博主就不多说了,大家多去官网看看,只有官方的才是最正确的,回归主题,我们的过滤器与断言如何加载进来的,并且是如何进行对请求进行过滤的. 大家如果对SpringBoot自动加载的熟悉的话,一定知道要看一个代码的源码,要找到META-INF下的spring

  • Android从xml加载到View对象过程解析

    我们从Activity的setContentView()入手,开始源码解析, //Activity.setContentView public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); initActionBar(); } //PhoneWindow.setContentView public void setContentView(int layoutResID) { if (

  • Android 开发使用Activity实现加载等待界面功能示例

    本文实例讲述了Android 开发使用Activity实现加载等待界面功能.分享给大家供大家参考,具体如下: 实现加载等待界面我用了两种方式,一种是用PopupWindow实现,另一种便是用Activity实现.用PopupWindow实现方法请见我的另一篇博客: android使用PopupWindow实现加载等待界面 好了,下面开始.先上效果: 基本原理就是在主界面点击按钮(以登录按钮为例)之后,打开一个新的Activity,此Activity以对话框形式展示.首先,主界面(一个登录按钮以及

  • android监听View加载完成的示例讲解

    最近项目中需要实现一个GridView显示6*5=30项,并铺满整个界面,界面中还有自定义ActionBar等其他控件,所以需要获取剩下屏幕的高度.通过百度得知View有一个监听函数,亲测使用有效,特此记录,方便日后查阅. gv_test.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayo

  • 详解android 用webview加载网页(https和http)

    1.Android 加载https请求的网页的时候 打不开 当load有ssl层的https页面时,如果这个网站的安全证书在Android无法得到认证,WebView就会变成一个空白页,而并不会像PC浏览器中那样跳出一个风险提示框.因此,我们必须针对这种情况进行处理.(这个证书限于2.1版本以上的Android 系统才可以) wv.setWebViewClient(new WebViewClient(){ @override public void onReceivedSslError(WebV

  • Android实现图片加载进度提示

    本文实例为大家分享了Android实现图片加载进度提示的具体代码,供大家参考,具体内容如下 先上图: 实现原理: 第一个控件的实现原理是重写ImageView的onDraw()方法,利用Canvas的clipRect()方法控制图片的显示区域,主键扩大图片的显示区域,从而实现逐渐增加的效果 关键代码: public class LoadingImageView extends ImageView { /*** 背景图片 */ private Drawable bgDrawable; /**前景图

  • 详解Android GLide图片加载常用几种方法

    目录 缓存浅析 GLide图片加载方法 图片加载周期 图片格式(Bitmap,Gif) 缓存 集成网络框架 权限 占位符 淡入效果 变换 启动页/广告页 banner 固定宽高 圆角 圆形 总结 缓存浅析 为啥要做缓存? android默认给每个应用只分配16M的内存,所以如果加载过多的图片,为了 防止内存溢出 ,应该将图片缓存起来. 图片的三级缓存分别是: 1.内存缓存 2.本地缓存 3.网络缓存 其中,内存缓存应优先加载,它速度最快:本地缓存次优先加载,它速度也快:网络缓存不应该优先加载,它

  • Android开发之加载图片的方法

    本文实例讲述了Android开发之加载图片的方法.分享给大家供大家参考.具体分析如下: 加载网络上的图片需要在manifest中配置访问网络的权限,如下: <uses-permission android:name="android.permission.INTERNET" /> 如果不配置这个权限的话,会报错:unknown host exception. package com.example.loadimgfromweb; import java.io.InputSt

  • Android Glide图片加载(加载监听、加载动画)

    本文实例为大家分享了Android Glide图片加载的具体代码,供大家参考,具体内容如下 1.普通用法 Glide.with(context) .load(url) .into(view); with中可以放context.activity.fragment..:当放activity.fragment时glide会根据生命周期来加载图片.推荐使用activity. 2.设置加载中和加载失败的图片 Glide.with(context) .load(url) .placeholder(R.dra

随机推荐