Material Designer的低版本兼容实现——View & Animation

Material Designer 宗旨:让不同大小不同用途的设备上拥有同一种设计风格 1.纸张 这种设计模式大量参考了纸墨的模式,将空间变得像纸张一样,而用户的手指就是毛笔。用户按到控件上就会产生墨晕效果。这样的好处是明确的告诉用户是否点击了控件,而且还能让用户一下子明白控件的布局思路。毕竟一张一张的纸叠加起来的控件是很容易让人接受的。这里还有一个词“引喻”,虽然控件像纸张,但是它具有变大变小,改变颜色等能力,所以完全可以不用拘泥于现实纸张。 2.深度 新的设计中希望所有的控件都是现实世界中的隐喻,比如你按下按钮,按钮就应该有被按下的状态,这里就要用到了涟漪(Ripple)效果了。其实涟漪效果是来表示你手指按上去后墨晕扩散的效果的,下面的图能很明白的说明这点。 3.动画 动画贯穿于Material Designer之中,官方文档中用了很大的篇幅来讲解动画效果,希望让设计的动画效果很美观。但我个人认为为了动画而动画是完全不可取的,比如下面的例子 这里的动画看起来十分自然和美观,但是在实际中用户切换activity是很常见的,如果经常出现这个动画用户会觉得“很腻”,十分不友好。动画其实是一个画龙点睛的东西,万不可变为画蛇添足。那么,上图的这个动画应该在什么时候使用呢?用在第一次用户进入一个新的界面的时候,我们为了凸显这个界面的某种特定功能,就可以让这个功能的图标动起来,表现出一个点我试试的效果。 4.排版 新的设计里面很在意排版,里面列出了很多详细的数据来支持我们的设计。对于留白也有了详细的说明。优秀的排班会让你的应用看起来干净,优雅,这点十分重要。在之后的文章中我也会多少说到这方面的知识。 设计文档(不用FQ) http://design.1sters.com/ http://www.ui.cn/Material/ 目录 Material Designer的低版本兼容实现(二)—— Theme{#cb_post_title_url.postTitle2} Material Designer的低版本兼容实现(三)——Color{#cb_post_title_url.postTitle2} Material Designer的低版本兼容实现(四)—— ToolBar{#cb_post_title_url.postTitle2} Material Designer的低版本兼容实现(五)—— ActivityOptionsCompat{#cb_post_title_url.postTitle2} Material Designer的低版本兼容实现(六)—— Ripple Layout{#cb_post_title_url.postTitle2} Material Designer的低版本兼容实现(七)—— Rectange Button{#cb_post_title_url.postTitle2} Material Designer的低版本兼容实现(八)—— Flat Button{#cb_post_title_url.postTitle2} Material Designer的低版本兼容实现(九)—— Float Button & Small Float Button{#cb_post_title_url.postTitle2} Material Designer的低版本兼容实现(十)—— CheckBox & RadioButton{#cb_post_title_url.postTitle2} Material Designer的低版本兼容实现(十一)—— Switch{#cb_post_title_url.postTitle2} ...

2016年1月5日 · 1 分钟 · 天边的星星

Android 增强版百分比布局库 为了适配而扩展

