Android 的 onMeasure 函数笔记

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 的 onMeasure 函数笔记相关的知识,希望对你有一定的参考价值。

参考技术A 重写onMeasure方法作用是为了给自定义的View 添加约束规则,常用在自定义ViewGroup中动态添加子view,对每一个子view测算其大小。

我们新建一个高度match_parent 的父类布局 在其中添加了三个子View,前两个指定高度50dp,最后一个指定高度wrap_content:

看下渲染效果:

当父View指定 match_parent 模式时,子View指定wrap_content模式时,子View的大小由其自身填充内容决定,父类的空白并不自动填充;
再看一个例子:我们把上一例中子View child3高度更改为match_parent

可以看到随着parent高度拉伸整个屏幕,child3高度也拉伸了。

也就是说,当父View指定match_parent 模式时,子view 填充模式如果采用match_parent 模式,那么他自身的大小是由parent View大小决定(也就是Exactly模式),当子view填充模式选择wrap_content时,子View大小由其自身填充内容决定(AT_MOST模式)。

我们再看一个列子:
把上面例子中的 parent 高度 由match_parent 更改为 wrap_content模式,看下渲染图效果;

渲染图效果:

可以看出,虽然child3定义了match_parent属性,但是child3的高度仅仅是填充内容的实际高度,效果和子View指定自己的wrap_content模式是一致的。

也就是说,当父View指定wrap_content模式时,子类的match_parent 属性并没有生效,子View 的实际大小由子View 的填充内容决定(子View大小随着内容动态变化,有的时候也会在计算的时候加上padding、margin此类属性)(也就是 AT_MOST 模式);

MeasureSpec,它封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求 MeasureSpec由size和mode组成。
specMode一共有三种类型:
UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;
EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
AT_MOST(至多),子元素至多达到指定大小的值。
源码:

附一个onMesure 练习demo,可以改变自定义View的填充模式fill_parent 和wrap_content看下效果。

Android复习与笔记

自定义view与view绘制时大小的确定

  • viewGroup
    • 需要重写的方法
      • onMeasure:用于计算childView的测量值以及模式,从而计算设置自己的宽高
      • onLayout: 设置所有childView的显示位置
    • 整体流程小结
      onMeasure会传递该viewGroup的父viewGroup的MeasureSpec(即父viewGroup的建议测量模式与测量尺寸,在重写onMeasure方法时,先调用measureChildren计算出所有childView的尺寸,然后变了所有childView获取到尺寸,再结合父viewGroup的建议测量模式与尺寸以及自己的业务需求设置setMeasuredDimension,这样就确定了viewGroup的尺寸。可以知道,viewGroup并不是绝对由谁去确定的,可以根据自己的业务需求动态设置)
  • view
    • 需要重写的方法
      • onMeasure:根据父viewGroup的建议测量模式与测量尺寸,计算设置自己的宽高
      • onDraw: 绘制view

内存抖动出现的原因

  • 指在短时间内有大量的对象被创建或者被回收的现象。
  • 内存抖动产生原因:频繁(如在循环里)创建对象,会导致垃圾回收机制频繁运行。
  • 内存抖动影响:频繁内存抖动会导致垃圾回收频繁运行,造成系统卡顿

cpu优化的处理

可以通过as的Profiler查看cpu使用率

gc是什么时候触发的

  • 新生代触发:Eden区中没有足够空间进行分配时,将会触发一次Minor GC
  • 老年代触发:老年代空间不足,JVM会进行Major GC,如果Major GC完后空间还是不足,就会抛出java.lang.OutOfMemoryError: Java heap space异常
  • 方法区空间不足触发:(方法区有些人又称为“永久代”),JVM会进行Major GC,如果Major GC完后空间还是不足,就会抛出java.lang.OutOfMemoryError: PermGen space异常
  • 调用System.gc()方法触发:System.gc()会建议JVM进行Major GC,因为是建议并不一定会进行,但是大多数情况下还是会进行Major GC,强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,可通过通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc()

hander里面的delay消息是怎么实现的

  • 插入消息机制:post(Runnable r)方法最终还是调用到sendMessageDelayed最终还是调用方法sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)。这个方法将传递了消息和消息要执行的时刻,然后调用插入enqueueMessage到消息队列中。消息队列的消息是根据时刻排序的链表,插入也是根据这个逻辑插入。
  • 读取消息机制:looper循环读取消息获取下一个消息,实际是类似于线程的休眠。插入消息时重新唤醒计算并插入,循环处理时,获取到下一个消息后,如果不需要休眠就立即处理,如果需要休眠,然后执行休眠到下次唤醒重新循环执行一次这个过程。休眠使用的是linux中的epoll机制,具体是调用了epoll_wait这个方法

