What is coroutines?
维基百科的定义
维基百科上对协程的定义:
1 | 协程是计算机程序的一类组件,推广了协作式多任务的子程序,允许执行被挂起与被恢复。 |
从线程说起
我们都知道线程是抢占式的,你可以给线程设置优先级,但是 CPU 实际执行的是哪个线程,我们并不能控制,只能通过锁的方式来保证程序的逻辑正确。
协程是协作式的,是编程语言层级的,而非系统层级。当我们切换协程时,不涉及任何系统调用,因此可以把协程看作是轻量的线程。
more >>维基百科上对协程的定义:
1 | 协程是计算机程序的一类组件,推广了协作式多任务的子程序,允许执行被挂起与被恢复。 |
我们都知道线程是抢占式的,你可以给线程设置优先级,但是 CPU 实际执行的是哪个线程,我们并不能控制,只能通过锁的方式来保证程序的逻辑正确。
协程是协作式的,是编程语言层级的,而非系统层级。当我们切换协程时,不涉及任何系统调用,因此可以把协程看作是轻量的线程。
more >>我们每天都要经历数十次的编译,那么什么是编译呢?简单来说,就是把高级语言转化为机器或虚拟机能识别的低级语言的过程。在 Android 中,就是把我们认识的 Java 或 Kotlin 语言转化为 Android 虚拟机可以执行的 Dalvik 字节码的过程。
more >>我们写的 Kotlin 代码越多,我们越喜欢她!Kotlin 的现代语言特性和 Android KTX 使我们的 Android 代码更加的简洁,清晰和优雅。我们 (@FMuntenescu 和 @objcode)启动了 #31DaysOfKotlin 系列作为分享我们最喜爱的 Kotlin 和 Android KTX 特性的一种方式,希望你和我们一样,越来越喜欢她。
在前 7 天的时间里,我们专注于基础知识。
需要处理代码中的空值?可以使用 elvis 操作符,避免您的 “空情况” (null-erplate)。这只是替换空作为值或者返回事件情况的一个小语法。文档: Elvis operator.
1 | val name: String = person.name ?: “unknown” |
格式化字符串?将 $ 符放在变量名的前面表达字符串中的变量和表达式。使用 ${expression} 求表达式的值。文档: string templates.
1 | val language = “Kotlin” |
很多的 Android App 中都有使用相机拍摄用户头像的功能。大部分开发者都会使用MediaStore.ACTION_IMAGE_CAPTURE
来满足这一需求。这可以节省很多时间,不需要单独开发相机 UI,直接调用系统相机;不需要向系统请求 Camera 权限。正如官方文档里面说的那样,Taking Photos Simply。然而在最近的一次的测试中,我发现并没有那么简单。因为运行了几年的代码竟然发生了 Crash。具体的 log 如下:
java.lang.SecurityException: Permission Denial: starting Intent { act=android.media.action.IMAGE_CAPTURE cat=[android.intent.category.DEFAULT] flg=0x3 cmp=com.google.android.GoogleCamera/com.android.camera.activity.CaptureActivity clip={text/uri-list U:content://com.imzhiqiang.example.fileprovider/imageCache/tmp_avatar.jpg} (has extras) } from ProcessRecord{bf70afd 18107:com.imzhiqiang.example/u0a108} (pid=18107, uid=10108) with revoked permission android.permission.CAMERA
看上去是因为没有处理运行时权限导致的 Crash。Interesting! 我并没有在 manifest 文件中声明 Camera 的权限,为什么会出现没有处理 Camera 运行时权限的问题呢?随后我想到了可能是引用的 library 中声明了该权限。在 Android Studio 中查看了 Merged Manifest,果然是这样。图中深色背景的权限是我自己声明的,下面的权限是第三方的 library 声明的。
然而这和 Intent 又有什么关系?使用 ACTION_IMAGE_CAPTURE 不是可以避免请求 Camera 权限吗?经过几番周折过后,最后终于在官方文档中找到了答案。
虽然很难理解 Google 这样的做法,不过总算找到了问题的根本所在。在对 Camera 权限进行正确的处理后,终于正常运行了。
结论
扩展阅读
本文主要介绍了如何配置和管理 Glide 中的缓存,其中大部分内容都可以直接在官方 Wiki 中找到,这里只是进行了整理和汇总。言归正传,Glide 支持图片的二级缓存(并不是三级缓存,因为从网络加载并不属于缓存),即内存缓存和磁盘缓存。
一般的图片缓存指的就是磁盘缓存,把网络上的图片缓存到本地,这样就不需要每次都从网络加载,既提高了加载速度,又为用户节省了流量。Glide 在默认情况下是开启磁盘缓存的,而且提供了丰富的 API 来让开发者自己配置和管理磁盘缓存。
缓存位置和大小
开发者可以通过构建一个自定义的GlideModule来配置 Glide 磁盘缓存的位置和大小。最简单的方法如下:
1 | public class DiskCacheMoudle implements GlideModule { |
其中 InternalCache 和 ExternalCache 都最多接收 3 个参数:第一个参数为 Context,没啥好说的;第二个为缓存的目录名称;第三个为缓存大小,单位是 Byte。它们之间唯一的不同就在于 InternalCache 构建的缓存是在应用的内部储存,而 ExternalCache 则是在外部储存。内部储存中的缓存文件是其他应用程序是无法获取到的,更加安全。关于内部储存和外部储存的更多内容,请点击这里查看官方文档。
如果不想把缓存放在上面的两个位置怎么办?Glide 当然也支持,具体通过 DiskLruCacheFactory 来实现:
1 | builder.setDiskCache( |
Note: getMyCacheLocationBlockingIO 方法返回的文件不能为空,而且必须是一个已经创建好的文件目录,不可以是文件。
缓存策略
与其他图片加载库的缓存机制不同,Glide 缓存图片时默认只缓存最终加载的那张图片。举个栗子,你要加载的图片分辨率为 1000x1000,但是最终显示该图片的 ImageView 大小只有 500x500,那么 Glide 就会只缓存 500x500 的小图。这也是在从磁盘缓存中加载图片时 Glide 比 Picasso 快的原因。Glide 目前提供了四种缓存策略:
缓存算法
在 Glide 中磁盘缓存默认使用的是 LRU(Least Recently Used)算法。如果你想使用其他的缓存算法,就只能通过实现 DiskCache 接口来完成了。
使用内存缓存可以获得更快的图片加载速度,因为减少了耗时的 IO 操作。众所周知,Bitmap 是 Android 中的内存大户,频繁的创建和回收 Bitmap 必然会引起内存抖动。Glide 中有一个叫做 BitmapPool 的类,可以复用其中的 Bitmap 对象,从而避免 Bitmap 对象的创建,减小内存开销。当配置内存缓存时,我们也应该同时配置 BitmapPool 的大小。具体方法也是通过自定义的 GlideModule 来实现的:
1 | builder.setMemoryCache(new LruResourceCache(yourSizeInBytes)); |
一般情况下,开发者是不需要自己去指定它们的大小的,因为 Glide 已经帮我们做好了。默认的内存缓存和 bitmapPool 的大小由MemorySizeCalculator根据当前设备的屏幕大小和可用内存计算得到。同时 Glide 还支持动态的缓存大小调整,在存在大量图片的 Activity/Fragment 中,开发者可以通过 setMemoryCategory 方法来提高 Glide 的内存缓存大小,从而加快图片的加载速度。
1 | Glide.get(context).setMemoryCategory(MemoryCategory.HIGH); |
MemoryCategory 有 3 个值可供选择:
在有些情况下我们不希望做内存缓存(比如加载 GIF 图片),这个时候可以调用 skipMemoryCache(true)方法跳过内存缓存。
一般情况下我们从网络上获取到的图片 Url 都是静态的,即一张图片对应一个 Url。那么如果是一张图片对应多个 Url 呢?缓存不就没有意义了。因为图片加载库都是拿图片的 Url 来作为缓存的 key 的,Glide 也不例外,只是会更加复杂一些。如果你开启了 Glide 的 log,就会在控制台看到 Glide 是如何指定缓存 key 的。关于如何打开 log,请参考这篇文章。一般来说,Glide 的 key 由图片的 url、view 的宽和高、屏幕的尺寸大小和 signature 组成。
在什么情况下才会出现动态的 Url 呢?一个很典型的例子就是因为图片的安全问题在原来图片的 Url 后面加上访问凭证。访问凭证与时间关联,这样一来,在不同时间同一图片的 Url 就会不同,缓存就会失效。以七牛的私有空间为例,我们来看看如何去缓存这类图片。从七牛关于私有空间的文档中可以得到:最终的 Url = 原 Url + ?e=过期时间 + token=下载凭证。那么就只需要在 Glide 缓存时将 Url 中“?”后面的字符串截去就可以了。
首先新建一个叫做 QiNiuImage 的类:
1 | public class QiNiuImage { |
其中 getImageUrl 方法返回真实的 Url,getImageId 方法返回未添加下载凭证前的 Url。
然后再自定义一个实现ModelLoader接口的 QiNiuImageLoader:
1 | public class QiNiuImageLoader implements StreamModelLoader<QiNiuImage> { |
其中 HttpUrlFetcher 的 getId 方法就是组成缓存的 key 的重要部分。这也是我们的核心原理。
将这个 ModelLoader 注册到 GlideModule 中,并在 AndroidManifest.xml 中注册:
1 | public class QiNiuModule implements GlideModule { |
1 | <meta-data |
最后只需要在加载此类图片时,使用下面这段代码就可以了。即使图片的 token 更换了也不会重新从网络上下载而是直接读取本地缓存。
1 | Glide.with(context) |
参考资料:
与 iOS 中强大的 AVFoundation 框架相比,Android framework 中提供的有关多媒体处理的类可谓屈指可数,但总比没有好吧。今天我们就来谈谈这几个类。
这里提到的多媒体处理主要是指音视频处理。包括音视频的裁剪、合并;视频画面的各种变换,旋转、缩放、翻转;视频滤镜;音视频的播放,快速、慢速、倒序播放等等。
主要涉及到的类有:
MediaExtractor 在 Android4.1(API16)加入。可以从一段音视频中提取出一帧一帧的数据,与 MediaMuxer 配合使用可以完成视频的裁剪和合并,与 MediaCodec、GLSurfaceView 配合使用可以完成视频的播放。
MediaCodec 在 Android4.1(API16)加入。在 Android4.3(API18)提供输入可以为 Surface。在 Android5.0(API21)又增加了异步处理模式。它是一个低等级的媒体编解码器,可以作为编码器,也可以作为解码器。 可攻可受,嘿嘿嘿。是音视频处理中最为核心的类。
由于相关文档在以前不是很完善,来自 Android 媒体团队的 fadden(现已不在)维护了一个网站 http://bigflake.com/mediacodec/ 。上面有大量的相关资源。此外在 stackoverflow 上面只要是 MeidaCodec 相关的问题随处可见 fadden 的身影。感谢 fadden。现在官方文档已经相当详细了,不过都是英文的,对于阅读困难的人,国内也有人进行了翻译。地址在[这里](http://www.cnblogs.com/xiaoshubao/archive/2016/04/11/5368183.html) 。
MediaMuxer 在 Android4.3(API18)。可以合成 MP4 格式的视频,输入源通常为从 MediaExtractor 或者 MediaCodec 提供的已编码的数据。
MediaMetadataRetriever 主要用来获取视频的方向信息。在合成视频时,可纠正视频方向。此外还可以获取视频某一帧画面的 bitmap,前提必须是 android 支持的视频格式。
MediaFormat 内部持有一个包含音视频帧信息的 map。
GLSurfaceView 可与 MediaPlayer 配合,完成视频变换的各种效果实时预览。
OpenGL 是一门单独的技术,然而关于其在 Android 中使用的文档和列子实在太少。唯一的一本书《OpenGL ES 应用开发实践指南 Android 卷》在网上也买不到,只能去淘宝买复印版。。。
与其他图片加载库不同,在 Glide 加载图片的过程中默认是没有任何 log 输出的。这样使得加载失败的原因难以调试。到底是网络错误还是图片根本就不存在亦或者解码出错,我们不得而知。当然官方也给出了调试的方法,这篇文章就来介绍下如何调试 Glide 加载图片,内容主要是对官方 wiki 的翻译。
more >>当 android support v4 包和 v7 包版本不一样时,v4 包中的 SwipeRefreshLayout 和 v7 包中的 RecyclerView 不能很好地一起工作,会导致 SwipeRefreshLayout 下拉刷新时动画卡住的情况,类似下面的情况:
详情请参考:
http://stackoverflow.com/questions/33032036/swiperefreshlayout-freezes-on-api-4-2-2。
more >>tag:
缺失模块。
1、请确保node版本大于6.2
2、在博客根目录(注意不是yilia根目录)执行以下命令:
npm i hexo-generator-json-content --save
3、在根目录_config.yml里添加配置:
jsonContent: meta: false pages: false posts: title: true date: true path: true text: false raw: false content: false slug: false updated: false comments: false link: false permalink: false excerpt: false categories: false tags: true