2019独角兽企业重金招聘Python工程师标准>>>

写在前面,如果面对复杂的动画效果你一筹莫展,不烦看看这篇文章:LottieAndroid使用详解及源码解析—轻而易举实现各种复杂动画

该文章是结合我司产品手机迅雷做的一个全面的性能分析及优化方案。本文篇幅较长,几乎涵盖了所有的性能方面问题,以及给出了如何查找和解决问题的方案,几乎是史上最全最实用的Android性能分析和优化文章。
另外,由于简书对MarkDown的支持问题导致图片格式看着有点乱,还请多多谅解,细心阅读。

结合以下四个部分讲解:

  • 性能问题分类

  • 性能优化原则和方法

  • 借助性能优化工具分析解决问题

  • 性能优化指标

性能问题分类

1、渲染问题:过度绘制、布局冗杂

2、内存问题:内存浪费(内存管理)、内存泄漏

3、功耗问题:耗电

性能优化原则和方法

1、性能优化原则

  • 坚持性能测试(开发和测试同学的测试方法略有不同):不要凭感觉去检测性能问题、评估性能优化的效果,应该保持足够多的测量,用数据说话(主要针对测试同学)。使用各种性能工具测试及快速定位问题(主要针对开发同学)。

  • 使用低配置的设备:同样的程序,在低端配置的设备中,相同的问题会暴露得更为明显。

  • 权衡利弊:在能够保证产品稳定、按时完成需求的前提下去做优化。

2、优化方法

  • 了解问题(分为可感知和不可感知的性能问题):对于性能问题来讲,这个步骤只适用于某些明显的性能问题,很多无法感知的性能问题需要通过工具定位。例如:内存泄漏、层级冗杂、过度绘制等无法感知。滑动卡顿是可以感知到的。

  • 定位问题:通过工具检测、分析数据,定位在什么地方存在性能问题。

  • 分析问题:找到问题后,分析针对这个问题该如何解决,确定解决方案。

  • 解决问题:根据分析结果寻找解决方案。

  • 验证问题:保证优化有效,没有产生新的问题,以及产品稳定性。

性能优化工具

以下优化工具在下面文章中具体介绍使用方法。

1、手机开发者选项:调试GPU过度绘制、启用严格模式、显示CPU使用情况、GPU呈现模式分析、显示所有"应用程序无响应"。(小米手机开发开发者选项中名字)

2、IDE中:Android Studio,比如静态代码检测工具、Memory Monitor、CPU Monitor、NetWork Monitor、GPU Monitor、Layout Inspector、Analyze APK等。

3、SDK中:sdk\tools,比如DDMS、HierarchyViewer、TraceView等。

4、第三方工具:MAT、LeakCanary、GT等。

性能优化指标

1、渲染

  • 滑动流畅度:FPS,即Frame per Second,一秒内的刷新帧数,越接近60帧越好;

  • 过度绘制:单页面的3X(粉红色区域) Overdraw小于25%

  • 启动时间:这里主要说的是Activity界面启动时间,一般低于300ms,需要用高频摄像机计算时间。

2、内存

  • 内存大小:峰值越低越好,需要优化前后做对比

  • 内存泄漏:需要用工具检查对比优化前后

3、功耗

  • 单位时间内的掉电量,掉电量越少越好,业内没有固定标准。华为有专门测试功耗的机器,以及自己的标准。

一、渲染问题

先来看看造成应用UI卡顿的常见原因都有哪些?

1、人为在UI线程中做轻微耗时操作,导致UI线程卡顿;

2、布局Layout过于复杂,无法在16ms内完成渲染;

3、同一时间动画执行的次数过多,导致CPU或GPU负载过重;

4、View过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使CPU或GPU负载过重;

5、View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染;

6、内存频繁触发GC过多(同一帧中频繁创建内存),导致暂时阻塞渲染操作;

7、冗余资源及逻辑等导致加载和执行缓慢;

8、臭名昭著的ANR;

大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能。(Google官方说的)

Android系统每隔16ms发出VSYNC信号(vertical synchronization --场扫描同步,场同步,垂直同步),触发对UI进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需要的60fps,为了能够实现60fps,这意味着程序的大多数操作都必须在16ms(1000/60=16.67ms)内完成。

