Android总结

话不多说,先罗列一些知识点:

  • 基础知识点:Fragment、Activity、Service、广播、Binder、WebView安全漏洞
  • 异步消息处理机制:Handler、IntentService、AsyncTask、HandlerThread
  • View相关:View渲染和绘制、事件分发、ListView
  • 构建:编译打包、Gradle、Proguard混淆、渠道包、Git
  • 开源框架源码:网络框架(Retrofit/Okhttp/Volley)、图片框架(Glide/Fresco/UIL)、Ioc框架(Dagger2/Butterknife)
  • 热门前沿技术:插件化、热更新、RxJava、进程保活

而作为一名高级android需要掌握的知识点如下:

  • 基础知识点:四大组件(Activity启动模式、Service启动模式、同步异步任务等)
  • 深入知识点:AIDL、Binder、多进程以及并发、View绘制流程、View事件分发、Handler消息循环机制

因此平时我们需要做下面这些事情:

  • 选一个相对比较擅长的领域
  • 基础API等一定要背,但不是死记硬背
  • 试着去了解这个领域市面上的技术
  • 如果有时间的话,去研究一个众所周知的库的源码

一、四大组件

1、Activity

1)、Activity的四种状态:

  • Runing:处于活动状态,用户触碰屏幕可以进行交互处于栈顶的状态
  • Paused:失去焦点的状态,被一个非全屏的Activity占据或者被一个对话框占据,此时Activity只是与用户失去焦点,但并没有被回收当系统内存紧张的时候有可能被回收
  • Stopped:不可见状态,被一个Activity覆盖的时候并且不可见的状态,但是并没有被回收当系统内存紧张的时候有可能被回收
  • Killed:被系统已经回收的状态,此时保存的成员变量肯定不存在了

2)、Activity的生命周期:

  • Activity启动场景:onCreate()创建 >onStart()启动但是还没有获取到焦点 >onResume()获取到焦点可以与用户进行交互
  • 点击Home回到主界面Activity不可见的场景: > onPause() > onStop()
  • 当我们再次回到原Activity的场景: > onRestart() > onStart() > onResume()
  • 退出当前Activity的场景: >onPause() > onStop() > onDestroy()

3)、Android的进程优先级:

  • 前台:处于栈顶与用户进行交互的Activity或者是与其进行绑定的Service
  • 可见:处于前台但是失去了焦点不可点击触摸交互的Activity
  • 服务:在后台开启的Service
  • 后台:处于前台的Activity被新的Activity覆盖,但是不会马上被系统回收,这样的进程的就叫后台进程
  • 空:不属于前面四种的任意一种进程就叫空进程,可能随时都会被杀掉

4)、Android的任务栈:

5)、Activity的启动模式:

  • standard:默认启动模式,当启动一个新的Activity,它就会进入任务栈,并处于栈顶的位置,系统不会判断这个Activity在栈中是否存在,每次启动都会创建一个新的实例
  • singletop:当启动的Activity已经位于栈顶时,则直接使用它不创建新的实例.如果启动的Activity没有位于栈顶时,则创建一个新的实例位于栈顶
  • singletask:启动Activity时,系统首先会检查栈中是否存在该Activity的实例,如果发现则直接使用该实例,并将当前之上的所有Activity出栈,如果没有发现则创建一个新的实例
  • singleinstance:加载Activity时,无论从哪个任务栈中启动该Activity,只会创建一个Activity实例,并且会使用一个全新的任务栈来装载该Activity实例

2、Fragment

Fragment被称为第五大组件,是因为Fragment有自己的生命周期,但是又不能独立的加载到Activity里面

1)、Fragment两种加载方式:

  • 静态加载:只需要把Fragment(片段)当成普通UI控件放到界面Layout中就行
  • 动态加载:Fragment是UI模块,自然在一个Activity中可以不只有一个模块,所以Android提供了FragmentManage类来管理Fragment,FragmentTransaction类来管理事务。我们对Fragment的动态加载就是先将添加、移除等操作提交到事务,然后通过FragmentManage完成的,例如在Activity中代码如下:

    FragmentManager manager = this.getFragmentManager();    //获取Fragment管理器
    fragment = new MyFragment();                            // 新建一个Fragment
    this.fragmentStack.push(fragment);                      // 将ID添加到栈中
    transaction = manager.beginTransaction();               // 开启一个新事务
    transaction.add(R.id.fragments, fragment);              //使用add方法添加Fragment
    transaction.commit();                                   // 提交事务,否则添加就没成功
  • 切换Fragment:首先分别获取FragmentManager与FragmentTransaction的实例,然后调用FragmentTransaction的replace()方法与commit()方法来完成Fragment的切换,代码如下:
    FragmentManager manager = this.getFragmentManager();    //获取Fragment管理器
    fragment = new MyFragment();                            // 新建一个Fragment
    transaction = manager.beginTransaction();               // 开启一个新事务
    transaction.replace(R.id.fragments, fragment);          //使用replace方法替换Fragment
    transaction.commit();                                   // 提交事务,否则添加就没成功

2)、Fragment与Activity的生命周期:

3)、Fragment与Activity的交互:

  • 在Activity中获取Fragment实例:如果Activity中保存了Fragment引用,除了通过访问公有方法外还可以通过使用接口回调的方式,如果Acticity没有保存Fragment引用,则可通过getFragmentManager().findFragmentByTag()或者findeFragmentById()来获得相应的Fragment实例
  • 在Fragment中获取Activity实例:在Fragment中可以通过getActivity()方法来得到当前绑定的Activity的实例以及Context,若需要让Context在Activity销毁后还存在,则可以用getActivity().getApplicationContext()来获取
  • 在Fragment中获取Fragment实例:可以先拿到Activity的实例,然后通过Activity的实例来获取其他的Fragment

4)、Fragment的三种方法区别:

  • add:在Activity中添加一个Fragment
  • remove:在Activity中移除一个Fragment
  • replace:在Activity最上层替换一个Fragment

3、Service

Service是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件,可以通过其他组件开启,也可以与Activity进行绑定,因此Service与Activity一样是运行在主线程中的,因此Service需要执行耗时操作需要开启一个子线程

1)、Service与Thread区别:

  • Thread是程序执行的最小单元,它是分配CPU的基本单位,可以用 Thread 来执行一些异步的操作
  • Service是android的一种机制,如果是Local Service,那么对应的Service 是运行在主进程的 main 线程上;如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上,service和调用者之间的通讯都是同步的(不论是远程service还是本地service),它跟线程一点关系都没有!
  • Service中文翻译后台服务,但不能理解成能够作耗时操作服务的意思,服务仅指针对android系统的服务,属于android系统级的组件,可以长期保持在后台运行,可以不依赖acticity的生命周期创建销毁机制。实际开发中Service需要作耗时任务需要开启子线程。

2)、Service的开启方式:

  • startService:这种方式启动的Service会长期在后台运行,即使启动它的应用组件已经被销毁该服务还是会运行,若资源不足时,则服务可能会被杀死。其步骤如下:

定义一个类继承Service且在Manifest.xml文件中配置该Service,使用Context.startService(Intent)启动该Service,不在使用的时候调用Context.stopService(Intent)方法停止该服务。

  • bindService:绑定的Service只有当应用组件绑定后才能运行,多个组件可以绑定一个Service,被绑定的服务的生命周期会跟调用者关联起来,调用者退出,服务也会跟着被销毁,通过绑定服务,可以间接借助onBind()方法返回的IBinder调用服务内部方法,当调用unbind()方法时,该Service就会被销毁。其步骤:

定义一个类继承Service且在Manifest.xml文件中配置该Service,然后在该Service中创建一个IBinder实现类对象并提供公共方法给客户,在onBind()回调方法返回此IBinder实例,启动方可通过onServiceConnected()回调方法接收上面的IBinder实例。

  • Service的混合开启模式:这种启动方式即保证了服务可以长期在后台运行,又可以让调用者远程调用服务中提供的方法。通常开启步骤如下:

开启服务start() ---> 绑定服务bind() ---> 解绑服务unbind() ps:此时服务还继续运行---> 结束服务stop() ps:不用时停止服务

3)、Service的生命周期:

4、Broadcast

在安卓中,Broadcast是一直广泛运用在应用程序之间传输信息的机制,可以实现应用程序之间的数据传输,也可以实现同一个应用程序内部不同进程或组件之间的数据传输。其内容是一个Intent,Intent可以携带数据

1)、Broadcast种类:

  • 普通广播 Normal Broadcast:Context.sendBroadcast。广播无序事件(也被称为有序广播 Normal broadcast),即所有的接收者在理论上是同时接收到事件,同时执行的,对消息传递的效率而言这是比较好的做法。
  • 系统广播 System Broadcast:Context.sendOrderedBrodacast。向系统广播有序事件(Ordered broadcast),接收者按照在Manifest.xml文件中设置的接收顺序依次接收Intent,顺序执行的,接收的优先级可以在系统配置文件中设置(声明在intent-filter元素的android:priority属性中,数值越大优先级别越高,其取值范围为-1000到1000。当然也可以在调用IntentFilter对象的setPriority()方法进行设置)。对于有序广播而言,前面的接收者可以对接收到得广播意图(Intent)进行处理,并将处理结果放置到广播意图中,然后传递给下一个接收者,当然前面的接收者有权终止广播的进一步传播。如果广播被前面的接收者终止后,后面的接收器就再也无法接收到广播了。
  • 本地广播 Local Broadcast:只在自身App内传播

2)、BroadcastReceiver的两种注册方式:

  • 静态注册:直接在AndroidManifest.xml文件中进行注册,通过该注册方式的广播接收者在系统运行一次后就会被注册到系统中,以后无需运行该应用程序也可以接收到广播。
  • 动态注册:无须在AndroidManifest.xml文件中进行注册,直接在代码中调用Context.registerReceiver()可实现动态注册,通过该注册方式的广播接收者只有在代码运行时才生效,若代码运行结束则广播接收者也失效。在Activity中使用的时候需要跟随Activity的生命周期进行Context.unregisterReceiver()进行取消注册。

3)、Broadcast内部机制:

  • 广播接收者BroadcastReceiver复写onRecvice()方法,通过Binder机制向AMS进行注册
  • 广播发送者通过Binder机制向AMS发送广播,其内容是一个Intent
  • AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,并将其发送到相应的消息队列中(一般情况是Activity的消息队列)
  • 消息循环执行并拿到该BroadcastReceiver并回调onRecvice()方法

4)、LocalBroadcastManager能够完成在应用内的广播发送,而且比全局广播更有一些优势如下:

  • 广播只会在你的应用内发送,所以无需担心数据泄露,更加安全
  • 其他应用无法发广播给你的应用,所以也不用担心你的应用有别人可以利用的安全漏洞
  • 相比较全局广播,它不需要发送给整个系统,所以更加高效

LocalBroadcastManager高效是因为它的内部是通过Handler实现的,它的sendBroadcast()方法其实是通过Handler发送一个Message实现的,因此与系统广播通过Binder实现的肯定高效。通过Handler实现的方式我们应用内发送的广播也离不开我们的应用,别的应用也无法通过这种方式向我们发送广播消息,从而保证了数据的安全性。LocalBroadcastManager内部协作主要靠两个Map集合:mReceivers和mActions,当然还有一个List集合的mPendingBroadcasts来存储待接收的广播对象。

广播注册代码如下:

    LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getActivity());
    IntentFilter filter = new IntentFilter();
    filter.addAction(ACTION);
    myBroadcastReciver = new MyBroadcastReciver();
    lbm.registerReceiver(myBroadcastReciver, filter);

广播发送代码如下:

    Intent intent = new Intent();
    intent.setAction(SaleLeftFragment.ACTION);
    intent.putExtra(TAG, data);
    LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);

使用注意事项:

  • LocalBroadcastManager注册广播只能通过代码注册的方式
  • LocalBroadcastManager注册广播后,一定要记得取消监听
  • LocalBroadcastManager注册的广播,在发送广播的时候务必使用LocalBroadcastManager.sendBroadcast(intent);否则您接收不到广播

5、Binder

通常意义讲Binder是一种通信机制,对于传输过程而言,Binder是可以进行跨进程传递的对象,因此在android中Binder是一种跨进程的通信机制。

1)、IPC通信机制:

为了保护进程空间不被别的进程破坏或者干扰,Linux中的进程是相互独立的,也就是所谓的进程隔离。(而且一个进程的内存空间还被分为了用户空间和内核空间,二者也是相互隔离的)所以在Linux中,进程与进程之间是相互隔离的,而且进程中的用户和内核空间也是隔离的。

也就是说为了安全和独立,进程之间是相互隔离的。在需要通信、协作的时候就需要使用进程间通信技术(即IPC,也称跨进程通信),我们都知道Android框架是建立在Linux之上的,当然也会面对进程间通讯的问题。如下:

每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。对应一个4GB的虚拟地址空间,其中3GB是用户空间,1GB是内核空间,当然内核空间的大小是可以通过参数配置调整的。对于用户空间,不同进程之间彼此是不能共享的,而内核空间却是可共享的。Client进程向Server进程通信,恰恰是利用进程间可共享的内核内存空间来完成底层通信工作的,Client端与Server端进程往往采用ioctl等方法跟内核空间的驱动进行交互。

2)、Binder的C/S架构:

Android开发中,我们大量使用到了系统Service,比如媒体播放、各种传感器以及WindowManagerService等。那么安卓是怎么管理这些服务,并且让用户跨进程调用这些服务呢?首先我们看看调用系统服务的过程。在Android开机启动过程中,安卓会初始化系统的各种Service,并将这些Service向ServiceManager注册(即ServiceManager管理)。客户端想要得到具体的Service直接向ServiceManager要即可。客户端首先向ServiceManager查询得到具体的Service引用,然后通过这个引用向具体的服务端发送请求,服务端执行完成后就返回。

服务端跨进程的类都要继承Binder类。我们所持有的Binder引用(即服务端的类引用)并不是实际真实的远程Binder对象,我们的引用在Binder驱动里还要做一次映射。也就是说,设备驱动根据我们的引用对象找到对应的远程进程。客户端要调用远程对象函数时,只需把数据写入到Parcel,在调用所持有的Binder引用的transact()函数,transact函数执行过程中会把参数、标识符(标记远程对象及其函数)等数据放入到Client的共享内存,Binder驱动从Client的共享内存中读取数据,根据这些数据找到对应的远程进程的共享内存,把数据拷贝到远程进程的共享内存中,并通知远程进程执行onTransact()函数,这个函数也是属于Binder类。远程进程Binder对象执行完成后,将得到的写入自己的共享内存中,Binder驱动再将远程进程的共享内存数据拷贝到客户端的共享内存,并唤醒客户端线程。如下:

//获取WindowManager服务引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  
//布局参数layoutParams相关设置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);  
//添加view
wm.addView(view, layoutParams);
  • 客户端通过调用getSystemService(getApplication().WINDOW_SERVICE)方法,向ServiceManager查询标识符为getApplication().WINDOW_SERVICE的远程对象的引用
  • ServiceManager将WindowManager对象的引用或者代理交给客户端,这里的wm 其实获取到的可能是远程服务的代理(垮进程)或者引用(当前进程)
  • 客户端通过这个代理或者引用进行系统调用,这里调用addView时,真正的实现是在代理里面,代理把参数打包到Parcel对象中,然后调用transact函数(该函数继承自Binder),再触发Binder驱动的一系列调用过程,并将结果返回

3)、Binder的运行机制:

  • Client进程:使用服务的进程
  • Server进程:提供服务的进程
  • ServiceManager进程:其作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。即所有的Service进程都会像ServiceManager进程进行注册,观察者模式
  • Binder驱动:驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持

