【新技能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中有重复的类,当用到这个重复的类的时候,系统会选择哪个类进行加载呢? 让我们来看看类加载的代码: `<strong>public</strong> Class findClass(String name, List<Throwable> suppressed) { <strong>for</strong> (Element element : dexElements) { //每个Element就是一个dex文件 DexFile dex = element.dexFile; <strong>if</strong> (dex != <strong>null</strong>) { Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed); <strong>if </strong>(clazz != <strong>null</strong>) { <strong> return </strong>clazz; } } } <strong> if </strong>(dexElementsSuppressedExceptions != <strong>null</strong>) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } <strong> return null</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 分钟 · 天边的星星

37个最好的学习新东西的网站(译)

{.title} 忘掉那些在学校或者课堂上学习反而收获甚微的方式吧。这些网站或者APP涵盖科学、艺术和技术。它们会教你一些特别的东东,像用node.js构建APP, 而且大部分是免费的。这不会强制你掌握一个新技能,却能扩展你的知识,甚至促进你的职业。你可以在你喜欢的地方学习或者是你自己的舒服的家里。真的不能再简单了。你还等什么呢? 1、在线课程 2、学习编程 [Codecademy](http://www.codecademy.com/)-交互式学习编码,免费 Stuk.io-零基础学习编程 Udacity-获取可被业界承认的技能 Platzi-在线学习设计、市场推广、编码 Learnable-最好的方式学习web开发 Code Scool-亲自动手学习编程 Thinkful-一对一的辅导 Code.org-根据指南现在就开始学习 BaseRails-掌握Ruby on Rails和其他web技术 Treehouse-学习HTML,CSS ,iPhone apps和更多 One Month-一个月内学习编程并构建web应用 Dash-学习制作酷炫的网站 3、和数据打交道 [DataCamp](https://www.datacamp.com/)-R语言的指南和数据课程 DataQuest-浏览器里学习数据科学 DataMonkey-简单有趣的方式开发你的分析技能 4、学习新的语言 [Duolingo](https://www.duolingo.com/)-免费学习新语言 Lingvist-200小时内学习一门新的语言 Busuu-免费的语言学习社区 Memrise-用识字卡来学习词汇 5、扩展你的知识 [TED-Ed](http://ed.ted.com/)-找到辅助的教育视频 Khan Academy-可交互的非常全面的图书馆 Guides.co-最大的在线指南的搜索 Squareknot-漂亮的指引,一步步的指南 Learnist-学习更专业的内容通过web、纸质、视频 Prismatic-学习社会推荐的一些有趣的内容 6、其它红利 [Chesscademy](http://www.chesscademy.com/)-免费学习国际象棋 Pianu-新的方式学习钢琴,可交互 Yousician-数字时代下你的个人吉他教程 [阅读原文](https://medium.com/life-learning/the-37-best-websites-to-learn-something-new-895e2cb0cad4#.fdh3dqi1q)

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

Android 手势检测实战 打造支持缩放平移的图片预览效果(下)

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39480503,本文出自:【张鸿洋的博客】 上一篇已经带大家实现了自由的放大缩小图片,简单介绍了下Matrix;具体请参考:Android 手势检测实战 打造支持缩放平移的图片预览效果(上);本篇继续完善我们的ImageView~~ 首先加入放大后的移动~~ 1、自由的进行移动 我们在onTouchEvent里面,加上移动的代码,当然了,必须长或宽大于屏幕才可以移动~~~ **[java]** [view plain](http://blog.csdn.net/lmj623565791/article/details/39480503#)[copy](http://blog.csdn.net/lmj623565791/article/details/39480503#) <div> <embed id="ZeroClipboardMovie_1" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_1"> </embed> </div> </div> - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">boolean</span> onTouch(View v, MotionEvent event) - { - mScaleGestureDetector.onTouchEvent(event); - - <span class="keyword">float</span> x = <span class="number"></span>, y = <span class="number"></span>; - <span class="comment">// 拿到触摸点的个数</span> - <span class="keyword">final</span> <span class="keyword">int</span> pointerCount = event.getPointerCount(); - <span class="comment">// 得到多个触摸点的x与y均值</span> - <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number"></span>; i < pointerCount; i++) - { - x += event.getX(i); - y += event.getY(i); - } - x = x / pointerCount; - y = y / pointerCount; - - <span class="comment">/**</span> - <span class="comment"> * 每当触摸点发生变化时,重置mLasX , mLastY </span> - <span class="comment"> */</span> - <span class="keyword">if</span> (pointerCount != lastPointerCount) - { - isCanDrag = <span class="keyword">false</span>; - mLastX = x; - mLastY = y; - } - - - lastPointerCount = pointerCount; - - <span class="keyword">switch</span> (event.getAction()) - { - <span class="keyword">case</span> MotionEvent.ACTION_MOVE: - Log.e(TAG, <span class="string">&#8220;ACTION_MOVE&#8221;</span>); - <span class="keyword">float</span> dx = x &#8211; mLastX; - <span class="keyword">float</span> dy = y &#8211; mLastY; - - <span class="keyword">if</span> (!isCanDrag) - { - isCanDrag = isCanDrag(dx, dy); - } - <span class="keyword">if</span> (isCanDrag) - { - RectF rectF = getMatrixRectF(); - <span class="keyword">if</span> (getDrawable() != <span class="keyword">null</span>) - { - isCheckLeftAndRight = isCheckTopAndBottom = <span class="keyword">true</span>; - <span class="comment">// 如果宽度小于屏幕宽度,则禁止左右移动</span> - <span class="keyword">if</span> (rectF.width() < getWidth()) - { - dx = <span class="number"></span>; - isCheckLeftAndRight = <span class="keyword">false</span>; - } - <span class="comment">// 如果高度小雨屏幕高度,则禁止上下移动</span> - <span class="keyword">if</span> (rectF.height() < getHeight()) - { - dy = <span class="number"></span>; - isCheckTopAndBottom = <span class="keyword">false</span>; - } - mScaleMatrix.postTranslate(dx, dy); - checkMatrixBounds(); - setImageMatrix(mScaleMatrix); - } - } - mLastX = x; - mLastY = y; - <span class="keyword">break</span>; - - <span class="keyword">case</span> MotionEvent.ACTION_UP: - <span class="keyword">case</span> MotionEvent.ACTION_CANCEL: - Log.e(TAG, <span class="string">&#8220;ACTION_UP&#8221;</span>); - lastPointerCount = <span class="number"></span>; - <span class="keyword">break</span>; - } - - <span class="keyword">return</span> <span class="keyword">true</span>; - } 首先我们拿到触摸点的数量,然后求出多个触摸点的平均值,设置给我们的mLastX , mLastY , 然后在移动的时候,得到dx ,dy 进行范围检查以后,调用mScaleMatrix.postTranslate进行设置偏移量,当然了,设置完成以后,还需要再次校验一下,不能把图片移动的与屏幕边界出现白边,校验完成后,调用setImageMatrix. ...

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

Android 6.0 运行时权限处理

运行时权限介绍 Android 6.0在我们原有的AndroidManifest.xml声明权限的基础上,又新增了运行时权限动态检测,以下权限都需要在运行时判断: 身体传感器 日历 摄像头 通讯录 地理位置 麦克风 电话 短信 存储空间 运行时权限处理 Android6.0系统默认为targetSdkVersion小于23的应用默认授予了所申请的所有权限,所以如果你以前的APP设置的targetSdkVersion低于23,在运行时也不会崩溃,但这也只是一个临时的救急策略,用户还是可以在设置中取消授予的权限。 声明目标SDK版本 我们需要在build.gradle中声明targetSdkVersion为23 android { compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig { applicationId "com.yourcomany.app minSdkVersion 18 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } 检查并申请权限 我们需要在用到权限的地方,每次都检查是否APP已经拥有权限,比如我们有一个下载功能,需要写SD卡的权限,我们在写入之前检查是否有WRITE_EXTERNAL_STORAGE权限,没有则申请权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { //申请WRITE_EXTERNAL_STORAGE权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_EXTERNAL_STORAGE_REQUEST_CODE); } 请求权限后,系统会弹出请求权限的Dialog ...

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

分享给Linux用户的10个有用工具

引言 在本教程中,我已经收集了10个给 Linux 用户的有用工具,其中包括各种网络监控,系统审计和一些其它实用的命令,它可以帮助用户提高工作效率。我希望你会喜欢他们。 1. w 显示谁登录了系统并执行了哪些程序。 $ w 不显示头部信息(LCTT译注:原文此处有误) w -h 显示指定用户的信息 w 2. nmon Nmon(nigel’s monitor 的简写)是一个显示系统性能信息的工具。 sudo apt-get install nmon nmon nmon 可以显示与 netwrok,cpu, memory 和磁盘使用情况的信息。 nmon 显示 cpu 信息 (按 c) nmon 显示 network 信息 (按 n) nman 显示 disk 信息 (按 d) 3. ncdu 是一个支持光标的du程序,这个命令是用来分析各种目录占用的磁盘空间。 apt-get install ncdu ncdu / 最终的输出: 按 n 则通过文件名来排序,按 s 则按文件大小来排序(默认的)。 4. slurm ...

2015年11月26日 · 2 分钟 · 天边的星星

美团Android DEX自动拆包及动态加载简介

概述 作为一个android开发者,在开发应用时,随着业务规模发展到一定程度,不断地加入新功能、添加新的类库,代码在急剧的膨胀,相应的apk包的大小也急剧增加, 那么终有一天,你会不幸遇到这个错误: 生成的apk在android 2.3或之前的机器上无法安装,提示INSTALL_FAILED_DEXOPT 方法数量过多,编译时出错,提示: Conversion to Dalvik format failed:Unable to execute dex: method ID not in [0, 0xffff]: 65536 而问题产生的具体原因如下: 无法安装(Android 2.3 INSTALL_FAILED_DEXOPT)问题,是由dexopt的LinearAlloc限制引起的,在Android版本不同分别经历了4M/5M/8M/16M限制,目前主流4.2.x系统上可能都已到16M, 在Gingerbread或者以下系统LinearAllocHdr分配空间只有5M大小的, 高于Gingerbread的系统提升到了8M。Dalvik linearAlloc是一个固定大小的缓冲区。在应用的安装过程中,系统会运行一个名为dexopt的程序为该应用在当前机型中运行做准备。dexopt使用LinearAlloc来存储应用的方法信息。Android 2.2和2.3的缓冲区只有5MB,Android 4.x提高到了8MB或16MB。当方法数量过多导致超出缓冲区大小时,会造成dexopt崩溃。 超过最大方法数限制的问题,是由于DEX文件格式限制,一个DEX文件中method个数采用使用原生类型short来索引文件中的方法,也就是4个字节共计最多表达65536个method,field/class的个数也均有此限制。对于DEX文件,则是将工程所需全部class文件合并且压缩到一个DEX文件期间,也就是Android打包的DEX过程中, 单个DEX文件可被引用的方法总数(自己开发的代码以及所引用的Android框架、类库的代码)被限制为65536; 插件化? MultiDex? 解决这个问题,一般有下面几种方案,一种方案是加大Proguard的力度来减小DEX的大小和方法数,但这是治标不治本的方案,随着业务代码的添加,方法数终究会到达这个限制,一种比较流行的方案是插件化方案,另外一种是采用google提供的MultiDex方案,以及google在推出MultiDex之前Android Developers博客介绍的通过自定义类加载过程, 再就是Facebook推出的为Android应用开发的Dalvik补丁, 但facebook博客里写的不是很详细;我们在插件化方案上也做了探索和尝试,发现部署插件化方案,首先需要梳理和修改各个业务线的代码,使之解耦,改动的面和量比较巨大,通过一定的探讨和分析,我们认为对我们目前来说采用MultiDex方案更靠谱一些,这样我们可以快速和简洁的对代码进行拆分,同时代码改动也在可以接受的范围内; 这样我们采用了google提供的MultiDex方式进行了开发。 插件化方案在业内有不同的实现原理,这里不再一一列举,这里只列举下Google为构建超过65K方法数的应用提供官方支持的方案:MultiDex。 首先使用Android SDK Manager升级到最新的Android SDK Build Tools和Android Support Library。然后进行以下两步操作: 1.修改Gradle配置文件,启用MultiDex并包含MultiDex支持: android { compileSdkVersion 21 buildToolsVersion "21.1.0" defaultConfig { ... minSdkVersion 14 targetSdkVersion 21 ... // Enabling MultiDex support. MultiDexEnabled true } ... } dependencies { compile 'com.android.support:MultiDex:1.0.0' } 2.让应用支持多DEX文件。在官方文档中描述了三种可选方法: ...

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