当前位置: 首页 > news >正文

深圳网站托管商丘网络

深圳网站托管,商丘网络,怎么自己做网站地图,重庆做网站 熊掌号一、什么是卡顿#xff1f;或者说我们怎么感知APP卡顿#xff1f; 这里面涉及到android UI渲染机制#xff0c;我们先了解一下android UI是怎么渲染的#xff0c;android的View到底是如何一步一步显示到屏幕上的#xff1f; android系统渲染页面流程#xff1a; 1或者说我们怎么感知APP卡顿 这里面涉及到android UI渲染机制我们先了解一下android UI是怎么渲染的android的View到底是如何一步一步显示到屏幕上的 android系统渲染页面流程 1通过 LayoutInflater 将 View 组件解析成 View 对象对象中封装了组件位置 、显示图片等信息加载到内存中 2CPU 将 View 对象进行计算处理最终得到该组件对应的多维向量图形 ( 使用向量表示的图形 ) 3GPU 接收上述多维向量图形GPU 将该向量图进行栅格化将向量图转为位图 ( 矢量图转为像素图 ) 计算出对应屏幕上每个像素点显示的值将图像数据写入到 Back Buffer 4Android系统每隔大概16.6ms发出VSYNC信号触发对UI进行下一帧的渲染显示屏会使用Frame Buffer跟Back buffer进行交互拿到最新的一帧数据的渲染到屏幕上。 这个渲染过程如果每次渲染都成功就能够达到一个流畅的画面如果16.6ms内CPU和GPU无法处理完一帧画面就会导致Frame Buffer没能交换导致上一帧被重复显示即丢了一帧当丢帧频率越高时用户越能感觉画面卡顿。 这里的16.6ms刷新一帧是由人眼对于每秒60帧的刷新频率感觉是很流畅的计算出来即一帧16.6ms为了能够实现60fps这意味着程序的大多数操作都必须在16ms内完成。  二、SystraceCPU Profiler卡顿分析 Systrace是Android平台提供的一款工具用于记录短期内的设备活动。该工具会生成一份报告其中汇总了Android 内核中的数据例如 CPU 调度程序、磁盘活动和应用线程。Systrace主要用来分析绘制性能方面的问题。在发生卡顿时通过这份报告可以知道当前整个系统所处的状态从而帮助开发者更直观的分析系统瓶颈改进性能。 也可以使用上一节说的CPU Profiler进行卡顿分析CPU Profiler不仅能分析出代码卡顿时间还能精准的定位到代码内容。连接http://t.csdn.cn/WGzhA 三、App层面监控卡顿 目前业界两种主流有效的app监控方式如下 1利用UI线程的Looper打印的日志匹配 2使用Choreographer.FrameCallback。 1、Looper日志检测卡顿 Android主线程更新UI。如果界面1秒钟刷新少于60次即FPS小于60用户就会产生卡顿感觉。简单来说Android使用消息机制进行UI更新UI线程有个Looper在其loop方法中会不断取出message调用其绑定的Handler在UI线程执行。如果在handler的dispatchMesaage方法里有耗时操作就会发生卡顿。 public static void loop() {//......for (;;) {//......Printer logging me.mLogging;if (logging ! null) {logging.println( Dispatching to msg.target msg.callback : msg.what);}msg.target.dispatchMessage(msg);if (logging ! null) {logging.println( Finished to msg.target msg.callback);}//......} } 只要检测 msg.target.dispatchMessage(msg) 的执行时间就能检测到部分UI线程是否有耗时的操作。注意到系统源码的这行代码的执行前后有两个logging.println函数如果设置了logging会分别打印出 Dispatching to和 Finished to 这样的日志这样我们就可以通过两次log的时间差值来计算dispatchMessage的执行时间从而设置阈值判断是否发生了卡顿。这里我们可以自定义LogMonitor并设置到Looper中替换系统的LogMonitor来实现这种方式也是 BlockCanary 的实现原理。 系统Looper和Printer 接口 public final class Looper {private Printer mLogging;public void setMessageLogging(Nullable Printer printer) {mLogging printer;} }public interface Printer {void println(String x); } 自定义BlockCanary public class BlockCanary {public static void install() {LogMonitor logMonitor new LogMonitor();Looper.getMainLooper().setMessageLogging(logMonitor);} } 自定义LogMonitor public class LogMonitor implements Printer {private StackSampler mStackSampler;private boolean mPrintingStarted false;private long mStartTimestamp;// 卡顿阈值private long mBlockThresholdMillis 3000;//采样频率private long mSampleInterval 1000;private Handler mLogHandler;public LogMonitor() {mStackSampler new StackSampler(mSampleInterval);HandlerThread handlerThread new HandlerThread(block-canary-io);handlerThread.start();mLogHandler new Handler(handlerThread.getLooper());}Overridepublic void println(String x) {//从if到else会执行 dispatchMessage如果执行耗时超过阈值输出卡顿信息if (!mPrintingStarted) {//记录开始时间mStartTimestamp System.currentTimeMillis();mPrintingStarted true;mStackSampler.startDump();} else {final long endTime System.currentTimeMillis();mPrintingStarted false;//出现卡顿if (isBlock(endTime)) {notifyBlockEvent(endTime);}mStackSampler.stopDump();}}private void notifyBlockEvent(final long endTime) {mLogHandler.post(new Runnable() {Overridepublic void run() {//获得卡顿时 主线程堆栈ListString stacks mStackSampler.getStacks(mStartTimestamp, endTime);for (String stack : stacks) {Log.e(block-canary, stack);}}});}private boolean isBlock(long endTime) {return endTime - mStartTimestamp mBlockThresholdMillis;} } 自定义StackSampler public class StackSampler {public static final String SEPARATOR \r\n;public static final SimpleDateFormat TIME_FORMATTER new SimpleDateFormat(MM-dd HH:mm:ss.SSS);private Handler mHandler;private MapLong, String mStackMap new LinkedHashMap();private int mMaxCount 100;private long mSampleInterval;//是否需要采样protected AtomicBoolean mShouldSample new AtomicBoolean(false);public StackSampler(long sampleInterval) {mSampleInterval sampleInterval;HandlerThread handlerThread new HandlerThread(block-canary-sampler);handlerThread.start();mHandler new Handler(handlerThread.getLooper());}/*** 开始采样 执行堆栈*/public void startDump() {//避免重复开始if (mShouldSample.get()) {return;}mShouldSample.set(true);mHandler.removeCallbacks(mRunnable);mHandler.postDelayed(mRunnable, mSampleInterval);}public void stopDump() {if (!mShouldSample.get()) {return;}mShouldSample.set(false);mHandler.removeCallbacks(mRunnable);}public ListString getStacks(long startTime, long endTime) {ArrayListString result new ArrayList();synchronized (mStackMap) {for (Long entryTime : mStackMap.keySet()) {if (startTime entryTime entryTime endTime) {result.add(TIME_FORMATTER.format(entryTime) SEPARATOR SEPARATOR mStackMap.get(entryTime));}}}return result;}private Runnable mRunnable new Runnable() {Overridepublic void run() {StringBuilder sb new StringBuilder();StackTraceElement[] stackTrace Looper.getMainLooper().getThread().getStackTrace();for (StackTraceElement s : stackTrace) {sb.append(s.toString()).append(\n);}synchronized (mStackMap) {//最多保存100条堆栈信息if (mStackMap.size() mMaxCount) {mStackMap.remove(mStackMap.keySet().iterator().next());}mStackMap.put(System.currentTimeMillis(), sb.toString());}if (mShouldSample.get()) {mHandler.postDelayed(mRunnable, mSampleInterval);}}}; } 2、Choreographer.FrameCallback检测卡顿 Android系统每隔16ms发出VSYNC信号来通知界面进行重绘、渲染每一次同步的周期约为16.6ms代表一帧的刷新频率。通过Choreographer类设置它的FrameCallback函数当每一帧被渲染时会触发回调FrameCallback.doFrame (long frameTimeNanos) 函数。frameTimeNanos是底层VSYNC信号到达的时间戳 。 public class ChoreographerHelper {public static void start() {if (Build.VERSION.SDK_INT Build.VERSION_CODES.JELLY_BEAN) {Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {long lastFrameTimeNanos 0;Overridepublic void doFrame(long frameTimeNanos) {//上次回调时间if (lastFrameTimeNanos 0) {lastFrameTimeNanos frameTimeNanos;Choreographer.getInstance().postFrameCallback(this);return;}long diff (frameTimeNanos - lastFrameTimeNanos) / 1_000_000;if (diff 16.6f) {//掉帧数int droppedCount (int) (diff / 16.6);}lastFrameTimeNanos frameTimeNanos;Choreographer.getInstance().postFrameCallback(this);}});}} } 通过 ChoreographerHelper 可以实时计算帧率和掉帧数实时监测App页面的帧率数据发现帧率过低还可以自动保存现场堆栈信息。 Looper比较适合在发布前进行测试或者小范围灰度测试然后定位问题ChoreographerHelper适合监控线上环境的 app 的掉帧情况来计算 app 在某些场景的流畅度然后有针对性的做性能优化。 四、布局优化  1、Layout Inspector层级优化  measure、layout、draw这三个过程都包含自顶向下的View Tree遍历耗时如果视图层级太深自然需要更多的时间来完成整个绘测过程从而造成启动速度慢、卡顿等问题。而onDraw在频繁刷新时可能多次出发因此onDraw更不能做耗时操作同时需要注意内存抖动。 使用Layout Inspector来检查应用的视图层次结构 选择需要查看的进程与Activity在id为content之下的就是我们写在XML中的布局。  排查是否存在Layout的多层嵌套我们应该尽量减少其层级也可以使用 ConstraintLayout 约束布局使得布局尽量扁平化移除非必需的UI组件。  2、使用merge标签  当我们有一些布局元素需要被多处使用时这时候我们会考虑将其抽取成一个单独的布局文件。在需要使用的地方通过 include 加载。  ?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:background#000000android:orientationvertical!-- include layout_merge布局 --include layoutlayout/layout_merge / /LinearLayout !-- layout_merge -- ?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:orientationverticalTextViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:background#ffffffandroid:text测试merge / /LinearLayout 这时候我们的主布局文件是垂直的LinearLayoutinclude的 layout_merge 也是垂直的LinearLayout这时候include的布局中使用的LinearLayout就没意义了使用的话反而减慢你的UI表现。这时可以使用merge标签优化。  !-- layout_merge -- merge xmlns:androidhttp://schemas.android.com/apk/res/androidTextViewandroid:background#ffffffandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:text测试merge / /merge 修改为merge后通过LayoutInspector能够发现include的布局中TextView直接被加入到父布局中。  3、使用ViewStub 标签 当我们布局中存在一个View/ViewGroup在某个特定时刻才需要他的展示时可能会把这个元素在xml中定义为invisible或者gone在需要显示时再设置为visible可见。比如在登陆时如果密码错误在密码输入框上显示提示。 1invisible view设置为invisible时view在layout布局文件中会占用位置但是view为不可见该view还是会创建对象会被初始化会占用资源。  2gone view设置gone时view在layout布局文件中不占用位置但是该view还是会创建对象会被初始化会占用资源。  如果view不一定会显示此时可以使用 ViewStub 来包裹此View 以避免不需要显示view但是又需要加载view消耗资源。viewstub是一个轻量级的view它不可见不用占用资源只有设置viewstub为visible或者调用其inflater()方法时其对应的布局文件才会被初始化。  ?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:background#000000android:orientationverticalViewStubandroid:idid/viewStubandroid:layout_width600dpandroid:layout_height500dpandroid:inflatedIdid/textViewandroid:layoutlayout/layout_viewstub / /LinearLayout !-- layout_viewstub -- ?xml version1.0 encodingutf-8? TextView xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:background#ffffffandroid:text测试viewStub / 加载viewStub后可以通过 inflatedId 找到layout_viewstub 中的根View。 五、过度渲染  过度绘制是指系统在渲染单个帧的过程中多次在屏幕上绘制某一个像素。例如如果我们有若干界面卡片堆叠在一起每张卡片都会遮盖其下面一张卡片的部分内容。但是系统仍然需要绘制堆叠中的卡片被遮盖的部分。  1、GPU 过度绘制检查 手机开发者选项中能够显示过度渲染检查功能通过对界面进行彩色编码来帮我们识别过度绘制。开启步骤如下 1. 进入开发者选项 (Developer Options)。 2. 找到调试 GPU 过度绘制(Debug GPU overdraw)。 3. 在弹出的对话框中选择显示过度绘制区域Show overdraw areas。  Android 将按如下方式为界面元素着色以确定过度绘制的次数  1. 真彩色没有过度绘制 2. 蓝色过度绘制 1 次 3. 绿色过度绘制 2 次 4. 粉色过度绘制 3 次 5. 红色过度绘制 4 次或更多次  有些过度绘制是不可避免的。在优化应用的界面时应尝试达到大部分显示真彩色或仅有 1 次过度绘制蓝色的视觉效果。  2、解决过度绘制问题 可以采取以下几种策略来减少甚至消除过度绘制 1. 移除布局中不需要的背景 默认情况下布局没有背景这表示布局本身不会直接渲染任何内容。但是当布局具有背景时其有可能会导致过度绘制。移除不必要的背景可以快速提高渲染性能。不必要的背景可能永远不可见因为它会被应用在该视图上绘制的任何其他内容完全覆盖。例如当系统在父视图上绘制子视图时可能会完全覆盖父视图的背景。 2.使视图层次结构扁平化 可以通过优化视图层次结构来减少重叠界面对象的数量从而提高性能。 3.降低透明度 对于不透明的 view 只需要渲染一次即可把它显示出来。但是如果这个 view 设置了 alpha 值则至少需要渲染两次。这是因为使用了 alpha 的 view 需要先知道混合 view 的下一层元素是什么然后再结合上层的 view 进行Blend混色处理。透明动画、淡入淡出和阴影等效果都涉及到某种透明度这就会造成了过度绘制。可以通过减少要渲染的透明对象的数量来改善这些情况下的过度绘制。例如如需获得灰色文本可以在 TextView 中绘制黑色文本再为其设置半透明的透明度值。但是简单地通过用灰色绘制文本也能获得同样的效果而且能够大幅提升性能。 六、布局加载优化  1、异步加载  LayoutInflater加载xml布局的过程会在主线程使用IO读取XML布局文件进行XML解析再根据解析结果利用反射创建布局中的View/ViewGroup对象。这个过程随着布局的复杂度上升耗时自然也会随之增大。Android为我们提供了 Asynclayoutinflater 把耗时的加载操作在异步线程中完成最后把加载结果再回调给主线程。  2、添加依赖  dependencies {implementation androidx.asynclayoutinflater:asynclayoutinflater:1.0.0 } 3、使用AsyncLayoutInflater new AsyncLayoutInflater(this).inflate(R.layout.activity_main, null, new AsyncLayoutInflater.OnInflateFinishedListener() {Overridepublic void onInflateFinished(NonNull View view, int resid, Nullable ViewGroup parent) {setContentView(view);//......}}); 1. 使用异步 inflate那么需要这个 layout 的 parent 的 generateLayoutParams 函数是线程安全的 2. 所有构建的 View 中必须不能创建 Handler 或者是调用 Looper.myLooper因为是在异步线程中加载的异步线程默认没有调用 Looper.prepare 3. AsyncLayoutInflater 不支持设置 LayoutInflater.Factory 或者 LayoutInflater.Factory2 4. 不支持加载包含 Fragment 的 layout 5. 如果 AsyncLayoutInflater 失败那么会自动回退到UI线程来加载布局。
http://www.pierceye.com/news/549951/