Binder跨进程传输并不是真的把一个对象传输到了另外一个进程;传输过程好像是Binder跨进程穿越的时候,它在一个进程留下了一个真身,在另外一个进程幻化出一个影子(这个影子可以很多个);Client进程的操作其实是对于影子的操作,影子利用Binder驱动最终让真身完成操作

 

4)、Binder的在Android实现原理及步骤:

Binder在Android的应用主要体现在Service与Activity之间,因为Binder是一种跨进程的通信机制,因此本地Service与Activity都属于同一个进程,在Binder源码中可以看出同进程返回当前进程对象,远程Service就与当前Activity不属于同一个进程,因此在ADLI中使用的是代理对象。其使用步骤如下:

  • 注册服务:Server进程通过Binder驱动向Service Manager进程注册服务

在Service中,Binder实现了IBinder接口,IBinder接口定义了远程操作对象的基本接口,代表了一种跨进程传输的能力系统会为每个实现了IBinder接口的对象提供跨进程传输能力。因此注册服务后,Binder驱动就持有 该Service创建的Binder实体。

  • 获取服务:Client进程使用某个上面注册的Server前(调用该Server的某函数),须通过Binder驱动向ServiceManager进程 获取相应的Server信息

A、Client向Binder驱动发起获取服务请求。在Activity中,通过bindService()绑定上面的Service进程

B、Binder驱动将该请求转发给ServiceManager,ServiceManager查找到Client需要获取的Server,并将其对应的Binder实体的引用或者代理返回给Binder驱动。在本地Service中,通过onBind()得到创建的Binder对象的引用,在远程Service中,通过onBind()的到创建的Binder对象的代理

C、Binder驱动将上面获取到的Binder引用或者代理对象返回给Client。在Activity中,通过onServiceConnected()方法获取到了Service的引用或者代理对象Binder

  • 连接建立:此时Client与Server连接建立成功,Client可通过获到的Binder引用或者代理来进行调用Server里面的方法。在Activity就可以通过获取的这个Binder来访问Service里面的公有方法

二、异步消息处理机制

1、Handler

因为Android线程不安全原因,所以更新UI界面的线程必须是主线程。Handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue,可以让对应的Message和Runnable在未来某个时间点进行相应处理,也可以实现耗时操作放在子线程刷新界面的操作放在主线程。

1)、Handler的主要对象:

  • Message:线程之间传递的消息
  • MessageQueue:消息队列作为一个消息集合,用来存放Runnable和Message
  • Lopper:不停循环消息队列,只要有消息就从消息队列中取出交给Handler
  • Handler:进行消息分发和消息处理

2)、Handler的源码解析:

  • Handler的构造方法:获取了Looper,并且还从Looper中获取了消息队列MessageQueue
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
        mLooper = Looper.myLooper();        //获取Looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;            //获取Looper中的消息队列
        mCallback = callback;
        mAsynchronous = async;
    }
  • Looper的静态方法:Looper.prepare()方法是给当前线程初始化一个循环Looper并存储在了线程本地变量里面,Looper.myLooper()方法则是从当前线程本地变量里面取出这个Looper,并且Looper的构造方法里面实例化了一个消息队列
     /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {    
        prepare(true);
    }
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));     //本地线程变量中实例化一个Looper
    }
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();                     //本地线程变量中获取这个Looper
    }
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);        //构造方法中创建了一个消息队列
        mThread = Thread.currentThread();
    }
  • Looper的消息循环:Looper.loop()方法开启了一个死循环进行消息轮训且处理
    public static void loop() {
        final Looper me = myLooper();        //拿到当前线程绑定的Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;//从Looper中获取消息队列 
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        for (;;) {                            //死循环进行消息轮训
            Message msg = queue.next();       //从消息队列中取出消息,可能会阻塞
            if (msg == null) return;
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to "+msg.target+" "+  msg.callback + ": " + msg.what);
            }
            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) 
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);    //进行消息分发与处理
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0)  Trace.traceEnd(traceTag);
            }
            ...省略...
            msg.recycleUnchecked();
        }
    }
  • Looper在主线程中的初始化和轮训:Android程序的运行入口点可以认为是android.app.ActivityThread类的main()方法,从中可以看出在main中先对Looper进行了初始化准备,然后进行了消息原理
public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
    SamplingProfilerIntegration.start();
    CloseGuard.setEnabled(false);
    Environment.initForCurrentUser();
    EventLogger.setReporter(new EventLoggingReporter());
    AndroidKeyStoreProvider.install();
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);
    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper();             //Looper进行初始化

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) sMainThreadHandler = thread.getHandler();
    if (false)Looper.myLooper().setMessageLogging(new  LogPrinter(Log.DEBUG, "ActivityThread"));
  
    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();                          //Looper进行消息循环

    throw new RuntimeException("Main thread loop unexpectedly exited");
}
  • ActivityThread中调用了Looper.loop()进行了死循环轮训,为何主线程不会阻塞卡顿呢?原来是ActivityThread里面也有一个对应的Handler,就是内部类 H,其继承 Handler,H的消息处理源码如下,看完这 Handler 里处理消息的内容应该明白了, Activity 的生命周期都有对应的 case 条件了,ActivityThread 有个 getHandler 方法,得到这个 handler 就可以发送消息,然后 loop 里就分发消息,然后就发给 handler, 然后就执行到 H(Handler )里的对应代码。所以这些代码就不会卡死,有消息过来就能执行。简单来说ActivityThread的main方法主要就是做消息循环,一旦退出消息循环,那么你的程序也就可以退出了。从消息队列中取消息可能会阻塞,取到消息会做出相应的处理。如果某个消息处理时间过长,就可能会影响UI线程的刷新速率,造成卡顿的现象。
public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                    r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
                case RELAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                    ActivityClientRecord r = (ActivityClientRecord)msg.obj;
                    handleRelaunchActivity(r);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
                case PAUSE_ACTIVITY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2, (msg.arg1&2) != 0);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case PAUSE_ACTIVITY_FINISHING:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    handlePauseActivity((IBinder)msg.obj, true, (msg.arg1&1) != 0, msg.arg2, (msg.arg1&1) != 0);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case STOP_ACTIVITY_SHOW:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
                    handleStopActivity((IBinder)msg.obj, true, msg.arg2);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case STOP_ACTIVITY_HIDE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
                    handleStopActivity((IBinder)msg.obj, false, msg.arg2);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case SHOW_WINDOW:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
                    handleWindowVisibility((IBinder)msg.obj, true);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case HIDE_WINDOW:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityHideWindow");
                    handleWindowVisibility((IBinder)msg.obj, false);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case RESUME_ACTIVITY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
                    handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case SEND_RESULT:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
                    handleSendResult((ResultData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;

            ...........
}
  • 结论:Handler的构造方法中获取了当前线程的唯一Looper(与当前线程绑定) ,且还从Looper中获取了的消息队列MessageQueue,所以当前实例化handler只能在当前线程进行发送消息和处理消息,因为当前的handler里面的Looper和MessageQueue都是线程本地对象。因此我们实际使用的时候通常是在Activity的成员变量里面实例化一个Handler,这个Handler因为是主线程创建,因此获取的也是主线程唯一的Looper。不论在主线程中实例化多少个Handler,其实他们都公用的同一个Looper,这个Looper中轮训的消息不仅仅包括自定义的消息,还包括ActivityThread中的H里面的所有消息,例如LAUNCH_ACTIVITY、SHOW_WINDOW等系统消息,所以主线程因为了Looper.loop()方法进行了死循环处理,实现了程序的运行及消息的处理,如果死循环退出了就意味着程序的退出。

3)、Handler的运行机制:首先需要在UI线程创建一个Handler对象,然后在子线程中调用Handler的sendMessage()方法,接着这个消息会存放在UI线程的MessageQueue中,通过Looper对象取出MessageQueue中的消息,最后分发回Handler的handleMessage()方法中。

4)、Handler引起的内存泄漏:因为在Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,静态的内部类不会持有外部类的引用。例如在Activity中创建了一个非静态内部类Handler,这个Handler就会持有Activity的引用,在Activity被销毁的时候,有可能Handler在进行操作没有及时释放,导致Activity的资源无法回收引起内存泄漏。

这段代码编辑器用黄色部分提示,简单翻译过来就是:由于handler定义为内部类,可能会阻止GC。如果handler的Looper或MessageQueue 非主线程,那么没有问题。如果handler的Looper或MessageQueue 在主线程,那么需要按如下定义:定义handler为静态内部类,当你实例化handler的时候,传入一个外部类的弱引用,以便通过弱引用使用外部类的所有成员。Handler引起的内存泄漏解决方案如下:

  • 将Handler定义成静态内部类,如下图用static修饰后黄色提示自动消失了

  • 在Activity销毁的时调用Handler.removeCallbacksAndMessages方法清除消息队列所有消息

  • Handler内部持有外部activity的弱引用

5)、子线程中使用Handler:因为子线程中没有Looper,所以在子线程中实例化的Handler发送消息但无法进行处理,是因为没有Looper进行消息轮训,所以在子线程中想通过Handler进行消息轮训,需要在子线程中对Looper进行初始化,代码如下:

public class MainActivity extends AppCompatActivity {
    private Handler threadHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Log.e("SHEN","启动线程");
                    Looper.prepare();        //对子线程进行Looper的初始化
                    threadHandler = new Handler(){
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            Log.e("SHEN","handleMessage-what="+msg.what);
                        }
                    };
                    threadHandler.sendEmptyMessage(1);
                    Looper.loop();            //对子线程进行Looper进行轮训
                    threadHandler.sendEmptyMessage(3);
                }catch (Exception e){
                    Log.e("SHEN","e="+e.toString());
                }
            }
        }).start();
    }
}

日志打印如下,在实例化Handler前进行了Looper的初始化,Handler实例化后发送了消息1,然后调用Looper.loop(),然后在发送消息3,通过日志打印发现收到了消息1但是没有收到消息3,是因为Looper.loop()里面有一个死循环,因此Looper.loop()后面的代码都无法执行,这时候其他子线程可以通过threadHandler.sendMessage进行消息发送,当前的线程进行消息处理。

2、AsyncTask

AsyncTask本质上是一个封装了线程池和Handler的异步框架,因为内部集成了Handler,所以能够很方便的在主线程和子线程之间进行切换

1)、AsyncTask的使用:AsyncTask有三个泛型参数,Params 代表“启动任务执行的输入参数”、Progress 代表“后台任务执行的进度”、Result代表“后台计算结果的类型”。在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用java.lang.Void类型代替,如AsyncTask<Void,Void,Void>。AsyncTask定义如下:

public abstract class AsyncTask<Params, Progress, Result> {...}

AsyncTask使用示例如下:

public class MainActivity extends Activity {
    private static final String TAG = "ASYNC_TASK";
    private Button execute;
    private Button cancel;
    private ProgressBar progressBar;
    private TextView textView;
    private MyTask mTask;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        execute = (Button) findViewById(R.id.execute);
        cancel = (Button) findViewById(R.id.cancel);
        progressBar = (ProgressBar) findViewById(R.id.progress_bar);
        textView = (TextView) findViewById(R.id.text_view);
    }
    @Override
    void onClick(View view){
        if(view.getId()==R.id.execute){
            mTask = new MyTask();        //注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常
            mTask.execute("http://www.baidu.com");    
            execute.setEnabled(false);
            cancel.setEnabled(true);
        }else if(view.getId()==R.id.cancel){
            mTask.cancel(true);         //取消一个正在执行的任务,onCancelled方法将会被调用
        }
    }
    private class MyTask extends AsyncTask<String, Integer, String> {
        @Override
        protected void onPreExecute() {                  
            //用于在执行后台任务前做一些UI操作
            textView.setText("loading...");
        }
        @Override
        protected String doInBackground(String... params) {//protected abstract Result doInBackground(Params... var1) 这里的参数是泛型Params,返回值是泛型Result
            //执行后台任务,不可在此方法内修改UI
            ...省略代码...
            return null;
        }
        @Override
        protected void onProgressUpdate(Integer... progresses) {
            //用于更新进度信息
            progressBar.setProgress(progresses[0]);
            textView.setText("loading..." + progresses[0] + "%");
        }
        @Override
        protected void onPostExecute(String result) {
            //在执行完后台任务后更新UI,显示结果
            textView.setText(result);
            execute.setEnabled(true);
            cancel.setEnabled(false);
        }
        @Override
        protected void onCancelled() {
            //在取消执行中的任务时更改UI
        }
    }
}
  • onPreExecute():准备运行,该回调函数在任务被执行之后立即由UI线程调用,这个步骤通常来完成在UI上显示进度条相关的操作
  • doInBackground(Params...):正在后台运行,该回调函数由后台线程在onPreExecute()方法执行结束后立即被调用,通常在这里执行耗时的后台计算,计算的结果必须由该函数返回,并被传递到onPostoExecute()中处理。在该函数内也可以使用publishProgress()发布进度值,这些进度值将会在onProgressUpdate()中被接收并发布到UI线程
  • onProgressUpdate(Progress...):进度更新,该函数由UI线程在publishProgress()方法调用后被调用,一般用于动态的更新一个进度条
  • onPostExecute(Result):完成后台任务,后台计算结束后被调用,后台计算的结果作为参赛传递给这一方法

2)、AsyncTask的原理:内部封装了一个线程池,通过Handler发送消息,在UI线程和子线程之间传递

  • AsyncTask的本质是一个静态的线程池,AsyncTask派生出的子类可以实现不同的异步任务,这些任务都是提交到线程池中执行
  • 线程池中的工作线程执行doInBackground(mParams)方法执行异步任务
  • 当任务状态改变之后,工作线程会向UI线程发送消息,AsyncTask内部的internalHandler响应这些消息并调用相关的函数

3)、AsyncTask的注意事项:

  • 内存泄漏:跟Handler原理一样,非静态内部类和匿名内部类会持有外部类的引用,解决方法就是将AsyncTask对象用static修饰
  • 生命周期:跟Handler原理一样,AsyncTask并不会随Activity的销毁而销毁,因此在Activity.onDestory方法中进行AsyncTask取消操作
  • 结果丢失:在Activity异常销毁然后重建,会导致AsyncTask持有之前已经被销毁的Activity的引用,从而导致无法刷新界面

4)、总结:Android系统中之所以有AsyncTask和Handler,是为了避免阻塞UI线程工作,由于UI的更新只能在主线程中完成,因此异步处理是不可避免的。AsyncTask与Handler都适用于简单的异步处理,相比之下AsyncTask代码上更轻量一些,而实际上要比Handler更耗资源。

3、HandlerThread

HandlerThread本质是上一个子线程,它继承了Thread,但是跟普通的线程有所不同,除了主线程外的普通子线程默认都没有开启Looper,但是HandlerThread内部创建了一个Looper对象,因此它可以进行消息轮训,但与线程池注重并发不同,HandlerThread是一个串行队列,HandlerThread背后只有一个线程。

1)、HandlerThread的使用:

  • 实例化HandlerThread:mThread=new HandlerThread("Handler线程");参数表示线程名
  • 启动这个线程:             mThread.start();跟启动Thread方式一样
  • 消息处理:可通过获取其中的Looper进行实例化一个Handler,例如 new Handler( mThread.getLooper()){....}
  • 退出线程循环:可以调用Looper的quit方法或quitSafely方法

当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。

当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。

无论是调用了quit方法还是quitSafely方法只会,Looper就不再接收新的消息。即在调用了Looper的quit或quitSafely方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,表示消息没有成功放入消息队列MessageQueue中,因为消息队列已经退出了