looper为啥不会堵塞主线程

阻塞在epoll wait,并不是for的死循环等待,并不占用CPU资源。另外主线程的looper,它才行ui消息处理的根本,它一直在等待并处理消息的。

线程、Handler跟Looper之间的对应关系

一个线程可以创建多个Handler,但只能创建一个Looper,一个MessageQueue。Handler跟Looper之间没有对应关系。比如在一个activity中可以创建多个handler,它都是用了默认的looper与MessageQueue,handler发的消息最终还是回调到对应的handler处理。

handler为啥设计使用obtainMessage方法

可以避免重复创建Message对象。首先可以节省了内存,适合频繁发送的同个消息。

handler发送大量消息会有什么问题

这是我的猜想:插入的过程是加了锁的,当消息队列里有大量延迟消息,同时大量消息堵塞着等待插入,有可能出现anr。

handler主线程更新的原理是什么

简单说Handler用于同一个进程的线程间通信。Looper让主线程无限循环地从自己的MessageQueue拿出消息处理,既然这样我们就知道处理消息肯定是在主线程中处理的,那么怎样在其他的线程往主线程的队列里放入消息呢?其实很简单,我们知道在同一进程中线程和线程之间资源是共享的,也就是对于任何变量在任何线程都是可以访问和修改的,只要考虑并发性做好同步就行了,那么只要拿到MessageQueue 的实例,就可以往主线程的MessageQueue放入消息,主线程在轮询的时候就会在主线程处理这个消息。那么怎么拿到主线程 MessageQueue的实例,是可以拿到的(在主线程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),

okhttp的实现原理

整个OkHttp功能的实现就在这五个默认的拦截器中,所以先理解拦截器模式的工作机制是先决条件。这五个拦截器分别为: 重试拦截器、桥接拦截器、缓存拦截器、连接拦截器、请求服务拦截器。每一个拦截器负责的工作不一样,就好像工厂流水线,最终经过这五道工序,就完成了最终的产品。

但是与流水线不同的是,OkHttp中的拦截器每次发起请求都会在交给下一个拦截器之前干一些事情,在获得了结果之后又干一些事情。整个过程在请求向是顺序的,而响应向则是逆序。

当用户发起一个请求后,会由任务分发起Dispatcher将请求包装并交给重试拦截器处理。

1、重试拦截器在交出(交给下一个拦截器)之前,负责判断用户是否取消了请求;在获得了结果之后,会根据响应码判断是否需要重定向,如果满足条件那么就会重启执行所有拦截器。

2、桥接拦截器在交出之前,负责将HTTP协议必备的请求头加入其中(如:Host)并添加一些默认的行为(如:GZIP压缩);在获得了结果后,调用保存cookie接口并解析GZIP数据。

3、缓存拦截器顾名思义,交出之前读取并判断是否使用缓存;获得结果后判断是否缓存。

4、连接拦截器在交出之前,负责找到或者新建一个连接,并获得对应的socket流;在获得结果后不进行额外的处理。

5、请求服务器拦截器进行真正的与服务器的通信,向服务器发送数据,解析读取的响应数据。

在经过了这一系列的流程后,就完成了一次HTTP请求!

OkHttp解析

okhttp的缓存机制是怎么实现的

OkHttp解析
手写okhttp

线程的sleep与wait有什么区别

  • 属于不同的两个类,sleep()方法是线程类(Thread)的静态方法,wait()方法是Object类里的方法。

  • sleep()方法不会释放锁,wait()方法释放对象锁。

  • sleep()方法可以在任何地方使用,wait()方法则只能在同步方法或同步块中使用。

  • sleep()必须捕获异常,wait()方法、notify()方法和notiftAll()方法不需要捕获异常。

  • sleep()使线程进入阻塞状态(线程睡眠),wait()方法使线程进入等待队列(线程挂起),也就是阻塞类别不同。

  • 它们都可以被interrupted方法中断。

Android中的多线程开发

  • new thread
  • AsyncTask
  • 线程池

synchronized与lock的区别

在这里插入图片描述

如何理解Android中的xmlns

英文叫做 XML namespace,中文翻译为 XML 命名bai空间。
在Android中,目前我们碰到的xmlns一共有三种:

  • xmlns:android=“http://schemas.android.com/apk/res/android”。android:命名空间android用于 Android 系统定义的一些属性
  • xmlns:tools=“http://schemas.android.com/tools”。tools:根据官方定义,tools命名空间用于在 XML 文档记录一些,当应用打包的时候,会把这部分信息给过滤掉,不会增加应用的 size,说直白点,这些属性是为IDE提供相关信息.
  • xmlns:app=“http://schemas.android.com/apk/res-auto” 。app:命名空间app用于我们应用自定义的一些属性