一 概述 上周一我们发布了[Android 百分比布局库(percent-support-lib) 解析与扩展][1]中对percent-support这个库进行了解析和添加了PercentLinearLayout的支持。 那么为什么本篇博客的存在的意义是什么呢? 首先我们回顾下百分比布局库的用法,提供了PercentRelativeLayout、PercentFrameLayout供大家在编写的时候,对于以下属性: layout_widthPercent、layout_heightPercent、 layout_marginPercent、layout_marginLeftPercent、 layout_marginTopPercent、layout_marginRightPercent、 layout_marginBottomPercent、layout_marginStartPercent、layout_marginEndPercent。 可以使用百分比进行设置宽、高、边距,的确给我们在适配上提供了极大的便利,但是在使用过程中,觉得存在一些场景无法得到满足。什么场景呢?下面我举几个例子。 当使用图片时,无法设置宽高的比例 比如我们的图片宽高是200*100的,我们在使用过程中我们设置宽高为20%、10%,这样会造成图片的比例失调。为什么呢?因为20%参考的是屏幕的宽度,而10%参考的是屏幕的高度。 很难使用百分比定义一个正方形的控件 比如,我现在界面的右下角有一个FloatingActionButton,我希望其宽度和高度都为屏幕宽度的10%,很难做到。 一个控件的margin四个方向值一致 有些时候,我设置margin,我希望四边的边距一致的,但是如果目前设置5%,会造成,上下为高度的5%,左右边距为宽度的5%。 综合上述这些问题,可以发现目前的percent-support-lib并不能完全满足我们的需求,所以我们考虑对其进行扩展。说白了,我们就希望在布局的时候可以自己设定参考看度还是高度,比如上述2,我们对于宽高可以写成10%w,10%w。也就是在不改变原库的用法的前提下,添加一些额外的支持。 二 扩展的功能 目前我初步对该库进行了改写,github地址:[android-percent-support-extend][2],对于官方库,做了如下的改变: 不改变原有库的用法 添加了PercentLinearLayout 支持百分比指定特定的参考值,比如宽度或者高度。 例如:app:layout_heightPercent="50%w", app:layout_marginPercent="15%w", app:layout_marginBottomPercent="20%h". 支持通过app:layout_textSizePercent设置textView的textSize 对于外层套ScrollView的问题,目前可以在PercentLinearLayout的外层使用ScrollView,不过对于宽度的百分比参考的就是android.R.id.content的高度(因为,无法参考父控件的高度,父控件的高度理论上依赖于子View高度,且模式为UNSPECIFIED)。 对于如何导入,也是相当的简单,android studio的用户,直接: ``` dependencies { //... compile 'com.zhy:percent-support-extends:1.0.1' } - 1 - 2 - 3 - 4 - 5 不需要导入官方的percent-support-lib了。 对于的三个类分别为: ``` `com.zhy.android.percent.support.PercentLinearLayout com.zhy.android.percent.support.PercentRelativeLayout com.zhy.android.percent.support.PercentFrameLayout` - 1 - 2 - 3 对于eclipse的用户:github上自行下载源码,就几个类和一个attrs.xml,也可以在[bintray.com/percent-support-extends][3] 下载相关文件。 下面看几个具体的示例。 * * * ### <a name="t2"></a>三 具体的示例 {#三-具体的示例} #### <a name="t3"></a>Demo 1 {#demo-1} ...

2015年12月26日 · 9 分钟 · 天边的星星

StrictMode 详解