public class MainActivity extends AppCompatActivity {
    private HandlerThread myHandlerThread;
    private Handler handler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myHandlerThread = new HandlerThread( "handler-thread") ;  //实例化HandlerThread
        myHandlerThread.start();                                  //开启一个线程
        handler = new Handler( myHandlerThread.getLooper() ){     //在这个线程中创建一个handler对象
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //这个方法是运行在 handler-thread 线程中的 ,可以执行耗时操作
                Log.e( "SHEN " , "handleMessage-what="+msg.what+" "+Thread.currentThread().getName() ) ;
            }
        };
        handler.sendEmptyMessage( 1 ) ;                            //在主线程给handler发送消息
        new Thread(new Runnable() {
            @Override
            public void run() {
                handler.sendEmptyMessage( 2 ) ;                     //在子线程给handler发送数据
            }
        }).start() ;
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        myHandlerThread.quit() ;                                    //释放资源
    }
}

 

2)、HandlerThread的源码:

public class HandlerThread extends Thread {        //HandlerThread的本质就是一个Thread
    int mPriority;
    int mTid = -1;
    Looper mLooper;                                //HandlerThread内部的Looper
    public HandlerThread(String name) {            //参数1指定表示线程名
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    public HandlerThread(String name, int priority) {//参数2指定线程的优先级注意使用的是 android.os.Process 而不是 java.lang.Thread 的优先级!
        super(name);
        mPriority = priority;
    }
    protected void onLooperPrepared() {            //子类需要重写的方法,在这里做一些执行前的初始化工作
    }
    public Looper getLooper() {                    //获取当前线程的Looper
        if (!isAlive()) {                          //如果线程不是正常运行的就返回null
            return null;
        }
        synchronized (this) {
            while (isAlive() && mLooper == null) {  //如果线程启动后,循环等待直到mLooper创建成功后返回Looper
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
    @Override
    public void run() {             //调用 start() 后就会执行的 run()
        mTid = Process.myTid();
        Looper.prepare();            //帮我们创建了 Looepr
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();            //Looper 已经创建,唤醒阻塞在获取 Looper 的线程
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();         //调用线程启动前的空方法,在需要进行资源初始化的时候复写此方法
        Looper.loop();              //开始循环
        mTid = -1;
    }
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }
    public int getThreadId() {
        return mTid;
    }
}

4、IntentService

IntenService本质上是继承了Service的抽象类,但是优先级高于Service。我们知道Service本身是不能执行耗时操作的,需要执行耗时操作需要开启一个子线程,然而IntentService内部创建了一个HandlerThread,通过HandlerThread和Handler来实现异步操作。

1)、IntentService的使用方法:IntentService的启动方式跟传统的Service一样,需要在xml文件中进行配置声明,然后通过startService方式进行启动,同时当任务执行完成后IntentService会自动停止,而不需要我们手动控制或者调用stopSelf(),另外IntentService可以被启动多次,而每一个耗时操作会以工作队列的方式在onHandleIntent()回调方法中执行,并且每次只会执行一个工作线程,执行完后在执行后面的,串行模式。

public class StudentService extends IntentService {
    public StudentService() {                    //无参构造方法一定需要
        super("StudentService");
    }
    public StudentService(String name) {
        super(name);
        Log.e("SHEN","StudentService-name="+name);
    }
    @Override
    protected void onHandleIntent(Intent arg0) { //执行耗时任务操作
        try{
            Log.e("SHEN","onHandleIntent1");
            Thread.sleep(5000);
            Log.e("SHEN","onHandleIntent2");
        }catch (Exception e){
        }
    }
}
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent=new Intent(MainActivity.this,StudentService.class);
        startService(intent);
        startService(intent);        //连续启动了4次
        startService(intent);
        startService(intent);
    }
}

2)、IntentService的源码解析:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);   //5. 收到onStar发送的消息并进行处理 直接在onHandleIntent中处理(由子类实现)
            stopSelf(msg.arg1);                //7. 会判断启动服务次数是否与startId相等 即判断所有的任务是否处理完成,处理完成后自动结束服务
        }
    }
    public IntentService(String name) {
        super();
        mName = name;
    }
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }
    @Override
    public void onCreate() {
        super.onCreate();                    //1. 创建一个HanlderThread
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        mServiceLooper = thread.getLooper(); //2. 通过HanlderThread的Looper来构建Handler对象mServiceHandler
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;                    //4. 通过mServiceHandler发送一个消息(该消息会在HanlderThread中处理)
        mServiceHandler.sendMessage(msg);
    }
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);            //3. 每次启动 直接调用onStart
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
    @Override
    public void onDestroy() {
        mServiceLooper.quit();                //8. 服务销毁的时候停止轮训器
    }
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }
    @WorkerThread                            //6、这里的回调是在HandlerThread里面的可以进行耗时处理 这里的Intent来自onStartCommand的intent
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

三、View

1、View的绘制流程

关于Android中View视图的呈现,我们平常看到最多的就是在Activity中通过setContentView(R.layout.activity_main);设置来显示。然而其实视图的显示并不是由Activity来完成的,其中涉及到了Window、DecorView、ViewRoot

1)、View的绘制相关的几大类:

  • Activity:Activity并不负责视图控制,它只是控制生命周期和处理事件。真正控制视图的是Window。一个Activity包含了一个Window,Window才是真正代表一个窗口。Activity就像一个控制器,统筹视图的添加与显示,以及通过其他回调方法,来与Window、以及View进行交互。
  • Window:Window是视图的承载器,内部持有一个 DecorView,而这个DecorView才是 view 的根布局。Window是一个抽象类,实际在Activity中持有的是其子类PhoneWindow。PhoneWindow中有个内部类DecorView,通过创建DecorView来加载Activity中设置的布局R.layout.activity_main。Window 通过WindowManager将DecorView加载其中,并将DecorView交给ViewRoot,进行视图绘制以及其他交互。
  • DecorView:DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。在Activity中通过setContentView所设置的布局文件其实就是被加到内容栏之中的,成为其唯一子View。
  • ViewRoot:ViewRoot对应ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成。所有View的绘制以及事件分发等交互都是通过它来执行或传递的。
  • WindowManager:其实安卓中所有的界面显示相关的,都是通过WindowManager.addView方法来将当前需要显示的View添加到window中。但是WindowManager其实只是一个接口,而真实实现他的功能的是他的实现类WindowManagerImpl。

2)、Activity启动到View的绘制过程:

在之前的handler源码分析中,我提到了ActivityThread,里面有个继承handler的H会处理Activity的各种状态,其实一个应用的启动就是从ActivityThread里面实现的,其实所谓的主线程就是ActivityThread里面的Looper。这里我只拿出来onCreate和onResume相关的:

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        case LAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
            final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
            r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
            handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            } break;
        case RESUME_ACTIVITY:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
            SomeArgs args = (SomeArgs) msg.obj;
            handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,args.argi3, "RESUME_ACTIVITY");
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;
        }
    Object obj = msg.obj;
    if (obj instanceof SomeArgs) {
        ((SomeArgs) obj).recycle();
    }
    if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}

我们先看看LAUNCH_ACTIVITY消息里面是如何进行启动一个Activity的呢,跳转到handleLaunchActivity方法里面去看看,代码如下:

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        unscheduleGcIdler();
        if (r.profileFd != null) {
            mProfiler.setProfiler(r.profileFile, r.profileFd);
            mProfiler.startProfiling();
            mProfiler.autoStopProfiler = r.autoStopProfiler;
        }
        handleConfigurationChanged(null, null);                //进行一些Activity的配置
        Activity a = performLaunchActivity(r, customIntent);   //这里面依次调用Activity.attach()、Activity.onCreate()、Activity.onStart()生命周期
        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,  //这里面执行了Activity.onResume()
                   !r.activity.mFinished && !r.startsNotResumed);
            if (!r.activity.mFinished && r.startsNotResumed) {
                try {
                    r.activity.mCalled = false;
                    mInstrumentation.callActivityOnPause(r.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 {
            ...省略...
        }
    }

由上面源码可知道在处理LAUNCH_ACTIVITY消息时,调用了performLaunchActivity()方法,performLaunchActivity依次调用了Activity的attach、onCreate、onStart方法,我们一般在重写Activity的onCreate方法进行setContentView()方法进行视图布局文件的设置,其实具体是通过是交给Window装载视图进行设置的,具体步骤如下:

步骤一:Activity中的attach()方法中,生成了PhoneWindow实例。既然有了Window对象,那么我们就可以设置DecorView给Window对象了。

 final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
            mWindow = new PhoneWindow(this, window);        //创建一个Window对象
            mWindow.setWindowControllerCallback(this);
            mWindow.setCallback(this);                     //设置回调,向Activity分发点击或状态改变等事件
            mWindow.setOnWindowDismissedCallback(this);
            mWindow.setWindowManager(                      //给Window设置WindowManager对象
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    }

步骤二:Activity的onCreate()方法中,通过调用setContentView()方法进行设置视图资源,内部通过上面创建的Window对象进行实现。

//Activity的setContentView方法
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}
//PhoneWindow的setContentView方法
public void setContentView(int layoutResID) {
    if (mContentParent == null) {        //mContentParent为空,创建一个DecroView
        installDecor();
    } else {
        mContentParent.removeAllViews();//mContentParent不为空,删除其中的View
    }
    mLayoutInflater.inflate(layoutResID, mContentParent);//为mContentParent添加子View,即Activity中设置的布局文件
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();            //回调通知,内容改变
    }
}

以上仅仅是将DecorView建立起来,通过setContentView()设置的界面,为什么在onResume()之后才对用户可见呢?其实这个时候还没有进行视图绘制。

我们在看看RESUME_ACTIVITY消息,主线程调用了handleResumeActivity()方法,其实在LAUNCH_ACTIVITY消息中启动一个Activity调用的handleLaunchActivity方法中看出,在进行了performLaunchActivity调用进行Activity的创建启动后也调用了handleResumeActivity()来实现DecorView的绘制,代码如下:

//ActivityThread主线程处理活动可见消息时候调用
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;
                  if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                //decor对用户不可见
                decor.setVisibility(View.INVISIBLE);            
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);              //被添加进WindowManager了,但是这个时候,还是不可见的
                }
                if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
                     if (r.activity.mVisibleFromClient) {
                            r.activity.makeVisible();    //在这里调用了Activity的makeVisible方法使得DecorView可见
                        }
                    }
            }
}
//Activity的设置视图可见方法
void makeVisible() {
   if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());//将DecorView添加到WindowManager
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);//DecorView可见
    }

由上面源码可以知道,ActivityThread在处理LAUNCH_ACTIVITY、RESUME_ACTIVITY消息的时候先进行调用了Activity的onResume()方法,然后从PhoneWindow对象中获取了自己的DecorView并交给了WindowManager,进行视图的添加与绘制,最后通过调用Activity的makeVisible()方法让Activity的界面可见。

我们现在知道了视图的绘制最后还是交给了WindowManger来处理,但是WindowManger其实只是一个接口,真正完成的是它的实现类WindowManagerImpl,代码如下:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Display mDisplay;
    private final Window mParentWindow;
    public WindowManagerImpl(Display display) {
        this(display, null);
    }
    private WindowManagerImpl(Display display, Window parentWindow) {
        mDisplay = display;
        mParentWindow = parentWindow;
    }
    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mDisplay, parentWindow);
    }
    public WindowManagerImpl createPresentationWindowManager(Display display) {
        return new WindowManagerImpl(display, mParentWindow);
    }
    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {    //添加视图
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
    @Override
    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        mGlobal.updateViewLayout(view, params);
    }
    @Override
    public void removeView(View view) {                                //移除视图
        mGlobal.removeView(view, false);
    }
    @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }
    @Override
    public Display getDefaultDisplay() {
        return mDisplay;
    }
}

原来WindowManagerImpl通过单例代理对象WindowManagerGlobal来进行,负责与系统窗口管理服务进行操作通信。

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
        ...
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
            ...
            root = new ViewRootImpl(view.getContext(), display); // 1
            view.setLayoutParams(wparams);
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }
        try {
            root.setView(view, wparams, panelParentView);        // 2
        } catch (RuntimeException e) {
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

先看①号代码处,实例化了ViewRootImpl类,接着,在②号代码处,调用ViewRootImpl#setView方法,并把DecorView作为参数传递进去,在这个方法内部,会通过跨进程的方式向WMS(WindowManagerService)发起一个调用,从而将DecorView最终添加到Window上,在这个过程中,ViewRootImpl、DecorView和WMS会彼此关联,至于详细过程这里不展开来说了。 
最后通过WMS调用ViewRootImpl#performTraverals方法开始View的测量、布局、绘制流程。

3)、ViewRootImpl有多重要:

网上都说ViewRootImpl是实现了ViewRoot类,然而我在android-sdk-17的源码上并没有找到一个叫做ViewRoot的接口,终于查阅资料发现ViewRoot其实是在android3.0以前才有,后续版本已经没有ViewRoot类,出现了一个ViewRootImpl来代替ViewRoot,而且ViewRootImpl根本不像网上说的实现了ViewRoot,其代码如下:

public final class ViewRootImpl implements ViewParent,
 View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {...省略代码...}

从上面可以看出ViewRootImpl或者ViewRoot跟View其实没有任何关系,但是它实现了ViewParent接口,实现了View和WindowManager之间的通信协议,实现的具体细节在WindowManagerGlobal这个类当中,WindowManagerGlobal在其内部存储着ViewRootImplView实例的映射关系(顺序存储)。

public final class WindowManagerGlobal {
    //...代码省略...
    private final ArrayList<View> mViews = new ArrayList<View>(); //所有Window对象中的View
    //所有Window对象中的View所对应的ViewRootImpl
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    //所有Window对象中的View所对应的布局参数
    private final ArrayList<WindowManager.LayoutParams> mParams=new ArrayList<WindowManager.LayoutParams>();                
    //...代码省略...
}
  • ViewRootImpl和ViewRoot关系:ViewRootImpl从名称上来理解似乎是“View树的根”,这很容易让人产生误解。因为ViewRootImpl并不属于View树的一份子。ViewRootImpl中的mView成员变量指向的就是它所管理的View树的根。ViewRootImpl的核心任务就是与WindowManagerService进行通信。
  • Activity和Window的关系:Activity是支持显示UI的,但是它不直接管理View树或者ViewRoot,而管理View树具体则是由系统在Activity.attach中调用PolicyManager.makeNewWindow实例了一个Window的对象PhoneWindow来进行管理。
  • Window和WindowManagerImpl的关系:Window的字面意思是"窗口",通常都是由PhoneWindow来规划的。Window的另一层含义是要与WindowManagerService进行通信,但它并没有直接在自身实现这一功能。原因是:一个应用程序中很可能存在多个Window。如果它们都单独与WMS通信,那么既浪费资源,又会造成管理的混乱。换句话说,它们需要统一的管理。于是就有了WindowManager,而它只是提供了一套标准,真正来实现他的还是类WindowManagerImpl来对所有的Window进行统一的管理。
  • ViewRoot和WindowManagerImpl的关系:在早期的系统版本中,WindowManagerImpl在每个进程中只有一个实例。通过调用WindowManagerImpl.getDefault()来获取,它有内部成员变量分别表示View树的根节点、ViewRoot以及Window的属性。由此也可以看出,一个进程中不仅有一个ViewRoot;而Activity与ViewRoot则是一对一的关系。自Android4.3开始对此做了修改,WindowManagerImpl不再直接存储上述三个数组变量,而是由一个称为“WindowMangerGlobal”的类统一管理。
  • ViewRoot和WindowManagerService的关系:每一个ViewRootImpl内部,都有一个全局变量IWindowSession ,这个变量用于ViewRoot到WMS的连接,它是ViewRoot利用WMS的openSession()接口来创建得到的。在此基础上,ViewRoot也会通过IWindowSession.add()方法提供一个IWindow对象——从而让WMS也可以通过这个IBinder对象来与ViewRoot进行双向通信。因此ViewRootImpl除了能够管理View树之外,还能通过WindowSession与WMS进行binder通信。