如果你的某个操作花费时间是24ms,系统在得到VSYNC信号的时候就无法进行正常渲染,这样就发生了丢帧现象。那么用户在32ms内看到的会是同一帧画面。

1、过度绘制

Overdraw(过度绘制)描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次的UI结构里面,如果不可见的UI也在做绘制的操作,这就会导致某些像素区域被绘制了多次。这就浪费大量的CPU以及GPU资源,找出界面滑动不流畅、界面启动速度慢、手机发热。

  • 如何查看过度绘制?

    设置 — 开发中选项 — 调试GPU过度绘制

  • 来看看手雷里的过度绘制和优化效果(目前手雷还存在很多待优化的页面)

  • 上图中的各种颜色都代表什么意思?

      每个颜色的说明如下:原色:没有过度绘制紫色:1 次过度绘制绿色:2 次过度绘制粉色:3 次过度绘制红色:4 次及以上过度绘制
    
  • 造成过度优化的关键是什么?多余的背景(Background)

  • 接下来举例说明:

    • 1、MainTabActivity

      在MainTabActivity的Theme中修改背景

      去除布局(main_activity_linerlayout.xml)中的background

      如果不给当前Activity设置主题,默认主题是什么,默认主题背景是什么?

        可以在默认主题中添加通用主题背景<item name="android:windowBackground">@drawable/common_layout_content_bkg</item>去除背景<item name="android:windowBackground">null</item>
      
    • 2、除了布局中多余背景,还有可能在代码里添加了多余的背景。

      查看分享弹窗的布局代码发现只有一个background,但为什么会过度绘制呢?

      代码修改(SharePlatformsDialog.java)

    • 3、弹窗底部布局不会导致弹窗本身过度绘制

      弹窗的绘制是属于剪切式绘制不是覆盖绘制,蒙层是透明度亮度的调节不是绘制一层灰色。
      如果我们不想用系统dialog而是自定义一个弹窗view,就需要考虑过度绘制问题。

    • 4、自定义view时,通过Canvas的clipRect方法控制每个视图每次刷新的区域,这样可以避免刷新不必要的区域,从而规避过渡绘制的问题。还可以使用canvas.quickreject()来判断是否和某个矩形相交,从而跳过那些非矩形区域内的绘制操作。参考:http://jaeger.itscoder.com/android/2016/09/29/android-performance-overdraw.html

  • 优化方法和步骤关键总结

      总结一下,优化步骤如下:1、移除或修改Window默认的Background2、移除XML布局文件中非必需的Background3、按需显示占位背景图片4、控制绘制区域
    

2、布局优化