StrictMode类是Android 2.3 (API 9)引入的一个工具类,可以用来帮助开发者发现代码中的一些不规范的问题。比如,如果你在UI线程中进行了网络或者磁盘操作,StrictMode就会通过Log(logcat )或者对话框的方式把信息提示给你,因为让你的UI线程处理这里操作会被认为是不规范的做法,可能会让你的应用变得比较卡顿。 官网文档:http://developer.android.com/reference/android/os/StrictMode.html 如何启用 StrictMode 我们通常在 Activity 或者自定义的Application类中启动 StrictMode,代码如下: ``` ` <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onCreate</span><span class="o">()</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">DEVELOPER_MODE</span><span class="o">)</span> <span class="o">{</span> <span class="n">StrictMode</span><span class="o">.</span><span class="na">setThreadPolicy</span><span class="o">(</span><span class="k">new</span> <span class="n">StrictMode</span><span class="o">.</span><span class="na">ThreadPolicy</span><span class="o">.</span><span class="na">Builder</span><span class="o">()</span> <span class="o">.</span><span class="na">detectDiskReads</span><span class="o">()</span> <span class="o">.</span><span class="na">detectDiskWrites</span><span class="o">()</span> <span class="o">.</span><span class="na">detectNetwork</span><span class="o">()</span> <span class="c1">// or .detectAll() for all detectable problems</span> <span class="o">.</span><span class="na">penaltyLog</span><span class="o">()</span> <span class="o">.</span><span class="na">build</span><span class="o">());</span> <span class="n">StrictMode</span><span class="o">.</span><span class="na">setVmPolicy</span><span class="o">(</span><span class="k">new</span> <span class="n">StrictMode</span><span class="o">.</span><span class="na">VmPolicy</span><span class="o">.</span><span class="na">Builder</span><span class="o">()</span> <span class="o">.</span><span class="na">detectLeakedSqlLiteObjects</span><span class="o">()</span> <span class="o">.</span><span class="na">detectLeakedClosableObjects</span><span class="o">()</span> <span class="o">.</span><span class="na">penaltyLog</span><span class="o">()</span> <span class="o">.</span><span class="na">penaltyDeath</span><span class="o">()</span> <span class="o">.</span><span class="na">build</span><span class="o">());</span> <span class="o">}</span> <span class="kd">super</span><span class="o">.</span><span class="na">onCreate</span><span class="o">();</span> <span class="o">}</span> ` ``` **注意:**我们只需要在app的开发版本下使用 StrictMode,线上版本避免使用 StrictMode,随意需要通过 诸如 DEVELOPER_MODE 这样的配置变量来进行控制。 下面我们举几个例子来说明 StrictMode 是如何发挥作用的。 代码1: ``` `<span class="kd">public</span> <span class="kd">class</span> <span class="nc">ActivitySimple</span> <span class="kd">extends</span> <span class="n">Activity</span> <span class="o">{</span> &lt;span class="nd">@Override&lt;/span> &lt;span class="kd">protected&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">onCreate&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Bundle&lt;/span> &lt;span class="n">savedInstanceState&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span> &lt;span class="kd">super&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">onCreate&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">savedInstanceState&lt;/span>&lt;span class="o">);&lt;/span> &lt;span class="n">setContentView&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">layout&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">activity_main&lt;/span>&lt;span class="o">);&lt;/span> &lt;span class="n">StrictMode&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">setThreadPolicy&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="n">ThreadPolicy&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">Builder&lt;/span>&lt;span class="o">()&lt;/span> &lt;span class="o">.&lt;/span>&lt;span class="na">detectAll&lt;/span>&lt;span class="o">()&lt;/span> &lt;span class="o">.&lt;/span>&lt;span class="na">penaltyDialog&lt;/span>&lt;span class="o">()&lt;/span> &lt;span class="c1">//弹出违规提示对话框&lt;/span> &lt;span class="o">.&lt;/span>&lt;span class="na">penaltyLog&lt;/span>&lt;span class="o">()&lt;/span> &lt;span class="c1">//在Logcat 中打印违规异常信息&lt;/span> &lt;span class="o">.&lt;/span>&lt;span class="na">build&lt;/span>&lt;span class="o">());&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">testNetwork&lt;/span>&lt;span class="o">();&lt;/span> &lt;span class="o">}&lt;/span> &lt;span class="kd">private&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">testNetwork&lt;/span>&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span> &lt;span class="k">try&lt;/span> &lt;span class="o">{&lt;/span> &lt;span class="n">URL&lt;/span> &lt;span class="n">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">URL&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">"http://www.baidu.com"&lt;/span>&lt;span class="o">);&lt;/span> &lt;span class="n">HttpURLConnection&lt;/span> &lt;span class="n">conn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="n">HttpURLConnection&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="n">url&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">openConnection&lt;/span>&lt;span class="o">();&lt;/span> &lt;span class="n">conn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">connect&lt;/span>&lt;span class="o">();&lt;/span> &lt;span class="n">BufferedReader&lt;/span> &lt;span class="n">reader&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">BufferedReader&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="n">InputStreamReader&lt;/span>&lt;span class="o">(&lt;/span> &lt;span class="n">conn&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getInputStream&lt;/span>&lt;span class="o">()));&lt;/span> &lt;span class="n">String&lt;/span> &lt;span class="n">lines&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="o">;&lt;/span> &lt;span class="n">StringBuffer&lt;/span> &lt;span class="n">sb&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">StringBuffer&lt;/span>&lt;span class="o">();&lt;/span> &lt;span class="k">while&lt;/span> &lt;span class="o">((&lt;/span>&lt;span class="n">lines&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">reader&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">readLine&lt;/span>&lt;span class="o">())&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span> &lt;span class="n">sb&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">append&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">lines&lt;/span>&lt;span class="o">);&lt;/span> &lt;span class="o">}&lt;/span> &lt;span class="o">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="n">Exception&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">printStackTrace&lt;/span>&lt;span class="o">();&lt;/span> &lt;span class="o">}&lt;/span> &lt;span class="o">}&lt;/span> <span class=“o”>}</span> ` ...

2015年12月16日 · 10 分钟 · 天边的星星

EventBus源码研读