从上面一层一层的分析,原来绕过去绕过来,activity在加载视图资源的时候通过attach方法中生成了Window实例对象来表示一个视图窗口,并将这个视图窗口的DecorView交给了WindowManager来进行统一管理,WindowManager如何来进行视图的添加与删除呢,原来WindowManager创建了一个所有Window对应的View树的映射,并创建了ViewRootImpl来与WMS进行进程通信来达到界面的绘制功能。我们可以看看ViewRootImpl源码:

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    //ViewRootImpl绑定Window所对应的View,并对该View进行测量、布局、绘制等
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;      //ViewRootImpl成员变量mView引用decorView,对应View树的某个节点,以后的操作都是对mView进行操作
                //...代码省略...
                requestLayout();   //对View完成异步刷新,执行View的绘制方法
                if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) mInputChannel = new InputChannel();
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    //通过IWindowSession接口(对应WMS的Binder客户端)与WMS进行AIDL跨进程通信将Window添加到屏幕上
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) attrs.restore();               
                 }
                //...代码省略...
                view.assignParent(this);        //设置当前View的mParent
                //...代码省略...
            }
        }
    }
    //请求对界面进行布局
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();                //校验当前线程是否是创建线程,否则抛出异常,因为是主线程创建的,所以只有主线程才能进行界面操作
            mLayoutRequested = true;
            scheduleTraversals();        //执行任务,因为上面已经校验了线程是否主线程,所以这里是主线程执行任务
        }
    }
    //主线程执行界面布局任务
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch)scheduleConsumeBatchedInput();
            notifyRendererOfFramePending();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
            if (mProfile)  Debug.startMethodTracing("ViewAncestor");
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
            try {
                performTraversals();        //终于执行布局任务了
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }
    //执行任务(分别调用mView的measure、layout、draw)
    private void performTraversals() {
        final View host = mView;
        //...代码省略...
        int desiredWindowWidth;        //想要展示窗口的宽高
        int desiredWindowHeight;
        //开始进行布局准备
        if (mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null) {
            //...代码省略...
            if (!mStopped) {
                boolean focusChangedDueToTouchMode = ensureTouchModeLocally((relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
                if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
                    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                    int width = host.getMeasuredWidth();
                    int height = host.getMeasuredHeight();
                    boolean measureAgain = false;
                    //...代码省略...
                    if (measureAgain) {        //View的测量
                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                    }
                    layoutRequested = true;
                }
            }
        } else {
           //...代码省略...
        }
        final boolean didLayout = layoutRequested /*&& !mStopped*/ ;
        boolean triggerGlobalLayoutListener = didLayout  || mAttachInfo.mRecomputeGlobalAttributes;
        if (didLayout) {                    //View的布局
            performLayout(lp, desiredWindowWidth, desiredWindowHeight);
            //...代码省略...
        }
        //...代码省略...
        if (!cancelDraw && !newSurface) {
            if (!skipDraw || mReportNextDraw) {
                //...代码省略...
                performDraw();             //View的绘制
            }
        } else {
            if (viewVisibility == View.VISIBLE) {
                scheduleTraversals();
            } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                for (int i = 0; i < mPendingTransitions.size(); ++i) mPendingTransitions.get(i).endChangingAnimations();
                mPendingTransitions.clear();
            }
        }
        mIsInTraversal = false;
    }
    //View的测量
    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {                    //mView在Activity中为DecorView(FrameLayout)
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
    //View的布局
    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {
        //...代码省略...
        final View host = mView;
        //...代码省略...
        try {
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());//调用View的Layout方法进行布局
            mInLayout = false;
            int numViewsRequestingLayout = mLayoutRequesters.size();        //在ViewRootImpl进行布局的期间,Window内的View自己进行requestLayout
            if (numViewsRequestingLayout > 0) {
                ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,false);
                if (validLayoutRequesters != null) {
                    mHandlingLayoutInLayoutRequest = true;
                    int numValidRequests = validLayoutRequesters.size();
                    for (int i = 0; i < numValidRequests; ++i) {
                        final View view = validLayoutRequesters.get(i);
                        view.requestLayout();            //请求对该View布局,最终回调到ViewRootImpl的requestLayout进行重新测量、布局、绘制
                    }
                    measureHierarchy(host, lp, mView.getContext().getResources(),desiredWindowWidth, desiredWindowHeight);
                    mInLayout = true;
                    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
                    mHandlingLayoutInLayoutRequest = false;
                    validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
                    if (validLayoutRequesters != null) {
                        final ArrayList<View> finalRequesters = validLayoutRequesters;
                        getRunQueue().post(new Runnable() {
                            @Override
                            public void run() {
                                int numValidRequests = finalRequesters.size();
                                for (int i = 0; i < numValidRequests; ++i) {
                                    final View view = finalRequesters.get(i);
                                    //...代码省略...
                                    view.requestLayout(); //请求对该View布局,最终回调到ViewRootImpl的requestLayout进行重新测量、布局、绘制
                                }
                            }
                        });
                    }
                }
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        mInLayout = false;
    }
    //View的绘制
    private void performDraw() {
        if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw)  return;
        final boolean fullRedrawNeeded = mFullRedrawNeeded;
        mFullRedrawNeeded = false;
        mIsDrawing = true;
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
        try {
            draw(fullRedrawNeeded);
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        //...代码省略...
    }
}

4)、View树的measure测量:

系统在遍历完布局文件后,针对布局文件,在内存中生成对应的View树结构,这个时候整棵View树的所有View对象,都还没有具体的尺寸,因为measure过程最终是要确定每个View打的准确尺寸,也就是准确的像素值。安卓系统提供了三种测量模式,分别如下:

  • MeasureSpec.EXACTLY:精确尺寸,当控件的layout_width或layout_height指定为具体数值时或者为FILL_PARENT时,都是控件大小已经确定的情况,都是精确尺寸。
  • MeasureSpec.AT_MOST:最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。
  • MeasureSpec.UNSPECIFIED:未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。

View的测量是从方法measure开始的,可以看到此方法是final的,不可以被重写,并且注释中也表明实际的测量工作是在onMeasure方法中进行的,onMeasure方法中根据父布局给出的限制信息,和自己的content大小,来合理的测量自己的尺寸。View测量结束后,必须调用setMeasuredDimension把测量结果保存起来,具体保存在mMeasuredWidth和mMeasuredHeight中。

//View开始测量,此方法不可复写,由View树根节点发起
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) {
    mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;    // first clears the measured dimension flag
    resolveRtlPropertiesIfNeeded();
    onMeasure(widthMeasureSpec, heightMeasureSpec);    // 进行测量
    if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) 
        throw new IllegalStateException("onMeasure() did not set the" + " measured dimension by calling"+ " setMeasuredDimension()");
            
    mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
    }
    mOldWidthMeasureSpec = widthMeasureSpec;
    mOldHeightMeasureSpec = heightMeasureSpec;
}
//View具体测量过程,自定义View可以复写此方法实现自己的测量逻辑,但是测量完成后必须调用setMeasuredDimension方法进行结果的设置与存储
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
    setMeasuredDimension(
                getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
//View默认测量逻辑
public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);
    switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
    }
    return result;
}
//View测量完成后的结果存储,此方法不能被复写
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
    mMeasuredWidth = measuredWidth;
    mMeasuredHeight = measuredHeight;
    mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}

ViewGroup是一个抽象类,它并没有重写onMeasure方法,具体的实现交由子类去处理,如LinearLayout、RelativeLayout、FrameLayout,这是因为不同ViewGroup的布局特性和实现细节各异,无法统一处理。在这里我们以FrameLayout为例分析ViewGroup的测量过程,FrameLayout重写了onMeasure方法,并在其中实现了自己的测量逻辑,源码如下:

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int count = getChildCount();
        //这里判断FrameLayout的宽高只要有一个不是精确的就需要对子View全部测量
        final boolean measureMatchParentChildren =  MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
        mMatchParentChildren.clear();
        //记录最大宽度和长度
        int maxHeight = 0;
        int maxWidth = 0;
        int childState = 0;
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (mMeasureAllChildren || child.getVisibility() != GONE) {
                //这里测量子View,已用空间传入0是因为FrameLayout的层叠布局特性
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                maxWidth = Math.max(maxWidth,  child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                childState = combineMeasuredStates(childState, child.getMeasuredState());
                if (measureMatchParentChildren) 
                    if (lp.width == LayoutParams.MATCH_PARENT ||  lp.height == LayoutParams.MATCH_PARENT) 
                        mMatchParentChildren.add(child);
            }
        }
        // Account for padding too
        maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
        maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
        // Check against our minimum height and width
        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
        // Check against our foreground's minimum height and width
        final Drawable drawable = getForeground();
        if (drawable != null) {
            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
        }
        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),  resolveSizeAndState(maxHeight, heightMeasureSpec,   childState << MEASURED_HEIGHT_STATE_SHIFT));
        count = mMatchParentChildren.size();
        if (count > 1) {
            for (int i = 0; i < count; i++) {
                final View child = mMatchParentChildren.get(i);
                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
                final int childWidthMeasureSpec;
                if (lp.width == LayoutParams.MATCH_PARENT) {
                    final int width = Math.max(0, getMeasuredWidth()  - getPaddingLeftWithForeground() - getPaddingRightWithForeground()  - lp.leftMargin - lp.rightMargin);
                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(  width, MeasureSpec.EXACTLY);
                } else {
                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeftWithForeground() + getPaddingRightWithForeground() +  lp.leftMargin + lp.rightMargin,   lp.width);
                }
                final int childHeightMeasureSpec;
                if (lp.height == LayoutParams.MATCH_PARENT) {
                    final int height = Math.max(0, getMeasuredHeight()  - getPaddingTopWithForeground() - getPaddingBottomWithForeground()    - lp.topMargin - lp.bottomMargin);
                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(    height, MeasureSpec.EXACTLY);
                } else {
                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,  getPaddingTopWithForeground() + getPaddingBottomWithForeground() +   lp.topMargin + lp.bottomMargin,   lp.height);
                }
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
    }

总结:测量过程从ViewRoot开始发起,因为ViewRoot与DecorView关联起来的,并且DecorView继承了FrameLayout,因此可以代表了View树的根节点,通过递归方式依次调用了子控件的measure方法,如果子控件是ViewGroup,ViewGroup的测量方法又会调用自己的子控件进行测量,直到所有的View全部被测量完成为止:

  • View:测量自己的宽高进行测量,并将结果保存在变量mMeasuredWidth和mMeasuredHeight中。
  • ViewGroup:遍历子控件测量方法后,根据子元素的测量结果来决定自己最终的测量大小,并调将结果保存在变量mMeasuredWidth和mMeasuredHeight中。

5)、View树的layout布局:

整棵View树的所有View对象在经过测量过程后,这个时候View的宽高还是无法确定的,还需要根据测量出来的结果进行合理的布局(确定View的四个顶点的位置)。

View的布局流程首先通过setFrame设置View的四个顶点在父View的位置,那么此View的位置就确定了;然后调用onLayout方法确定各个子View的位置,因为View没有子控件,所有View里面的onLayout什么都没有干。当View调用setFrame,我们就可以通过getWidth,getHeight方法来获取View的宽高的。

    public void layout(int l, int t, int r, int b) {
        int oldL = mLeft;                        //声明View的四个坐标点
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;
        boolean changed = setFrame(l, t, r, b);  //最终都会调用setFrame方法确定自身的四个坐标
        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            onLayout(changed, l, t, r, b);       //调用onLayout
            mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnLayoutChangeListeners != null) {
                ArrayList<OnLayoutChangeListener> listenersCopy = (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
                int numListeners = listenersCopy.size();
                for (int i = 0; i < numListeners; ++i) {
                    listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
                }
            }
        }
        mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
    }
    //View里面的onLayout方法什么都没有干
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    }
    //View设置四个顶点坐标
    protected boolean setFrame(int left, int top, int right, int bottom) {
         boolean changed = false;
        if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
            changed = true;                  //如果有一个值发生了变化就要重新调用onLayout方法
            int drawn = mPrivateFlags & PFLAG_DRAWN;
            int oldWidth = mRight - mLeft;   //保存旧的宽和高
            int oldHeight = mBottom - mTop;
            int newWidth = right - left;     //计算新的宽和高
            int newHeight = bottom - top;
            boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
            invalidate(sizeChanged);        //判断宽高是否有分生变化 如果变化了请求重新绘制
            mLeft = left;                   //存储新的值
            mTop = top;
            mRight = right;
            mBottom = bottom;
            mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
            mPrivateFlags |= PFLAG_HAS_BOUNDS;
            if (sizeChanged) {              //大小变化时进行处理
                sizeChange(newWidth, newHeight, oldWidth, oldHeight);
            }
            if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) {
                invalidate(sizeChanged);       //如果此时View是可见状态下,立即执行绘制操作
            }
            mPrivateFlags |= drawn;
            mBackgroundSizeChanged = true;
            if (mForegroundInfo != null) {
                mForegroundInfo.mBoundsChanged = true;
            }
            notifySubtreeAccessibilityStateChangedIfNeeded();
        }
        return changed;
    }

ViewGroup的继承了View,layout方法被final修饰,最终还是调用View的布局流程,但是ViewGroup的onLayout方法是抽象方法

    @Override
    public final void layout(int l, int t, int r, int b) {
        if (mTransition == null || !mTransition.isChangingLayout()) {
            if (mTransition != null) {
                mTransition.layoutChange(this);
            }
            super.layout(l, t, r, b);    //调用了View的layout,实际上还是View的布局流程
        } else {
            mLayoutSuppressed = true;
        }
    }
    @Override
    protected abstract void onLayout(boolean changed,int l, int t, int r, int b);

LinearLayout继承了ViewGroup,并且实现了onLayout方法,通过源码分析LinearLayout在layout先调用了View的layout方法,首先调用了setFrame确定了自己的四个顶点,然后在onLayout中对自己所有的子控件进行遍历,并获取所有子控件测量出来的宽高child.getMeasuredWidth进行来进行水平或垂直布局,确定所有子控件的四个坐标,并通过setChildFrame调用子控件的layout方法