布局太过复杂,层级嵌套太深导致绘制操作耗时,且增加内存的消耗。
我们的目标就是,层级扁平化

  • 布局优化的建议:

    • 第一个建议:可以使用相对布局减少层级的就使用相对布局,否则使用线性布局。Android中RelativeLayout和LinearLayout性能分析,参考:http://www.jianshu.com/p/8a7d059da746#

    • 第二个建议:用merge标签来合并布局,这可以减少布局层次

    • 第三个建议:用include标签来重用布局,抽取通用的布局可以让布局的逻辑更清晰明了,但要避免include乱用

    • 第四个建议:避免创建不必要的布局层级。(最容易发生的!

    • 第五个建议:使用惰性控件ViewStub实现布局动态加载

  • 如何借助工具查看代码布局?

    Android SDK 工具箱中有一个叫做 Hierarchy Viewer的工具,能够在程序运行时分析 Layout。
    可以用这个工具找到 Layout 的性能瓶颈
    该工具的使用条件:模拟器或者Root版真机。
    如何开启该功能:AndroidStudio中,Tools — Android — Android Devices Monitor
    该工具的缺点:使用起来麻烦。

  • 看看项目中遇到的问题(MainTabAvtivity)。

    • merge标签的使用

      未使用merge,例如:XLTabLayout.java

      使用merge,例如:账号信息页的条目UserAccountItem

    • include标签的使用导致的问题

    • 避免创建不必要的层级(MainTabActivity)

    • ViewStub的使用

      这个标签最大的优点是当你需要时才会加载,使用他并不会影响UI初始化时的性能。
      通常情况下我们需要在某个条件下使用某个布局的时候会通过gone或者invisible来隐藏,其实这样的方式虽然隐藏了布局,但是当显示该界面的时候还是将该布局实例化的。使用ViewStub可以避免内存的浪费,加快渲染速度。
      其实ViewStub就是一个宽高都为0的一个View,它默认是不可见的,只有通过调用setVisibility函数或者Inflate函数才会将其要装载的目标布局给加载出来,从而达到延迟加载的效果,这个要被加载的布局通过android:layout属性来设置。

      当准备inflate ViewStub时,调用inflate()方法即可。还可以设定ViewStub的Visibility为VISIBLE或INVISIBLE,也会触发inflate。注意的是,使用inflate()方法能返回布局文件的根View。

        ((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);// orView importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();
      

      setVisibility的时候会触发了inflate

      注意:使用ViewStub加载的布局中不能使用merge标签。

  • 看看Space标签(不常用)

    space标签可以只在布局文件中占位,不绘制,Space标签有对应的java类Space.java,通过阅读源码可以发现,它继承至View.java,并且复写了draw方法,该方法为空,既没有调用父类的draw方法,也没有执行自己的代码,表示该类是没有绘制操作的,但onMeasure方法正常调用,说明是有宽高的。
    主要功能用来设置间距,这个标签不常用,常使用margin或padding。

3、介绍一下查看渲染性能的工具

  • GPU呈现模式分析(大致定位问题)

    开发者选项 — GPU呈现模式分析 — 选择“在屏幕上显示为条形图”

    Android开发者选项——Gpu呈现模式分析,参考:http://www.voidcn.com/blog/gjy211/article/p-6210447.html
    自动播放的视频停止的时候会有两条很长的柱线,下个视频播放的时候还会有一条。这里有一个明显的卡顿。
    播放器操作(DefaultPlayerView.java - doPlay,player_auto_control_layout.xml):

      上图的E total time = 68 是播放器停止播放的时候耗费的时间。total time = 29 是播放器开始播放的时候耗费的时间。其中,大部分时间耗费在了 5total time = 18 上面,这个是inflate播放器界面的时候耗费的时间。
    

    是不是所有的inflate都很耗费时间,看一下账号信息页:

  • GPU Monitor

  • 启用严格模式(不止渲染性能)

    应用在主线程上执行长时间操作时会闪烁屏幕。
    通过代码进行严格模式(StrictMode)调试,参考:http://www.tuicool.com/articles/ueeM7b6

二、内存问题

1、内存浪费

程序内存的管理是否合理高效对应用的性能有着很大的影响。
推荐阅读Android性能优化典范-第3季,参考:http://hukai.me/android-performance-patterns-season-3/

  • ArrayMap(我们项目中没有用到,Android源码中很多使用)

    Android为移动操作系统特意编写了一些更加高效的容器,例如ArrayMap、SparseArray。
    为了解决HashMap更占内存的弊端,Android提供了内存效率更高的ArrayMap。

    • 先来看看HashMap的原理

      HashMap的整体结构如下:

      存储位置的确定流程:

      !()[http://img.hb.aicdn.com/16b2993c41a413b24470e1b9b74227674cd16fdb9a09-UTrZIe]

    • 再看来看看ArrayMap是如何优化内存的

      它内部使用两个数组进行工作,其中一个数组记录key hash过后的顺序列表,另外一个数组按key的顺序记录Key-Value值,如下图所示:

      当你想获取某个value的时候,ArrayMap会计算输入key转换过后的hash值,然后对hash数组使用二分查找法寻找到对应的index,然后我们可以通过这个index在另外一个数组中直接访问到需要的键值对。

      既然ArrayMap中的内存占用是连续不间断的,那么它是如何处理插入与删除操作的呢?它跟HashMap有什么区别?二者之间的删除插入效率有什么差异?请看下图所示,演示了Array的特性:

      HashMap与ArrayMap之间的内存占用效率对比图如下:

      与HashMap相比,ArrayMap在循环遍历的时候更加高效。

      什么时候使用ArrayMap呢?

        1、对象个数的数量级最好是千以内,没有频繁的插入删除操作2、数据组织形式包含Map结构
      
  • Autoboxing(避免自动装箱)

    Autoboxing的行为还经常发生在类似HashMap这样的容器里面,对HashMap的增删改查操作都会发生了大量的autoboxing的行为。当key是int类型的时候,HashMap和ArrayMap都有Autoboxing行为。

  • SparseArray(项目中用到较多 -- 后面再说如何利用工具查找该用SparseArray而没有用到的地方)

    为了避免Autoboxing行为Android提供了SparseArray,此容器使用于key为int类型
    SparseBooleanMap,SparseIntMap,SparseLongMap等容器,是key为int,value类型相应为boolean、int、long等。

  • Enum(枚举,项目中较多使用,应尽量避免)

    Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.
    Android官方强烈建议不要在Android程序里面使用到enum。
    关于enum的效率,请看下面的讨论。假设我们有这样一份代码,编译之后的dex大小是2556 bytes,在此基础之上,添加一些如下代码,这些代码使用普通static常量相关作为判断值:

    增加上面那段代码之后,编译成dex的大小是2680 bytes,相比起之前的2556 bytes只增加124 bytes。假如换做使用enum,情况如下:

    使用enum之后的dex大小是4188 bytes,相比起2556增加了1632 bytes,增长量是使用static int的13倍。不仅仅如此,使用enum,运行时还会产生额外的内存占用,如下图所示:

  • 推荐一些文章:

    • HashMap,ArrayMap,SparseArray源码分析及性能对比,参考:http://www.jianshu.com/p/7b9a1b386265#

    • Android性能优化--小心自动装箱:http://blog.csdn.net/lgz_ei/article/details/69208784

    • Android性能优化篇:Android中如何避免创建不必要的对象:http://blog.csdn.net/jia635/article/details/52525243

    • HashMap、ArrayMap、SparseArray分析比较:http://blog.csdn.net/chen_lifeng/article/details/52057427

    • Android性能优化之String篇:http://www.androidchina.net/5940.html

    • SharedPreferences的commit和apply分析:http://blog.csdn.net/u010198148/article/details/51706483

2、内存泄漏

  • 什么是内存泄漏?

    一些不用的对象被长期持有,导致内存无法被释放。

  • 可能发生内存泄漏的地方有哪些?

    • 内部类引用导致Activity的泄漏

      在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。
      最典型的场景是Handler导致的Activity泄漏,如果Handler中有延迟的任务或者是等待执行的任务队列过长,都有可能因为Handler继续执行而导致Activity发生泄漏。
      为了解决这个问题,可以在UI退出之前,执行remove Handler消息队列中的消息与runnable对象。或者是使用Static + WeakReference的方式来达到断开Handler与Activity之间存在引用关系的目的。
      举例,MainTabActivity - MainTabHandler:

      如何修复?

      Android Weak Handler:可以避免内存泄漏的Handler库,参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1123/2047.html

    • Activity Context被传递到其他实例中,这可能导致自身被引用而发生泄漏。

      考虑使用Application Context而不是Activity Context。
      例如:全局Dialog或者Context被单例持有。

    • 静态造成的内存泄漏

      还有静态变量持有View,例如:

        private static View view;void setStaticView() {view = findViewById(R.id.sv_button);}
      
    • 注意监听器的注销(稍后利用工具分析一个例子)

      regist就要unregist

    • 注意Cursor对象是否及时关闭(项目中也存在,不再列举)

    • WebView的引起的泄漏(暂时没有研究)

  • 使用工具分析定位解决内存泄漏

    • Memory monitor

      通过MemoryMonitor可以看到,启动手雷进入手雷的内存情况如下(为什么没有做任何操作内存一直在增加?):

      通过实例分析一处内存泄漏,操作步骤如下:
      启动手雷 - 进入首页 - 切换底部tab到我的tab - 点击登录弹窗弹窗 - 登录成功返回首页

    • MAT(Memory Analyzer Tool)

      需要下载MAT独立版,可到这里下载解压使用:\\192.168.8.188\上传2\ltsoft
      分析刚才的操作内存情况

      进入首页并没有进行任何活动操作,为什么会有那么多bitmap对象呢?

    • LeakCanary

      LeakCanary 中文使用说明,参考:https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/
      LeakCanary:让内存泄露无所遁形,参考:https://www.liaohuqiu.net/cn/posts/leak-canary/

    • TraceView(不做详细分析)

    • GT(应该更适合测试同学测试APP性能)

      利用GT,仅凭一部手机,无需连接电脑,您即可对APP进行快速的性能测试(CPU、内存、流量、电量、帧率/流畅度等等)、开发日志的查看、Crash日志查看、网络数据包的抓取、APP内部参数的调试、真机代码耗时统计等。
      GT官网:http://gt.qq.com/index.html

  • 内存使用策略优化

    • 看看下载一个视频加上浏览一下精选页,然后将应用切到后台,内存使用情况

    • 有什么优化内存的策略

      • onLowMemory():Android系统提供了一些回调来通知当前应用的内存使用情况,通常来说,当所有的background应用都被kill掉的时候,forground应用会收到onLowMemory()的回调。在这种情况下,需要尽快释放当前应用的非必须的内存资源,从而确保系统能够继续稳定运行。

      • onTrimMemory(int):Android系统从4.0开始还提供了onTrimMemory()的回调,当系统内存达到某些条件的时候,所有正在运行的应用都会收到这个回调,同时在这个回调里面会传递参数,代表不同的内存使用情况,收到onTrimMemory()回调的时候,需要根据传递的参数类型进行判断,合理的选择释放自身的一些内存占用,一方面可以提高系统的整体运行流畅度,另外也可以避免自己被系统判断为优先需要杀掉的应用。

  • 文章推荐:

    • Android内存优化之OOM:http://hukai.me/android-performance-oom/

    • 内存泄露从入门到精通三部曲之基础知识篇:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=21&extra=page%3D4

    • 内存泄露从入门到精通三部曲之排查方法篇:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=62&extra=page%3D5

    • 内存泄露从入门到精通三部曲之常见原因与用户实践:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=125&highlight=%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2

    • 胡凯Android性能典范系列

3、性能优化必备神器推荐(Lint)

  • 上面分析的一些项目中的问题,怎么找到的呢?

    Lint:静态代码分析工具

  • 如何通过Lint查找项目中的问题,如何使用?

  • 如果只想分析某个文件夹的代码

  • 设置代码分析选项

三、耗电问题

例如:关闭屏幕时关闭掉登录ping,屏幕亮时再打开。
以上的很多问题都会导致耗电量增加。
如何优化后台下载时的耗电手机发烫问题?
需要具体的硬件工具测试应用耗电量,需要一套耗电量测试标准。

作者:李通Tookie
链接:https://www.jianshu.com/p/307ba8911799
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

转载于:https://my.oschina.net/JiangTun/blog/1805377

Android性能全面分析与优化方案研究—几乎是史上最全最实用的相关推荐

  1. Android 系统性能优化(30)---Android性能全面分析与优化方案研究

    Android 性能优化 1.结合以下四个部分讲解: 性能问题分类 性能优化原则和方法 借助性能优化工具分析解决问题 性能优化指标 2性能问题分类 1.渲染问题:过度绘制.布局冗杂 2.内存问题:内存 ...

  2. 几乎是史上最全最实用的Android性能全面分析与优化方案研究

    结合以下四个部分讲解: 性能问题分类 性能优化原则和方法 借助性能优化工具分析解决问题 性能优化指标 性能问题分类 1.渲染问题: 过度绘制.布局冗杂 2.内存问题: 内存浪费(内存管理).内存泄漏 ...

  3. 2022年最新Android面试题整理,全网都在看,史上最全面试攻略

    Android面试现在什么东西是必须考察的?专业素养还是面试技巧?四大组件还是Framework层源码?哪有什么必考的,可以说所有技术栈都不是必考,但又是必考.话虽看似矛盾,但却反映了当前国内互联网环 ...

  4. 数据图表与分析图_史上最全最实用的数据可视化分析图表制作工具汇总

    俗话说的好:工欲善其事,必先利其器!一款好的工具可以让你事半功倍,尤其是在大数据时代,更需要强有力的工具通过使数据有意义的方式实现数据可视化,还有数据的可交互性;我们还需要跨学科的团队,而不是单个数据 ...

  5. Android后台耗电分析及优化

    原文见: 在路上的blog Android后台耗电分析及优化 一.什么是耗电优化? 二.耗电优化第一个方向:优化后台耗电 1.唤醒锁定操作卡住(前台&后台) 2.唤醒次数过多 3.WLAN扫描 ...

  6. Android Q app内存压缩优化方案介绍

    Android Q app内存压缩优化方案介绍 原创文章,谢绝转载! Android Q新增了部分系统性能优化方案,这里简单学习下,本篇文章先分析app compaction. 一.愿景: 在保证后台 ...

  7. mysql io 100_MySQL服务器 IO 100%的分析与优化方案

    压力测试过程中,如果因为资源使用瓶颈等问题引发最直接性能问题是业务交易响应时间偏大,TPS逐渐降低等.而问题定位分析通常情况下,最优先排查的是监控服务器资源利用率,例如先用TOP 或者nmon等查看C ...

  8. mysql io_MySQL服务器 IO 100%的分析与优化方案

    前言 压力测试过程中,如果因为资源使用瓶颈等问题引发最直接性能问题是业务交易响应时间偏大,TPS逐渐降低等.而问题定位分析通常情况下,最优先排查的是监控服务器资源利用率,例如先用TOP 或者nmon等 ...

  9. UE3 性能、分析及优化

    性能.分析及优化 概述 通用维护 基础工具和技术 内存分析 内容分析和优化 CPU 性能 游戏线程性能 渲染线程性能 GPU 性能 网络分析 移动设备分析 概述 使游戏可以正常运行的同时符合内存约束条 ...

  10. 史上最全MySQL 大表优化方案(长文)

    转载自  史上最全MySQL 大表优化方案(长文) 当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 一.单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑 ...

最新文章

  1. java数据类型_java 数据类型
  2. sql语句数据行操作-虽然一般不用
  3. phpmyadmin 安装
  4. tableau实战系列(四)用条形图或环形图来呈现进度百分比
  5. linux下的/dev/shm/ 以及与swap目录的区别【转】
  6. SAP C4C的URL Mashup无法添加到embedded component里去
  7. [渝粤教育] 西南科技大学 机械控制工程基础在线考试复习资料
  8. 08-mysql-条件查询-常见函数与小结
  9. Python 列表 reverse( )方法
  10. (转)百度Map API
  11. android自定义静态广播失效,自定义的静态广播没有作用怎么处理
  12. ajax异步session值不唯一 总是改变 解决办法
  13. Shell脚本实战:日志关键字监控+自动告警
  14. 网页与服务器 — 重定向常见异常状态码
  15. epoll 和select/poll的区别
  16. 用DIV+CSS技术设计的网上书城网页与实现制作(大一Web课程设计)
  17. adb locat 过滤日志-命令行
  18. HBuilderX使用手机模拟器进行App开发详解【0基础讲解】
  19. lol服务器维护补偿,LOL官方:服务器崩溃补偿!全服再次免费赠送皮肤一款!
  20. qq授权登录【网站应用】-java版本

热门文章

  1. 查看-增强会话_会话助手平台-Hinglish Voice等!
  2. postgresql保存图片_第一章 PostgreSQL中的数据库集群、数据库和表
  3. 尚学人工智能课程---2、算法工程师和大数据介绍
  4. 从Android到React Native开发(三、自定义原生控件支持)
  5. rails ruby 中对于使用Savon请求web service 获取到大量json数据的处理
  6. 9.6 awk(上);9.7 awk(下)
  7. 大型项目开发: 隔离 (《大规模C++程序设计》书摘)
  8. AC日记——计算循环节长度 51nod 1035
  9. asp.net core跨域访问ajax的验证访问
  10. 【荐】Redis学习资料汇总