摘要 转载:http://kymjs.com/code/2015/12/12/01 本文总共分三部分,从源码角度分析了 EventBus 库。以及介绍了其内部实现注册、发送、响应、取消注册的原理。 EventBus 是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent, Handler, BroadCast 在 Fragment,Activity,Service,线程之间传递消息.优点是开销小,使用方便,可以很大程度上降低它们之间的耦合,使得我们的代码更加简洁,耦合性更低,提升我们的代码质量。 类似的库还有 Otto ,今天就带大家一起研读 EventBus 的源码. 在写这篇文章之前,我已经将本文相关的中文注释代码上传到了GitHub:https://github.com/kymjs/EventBus 基础用法 在读代码之前,首先你得了解它的基本用法.如果你已经能够很熟练的使用EventBus等事件总线库了,那么你可以跳过本节. 首先引入依赖包,查看GitHub主页的说明: https://github.com/greenrobot/EventBus 在Gradle文件加入 compile 'de.greenrobot:eventbus:2.4.0' 用法与广播相同,且比广播更简单: 注册订阅者 首先你需要注册一个事件订阅者,为了方便理解你可以把他当成广播的广播接收者 你可以在任何一个类中使用如下代码注册以及解除注册 ``` `<span class="c1"><span class="hljs-comment">//把当前类注册为订阅者(接收者)</span></span> <span class="n">EventBus</span><span class="o">.</span><span class="na">getDefault</span><span class="o">().</span><span class="na">register</span><span class="o">(</span><span class="k"><span class="hljs-keyword">this</span></span><span class="o">);</span> <span class=“c1”><span class=“hljs-comment”>//解除注册当前类(同广播一样,一定要调用,否则会内存泄露)</span></span> <span class=“n”>EventBus</span><span class=“o”>.</span><span class=“na”>getDefault</span><span class=“o”>().</span><span class=“na”>unregister</span><span class=“o”>(</span><span class=“k”><span class=“hljs-keyword”>this</span></span><span class=“o”>);</span>` </div> 注册了订阅者以后,我们需要创建一个回调方法`onEvent`,当我们订阅的事件发送的时候就会回调它 <div class="highlight"> &lt;span class="c1">&lt;span class="hljs-comment">//其实命名不一定必须是onEvent(),但那属于高级用法了,这里我们只说最简单的&lt;/span>&lt;/span> &lt;span class="kd">&lt;span class="hljs-function">&lt;span class="hljs-keyword">public&lt;/span>&lt;/span>&lt;/span> &lt;span class="kt">&lt;span class="hljs-function">&lt;span class="hljs-keyword">void&lt;/span>&lt;/span>&lt;/span> &lt;span class="nf">&lt;span class="hljs-function">&lt;span class="hljs-title">onEvent&lt;/span>&lt;/span>&lt;/span>&lt;span class="o">&lt;span class="hljs-function">&lt;span class="hljs-params">(&lt;/span>&lt;/span>&lt;/span>&lt;span class="n">&lt;span class="hljs-function">&lt;span class="hljs-params">Object&lt;/span>&lt;/span>&lt;/span> &lt;span class="n">&lt;span class="hljs-function">&lt;span class="hljs-params">event&lt;/span>&lt;/span>&lt;/span>&lt;span class="o">&lt;span class="hljs-function">&lt;span class="hljs-params">)&lt;/span>&lt;/span>&lt;/span> &lt;span class="o">{}&lt;/span> ...

2015年12月16日 · 15 分钟 · 天边的星星

android 源码角度全方位理解filter 及简单使用