protected void onLayout(boolean changed, int l, int t, int r, int b) { 
    if (mOrientation == VERTICAL) { 
        layoutVertical(l, t, r, b);           //垂直布局 
    } else { 
        layoutHorizontal(l, t, r, b);         //水平布局 
    } 
} 
void layoutVertical(int left, int top, int right, int bottom) { 
    final int paddingLeft = mPaddingLeft; 
    int childTop; 
    int childLeft; 
    final int width = right - left;             //父View的宽度 
    int childRight = width - mPaddingRight;     //得到所有子View的最右边界 
    int childSpace = width - paddingLeft - mPaddingRight; //所有子View占用的横向空间 
    final int count = getVirtualChildCount(); 
    final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 
    final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 
    switch (majorGravity) {                     //根据子View在父View中的Gravity(上,下,左,右,中)来计算子所有View的上边界 
        case Gravity.BOTTOM: 
            childTop = mPaddingTop + bottom - top - mTotalLength; 
            break; 
        case Gravity.CENTER_VERTICAL: 
            childTop = mPaddingTop + (bottom - top - mTotalLength) / 2; 
            break; 
        case Gravity.TOP: 
        default: 
            childTop = mPaddingTop; 
            break; 
    } 
    //对遍历子控件并进行布局 
    for (int i = 0; i < count; i++) { 
        final View child = getVirtualChildAt(i); 
        if (child == null) { 
            childTop += measureNullChild(i); 
        } else if (child.getVisibility() != GONE) { 
            final int childWidth = child.getMeasuredWidth(); //获取子View的测量的宽高   
            final int childHeight = child.getMeasuredHeight(); 
            final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); 
            int gravity = lp.gravity; 
            if (gravity < 0) gravity = minorGravity; 
            final int layoutDirection = getLayoutDirection(); 
            final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); //获取每个子View的左边界 
            switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 
                case Gravity.CENTER_HORIZONTAL: 
                    childLeft = paddingLeft + ((childSpace - childWidth) / 2)+ lp.leftMargin - lp.rightMargin; 
                    break; 
                case Gravity.RIGHT: 
                    childLeft = childRight - childWidth - lp.rightMargin; 
                    break; 
                case Gravity.LEFT: 
                default: 
                    childLeft = paddingLeft + lp.leftMargin; 
                    break; 
            } 
            if (hasDividerBeforeChildAt(i))  //逐个累计各个子View的竖直方向占用空间为布置下一个子View做准备 
                childTop += mDividerHeight;    
            childTop += lp.topMargin;         //最终让每个子View各自完成自己的layout   
            setChildFrame(child, childLeft, childTop + getLocationOffset(child), childWidth, childHeight); 
            childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child); 
            i += getChildrenSkipCount(child, i); 
        } 
    } 
} 
private void setChildFrame(View child, int left, int top, int width, int height) { 
    child.layout(left, top, left + width, top + height);     //调用了子View的layout 
}

总结:布局过程从ViewRoot开始发起,因为ViewRoot与DecorView关联起来的,并且DecorView继承了FrameLayout,因此可以代表了View树的根节点,通过递归方式依次调用了子控件的layout方法,如果子控件是ViewGroup,ViewGroup的布局方法又会调用自己的子控件进行布局,直到所有的View全部被布局完成为止:

  • View:通过layout方法来确认自己在父容器中的位置
  • ViewGroup:通过layout方法来确认自己在父容器中的位置之后,通过onLayout 方法来确定子View在容器中的位置

6)、View树的draw绘制:绘制过程就比较简单了,它的作用是将View绘制到屏幕上面。

    @CallSuper
    public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE && (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
        //步骤1:绘制背景
        int saveCount;
        if (!dirtyOpaque) {
            drawBackground(canvas);
        }
        //步骤2:如果需要,保存画布的图层以备褪色
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            //步骤3:绘制内容
            if (!dirtyOpaque) onDraw(canvas);
            //步骤4:绘制子控件
            dispatchDraw(canvas);
            // Overlay is part of the content and draws beneath Foreground
            //步骤5:如果需要,绘制衰落边缘并恢复图层
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }
            //步骤6:绘制装饰(前景、滚动条)
            onDrawForeground(canvas);
            return;
        }
    ...
    }

View的绘制流程遵循几个步骤:先通过drawBackground绘制背景,在通过onDraw绘制自己,在通过dispatchDraw绘制子控件,最后通过onDrawForeground绘制装饰。我们可以通过重写View的onDraw方法来实现绘制自定义的View,因为View没有子控件,所以dispatchDraw也什么都没有干。

    protected void onDraw(Canvas canvas) {
    }
    protected void dispatchDraw(Canvas canvas) {
    }

ViewGroup继承了View,但是ViewGroup作为容器,重写了dispatchDraw方法,在dispatchDraw遍历了所有的子控件并调用了他们的draw方法进行子控件的绘制

    protected void dispatchDraw(Canvas canvas) {
        ...省略代码...
        for (int i = 0; i < childrenCount; i++) {
            while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
                final View transientChild = mTransientViews.get(transientIndex);
                if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||  transientChild.getAnimation() != null) {      
                    more |= drawChild(canvas, transientChild, drawingTime);//绘制子元素
                }
                transientIndex++;
                if (transientIndex >= transientCount) {
                    transientIndex = -1;
                }
            }
            final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
            final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                more |= drawChild(canvas, child, drawingTime);    //绘制子元素
            }
        }
       ...省略代码...            //一堆绘制子元素的逻辑
    }
    //调用了子控件的draw方法
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }

总结:绘制过程从ViewRoot开始发起,因为ViewRoot与DecorView关联起来的,并且DecorView继承了FrameLayout,因此可以代表了View树的根节点,通过递归方式依次调用了子控件的draw方法,如果子控件是ViewGroup,ViewGroup的绘制方法又会调用自己的子控件进行绘制,直到所有的View全部被绘制完成为止:

  • View:通过draw方法来进行绘制,重写onDraw方法进行对自己内容的绘制
  • ViewGroup:通过draw方法来进行绘制,并通过dispatchDraw方法遍历所有的子控件并调用子控件的draw方法

7)、View的绘制流程总结:在ActivityThread中,当Activity对象被创建完之后,会将DecorView添加到Window中,同时会创建对应的ViewRootImpl,并将ViewRootImpl和DecorView建立关联,并保存到WindowManagerGlobal对象中。View的绘制流程是从ViewRoot的performTraversals方法开始的,它经过measure、layout和draw三个过程才能最终将一个View绘制出来。大致流程如下图:

View的measure、layout和draw三个过程在ViewRoot中开始,因为ViewRoot是通过DecorView实例化,因此performTraversals方法自上而下递归方式对所有的View进行测量、布局、绘制

8)、View的重绘:

  • invalidate:当View的appearance发生改变,比如状态改变(enable,focus),背景改变,隐显改变等,这些都属于appearance范畴,都会引起invalidate操作。所以当我们改变了View的appearance,需要更新界面显示,就可以直接调用invalidate方法。View(非容器类)调用invalidate方法只会重绘自身,ViewGroup调用则会重绘整个View树。
  • requestLayout:当View的边界即宽高发生了变化,不再适合现在的区域,可以调用requestLayout方法重新对View布局。View执行requestLayout方法,会向上递归到顶级父View中,再执行这个顶级父View的requestLayout,所以其他View的onMeasure,onLayout也可能会被调用。但并不会执行onDraw方法。
  • postInvalidate:invalidate方法只能用于UI线程中,在非UI线程中,可直接使用postInvalidate方法,这样就省去使用handler的烦恼。

所以当我们进行View更新时,若仅View的显示内容发生改变且新显示内容不影响View的大小、位置,则只需调用invalidate方法,如果View宽高、位置发生改变且显示内容不变,只需调用requestLayout方法;若两者均发生改变,则需调用两者,按照View的绘制流程,推荐先调用requestLayout方法再调用invalidate方法。

2、View的事件分发

安卓上面的View是树型结构,View可能会重叠在一起,当我们点击的地方有多个View响应的时候,这个事件应该给谁来处理,因此就有了事件分发机制。

1)、事件分发三个重要的方法:

  • public boolean dispatchTouchEvent(MotionEvent event):事件分发,当一个事件传递到Activity、ViewGroup、View的时候,此方法第一时间被调用,并且是必须调用的。如果当前Activity、ViewGroup、View处理了当前事件,那么返回true,表示该事件已经结束不会再向下分发。
  • public boolean onInterceptTouchEvent(MotionEvent ev):事件拦截,当一个ViewGroup在接到MotionEvent事件序列时候,首先会调用此方法判断是否需要拦截,也就是是否要向下传递事件。特别注意Activity因为是事件分发最上层,View是事件分发最底层,因此Activity、View都不能进行事件拦截,否则会导致事件无法响应屏幕暂停。
  • public boolean onTouchEvent(MotionEvent ev):事件处理,当一个事件传递到Activity、ViewGroup、View可以选择是否进行事件处理,返回值为true,表示当前事件已经被处理,该事件已经结束不会再向下分发。

2)、事件分发流程:

点击事件产生最先传递到当前的Activity,由Acivity的dispatchTouchEvent方法来对事件进行分发,其中会给Activity附属的Window进行分发。如果返回true,那么事件被处理。如果返回false表示事件发下去却没有View可以进行处理,Activity调用自己的onTouchEvent方法进行处理。

    //Activity进行事件分发
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {  //事件分发到DecorView并返回结果,
            return true;                               
        }
        return onTouchEvent(ev);                        //DecorView没有处理了事件理,Activity调用onTouchEvent方法自己进行处理
    }
    //Activity进行事件处理    
    public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }
        return false;
    }
    //PhoneWindow代理Activity进行事件分发
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);    //DecorView继承了ViewGroup
    }
    //DecorView继承了ViewGroup,DecorView开启了View树的分发机制
    public boolean superDispatchTouchEvent(MotionEvent event) {
            return super.dispatchTouchEvent(event);
    }

PhoneWindow内部类DecorView继承了ViewGroup,从此开启了View树的分发机制。ViewGroup先调用onInterceptTouchEvent方法来判断是否进行事件拦截,没有拦截就遍历子控件,通过调用dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)来判断当前事件是否在这个子控件范围内并进行分发,最后如果没有子控件处理当前事件,通过调用dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS)来进行处理当前事件。注意两者参数不一样,通过dispatchTransformedTouchEvent源码可以的知道第三个参数为null的时候调用了ViewGroup父类的dispatchTouchEvent方法,不为null的时候调用了参数child的dispatchTouchEvent方法。

@Override 
public boolean dispatchTouchEvent(MotionEvent ev) { 
    ...省略代码... 
    if (onFilterTouchEventForSecurity(ev)) { 
        final int action = ev.getAction(); 
        final int actionMasked = action & MotionEvent.ACTION_MASK; 
        //处理第一个DOWN事件按下 
        if (actionMasked == MotionEvent.ACTION_DOWN) { 
            cancelAndClearTouchTargets(ev); 
            resetTouchState(); 
        } 
        //检查是否进行了拦截 
        final boolean intercepted; 
        if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { 
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; 
            if (!disallowIntercept) { 
                intercepted = onInterceptTouchEvent(ev); //调用onInterceptTouchEvent来判断是否进行了拦截 
                ev.setAction(action); 
            } else { 
                intercepted = false; 
            } 
        } else { 
            intercepted = true; 
        } 
        ...省略代码... 
        if (!canceled && !intercepted) {     //当前事件没有被拦截 
            ...省略代码... 
            final int childrenCount = mChildrenCount; 
            if (childrenCount != 0) { 
                //遍历子控件,寻找一个可以接收当前事件的子控件 
                final View[] children = mChildren; 
                final float x = ev.getX(actionIndex); 
                final float y = ev.getY(actionIndex); 
                final boolean customOrder = isChildrenDrawingOrderEnabled(); 
                for (int i = childrenCount - 1; i >= 0; i--) { 
                    ...省略代码... 
                    //分发事件给子控件 
                    if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { 
                        //子控件想处理当前事件 mLastTouchDownTime = ev.getDownTime();
                        mLastTouchDownIndex = childIndex; 
                        mLastTouchDownX = ev.getX(); 
                        mLastTouchDownY = ev.getY(); 
                        newTouchTarget = addTouchTarget(child, idBitsToAssign); 
                        alreadyDispatchedToNewTouchTarget = true; 
                        break; 
                    } 
                } 
            } 
            ...省略代码... 
        }             
    } 
    if (mFirstTouchTarget == null) { 
        //没有子控件进行处理当前事件,自己进行处理,参数为null 
        handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); 
     }
    ...省略代码 
    return handled; 
} 
//参数child不为null将事件分发给子控件 child为null调用父类dispatchTouchEvent方法 
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { 
    final boolean handled; 
    final int oldAction = event.getAction(); 
    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {     
        event.setAction(MotionEvent.ACTION_CANCEL); 
        if (child == null) { 
            handled = super.dispatchTouchEvent(event); //调用父类dispatchTouchEvent,因为ViewGroup继承View,因此自己进行事件的处理 
        } else { 
            handled = child.dispatchTouchEvent(event); //调用子控件的dispatchTouchEvent,子控件进行事件的处理 
        } 
        event.setAction(oldAction); 
        return handled; 
    } 
    ...代码省略... 
} 
//ViewGroup的onInterceptTouchEvent默认返回false,不进行事件的拦截 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
    return false; 
}

最后事件分发终于经过树型结构层层分发,达到最底层的View。View会先判断是否设置了OnTouchListener,如果设置了OnTouchListener并且onTouch方法返回了true,那么onTouchEvent不会被调用;当没有设置OnTouchListener或者设置了OnTouchListener但是onTouch方法返回false则会调用View自己的onTouchEvent方法并实现了一些常用事件的逻辑以及事件回调。

    public boolean dispatchTouchEvent(MotionEvent event) {
        ...省略代码...
        if (onFilterTouchEventForSecurity(event)) {
            ListenerInfo li = mListenerInfo;
            //判断是否设置了监听器,设置了监听器并已经在监听器中处理了返回true,向树型结构的上层View告知当前事件已经处理
            if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED   && li.mOnTouchListener.onTouch(this, event)) {
                return true;
            }
            //监听器没有处理当前事件,调用onTouchEvent返回true,向树型结构的上层View告知当前事件已经处理
            if (onTouchEvent(event)) {
                return true;
            }
        }
        ...省略代码...
        return false;    //如果没有监听器或者监听器没有处理,onTouchEvent也没有处理,返回flase,向树型结构的上层View告知当前事件没有被处理
    }
    //View默认实现事件处理方法
    public boolean onTouchEvent(MotionEvent event) {
        final int viewFlags = mViewFlags;
        //如果View是设置成不可用的(DISABLED)仍然会处理点击事件
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) setPressed(false);
            return (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
        }
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event))return true;
        }
        //CLICKABLE 和LONG_CLICKABLE只要有一个为true就处理这个事件
        if (((viewFlags & CLICKABLE) == CLICKABLE ||  (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_UP:
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused())  focusTaken = requestFocus();
                        if (prepressed)  setPressed(true, x, y);
                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) { 
                            removeLongPressCallback();
                            if (!focusTaken) {
                                if (mPerformClick == null) mPerformClick = new PerformClick();
                                if (!post(mPerformClick)) {
                                    //在ACTION_UP方法发生时会触发performClick()方法
                                    performClick();
                                }
                            }
                        }
                    break;
                case MotionEvent.ACTION_DOWN:
                    ...省略代码...
                    break;
                case MotionEvent.ACTION_CANCEL:
                   ...省略代码...
                    break;
                case MotionEvent.ACTION_MOVE:
                   ...省略代码...
                    break;
            }
            return true;
        }
        return false;
    }
    public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);        //onClick监听器的事件回调
            result = true;
        } else {
            result = false;
        }
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }
    //View的setOnClickListener会默认将View的clickable设置成true
    public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            //View的clickable设置成false,又给View设置了OnClickListener,就会出现setClickable失效了,实际上是在设置OnClickListener的时候更改了clickable的值
            setClickable(true);  
        }
        getListenerInfo().mOnClickListener = l;
    }
    //View的setOnLongClickListener同样会将View的longClickable设置成true
    public void setOnLongClickListener(@Nullable OnLongClickListener l) {
        if (!isLongClickable()) {
            setLongClickable(true);
        }
        getListenerInfo().mOnLongClickListener = l;
    }