Android-Service概念和用途

在这里插入图片描述

service的意义

  • Activity能看见,service看不见。打个比方,比如你要 启动一个推送的服务,并且后台维持,acitivity显然不合适
  • 比如你做个上传功能,如果放到Activity里面,用户一结束Activity(比如那种一键清理),你的上传就挂了,就需要用Service

线程会不会随着activity的结束而结束

不会的 所以理论上 最好在service中去开启线程 好控制线程的生命周期 但实际开发中 大多都直接在activity new Thread

handler内测泄露的原因

Handler对象隐性地持有了Activity的对象。当handler延迟发送信息时,而在activity跳转之后就出现内存没有回收,导致内存泄漏

图片占据的内存算法

https://www.cnblogs.com/dasusu/p/9789389.html

IM即时通讯部分概念理解

有关(IM)即时通讯的基本概念

getSystemService过程及后续流程是怎么样的

【Android源码解析–SystemServer启动流程】

as打包过程过程做了什么

什么是依赖倒置

  • 高层模块不应该依赖低层模块,两者都应该依赖其抽象
  • 抽象不应该依赖细节
  • 细节应该依赖抽象

下面是依赖倒置原则在Java语言中的表现:

  • 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的
  • 接口或抽象类不依赖于实现类
  • 实现类依赖于接口或抽象类

数据加密的做法

  • https方式。Android下面如何使用
  • 对称加密(根据版本更新秘钥)、md5校验是否篡改数据

代码混淆如何做

常见使用ProGuard

组件化插件化是什么

从桌面启动一个应用,整个流程是怎么样的

jetpack组件了解

根搜索算法的根是什么

在Java语言中,可以作为GCRoots的对象包括下面几种:

  • (1). 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。

  • (2). 方法区中的类静态属性引用的对象。

  • (3). 方法区中常量引用的对象。

  • (4). 本地方法栈中JNI(Native方法)引用的对象。

Touch事件如何传递到Activity

ctivity里面有个PhoneWindow,PhoneWindow里面有个DecorView,而DecorView的parent是ViewRootImpl,所以Activity的很多方法调用都是从ViewRootImpl开始的

单例模式双重锁定深入了解下,第一锁第二锁用处

双重校验锁第一次进行判空原因:当程序顺序执行的时候,如果不进行判空,每一个线程都会先去获得当前类的类锁,而其他线程都进入阻塞状态。单例模式中初始化单例的程序只会执行一次,大部分情况下会直接到return语句返回,如果都阻塞在获取锁的位置,会大大降低程序的运行速度双重校验锁第二次进行判空原因:假设有两个线程A和B,都进行完第一次判空了,A和B都阻塞,这个时候A线程获取了类锁,然后B线程被阻塞,A线程新建了一个实例后释放了锁,B线程获取锁,又新建了一个实例,这破坏了单例设计模式的初衷

Android内存优化

构造单例的时候尽量别用Activity的引用;
静态引用时注意应用对象的置空或者少用静态引用;
使用静态内部类+软引用代替非静态内部类;
及时取消广播或者观察者注册;
耗时任务、属性动画在Activity销毁时记得cancel;
文件流、Cursor等资源及时关闭;
Activity销毁时WebView的移除和销毁。

Android的内存优化的几种方案

内存泄露检测

Android内存泄漏检测和定位

getWidth与getMeasuredWidth的区别

  • getWidth是view的显示宽度,由绘制view的 mRight 、 mLeft确定。onLayout方法后
 public final int getWidth() {
        return mRight - mLeft;
    }
  • getMeasuredWidth是测量宽度,由measure方法后确定。onMeasure方法后
public final int getMeasuredWidth() {
       return mMeasuredWidth & MEASURED_SIZE_MASK;
   }

可知,如果内容超出屏幕而且可以滑动显示,那么view的测量宽度是比显示宽度大的。一般情况下两个值是一样的,MeasureSpec.UNSPECIFIED(如NestedScrollView子view的测量模式强行设置为这个模式)情况下才有超出父类控件限制大小

以上是关于Android 的 onMeasure 函数笔记的主要内容,如果未能解决你的问题,请参考以下文章

什么时候调用onMeasure方法 Android的onMeasure方法

[Android Pro] Android开发实践:为什么要继承onMeasure()

Android复习与笔记

Android复习与笔记

Android学习笔记之View

Andoid自定义View的OnMeasure详解和自定义属性