写一个listview容易,写一个adapter容易,自己new一个线程过滤数据也容易,但是如何将过滤的效率发挥到最大化,不得不提一下android自带的filter类。 有同学肯定要问,过滤数据自己写一个完全没问题,为什么要用android自带的filter类?我原来也是自己写线程过滤,然而最近项目中遇到一个低配机,双核0.8GCPU,过滤效果实在是卡顿厉害,优化起见,使用了android内部filter试一下效果,结果真是比自己写的好用,于是认真学习了下源码,从头至尾备忘如下: ![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif) ![复制代码](http://common.cnblogs.com/images/copycode.gif) ``` private static final String LOG_TAG = “Filter”; private static final String THREAD_NAME = "Filter"; private static final int FILTER_TOKEN = 0xD0D0F00D; private static final int FINISH_TOKEN = 0xDEADBEEF; private Handler mThreadHandler; private Handler mResultHandler; private Delayer mDelayer; private final Object mLock = new Object(); <div class="cnblogs_code_toolbar"> <span class="cnblogs_code_copy"><a title="复制代码">![复制代码](http://common.cnblogs.com/images/copycode.gif)</a></span> </div> </div> </div> 其实用到的全局变量只有8个,并且有一半是常量:两个handler,一个delayer,一个对象锁。google开发大牛用这几个变量加上为数不多的几个局部变量就做出来了一个拓展性极佳的过滤器,不得不让人钦佩。 首先看构造方法: <div class="cnblogs_code"> ![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif) <div id="cnblogs_code_open_c440c2da-b7c0-476f-bacb-a745b6a72f88" class="cnblogs_code_hide"> ``` public Filter() { mResultHandler = new ResultsHandler(); } 构造方法中二小强之一——ResultsHandler已经被创建了,顾名思义处理过滤操作结果。我们看看这个类的定义: ...

2015年12月4日 · 5 分钟 · 天边的星星

【新技能get】让App像Web一样发布新版本

2015.10.22 腾讯Bugly [微信分享](http://bugly.qq.com/blog/?p=781#) 背景 当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就会忙得焦头烂额:重新打包App、测试、向各个应用市场和渠道换包、提示用户升级、用户下载、覆盖安装。有时候仅仅是为了修改了一行代码,也要付出巨大的成本进行换包和重新发布。 这时候就提出一个问题:**有没有办法以补丁的方式动态修复紧急Bug,不再需要重新发布App,不再需要用户重新下载,覆盖安装?** 虽然Android系统并没有提供这个技术,但是很幸运的告诉大家,答案是:可以。 解决方案 该方案基于的是android dex分包方案的,关于dex分包方案,网上有几篇解释了,所以这里就不再赘述,具体可以看这里:https://m.oschina.net/blog/308583(请复制链接到浏览器打开)。 简单的概括一下,就是把多个dex文件塞入到app的classloader之中,但是android dex拆包方案中的类是没有重复的,如果classes.dex和classes1.dex中有重复的类,当用到这个重复的类的时候,系统会选择哪个类进行加载呢? 让我们来看看类加载的代码: `&lt;strong>public&lt;/strong> Class findClass(String name, List&lt;Throwable&gt; suppressed) { &lt;strong>for&lt;/strong> (Element element : dexElements) { //每个Element就是一个dex文件 DexFile dex = element.dexFile; &lt;strong>if&lt;/strong> (dex != &lt;strong>null&lt;/strong>) { Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed); &lt;strong>if &lt;/strong>(clazz != &lt;strong>null&lt;/strong>) { &lt;strong> return &lt;/strong>clazz; } } } &lt;strong> if &lt;/strong>(dexElementsSuppressedExceptions != &lt;strong>null&lt;/strong>) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } &lt;strong> return null&lt;/strong>; } ` 一个ClassLoader可以包含多个dex文件,每个dex文件是一个Element,多个dex文件排列成一个有序的数组dexElements,当找类的时候,会按顺序遍历dex文件,然后从当前遍历的dex文件中找类,如果找类则返回,如果找不到从下一个dex文件继续查找。 理论上,如果在不同的dex中有相同的类存在,那么会优先选择排在前面的dex文件的类,如下图: ![](https://mmbiz.qlogo.cn/mmbiz/tnZGrhTk4de39gh3QXrLudkAtkVzFOtDJoCpVwNq85HU9Lpw9tuCHLibDzQHjNicYnWNET0wvCWNVdZc6XUFI4yw/0?wx_fmt=jpeg) 在此基础上,我们构想了热补丁的方案,把有问题的类打包到一个dex(patch.dex)中去,然后把这个dex插入到Elements的最前面,如下图 ![](https://mmbiz.qlogo.cn/mmbiz/tnZGrhTk4de39gh3QXrLudkAtkVzFOtD4icmchNG1Z6HgDD20mzQBs11XyUyxlAL6OZBqO2Pnicf8t4vjnA01N8Q/0?wx_fmt=jpeg) 好,该方案基于第二个拆分dex的方案,方案实现如果懂拆分dex的原理的话,大家应该很快就会实现该方案,如果没有拆分dex的项目的话,可以参考一下谷歌的multidex方案实现。然后在插入数组的时候,把补丁包插入到最前面去。 好,看似问题很简单,轻松的搞定了,让我们来试验一下,修改某个类,然后打包成dex,插入到classloader,当加载类的时候出现了(本例中是ActivityManager要被替换): ![](https://mmbiz.qlogo.cn/mmbiz/tnZGrhTk4deljxecuyiaGnrNtAnOS3WkIicoAnyViaKrpmWcAwO3kebB20AbpkqbCVP0zlXkGQe5XpjZUiaYMw1NVw/0?wx_fmt=jpeg) **为什么会出现以上问题呢?** **从log的意思上来讲,ModuleManager引用了ActivityManager,但是发现这这两个类所在的dex不在一起,其中:** **1. ModuleManager在classes.dex中** **2. ActivityManager在patch.dex中** **结果发生了错误。** **这里有个问题,拆分dex的很多类都不是在同一个dex内的,怎么没有问题?** **让我们搜索一下抛出错误的代码所在,嘿咻嘿咻,找到了一下代码:** ![](https://mmbiz.qlogo.cn/mmbiz/tnZGrhTk4deljxecuyiaGnrNtAnOS3WkIiavUvrYI3Via0BrrMfBAXcicwZV2pejc56PVH1IwmruNmvibcbw8boCnVg/0?wx_fmt=png) 从代码上来看,如果两个相关联的类在不同的dex中就会报错,但是拆分dex没有报错这是为什么,原来这个校验的前提是: ![](https://mmbiz.qlogo.cn/mmbiz/tnZGrhTk4de39gh3QXrLudkAtkVzFOtDVqPD8yt3X72ETcJDG4icZyeDt3Sic6CLNAXAmicN3yZzQEu6ODr9CHWBw/0?wx_fmt=png) 如果引用者(也就是ModuleManager)这个类被打上了**CLASS_ISPREVERIFIED标志**,**那么就会进行dex的校验。那么这个标志是什么时候被打上去的?** **让我们在继续搜索一下代码,嘿咻嘿咻~~,在DexPrepare.cpp找到了一下代码:** ** ...

2015年12月2日 · 1 分钟 · 天边的星星

Dex分包变形记

原创 2015-11-26 李金涛 腾讯Bugly 一、背景 就在项目灰度测试前不久,爆出了在 Android 3.0以下手机上安装时出现 INSTALL _ FAILED_DEXOPT,导致安装失败。这一问题意味着项目将不能在 Android 3.0以下的手机上安装使用,对项目的发布有比较大的影响,所以必须尽快解决。 INSTAL L_FAILED_DEXOPT导致无法安装的问题,从根本上来说,可能是两个原因造成的: (1) 单个 dex 文件方法总数65K 的限制。 (2) Dexopt 的 LinearAlloc 限制。 当 Android 系统安装一个应用的时候,有一步是对 Dex 进行优化,这个过程有一个专门的工具来处理,叫 DexOpt。DexOpt 是在第一次加载 Dex 文件的时候执行的。这个过程会生成一个 ODEX 文件,即 Optimised Dex。执行 ODEX 的效率会比直接执行 Dex 文件的效率要高很多。 但是在早期的 Android 系统中,DexOpt 有两个问题。(一):DexOpt 会把每一个类的方法 id 检索起来,存在一个链表结构里面,但是这个链表的长度是用一个 short 类型来保存的,导致了方法 id 的数目不能够超过65536个。当一个项目足够大的时候,显然这个方法数的上限是不够的。(二):Dexopt 使用 LinearAlloc 来存储应用的方法信息。Dalvik LinearAlloc 是一个固定大小的缓冲区。在Android 版本的历史上,LinearAlloc 分别经历了4M/5M/8M/16M限制。Android 2.2和2.3的缓冲区只有5MB,Android 4.x提高到了8MB 或16MB。当方法数量过多导致超出缓冲区大小时,也会造成dexopt崩溃。 尽管在新版本的 Android 系统中,DexOpt 修复了方法数65K的限制问题,并且扩大了 LinearAlloc 限制,但是我们仍然需要对低版本的 Android 系统做兼容。 ...

2015年12月2日 · 5 分钟 · 天边的星星

Android开发者必须深入学习的10个应用开源项目

Android开发又将带来新一轮热潮,很多开发者都投入到这个浪潮中去了,创造了许许多多相当优秀的应用。其中也有许许多多的开发者提供了应用开源项 目,贡献出他们的智慧和创造力。学习开源代码是掌握技术的一个最佳方式。下面推荐几个应用开源项目,这些项目不仅提供了优秀的创意,也可以直接掌握 Android内核的接口使用: 1.Android团队提供的示例项目 如果不是从学习Android SDK中提供的那些样例代码开始,可能没有更好的方法来掌握在Android这个框架上开发。由Android的核心开发团队提供了15个优秀的示例项 目,包含了游戏、图像处理、时间显示、开始菜单快捷方式等。 地址:http://code.google.com/p/apps-for-android/ 2.Remote Droid RemoteDroid是一个Android应用,能够让用户使用自己的无线网络使用无线键盘、触摸屏操作手机。这个项目为开发者提供了如网络连接、触 摸屏手指运动等很好的样例。 地址:http://code.google.com/p/remotedroid/ 3.TorProxy和Shadow TorProxy应用实现了Android手机无线电电传通讯(TOR),和Shadow应用一起使用,可以使用手机匿名上网。从该项目源代码中,可以 掌握socket连接、管理cookie等方法。 地址:http://www.cl.cam.ac.uk/research/dtg/code/svn/android-tor/ http://www.cl.cam.ac.uk/research/dtg/android/tor/ 4、 Android SMSPopup SMSPopup可以截获短信内容显示在一个泡泡形状的窗口中。从这个项目中可以掌握到如何使用内置的短信SMS接口。 地址:http://code.google.com/p/android-smspopup/ 5、 Standup Timer Standup Timer应用用于控制站立会议时间,类似秒表倒计时,可以提醒每个人的讲话时间已到,从而保证每个与会者使用时间一样。从该项目的代码中,可以学会如何 使用时间函数。另外,这个项目的代码是采用视图view、模型model严格分离的设计思路。 地址:http://github.com/jwood/standup-timer 6、 Foursquare 是Foursquare.com的一个客户端应用,该应用主要分为两个模块:API(com.joelapenna.foursquare)和界面前端 (com.joelapenna.foursquared)两部分。从该项目代码中,可以学会如何同步、多线程、HTTP连接等技术。 地址:http://code.google.com/p/foursquared/ 7、 Pedometer Pedometer应用用于记录你每天走路步数的。尽管记录不一定精准,但是从这个项目中,可以学习几个不同的技术:加速器交互、语音更新、后台运行服 务等。 地址:http://code.google.com/p/pedometer/ 8、 OpenSudoku-android OpenSudoku是一个简单的九宫格数独游戏。从代码中可以学习到如何在视图中显示表格数据,以及如何和一个网站交互等技术。 地址:http://code.google.com/p/opensudoku-android/ 9、 ConnectBot ConnectBot是Android平台的一个客户端安全壳应用。从该项目代码中,可以学习到很多Android安全方面的内容,这些是你在开发应用 时经常需要考虑的安全问题。 地址:http://code.google.com/p/connectbot/ 10、 WordPress的Android应用 当然在最后不能不提WordPress的Android应用了,这是WordPress官方开发团队提供的一个项目。从代码中可以学习到XMLRPC调 用(当然还有更多的优秀内容)。 地址:http://android.svn.wordpress.org/trunk/

2015年12月2日 · 1 分钟 · 天边的星星

10 条提升 Android 性能的建议

每个人都知道一个 App 的成功,与这个 App 的性能体验有着很密切的关系。但是如何让你的 App 拥有极致性能体验呢?在 DroidCon NYC 2015 的这个分享里,Boris Farber 带来了他关于 Android Api 以及如何避免一些常见坑的经验。带你了解如何缩短启动时间,优化滑动效果,创建更加顺滑的用户体验。 <div class="company-info"> Transcription below provided by Realm: a replacement for SQLite that you can use in Java or Kotlin. [Check out the docs!](https://realm.io/docs/java/latest/) </div> [Sign up to be notified of new videos](http://eepurl.com/2mbQX) — we won’t email you for any other reason, ever. <hr /> <div class="author-info"> <div class="author-info-name"> About the Speaker: Boris Farber </div> <div class="author-info-bio"> 每个人都知道一个 App 的成功,更这个 App 的性能体验有着很密切的关系。但是如何让你的 App 拥有极致性能体验呢?在 DroidCon NYC 2015 的这个分享里,Boris Farber 带来了他关于 Android Api 以及如何避免一些常见的坑的经验。了解如何缩短启动时间,优化滑动效果,创建更加顺滑的用户体验。 </div> <div class="author-info-links"> [**@borisfarber](https://twitter.com/borisfarber) </div> </div> <hr /> *Save the date for [Droidcon SF](http://sf.droidcon.com/) in March — a conference with best-in-class presentations from leaders in all parts of the Android ecosystem.* <hr /> ### 简介 <a>(0:00)</a> 大家好,我是 Boris,现在是 Google 的一枚员工,目前专注于需要高性能的 App。这个分享是我长期以来从错误中,以及在给合作伙伴做咨询的时候攒下的最佳实践。如果你有一个小型的 App,读过之后,会在你的 App 成长阶段起到帮助。 我常常会见到那些启动时间很长,滑动不流畅,甚至出现没有反应的 App。我们通常要花很多时间去改善这些问题,毕竟我们都希望自己的 App 能够成功。 ### Activity 泄漏 <a>(1:17)</a> 我们第一个需要修复的问题就是 Activity 泄漏,我们先来看看内存泄漏是怎么发生的。 Activity 泄漏通常是内存泄漏的一种。为什么会泄漏呢?如果你持有一个未使用的 Activity 的引用,其实也就持有了 Activity 的布局,自然也就包含了所有的 View。最棘手的是持有静态引用。别忘了,Activity 和 Fragment 都有自己的生命周期。一旦我们持有了静态引用,Activity 和 Fragment 就不会被垃圾回收器清理掉了。这就是为什么静态引用很危险。 <div class="highlighter-rouge"> ``` &lt;span class="n">m_staticActivity&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">staticFragment&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getActivity&lt;/span>&lt;span class="o">()&lt;/span> ...

2015年12月2日 · 4 分钟 · 天边的星星

Gradle脚本基础全攻略

**【工匠若水 [http://blog.csdn.net/yanbober](http://blog.csdn.net/yanbober) 转载请注明出处。[点我开始Android技术交流](https://github.com/yanbober/AndroidCommunication)】** # <a name="t0"></a>**1 背景** 在开始Gradle之前请务必保证自己已经初步了解了Groovy脚本,特别是闭包规则,如果还不了解Groovy则可以先看[《Groovy脚本基础全攻略》](http://blog.csdn.net/yanbober/article/details/49047515)这一篇博客速成一下Groovy基础,然后再看此文即可。关于Gradle速成干货基础详情也请参考[Geadle官方网站](https://www.gradle.org/),不好意思我太Low了。 ![这里写图片描述](http://img.blog.csdn.net/20151025165437078) Gradle核心是基于Groovy的领域特定语言(DSL,具体概念参见[《Groovy脚本基础全攻略》](http://blog.csdn.net/yanbober/article/details/49047515)),具有非常好的扩展性,所以不管是简单的独立项目还是大型的多项目构建它都能高效的提高构建任务,尤其对多项目支持是非常牛逼的;Gradle还提供了局部构建功能,譬如构建一个单独子项目时它会构建这个子项目依赖的所有子项目;当然了他对远程仓库和本地库的支持也很到位;哎呀,总之后面你就明白他的牛逼之处了。 既然Gradle核心是Groovy,Groovy本质又是Java,所以很明显可以发现Gradle环境必须依赖JDK与Groovy库,具体如下: - JDK版本必须是JDK6以上; - 因为Gradle自带Groovy库, 所以已安装的Groovy会被Gradle忽略; 具体Gradle环境配置好了以后如下图: ![这里写图片描述](http://img.blog.csdn.net/20151022113143209) **【工匠若水 [http://blog.csdn.net/yanbober](http://blog.csdn.net/yanbober) 转载请注明出处。[点我开始Android技术交流](https://github.com/yanbober/AndroidCommunication)】** # <a name="t1"></a>**2 Gradle DSL基础** Gradle的实质是配置脚本,执行一种类型的配置脚本时就会创建一个关联的对象,譬如执行Build script脚本就会创建一个Project对象,这个对象其实就是Gradle的代理对象。下面给出来各种类型Gradle对应的对象类型: <table> <tr> <th> 脚本类型 </th> <th> 关联对象类型 </th> </tr> <tr> <td> Build script </td> <td> Project </td> </tr> <tr> <td> Init script </td> <td> Gradle </td> </tr> <tr> <td> Settings script </td> <td> Settings </td> </tr> </table> Gradle的三种主要对象解释如下: - Project对象:每个build.gradle会转换成一个Project对象。 - Gradle对象:构建初始化时创建,整个构建执行过程中只有这么一个对象,一般很少去修改这个默认配置脚本。 - Settings对象:每个settings.gradle会转换成一个Settings对象。 可以看见,当我们编写指定类型Gradle脚本时我们可以直接使用关联对象的属性和方法;当然了,每个脚本也都实现了Script接口,也就是说我们也可以直接使用Script接口的属性与方法。 ## <a name="t2"></a>**2-1 构建脚本Build script(Project)** 在Gradle中每个待编译的工程都是一个Project(每个工程的build.gradle对应一个Project对象),每个Project在构建的时候都包含一系列Task,这些Task中很多又是Gradle的插件默认支持的。 PS:所谓的我们编写Gradle脚本,实质大多数时候都是在编写构建脚本Build script,所以说Project和Script对象的属性和方法等API非常重要。 每一个Project对象和build.gradle一一对应,一个项目在构建时都具备如下流程: - 为当前项目创建一个Settings类型的实例。 - 如果当前项目存在settings.gradle文件,则通过该文件配置刚才创建的Settings实例。 - 通过Settings实例的配置创建项目层级结构的Project对象实例。 - 最后通过上面创建的项目层级结构Project对象实例去执行每个Project对应的build.gradle脚本。 ## <a name="t3"></a>**2-2 初始化脚本Init script(Gradle)和设置脚本Settings script(Settings)** **Gradle对象:** 初始化脚本Init script(Gradle)类似于Gradle的其他类型脚本,这种脚本在构建开始之前运行,主要的用途是为接下来的Build script做一些准备工作。我们如果需要编写初始化脚本Init script,则可以把它按规则放置在USER_HOME/.gradle/相关目录下。譬如: ![这里写图片描述](http://img.blog.csdn.net/20151025171346780) 初始化脚本的Gradle对象代表了Gradle的调运,我们可以通过调用Project对象的getGradle()方法获得Gradle实例对象。 **Settings对象:** 在对工程进行配置(譬如多项目树构建)时Settings实例与settings.gradle文件一一对应,它用来进行一些项目设置的配置。这个文件一般放置在工程的根目录。譬如: ![这里写图片描述](http://img.blog.csdn.net/20151025171558864) ## <a name="t4"></a>**2-3 Build生命周期** Gradle的构建脚本生命周期具备三大步,如下: ![这里写图片描述](http://img.blog.csdn.net/20151025173643212) 可以看见,生命周期其实和上面构建脚本Build script的执行流程是可以关联上的。有了这个流程图我们接下里详细看下每个过程。 **settings.gradle文件:** 除了构建脚本文件,Gradle还定义了一个约定名称的设置文件(默认为settings.gradle)。该文件在初始化阶段被执行,对于多项目构建必须保证在根目录下有settings.gradle文件,对于单项目构建设置文件是可选的,不过建议还是写上。 如下是单项目构建的一个例子: ``` &lt;span class="hljs-comment">//settings.gradle&lt;/span> &lt;span class="hljs-built_in">println&lt;/span> &lt;span class="hljs-string">'This is executed during the initialization phase.'&lt;/span> ...

2015年12月2日 · 17 分钟 · 天边的星星