3)、总结:当我们的手指触摸到手机屏幕时,当前处于onStart()状态的Activity最先接收到此Touch事件下的ACTON_DOWN,然后开始调用它自己dispatchTouchEvent()开始进行DOWN事件分发,如果此方法返回true,则Activity不向下分发事件,则整个布局都不会收到DOWN事件,TouchEvent直接到Activity的onTouchEvent()方法进行事件处理.如果返回false,则表示DOWN是要被分发到下层的,此时DOWN事件被直接分发(因为没有过滤方法)到UI树的根布局(即最外层的布局),根布局拿到DOWN事件时,执行自己的dispatchTouchEvent方法,返回true,则事件直接交到根布局的onTouchEvent()中进行处理,false则表示还得向下分发,此时事件被传递到根布局的onInterceptTouchEvent()方法中,如果此方法返回true,表示要对此事件进行过滤,则此DOWN事件又直接进行到根布局的onTouchEvent()方法直接处理,false则,要根布局不对事件进行过滤,DOWN事件继续向下传递,直到达到目标组件后,目标组件调用自己的dispatchTouchEvent()方法,由于是目标组件,直接分发事件到自己的onTouchEvent方法中,目标组件如果处理完这个DOWN事件后返回true,表示该事件被消费完毕,不再向上层传递,如果返回false,则表示没有消费完这个DOWN事件,DOWN向上传递到自己的父组件中,父组件再进行DOWN事件的处理.一直向上传递直到事件被扔到虚拟机,DOWN事件才算处理完成。


3、ListView

ListView是一个数据集合能以动态滚动的方式展示在用户界面的View。ListView继承抽象类AbsListView,AbsListView继承抽象类AdapterView,AdapterView继承了ViewGroup,但是AdapterView是不允许直接addView或removeView。

public abstract class AdapterView<T extends Adapter> extends ViewGroup {
    ...省略代码...
    @Override
    public void addView(View child, LayoutParams params) {
        throw new UnsupportedOperationException("addView(View, LayoutParams) " + "is not supported in AdapterView");
    }
    @Override
    public void addView(View child, int index, LayoutParams params) {
        throw new UnsupportedOperationException("addView(View, int, LayoutParams) " + "is not supported in AdapterView");
    }
    @Override
    public void removeView(View child) {
        throw new UnsupportedOperationException("removeView(View) is not supported in AdapterView");
    }
    @Override
    public void removeViewAt(int index) {
        throw new UnsupportedOperationException("removeViewAt(int) is not supported in AdapterView");
    }
    @Override
    public void removeAllViews() {
        throw new UnsupportedOperationException("removeAllViews() is not supported in AdapterView");
    }
}

1)、ListView的适配器模式:ListView不支持直接向其添加或者移除子控件,采用了适配器的模式来实现View与数据源的分离

2)、ListView的缓存机制RecycleBin:ListView的父类AbsListView有一个内部类RecycleBin,RecycleBin实现了这个缓存机制,不管你有任意多条数据需要显示,ListView中的子View其实来来回回就那么几个,移出屏幕的子View会很快被移入屏幕的数据重新利用起来,因而不管我们加载多少数据都不会出现OOM的情况,甚至内存都不会有所增加。

3)、ListView的优化:convertView的复用和ViewHolder

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {    //convertView没有复用,加载布局
            convertView = mInflater.inflate(R.layout.item_listview, parent, false); 
            holder = new ViewHolder(); 
            holder.titleTv = (TextView) convertView.findViewById(R.id.titleTv);
            holder.descTv = (TextView) convertView.findViewById(R.id.descTv);
            convertView.setTag(holder);//使用Holder减少findViewById次数
        } else {                      //convertView已经复用,通过Holder方式拿到句柄
            holder = (ViewHolder) convertView.getTag();
        }
        ...省略代码...
        return convertView;
    }
    private class ViewHolder {        //这个ViewHolder只能服务于当前这个特定的adapter,因为ViewHolder里会指定item的控件,不同的ListView,item可能不同,所以ViewHolder写成一个私有的类
        TextView titleTv;
        TextView descTv;
    }

4)、View的setTag方法:tag从本质上来讲是就是相关联的view的额外的信息。它们经常用来存储一些view的数据,这样做非常方便而不用存入另外的单独结构。

    public void setTag(final Object tag) {
        mTag = tag;
    }    
    public void setTag(int key, final Object tag) {
        if ((key >>> 24) < 2) throw new IllegalArgumentException("The key must be an application-specific "  + "resource id.");
        setKeyedTag(key, tag);
    }
    public Object getTag() {
        return mTag;
    }
    public Object getTag(int key) {
        if (mKeyedTags != null) return mKeyedTags.get(key);
        return null;
    }

可以通过View的setTag方法设置一个对象进去,然后通过getTag方法将这个对象取出来,因此在convertView没有复用的时候,通过inflate加装一个布局,将这个布局里面的元素存储在ViewHolder中,最后将ViewHolder作为一个对象设置​到tag中,在下次convertView复用的时候,就可以通过getTag将ViewHolder提取出来,这样避免了频繁的调用findViewById。

注意如果converView.setTag("pic",picHolder)或者converView.setTag(100,picHolder)这样使用的话,程序将抛出异常,因为key必须要唯一,这个时候可以将参数1作为一个资源id进行使用。

三、性能优化

1、ANR

ANR就是应用程序无响应的对话框,默认情况下Activity执行时间超过5s,广播执行时间超过10s都会弹出这个对话框,出现ANR的原因都基本上是在主线程中作了耗时操作。

1)、ANR产生的主要原因:

  • 主线程作了IO操作(4.0后网络IO不允许在主线程)阻塞
  • 主线程存在耗时计算

2)、执行在主线程的常用操作:

  • Activity的所有生命周期回调方法都是执行在主线程
  • Service默认是执行在主线程,IntentService除外
  • BroadcastReceiver的onReceive回调方法也是执行在主线程
  • AsyncTask的回调除了doInBackground其他方法也是执行在主线程
  • 主线程执行new Handler().post(Runnable)这个时候并没有开启一个子线程,而是主线程的Handler将这个任务封装成一个消息传递到了主线程的Looper中,轮训器拿到这个任务后,在主线程执行这个任务的run里面的代码

2、OOM

当前占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就会抛出Out of memory异常。OOM既内存溢出,就是我们需要分配的内存已经超过最大限制。优化OOM主要方式如下:

  • 图片显示:ListView等这样的滑动控件滑动的时候禁止网络请求图片,显示缩略图的时候禁止网络请求
  • 释放bitmap:ImagView在被销毁的时候会自己释放资源,虽然说Bitmap不再被引用时他会自己释放资源但是它是不及时的,所以当遇到不被占用的资源时我们应该及时的去调用recycle()方法将bitmap资源释放掉;Activity中的Bitmap对象,如果是成员变量,需要在onDestroy()中明确释放。 因为,即使在Activity调用Finish()结束后,虚拟机都不会立即回收Bitmap的内存(什么时候回收不得而知)
if(bitmap != null && !bitmap.isRecycled()){ 
        bitmap.recycle();     // 回收并且置为null
        bitmap = null; 
} 
System.gc();
  • 加载bitmap捕获异常:因为Bitmap是吃内存大户,为了避免应用在分配Bitmap内存的时候出现OutOfMemory异常以后Crash掉,需要特别注意实例化Bitmap部分的代码。通常,在实例化Bitmap的代码中,一定要对OutOfMemory异常进行捕获。
Bitmap bitmap = null;
try {
    bitmap = BitmapFactory.decodeFile(path);//实例化Bitmap
} catch (OutOfMemoryError e) {
}
if (bitmap == null) {
    return defaultBitmapMap;                //如果实例化失败 返回默认的Bitmap对象
}
  • 预压缩处理图片bitmap:使用inJustDecodeBounds和inSampleSize属性进行加载很大的图片资源
  • 复用bitmap:使用inBitmap属性对内存进行复用(表示还没有用过)
  • 避免在执行频率很高的方法(例如onDraw)里面进行对象实例化创建
  • 在没有必要的时候谨慎使用多线程
  • ListView在convertview加载图片的时候使用三级缓存

3、Bitmap相关优化

1)、recycle:从源码注释可以看出android系统为了提高对图片的处理效率,对于图片的处理都是调用了底层的功能(由C语言实现的),也就是说一个图片加载到内存里后是使用两部分的内存区域,简单的说:一部分是java可用的内存区,一部分是c可用的内存区,这两个内存区域是不能相互直接使用的,这个bitmap对象是有java分配的,当然不用的时候系统会自动回收了,可是那个对应的C可用的内存区域jvm是不能直接回收的,这个只能调用底层的功能释放。所以你要调用recycle方法来释放那一部分内存。

  /**
     * Free the native object associated with this bitmap, and clear the
     * reference to the pixel data. This will not free the pixel data synchronously;
     * it simply allows it to be garbage collected if there are no other references.
     * The bitmap is marked as "dead", meaning it will throw an exception if
     * getPixels() or setPixels() is called, and will draw nothing. This operation
     * cannot be reversed, so it should only be called if you are sure there are no
     * further uses for the bitmap. This is an advanced call, and normally need
     * not be called, since the normal GC process will free up this memory when
     * there are no more references to this bitmap.
     */
    public void recycle() {
        if (!mRecycled) {
            if (nativeRecycle(mNativeBitmap)) {
                mBuffer = null;
                mNinePatchChunk = null;
            }
            mRecycled = true;
        }
    }

Bitmap.recycle()会释放Bitmap所关联的像素资源(个人理解释放了c空间分配的内存),并且释放是单向的,一旦释放不可再用,因此才引发了一桩血案。这桩血案释放了bitmap,使用targetBitmap的时候导致程序崩溃,分析源码createBitmap的注释可以看出来createBitmap出来的bitmap在c空间里面可能就是公用的同一块内存,因此对bitmap.recycle后,targetBitmap也无法使用。因此调用Bitmap.recycle()前应该考虑后续将一定不会使用,因此在onDestroy()等方法周期调用是安全的。

//引发血案的代码:
    ImageView imageView = (ImageView) findViewById(R.id.test);
    Matrix matrix = new Matrix();
    matrix.setRotate(0.013558723994643297f);
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
    Bitmap targetBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    if (!bitmap.isRecycled()) {
        bitmap.recycle();
    }
    imageView.setImageBitmap(targetBmp);
//Bitmap.createBitmap源码注释:
    /**
     * Returns an immutable bitmap from subset of the source bitmap,
     * transformed by the optional matrix. The new bitmap may be the
     * same object as source, or a copy may have been made. It is
     * initialized with the same density as the original bitmap.
     */
    public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter) {
        ......
    }

2)、LruCache:最少最近算法缓存,LruCache内部利用了LinkedHashMap集合来实现缓存的get和put操作,在缓存满的时候通过trimToSize方法对最早的缓存数据进行删除,从而实现最少最近算法的存储方式。

LinkedHashMap通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序该迭代顺序可以是插入顺序或者是访问顺序。实验结果证明,LinkedHashMap是有顺序的,每次访问一个元素(get或put),被访问的元素都被提到最后面去了

public static void main(String[] args){
    LinkedHashMap<String, String> linkedHashMap = 
                new LinkedHashMap<String, String>(16, 0.75f, true);//第三个参数需要传入true表示按顺序访问
    linkedHashMap.put("key1", "1");
    linkedHashMap.put("key2", "2");
    linkedHashMap.put("key3", "3");
    linkedHashMap.put("key4", "4");
    loopLinkedHashMap(linkedHashMap);    //打印结果是 1 2 3 4 
    linkedHashMap.get("key1");           //操作了key1,因此key1被放在了链表的最后面 
    loopLinkedHashMap(linkedHashMap);    //打印结果是 2 3 4 1  
    linkedHashMap.put("key2", "2x");     //操作了key2,因此key2被放在了链表的最后面   
    loopLinkedHashMap(linkedHashMap);    //打印结果是 3 4 1 2x
}
//遍历打印LinkedHashMap
public static void loopLinkedHashMap(LinkedHashMap<String, String> linkedHashMap){
    Set<Map.Entry<String, String>> set = inkedHashMap.entrySet();
    Iterator<Map.Entry<String, String>> iterator = set.iterator();
    while (iterator.hasNext()) System.out.print(iterator.next() + "\t");
}

LruCache利用了LinkedHashMap的get和put操作会将原始提取到最后面的特性,并对其进行了封装,每次get和put的时候调用了trimToSize方法对前面的元素进行删除,从而实现了最近最优算法缓存的机制

public class LruCache<K, V> {
    ...省略代码...
    private final LinkedHashMap<K, V> map;
    public LruCache(int maxSize) {
        if (maxSize <= 0) throw new IllegalArgumentException("maxSize <= 0");
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);    //实例化LinkedHashMap 最后一个参数是true
    }
    public final V get(K key) {
        if (key == null)  throw new NullPointerException("key == null");
        V mapValue;
        synchronized (this) {
            mapValue = map.get(key);
            if (mapValue != null) {
                hitCount++;
                return mapValue;
            }
            missCount++;
        }
        ...省略代码...
        if (mapValue != null) {
            entryRemoved(false, key, createdValue, mapValue);
            return mapValue;
        } else {
            trimToSize(maxSize);    //调用trimToSize进行条件判断是否删除前面的元素
            return createdValue;
        }
    }
    public final V put(K key, V value) {
        if (key == null || value == null)throw new NullPointerException("key == null || value == null");
        V previous;
        synchronized (this) {
            putCount++;
            size += safeSizeOf(key, value);
            previous = map.put(key, value);
            if (previous != null) size -= safeSizeOf(key, previous);
        }
        ...省略代码...
        trimToSize(maxSize);
        return previous;
    }
    public void trimToSize(int maxSize) {
        while (true) {
            K key;
            V value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) throw new IllegalStateException(getClass().getName()    + ".sizeOf() is reporting inconsistent results!");
                if (size <= maxSize || map.isEmpty())
                    break;                //如果缓存没有满或者缓存为空就退出循环
                Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);          //移除前面的元素
                size -= safeSizeOf(key, value);
                evictionCount++;
            }
            entryRemoved(true, key, value, null);
        }
    }
    ...省略代码...
}

LruCache内存缓存技术,这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。我们通常利用LruCache来对bitmap进行内存缓存: 

public class MemoryCacheUtil{
    private LruCache<String, Bitmap> mMemoryCache;             //创建Cache缓存,第一个泛型表示缓存的标识key,第二个泛型表示需要缓存的对象
    public MemoryCacheUtil() {
        int maxMemory=(int) Runtime.getRuntime().maxMemory();   //获取最大的应用运行时的最大内存 通过获得最大的运行时候的内存,合理分配缓存的内存空间大小
        int cacheSize=maxMemory/8;                              //取最大运行内存的1/4;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {//使用最大可用内存值的1/8作为缓存的大小
            protected int sizeOf(String key, Bitmap value) {    //加载正确的内存大小
                return value.getByteCount();                    //在每次存入缓存的时候调用
            }
        };
    }
    public void setCache(String url, Bitmap bitmap) {            //写内存缓存
        mMemoryCache.put(url, bitmap);
    }
    public Bitmap getCache(String url) {                         //读内存缓存
        return mMemoryCache.get(url);
    }
}

3)、BitmapFactory.Options:加载大图的时候设置options.inJustDecodeBounds来计算options.inSampleSize进行加载缩略图

​public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int  reqWidth, int reqHeight) {   
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    //因为options.inJustDecodeBounds为true,BitmapFactory只会解析图片的原始宽高信息,并不会真正的加载图片
    BitmapFactory.decodeResource(res, resId, options);    
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    options.inJustDecodeBounds = false;
    //因为Options.inJustDecodeBounds为false,BitmapFactory会通过设置的inSampleSize比例加载图片
    return BitmapFactory.decodeResource(res, resId, options);
}
//官方提供的计算缩略图比例的方式
public static int calSampleSize(BitmapFactory.Options options, int dstWidth, int dstHeight){
    int rawWidth = options.outWidth;
    int rawHeight = options.outHeight;
    int inSampleSize = 1;
    if (rawWidth > dstWidth || rawHeight > dstHeight) {
        float ratioHeight = (float) rawHeight / dstHeight;
        float ratioWidth = (float) rawWidth / dstHeight;
        inSampleSize = (int) Math.min(ratioWidth, ratioHeight);
    }
    return inSampleSize;        //计算缩放比例
}