相关文章:

  • 如何做问卷调查网站济南网络公司
  • 纯js做网站Wordpress税
  • 建云科技网站首页阿里云 wordpress 安装
  • 浙江省建设工程协会网站动漫网站的设计与实现
  • wordpress能导出网站吗企业内部网站开发
  • 景观石网站建设方案一 网站开发背景
  • 开发app的平台外贸seo建站
  • 网站界面设计实训报告深圳设计公司办公室
  • 京东网站的公司地址别人网站建设多少钱
  • 如何加快网站打开速度wordpress注册怎样通过邮箱验证码
  • 有关图书网站建设策划书电脑公司网站系统源码
  • 西班牙网站后缀360收录提交入口网址
  • 济宁网站建设工程教育网官网学员登录
  • html5导航网站源码下载wordpress 调用 discuz
  • 住房和城乡建设部网站 投诉有哪些网站可以学做糕点的
  • 电商购物网站m3u8插件 wordpress
  • 河北手机网站制作多少钱wordpress文章头部
  • 悠悠我心的个人网站素材网站建设教育培训
  • 网站建设定金做什么会计分录湘潭有实力的关键词优化公司
  • 网站备案 网站建设方案书云搜索app
  • 青岛网络推广建站民营医院建设网站
  • 罗湖住房和建设局网站wordpress调用内容代码
  • 网络logo设计优化设计七年级下册语文答案
  • 贵港网站seo安新网站建设
  • 网站怎么自己编辑模块创意网名大全
  • php的网站架构建设框架wordpress如何运行
  • 广州seo网站排名优化数码设计网站
  • 免费做视频相册的网站网站建设的功能描述
  • 网站建设方案浩森宇特alexa排名是什么意思
  • 网上手机网站建设计划书百度小说风云榜排名