4)、三级缓存加载图片:即内存缓存(LruCache)、本地缓存(文件存储)、网络缓存。其原理为首次加载从网络获取,然后存储到本地缓存或内存缓存,二次加载就可以从本地或者内存直接加载。

4、UI卡顿

安卓系统每隔16ms就会发送信号进行界面渲染,16ms之类如果没有完成渲染或者其他原因导致16ms没有进行渲染就会导致界面卡顿。

1)、UI卡顿的原因:

  • 人为在UI线程中做了轻微耗时操作,即没有引发ANR的情况下,导致UI线程卡顿
  • 布局Layout过于复杂,无法再16ms内完成渲染
  • 同一时间动画执行的次数过多,导致CPU或GPU负载过重
  • View过渡绘制,导致某些像素在同一帧时间内被绘制多次,导致CPU或GPU负载过重
  • View频繁的触发measure/layou,导致measure/layout累计耗时过多及View频繁的重新渲染
  • 内存频繁触发GC过多,导致暂时阻塞渲染操作
  • 冗余资源及逻辑等导致家中和执行缓慢
  • UI卡顿就是轻量级的ANR,因此禁止在主线程作耗时操作

2)、布局优化:

  • 尽量不要进行布局嵌套:在RelativeLayout和LinearLayout同时能够满足需求时,尽量使用RelativeLayout
  • 使用ViewStub标签:轻量级隐藏的没有尺寸的View,可用惰性加载原本隐藏的视图
  • 使用merge标签:防止在引用布局文件时产生多余的布局嵌套
  • 使用include标签:常用于将布局中的公共部分提取出来

5、内存泄漏

内存泄漏是指无用对象(不再使用的对象)持续占用内存或无用对象的内存得不到释放,从而造成内存空间的浪费,持续的内存泄漏将会引发OOM。Java内存泄漏的根本原因是什么呢?长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏,尽管短生命周期对象已经不再需要,但是因为长生命周期持有它的引用而导致不能被回收,这就是Java中内存泄漏的发生场景。

1、内存分配策略

  • 静态存储区:也叫方法区,除此之外存储静态数据和全局变量,编译后已经分配好了
  • 栈区:也叫暂时分配区,效率很高,容量有限,方法体的局部变量被分配在栈区,在方法体执行完或者生命周期之外无效
  • 堆区:也叫动态内存分配区,new实例化存储的区域,这区域的变量在不使用的时候会被垃圾回收机制回收

2、Java如何进行内存管理

java的由我们进行内存的分配,即直接实例化new对象,通过虚拟机GC触发进行回收没有使用到的对象(没有引用的对象),于程序员来说,GC基本是透明的,不可见的。虽然,我们只有几个函数可以访问GC,例如运行GC的函数System.gc(),但是根据Java语言规范定义, 该函数不保证JVM的垃圾收集器一定会执行。

3、Android内存管理机制

  • 分配机制:系统为每个进程分配合理的内存大小。采用了弹性的分配方式,刚开始不会分配太多内存,随着APP运行会有限制的为每个进程分配额外的内存,当应用不在使用的时候会将进程存储在系统中,用户再次启动应用的时候只需要恢复应用,不用再重新开启新进程。
  • 回收机制:在系统资源不足的时候合理回收再分配的机制,从而保证新的进程运行。系统将保存更多的进程的信息在系统中,不建议直接退出应用,在应用再次启动的时候只需要恢复当前进程,在前台进程内存不足的时候将根据进程优先级来回收杀死其他进程。

4、Android的几种内存泄漏

1)、单例:单例的静态特性导致单例的生命周期跟应用的生命周期一样长,因此单例里面不能持有生命周期很短的对象,否则将导致这个对象无法得到释放,进而引发内存泄漏

public class SingleInstance{
    private static volatile SingleInstance instance;//静态特性导致生命周期跟应用一样长
    private Context context;                        //单例持有的对象生命周期需要跟应用一样长
    private SingleInstance(Context context){        //如果context为activity这样生命周期不长的对象就会导致内存泄漏
        this.context = context.getApplicationContext();//通过context获取应用的上下文 
    }
    public static SingleInstance getInstance(Context context){
        if(instance == null){
            synchronized(SingleInstance.class){
                if(instance == null)instance = new SingleInstance(context);
            }
        }
        return instance;
    }
}

2)、非静态匿名内部类执行耗时工作:因为非静态匿名内部类会持有外部类的隐式引用,所以在外部类进行回收的时候,如果非匿名内部类还有线程或者耗时任务执行将会导致外部类无法进行回收,这时外部类在非静态匿名内部类的线程或者耗时任务执行完后才能进行回收

public class TestActivity extend Activity{
    ...省略代码...
    private void spawnThread(){
        new Thread() {         //匿名内部类的创建,持有TestActivity的引用
            @Override public void run() {
                while(true);   //匿名内部类执行了耗时任务,此任务结束后TestActivity才能够得到释放
            }
        }.start();
    }
}

3)、非静态内部类持有静态变量实例:因为非静态内部类会持有外部类的隐式引用,非静态内部类中创建了一个静态实例导致该非静态匿名内部类的生命周期和应用生命周期一样长,这个时候外部类无法进行回收

public class TestActivity extend Activity{
    ...省略代码...
    public class Inner1{                   //TestActivity无法被释放,因为Inner1一直持有TestActivity引用
        private static String = "SHEN_TAG";//内部类Inner1持有静态变量,应用周期提升为跟应用一样长 
    }
    public static class Inner2{            //TestActivity能够被释放,因为Inner2静态类没有持有TestActivity引用
        private static String = "SHEN_TAG";//内部类Inner1持有静态变量,应用周期提升为跟应用一样长   
    }
}

4)、Handler:Handler作为内部类的时候也持有外部类的引用,因为Handler内部持有Looper,因此Handler的生命周期跟应用一样长,在外部类被释放的时候,Handler有消息处理也将得不到释放,这个时候需要手动释放Handler或者将Handler设置为静态内部类和持有外部类的弱引用

public class NoLeakActivity extends AppCompatActivity {
    private static NoLeakHandler mHandler=new Handler(){    //Handler修改为静态类
        private WeakReference<NoLeakActivity> mActivity;    //Handler持有外部类的弱引用
        public NoLeakHandler(NoLeakActivity activity){
            mActivity = new WeakReference<>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mActivity.showUI();                             //通过外部类弱引用调用外部类方法
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new NoLeakHandler(this);
        Message message = Message.obtain();
        mHandler.sendMessageDelayed(message,10*60*1000);
    }
    public void showUI(){
        ...省略代码...
    }
}

5)、避免使用static关键词:被static修饰的对象会被存储在静态存储区,这个区域的内存即使在系统内存不足的时候也不会得到释放。android的内存管理原则是优先回收较大内存的进程,进程被回收的应用很不安全,因此可以通过懒加载来代替静态变量,避免用static修饰占用内存较多的对象。当我们启动一个app的时候,系统会创建一个进程,此进程会加载一个DalvikVM的实例,然后代码就运行在DVM之上,类的加载和卸载,垃圾回收等事情都由DVM负责。也就是说在进程启动的时候,类被加载,静态变量被分配内存。一般情况下,所有的类都是默认的ClassLoader加载的,只要ClassLoader存在,类就不会被卸载,而默认的ClassLoader生命周期是与进程一致的。

6)、资源未被关闭导致对象无法被释放:例如Activity关闭的时候应该关闭IO流的操作

5、冷启动优化

冷启动就是在启动应用前,系统中没有该应用的任何进程信息(包括Activity、Service等,例如在开机设备后第一次启动这个应用,或者该应用进程被杀死后再次开启),需要重新创建一个进程和application。热启动就是用户使用返回键或者Home键退出应用,然后马上又重启应用,当一个应用退出后进程信息被系统保存了下来,可以从已有的进程中启动,这时只需要创建初始化一个Activity就启动了应用。

1)、冷启动的流程:

  • Zygote进程中fork创建出一个新的进程
  • 创建和初始Application:Application构造方法->attachBaseContext->onCreate
  • 创建和绘制MainActivity:MainActivity构造方法-》onCreate->配置主题中背景等属性->onStart->onResume->测量布局绘制

2)、冷启动的优化:

  • 减少Application的onCreate方法的工作量,一些三方框架可以使用懒加载方式进行
  • 不要在Application进行业务操作和耗时操作
  • 不要在Application以静态变量的方式保存数据
  • 优化布局,降低布局的复杂度

6、其他优化

  • android不用静态变量来存储重要数据:因为在android系统中,进程是不安全的,应用Application有可能被杀死,然而在应用被杀死后,程序还能运行是因为android系统重新创建了Application,这个时候静态变量存储的数据都已经丢失了。在这种情况下,谷歌建议使用文件、Sharepreferen、contentProvider的方式来进行数据存储。
  • Sharepreferen安全问题:android系统会为每个进程维护一个Sharepreferen,因此Sharepreferen不支持进行跨进程同步。除此之外,对过大的Sharepreferen进行读写的时候,会特别影响效率造成界面卡顿,因为解析的时候会产生大量的临时变量
  • 内存对象序列化:Serializeble是java的序列化方式,但是在序列化的时候会产生大量的临时变量,Parcelable是android特有的序列化方式,Parcelable侧重于内存上进行数据传输,效率比Serializeble高,但是不能将数据存储在磁盘上。因此在使用内存序列化的时候建议用Parcelable,在进行磁盘序列化的时候使用Serializeble。

四、热门前沿技术

1、MVC

MVC的全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写。MVC要实现的目标是将软件用户界面和业务逻辑分离以使代码可扩展性、可复用性、可维护性、灵活性加强。View层是界面,Model层是业务逻辑,Controller层用来调度View层和Model层,将用户界面和业务逻辑合理的组织在一起,起粘合剂的效果。所以Controller中的内容能少则少,这样才能提供最大的灵活性。

  • V:视图是用户看到并与之交互的界面(它可以包括一些可以显示数据信息的页面,或者展示形式。例如jsp,html,asp,php)。对老式的Web应用程序来说,视图就是由HTML元素组成的界面,在新式的Web应用程序中,HTML依旧在视图中扮演着重要的角色,但一些新的技术已层出不穷,它们包括Macromedia Flash和象XHTML,XML/XSL,WML等一些标识语言和Web services
  • M:模型表示企业数据和业务规则(可以说就是后端接口,用于业务处理)。
  • C:控制器接受用户的输入并调用模型和视图去完成用户的需求(接受客户发送的请求,根据请求调用所对应的接口,然后模型业务处理后返回的数据,由控制器决定调用那个View展示)。所以当单击Web页面中的超链接和发送HTML表单时,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后用确定用哪个视图来显示模型处理返回的数据。

现在我们总结MVC的处理过程,首先控制器接收用户的请求,并决定应该调用哪个模型来进行处理,然后模型用业务逻辑来处理用户的请求并返回数据,最后控制器用相应的视图格式化模型返回的数据,并通过表示层呈现给用户。 

MVC要实现的目标是将软件用户界面V和业务逻辑M分离以使代码耦合度降低、可扩展性好、模块职责划分明确。注意很多程序员偏爱于将业务逻辑放在Controller中,业务逻辑的重复使用是经常要面对的场景。 如果业务逻辑写在控制器中, 要重用它的唯一方法就是将它提升到父类之中,通过继承来达到代码复用的效果。但这么做会带来一个巨大的副作用,违背了一项重要的面向对象设计原则:接口隔离。因此足以将“厚Controller,薄Model”这种不健康的MVC思想打入十八层地狱。

2、MVP

MVP模式是MVC模式在Android上的一种变体。在MVC模式中,Activity应该是属于View这一层,而实质上,它既承担了View,同时也包含一些Controller的东西在里面。这对于开发与维护来说不太友好,耦合度大高了。把Activity的View和Controller抽离出来就变成了View和Presenter,这就是MVP模式。

  • V:视图对应于Activity或者Fragment,负责View的绘制以及与用户交互。
  • M:提供数据来源
  • P:将Activity中业务逻辑抽象分离了出来,完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以保持Presenter的不变,即重用!

在android系统中,MVP的应用比MVC更加具有优势,因为MVP抽离了Activity的复杂的逻辑代码形成了P,这样更低的降低了耦合,除此之外,MVP模式完全的分离了View和Model,但是MVC中View和Model还能相互交互这是最大的区别。

MVP在实现代码简洁的同时,额外增加了大量的接口、类,不方便进行管理,于是Contract就登场了,Contract 如其名,是一个契约,将Model、View、Presenter 进行约束管理,方便后期类的查找、维护。

public interface MainContract {
    interface Model {
        Flowable<BaseObjectBean<LoginBean>> login(String username, String password);
    }
    interface View extends BaseView {                    //界面的显示接口
        @Override
        void showLoading();
        @Override
        void hideLoading();
        @Override
        void onError(Throwable throwable);
        void onSuccess(BaseObjectBean<LoginBean> bean);
    }

    interface Presenter {
        void login(String username, String password);    //登录逻辑
    }
}

3、进程保活

1)、Android进程优先级:

  • 前台进程:可与与用户交互的进程,满足以下的都是(有可与用户交互的Activity(已调onResume())、有广播接收者(BroadcastReceiver)正在接收广播、有服务(Service)正在执行它的回调方法(Service.onCreate(), Service.onStart(), or Service.onDestroy())、有Service调用了startForeground()方法使之位于前台运行)。前台进程系统是不会销毁的,除非极端情况。
  • 可见进程:处于前台但是失去了焦点不可点击触摸交互的Activity的进程,满足以下的都是(有不在前台仍对用户可见的Activity(已调用 onPause())、有绑定到可见(或前台)Activity 的 Service)。一般系统也不会回收可见进程的,除非要保持某个或多个前台进程存活而且资源吃紧的情况下。
  • 服务进程:在后台开启的Service的进程,即运行着一个通过startService()启动的Service。在内存不够维持所有前台进程与可见进程运行时,服务进程会被销毁。
  • 后台进程:处于前台的Activity被新的Activity覆盖,用户返回到界面或切换到其他APP,看不到但是还在运行的程序。这样的进程可能随时被系统销毁,回收内存。
  • 空进程:没有任何活跃组件的进程,可能随时都会被杀掉。

2)、Android进程回收策略:Android和linux都通过触发系统进程管理机制回收(Lowmemorykiller)来进行进程回收,这种方法通过判断OOM_ODJ来判断进程的优先级按照阈值从大到小进行清理。OOM_ODJ越低表示进程优先级越高,越不容易被系统回收。

3)、Android进程保活方案:

  • 利用系统广播拉活,例如监听android.intent.action.BOOT_COMPLETED广播事件启动应用
  • 利用Service机制拉活,例如双Service进行相互监听和拉活
  • 利用Native进程拉活,例如在Native中fork出一个进程对应用进行检测
  • 利用JobScheduler机制拉活,JobScheduler是系统级服务,每过一段时间就会触发一次
  • 利用账号同步机制拉活

4)、进程保活之前台服务:android中实现后台一般通过service方式,但系统本身会在内存低等情况下杀死service。通过将service绑定到notification,就成为了一个用户可见的前台服务,这样可以大大提高存活率。

public class BootstartService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        initForeground();                    //前台服务初始化
        startForeground(8888, notification);
        //清除通知 也可以不用清除
        notificationManager.cancel(8888);    
        stopForeground(true);
        stopSelf();
        return START_NOT_STICKY;
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        stopForeground(true);
    }
    private NotificationManager notificationManager;    
    private Notification notification;
    public static final String CHANNEL_ID_STRING = "nyd0011";
    private void initForeground(){                                                                 
        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            NotificationChannel mChannel = null;
            mChannel = new NotificationChannel(CHANNEL_ID_STRING, "GymSmar1111111t", NotificationManager.IMPORTANCE_HIGH);
            notificationManager.createNotificationChannel(mChannel);
        }
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this,CHANNEL_ID_STRING);
        Intent resultIntent = new Intent();                                                        
        PendingIntent resultPendingIntent = PendingIntent.getActivity(this,0,resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);//封装一个Intent
        mBuilder.setSmallIcon(0).setContentTitle("").setContentText("");    //必须设置了SmallIcon才有效果
        mBuilder.setContentIntent(resultPendingIntent);                                            
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID_STRING, "111",  NotificationManager.IMPORTANCE_MIN);
            channel.enableLights(false);
            channel.enableVibration(false);
            channel.setVibrationPattern(new long[]{0});
            channel.setSound(null, null);
            notificationManager.createNotificationChannel(channel);
        }else{
            mBuilder.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE);
        }
        notification = mBuilder.build();
    }
}

5)、进程保活之1像素Activity:其思路就是监听屏幕是否锁屏或者熄灭事件,得到锁屏熄灭事件就开启一个1像素的Activity,得到亮屏或者解锁事件,就停止这个1像素的Activity

/**
 * @name        应用保护管理器
 * @author      SHEN
 * @version     v2.0
 * 使用方法
 * step1:
 * 在androidmanifest.xml文件中配置
 *  <activity
 *      android:name="com.shen.live.PixelActivity"
 *      android:configChanges="keyboardHidden|orientation|screenSize|navigation|keyboard"
 *      android:excludeFromRecents="true"
 *      android:exported="false"
 *      android:finishOnTaskLaunch="false"
 *      android:launchMode="singleInstance"
 *      android:process=":live"
 *      android:theme="@style/PixelActivity"/>
 *  <style name="PixelActivity">
 *      <item name="android:windowBackground">@android:color/transparent</item>
 *      <item name="android:windowFrame">@null</item>
 *      <item name="android:windowNoTitle">true</item>
 *      <item name="android:windowIsFloating">true</item>
 *      <item name="android:windowIsTranslucent">true</item>
 *      <item name="android:windowContentOverlay">@null</item>
 *      <item name="android:backgroundDimEnabled">false</item>
 *      <item name="android:windowAnimationStyle">@null</item>
 *      <item name="android:windowDisablePreview">true</item>
 *      <item name="android:windowNoDisplay">false</item>
 *  </style>
 *  step2:
 *  在application的oncreate方法中进行初始化
 *  ProtectedAppsManager.getInstance().startProtectedApp(getContext());
 */
public class ProtectedAppsManager {
    private static ProtectedAppsManager manager=new ProtectedAppsManager();
    private ScreenBroadcastReceiver receiver=new ScreenBroadcastReceiver(){
        @Override
        protected void onScreenOn(Context context) {
            Log.e("SHEN_LOG","ProtectedAppsManager-onScreenOn");
        }
        @Override
        protected void onScreenOff(Context context) {
            Log.e("SHEN_LOG","ProtectedAppsManager-onScreenOff-启动一像素Activity");
            context.startActivity(new Intent(context,PixelActivity.class));
        }
        @Override
        protected void onUserPresent(Context context) {
            Log.e("SHEN_LOG","ProtectedAppsManager-onUserPresent");
        }
    };

    public static ProtectedAppsManager getInstance(){
        return manager;
    }
    private ProtectedAppsManager(){
    }
    //开始应用保护 注册广播
    public void startProtectedApp(Context context){
        try {
            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_SCREEN_ON);
            filter.addAction(Intent.ACTION_SCREEN_OFF);
            filter.addAction(Intent.ACTION_USER_PRESENT);
            context.getApplicationContext().registerReceiver(receiver, filter);
        }catch (Exception e){
        }
    }
    public void stopProtectedApp(Context context){
        context.getApplicationContext().unregisterReceiver(receiver);
    }
    //屏幕状态广播接收者
    public abstract class ScreenBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {                               
                onScreenOn(context);    //屏幕开启事件
            } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {                       
                onScreenOff(context);   //屏幕关闭事件
            } else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {                    
                onUserPresent(context);//用户激活事件
            }
        }
        protected abstract void onScreenOn(Context context);
        protected abstract void onScreenOff(Context context);
        protected abstract void onUserPresent(Context context);
    }
}

我们现在来看看一像素Activity是怎么实现的

public class PixelActivity extends Activity {
    private ProtectedAppsManager.ScreenBroadcastReceiver receiver=ProtectedAppsManager.getInstance().new ScreenBroadcastReceiver(){
        @Override
        protected void onScreenOn(Context context) {
            PixelActivity.this.finish();    //关闭自己
        }
        @Override
        protected void onScreenOff(Context context) {
        }
        @Override
        protected void onUserPresent(Context context) {
            PixelActivity.this.finish();    //关闭自己
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_USER_PRESENT);
        registerReceiver(receiver, filter);
        Window window=getWindow();
        window.setGravity(Gravity.LEFT|Gravity.TOP);
        WindowManager.LayoutParams params=window.getAttributes();
        params.x=0;
        params.y=0;
        params.height=1;
        params.width=1;
        window.setAttributes(params);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(receiver);
    }
}

6)、进程保活之监听ACTION_TIME_TICK:在应用进入后台运行的时候,一些比较重要的Service有时候被异常销毁,而且被销毁后不能恢复,可以监听系统事件ACTION_TIME_TICK(一分钟一次),进行检查Service是否运行状态,否则重启启动服务

/**
 * @name    Service守护 监听广播器
 * @author  SHEN
 * @version 1.0
 * 使用方法
 * step1:   继承此类并实现抽象方法
 *     @Override
 *     protected String getServiceName() {
 *         return GymSCLService.class.getName();
 *     }
 *     @Override
 *     protected void startService(Context context) {
 *         Intent inten = new Intent(context, GymSCLService.class);
 *         context.startService(inten);
 *     }
 * setp2:   将改广播进行动态注册,注意静态注册是不行的 在Application进行动态注册
 *     @Override
 *     public void onCreate() {
 *        GymSCLReceiver receiver = new GymSCLReceiver();
 *        receiver.registerReceiver(this);
 *     }
 */
public abstract class ServiceDaemonReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getAction().equals(Intent.ACTION_TIME_TICK)){
            if(isKeepLive()){
                try {
                    boolean isRuning = LiveUtils.isServiceRunning(context, getServiceName());
                    if (!isRuning) {
                        startService(context);
                    }
                }catch (Exception e){
                }
            }
        }
    }
    //动态注册广播 不能静态注册 部分机型会屏蔽时间广播
    public void registerReceiver(Context context) {
        try {
            IntentFilter intentFilter = new IntentFilter(); 
            intentFilter.addAction(Intent.ACTION_TIME_TICK);//系统时间,每分钟发送一次
            intentFilter.addAction(Intent.ACTION_SCREEN_ON);//屏幕打开(解锁)
            intentFilter.addAction(Intent.ACTION_SCREEN_OFF);//屏幕关闭
            context.registerReceiver(this, intentFilter);
        }catch (Exception e){}
    }
    public void unRegisterReceiver(Context context){
        try {
            context.unregisterReceiver(this);
        }catch (Exception e){}
    }
    //是否保活
    protected abstract boolean isKeepLive();
    //获取要保护的服务的全名
    protected abstract String getServiceName();
    //启动要保护的服务
    protected abstract void startService(Context context);
}

五、开源框架

1、okhttp

1)、使用方法:通过实例化OkHttpClient客户端,和实例化一个Request,通过okHttpClient.newCall(request)来获取一个RealCall,同步就调用RealCall.execute(),异步就调用RealCall.enqueue()

//同步请求
public void syncRequest(){
    OkHttpClient okHttpClient = new OkHttpClient();    //实例化OKHTTP客户端 
    final Request request = new Request.Builder()      //实例化Request Builder模式构建
            .url("http://wwww.baidu.com")
            .build();
    final Call call = okHttpClient.newCall(request);    //获取Call 实现类为RealCall
    Response response = call.execute();                 //通过call进行同步请求
}
//异步请求
public void asynRequest(){
    OkHttpClient okHttpClient = new OkHttpClient();    //实例化OKHTTP客户端 
    final Request request = new Request.Builder()      //实例化Request Builder模式构建
            .url("http://wwww.baidu.com")
            .get()                                     //默认就是GET请求,可以不写
            .build();
    Call call = okHttpClient.newCall(request);         //获取Call 实现类为RealCall
    call.enqueue(new Callback() {                      //通过call进行异步请求
        @Override
        public void onFailure(Call call, IOException e) {    }
        @Override
        public void onResponse(Call call, Response response) throws IOException { }
    });
}

2)、源码解析:

通过上面简单示例可以知道不论是同步还是异步都是通过RealCall来实现请求的,通过RealCall的源码,我们可以发现,无论是异步还是同步操作,都先通过Dispatcher进行调度,最后都是通过RealCall.getResponseWithInterceptorChain方法来获取结果。

final class RealCall implements Call {
    final OkHttpClient client;
    ...省略代码...
    @Override 
    public Response execute() throws IOException {
        ...省略代码...
        timeout.enter();
        transmitter.callStart();
        try {
            client.dispatcher().executed(this);        //调用了Dispatcher.executed方法
            return getResponseWithInterceptorChain();  //获取Response 
        } finally {
            client.dispatcher().finished(this);        //调用了Dispatcher.finished方法
        }
    }
    @Override 
    public void enqueue(Callback responseCallback) {
        ...省略代码...
        transmitter.callStart();                        //调用了Dispatcher.enqueue方法
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }
    //异步Call内部类
    final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;
        private volatile AtomicInteger callsPerHost = new AtomicInteger(0);
        AsyncCall(Callback responseCallback) {
            super("OkHttp %s", redactedUrl());
            this.responseCallback = responseCallback;
        }
        //在线程池executorService对自己进行异步调用
        void executeOn(ExecutorService executorService) {
            boolean success = false;
            try {
                executorService.execute(this);        //通过线程池进行任务请求
                success = true;
            } catch (RejectedExecutionException e) {  //触发异常回调onFailure 
                responseCallback.onFailure(RealCall.this, ioException);
            } finally {                               //调用了Dispatcher.finished方法
                if (!success) client.dispatcher().finished(this); 
            }
        }
        @Override 
        protected void execute() {
            boolean signalledCallback = false;
            timeout.enter();
            try {                                    //获取Response 
                Response response = getResponseWithInterceptorChain();
                signalledCallback = true;            //触发回调onResponse
                responseCallback.onResponse(RealCall.this, response);
            } catch (IOException e) {                //触发异常回调onFailure 
                responseCallback.onFailure(RealCall.this, e);
            } finally {                              //调用了Dispatcher.finished方法
                client.dispatcher().finished(this);    
            }
        }
    }
    //进行拦截器责任链操作获取结果Response 
    Response getResponseWithInterceptorChain() throws IOException {
        ...省略代码...
    }
}

我们先看Dispatcher都干了些什么,Dispatcher内部维护者一套线程队列,一个等待线程队列,一个正在运行的同步队列,正在运行的异步队列,还有一个线程池ExecutorService作为管理角色。

public final class Dispatcher {
    private int maxRequests = 64;                           //最大请求数64
    private int maxRequestsPerHost = 5;                     //每个主机最大请求数量为5
    private @Nullable ExecutorService executorService;      //线程池  
    //等待异步Call队列
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
    //运行异步Call队列
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    //运行同步Call队列
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
    public Dispatcher(ExecutorService executorService) {
        this.executorService = executorService;
    }
    synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);        //同步请求直接添加到runningSyncCalls
    }
    void enqueue(AsyncCall call) {
        synchronized (this) {
            readyAsyncCalls.add(call);    //call先放入readyAsyncCalls队列
        }
        promoteAndExecute();
    }
    private boolean promoteAndExecute() {
        List<AsyncCall> executableCalls = new ArrayList<>();
        boolean isRunning;
        synchronized (this) { //如果runningAsyncCalls有空闲位置就将准备队列的call移动到运行队列中
            for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
                AsyncCall asyncCall = i.next();
                if (runningAsyncCalls.size() >= maxRequests) break; 
                if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; 
                i.remove();
                executableCalls.add(asyncCall);
                runningAsyncCalls.add(asyncCall);
            }
            isRunning = runningCallsCount() > 0;
        }
        for (int i = 0, size = executableCalls.size(); i < size; i++) {
            AsyncCall asyncCall = executableCalls.get(i);
            asyncCall.executeOn(executorService()); //这里其实最后都调用了AsyncCall里面的execute方
        }
        return isRunning;
    }
}

Dispatcher首先将请求加入到准备请求队列readyAsyncCalls中,然后在promoteAndExecute方法中遍历准备请求队列,如果正在请求队列runningAsyncCalls小于支持的最大请求数maxRequests,也就是说还有位置的话,就把准备请求队列添加到正在请求队列中。可以将Dispatcher调度流程总结如下:

  • 当发送一个异步请求时:如果runningAsyncCalls没达到阈值,那么会将这个请求加入到runningAsyncCalls立即执行,否则会将这个请求加入到readyAsyncCalls中等待执行。当一个异步请求执行完毕时会试图去执行readyAsyncCalls中的请求
  • 当发送一个同步请求时:该请求会直接加入到runningSyncCalls中,并且马上开始执行,注意这个执行并不是由Dispatcher调度的
  • 所有异步执行的请求都会通过executorService线程池来执行,这是个懒汉方式创建的线程池

Okhttp无论是同步还是异步,最后都殊途同归的调用了RealCall的getResponseWithInterceptorChain方法,此方法使用了责任链拦截器设计模式,将网络请求的整个流程进行分解成几个Interceptor拦截器(动作),然后通过责任链的方式根据添加顺序依次处理拦截器(动作),由源码可知先进行重定向和重试的逻辑处理,然后进行网络层的桥接处理,进行缓存逻辑处理,最后进行网络连接和服务器请求,最后将获取的Response 返回,源码如下:

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();            //构建拦截器
    interceptors.addAll(client.interceptors());                    //用户添加的应用拦截器
    interceptors.add(new RetryAndFollowUpInterceptor(client));     //主要负责重定向和重试
    //桥接网络层和应用层用主要包含:将用户的request,转变为网络层的request,比如添加各种请求头/UA/Cookie/Content-Type等; 
    //将网络层的response转变为用户层的response,比如解压缩,除去各种请求头
    interceptors.add(new BridgeInterceptor(client.cookieJar()));   
    interceptors.add(new CacheInterceptor(client.internalCache()));//缓存处理
    interceptors.add(new ConnectInterceptor(client));              //建立连接
    if (!forWebSocket)interceptors.addAll(client.networkInterceptors());//网络连接器
    interceptors.add(new CallServerInterceptor(forWebSocket));     //请求服务端
    //通过链式请求的得到response
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0, originalRequest, this, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis());
    IOException ioException = null;
    try {
      return chain.proceed(originalRequest);    //责任链模式进行依次处理上面添加的拦截器
    } catch (IOException e) {
      ioException = e;  throw e;
    } finally {
      transmitter.noMoreExchanges(ioException);
    }
  }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

诸神黄昏EX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值