android快速获取系统中的图片和视频

本文来源于实际项目遇到的需求。如果想要直接看源码(实际项目是java所写,但git上的demo是kotlin所写,毕竟android目标是将kotlin逐步替代java),访问:https://github.com/life2smile/PhotoAlbum.git。切记这只是个demo。

一、需求背景

需要扫描出系统中存在的视频及图片,并展示在宫格视图中,同时图片以其所在文件夹进行分组区分(demo中并未实现,可自行实现)。

二、目标

(1)实现基本的相册预览功能,包括视频及图片。

(2)相册按时间创建顺序就近排序即新创建的在前面展示。

(3)视频预览展示播放时长。

最重要的是:

优化扫描速度、优化扫描速度、优化扫描速度。。。

为什么强调优化扫描速度?文章后面会讲。

三、实现方案

需求的难点在于既要获取视频又要获取图片,图片的预览可以很快获取,但是视频预览相对要耗时些,所以二者存在着天然的时间差,这里采用两个线程任务来分别扫描图片及视频,最后先后合并到一个集合中,进行数据渲染。

所以,这里首先要有一个统一的数据结构。众所周知,android本身已经存储了相册预览的相关数据并通过ContentResolver暴露了查询接口,事实上这些数据有很多的公共性,比如创建时间、路径等,因此这里可以抽象出一个多媒体数据结构 MediaData来进行统一表示。除了这些共有字段外,还需要添加特定多媒体类型下的字段,比如视频的duration、相册的经纬度等,统一于MediaData。

下面逐步讨论各模块实现。

四、界面搭建

界面搭建的实现思路很简单,使用RecyclerView + GridLayoutManager布局即可。需要注意的地方是,我们想要的效果是各个宫格等分居中于屏幕,且大小一致。所以应该首先获取屏幕宽度,基于宫格的列数进行等分,获取到size就是每个宫格的高和宽。当然这个只是常见的默认宫格实现方案,有其他高宽定制需求的,按照自己需求定制即可。

五、图片扫描

首先,抽出一个图片扫描的工具类ImageScanHelper,具体完成的功能会在代码架构中阐述。

//kotlin中的单例写法(再也不用纠结懒加载、多线程下的java写法了)

//这里当然可以改成伴随对象,以实现和java static相匹配的方式。

object ImageScanHelper {

//start为对外暴露的扫描接口,在相册预览的activity中,触发该方法调用

//形如:ImageScanHelper.start(this.getApplicationContext(), handler)。

//第一个参数为context,第二个为handler,目的是拿到扫描数据后通知主线程进行ui更新。

fun start(context:Context, handler:Handler) {

//图片扫描相对比较耗时,这里单独开一个扫描线程

Thread{

            doScan(context,handler)

}.start()

}

private fun doScan(context:Context, handler:Handler) {

//这里完成数据查询,查询结果可通过游标cursor拿到

cursor = context.contentResolver.query(...)

parseData(cursor,handler)

}

private fun parseData(cursor:Cursor, handler:Handler) {

//遍历数据,检出我们需要的数据,并通过加入到imageList中。

do {

imageList.add(MediaData(id,createTime, ...))

}while(cursor.moveToNext())

//通过handler将数据传递给ui主线程进行界面更新

val msg = Message()

msg.obj = imageList//这里的

msg.what =MediaType.MEDIA_TYPE_IMAGE

handler.sendMessage(msg)

}

六、视频扫描

前面说过,这个是个难点,原因在于视频缩略图的获取。android中有多种方案可以获取视频缩略图,如通过MediaMetadataRetriever获取视频第一帧、通过ThumbnailUtils获取第一帧等等。这些方案完全能获取到视频缩略图,but,这些有个很大的弊端,就是这些都是非常耗时的方案,用户从进到预览界面开始,到真正看到视频预览的效果需要很长时间,如果视频数目较小还能接受,反之就慢到令人发指了。所以这些方案实际上并不可取。

那么有没有更快的方案能获取到视频缩略图?当然有,那就是查询系统早就给我们保存好了的视频缩略图信息,这样就大大缩短了获取速度,但是这个方案依然存在弊端,那就是很多机型拿不到最新拍摄的视频缩略图,甚至有的机型除非重新启动手机,才能看到新拍摄的视频缩略图,这显然对用户来说也是不可接受的。

那么还有没有兼容性更好、扫描速度更快的手段获取视频缩略图?

有!那就是结合上述两种方案。具体阐述如下:

(1)查询手机已缓存的缩略图,如果有则保存地址

(2)对于没有缩略图的视频,人工生成缩略图并缓存。然后返回视频缩略图地址

实际上,对于没有缩略图的视频毕竟是少数,所以,上述方案很接近单纯扫描系统数据缓存的时间消耗。

代码结构描述如下:

//功能同图片扫描

fun start(context:Context, handler:Handler) {

Thread{

        doScan(context,handler)

}.start()

}

//功能同图片扫描

private fun doScan(context:Context, handler:Handler) {

//这里先扫描视频数据

cursor = context.contentResolver.query(...)

}

//功能同图片扫描

private fun parseData(context:Context, cursor:Cursor, handler:Handler) {

do {

        try {

//这里会根据拿到的视频数据,触发一次视频缩略图的扫描

thumbCursor = context.contentResolver.query(...)

//获取视频缩略图路径(可能为空),如果有的话直接获取,如果没有则生成缩略图

 thumbNailPath = thumbNailPath.isNullOrEmpty().let {

//这里生成缩略图

generateThumbNail(filePath)

}

//添加扫描出来的视频及其缩略图数据

videoList.add(MediaData(id, createTime, duration, albumName, filePath, thumbNailPath, mimeType,null,null))

}

}while (cursor.moveToNext())

//发送消息至ui线程,携带有扫描的视频数据

val msg:Message =Message.obtain()

msg.obj = videoList

msg.what =MediaType.MEDIA_TYPE_VIDEO

    handler.sendMessage(msg)

}

至此,扫描视频的代码逻辑完成。

七、数据合并

前面提到,图片的扫描速度远远快于视频扫描速度,所以二者存在时间差,但数据最终要合并到一起并渲染。

其实到这里已经很简单了,因为二者有共同的数据结构MediaData,在将一个类型的数据添加到adapter中后,调用notifyDataSetChanged()即可。

八、保证时间有序

这个也很简单,我们只需要在添加数据到adapter的时候对list进行排序即可。

对于java来说,只需要MediaData实现compareTo方法,即可调用Collections.sort进行排序。

对于kotlin来说,调用List.sort{}即可。

九、图片压缩

由于android对运行的应用有内存限制(具体参考我的另一篇博客https://www.jianshu.com/p/a06466971bff),所以在处理图片加载的时候要尤其注意,稍有不慎就有可能oom。常见的第三方图片加载库都有对图片进行过处理,这里由于我们采用的是原生控件,所以需要对图片进行处理。代码如下:

class ImageResizeUtil {
    companion object {
        fun resize(path: String, w: Int, h: Int): Bitmap {//根据传入的宽高进行图片裁剪
            val options = BitmapFactory.Options()
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(path, options)

            //获取缩放比例,主要在decode失败的时候options测量的宽高值是-1,要考虑这种情况进行处理
            options.inSampleSize = Math.max(1, Math.ceil(Math.max(
                    options.outWidth / w, options.outHeight / h
            ).toDouble()).toInt())

            options.inJustDecodeBounds = false
            return BitmapFactory.decodeFile(path, options)
        }
    }
}

十、过滤

前面扫描图片和视频的过程中有可能产生一些脏数据或者不符合我们需要的数据,所以这里要对数据进行过滤。

很简单我们采用过滤器模式即可,首先抽象出一个过滤器接口:

//这里采用了泛型的设计,满足各种数据传入
interface IFilter<T> {
    fun doFilter(t: T)
}

接着可以针对不同的类型实现过滤功能,比如过滤掉不符合大小的图片(这里仅仅列举个例子,具体可以参考git代码):

class ImageSizeFilter : IFilter<MutableList<MediaData>> {
    override fun doFilter(list: MutableList<MediaData>) {
        val iterator = list.iterator()//这里必须要采用迭代器删除,避免遍历的时候有数据改动引起异常
        while (iterator.hasNext()) {
            val mediaData: MediaData = iterator.next()
            val options: BitmapFactory.Options = BitmapFactory.Options()
            BitmapFactory.decodeFile(mediaData.filePath, options)
            if (options.outWidth <= 50 || options.outHeight <= 50) {
                iterator.remove()
            }
        }
    }
}

十一、The End

最后,首尾呼应,源码地址见:https://github.com/life2smile/PhotoAlbum.git。再次强调,代码是基于kotlin写的,如果想要java版的,自己可以参照逻辑实现一遍,或者使用插件转换一下即可。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容简介   本书内容上涵盖了用android开发的大部分场景,从android基础介绍、环境搭建、sdk介绍、market使用,到应用剖析、组件介绍、实例演示等方面。从技术实现上,讲解了5个android平台下的完整综合实例及源代码分析,分别是rss阅读器、基于google map的个人gps、豆瓣网(web 2.0)客户端、在线音乐播放器、手机信息查看助手。本书注重对实际动手能力的指导,在遵循技术研发知识体系的严密性同时,在容易产生错误、不易理解的环节配以了翔实的开发情景截图,并将重要的知识点和开发技巧以“小实验”、“小提醒”、“小知识”、“注意”等的活泼形式呈现给读者。在程序实例的讲解方面,主要将实例安插在android开发的精髓知识章节,这为初学者学习与实践结合提供了很好的指导。.    本书配套有400多分钟的全程开发视频光盘,指导读者快速、无障碍地学通android实战开发技术。..    本书适合具备一定软件开发经验,想快速进入android开发领域的程序员;具备一些手机开发经验的开发者和android开发爱好者学习用书;也适合作为相关培训学校的android培训教材。... 目录 第1章 掀起你的盖头来——初识android. 1 1.1 认识android 1 1.2 android的背景 2 1.2.1 android的历史 2 1.2.2 android的发展 2 1.3 我的android我做主 2 1.3.1 开发基于android平台的应用 3 1.3.2 参加android开发者大赛 3 1.3.3 个人英雄主义再现——得到更多人的认可和尊重 3 1.3.4 获得应有的收益——android market 3 1.4 真实体验——android模拟器 4 1.4.1 模拟器概述 4 1.4.2 模拟器和真机的区别 4 1.4.3 模拟器使用注意事项 4 1.5 更上一层楼——加入android开发社区 5 1.6 本章小结 6 第2章 工欲善其事 必先利其器——搭建android开发环境 7 2.1 开发android应用前的准备 7 2.1.1 android开发系统要求 7 2.1.2 android软件开发包 7 .2.1.3 其他注意事项 8 2.2 windows开发环境搭建 8 2.2.1 jdk、eclipse、android sdk软件安装 8 2.2.2 sdk的家在哪里——设定android sdk home 14 2.2.3 真的准备好了吗——开发环境验证 14 2.2.4 创建android 虚拟设备(avd) 15 2.3 linux一族——ubuntu开发环境搭建 17 2.3.1 java、eclipse和adt插件安装 17 2.3.2 设定android sdk home 23 2.4 mac os一族——苹果开发环境搭建 24 2.5 本章小结 24 第3章 清点可用资本——android sdk介绍 25 3.1 android sdk 基础 25 3.2 深入探寻android sdk的密码 25 3.2.1 android sdk目录结构 25 3.2.2 android.jar及内部结构 27 3.2.3 sdk文档及阅读技巧 27 3.2.4 先来热热身——android sdk例子解析 28 3.2.5 sdk提供的工具介绍 31 3.3 android典型包分析 33 3.3.1 开发的基石——android api核心开发包介绍 33 3.3.2 拓展开发外延——android可选api介绍 34 3.4 本章小结 34 第4章 赚钱的市场——android market及应用发布 35 4.1 google market产生背景与目的 35 4.2 体验“选货”的乐趣——在g1上体验market的使用 35 4.3 android开发活动及特色应用 37 4.3.1 开发应用的领域 37 4.3.2 android market特色应用一览 38 4.4 你也可以做东家——申请market账号 43 4.4.1 卖东西要先入伙——准备工作 43 4.4.2 入伙过程——申请 44 4.5 开张了——在market上发布应用 45 4.5.1 发布时可能遇到的错误 45 4.5.2 卖东西也要签名——生成签名文件 46 4.5.3 打包、签名、发布应用 48 4.6 本章小结 51 第5章 千里之行 始于足下——第一个应用helloworld 52 5.1 helloworld应用分析 52 5.1.1 新建一个andr
Android获取录制视频某张图片的方法如下: 1. 首先,使用MediaRecorder类录制视频。MediaRecorder是一个API,允许我们在Android设备上录制视频。你可以使用它来设置视频的输出格式、编码器、帧率等参数,并调用start()方法开始录制。 2. 录制视频后,你需要在录制视频的过程获取视频的每一帧,并保存为图片。在每一帧保存为图片之前,你需要通过SurfaceHolder类来获取当前录制视频的画面。SurfaceHolder包含一个SurfaceView对象,你可以通过它来获取视频画面并进行操作。 3. 使用MediaMetadataRetriever类提取视频帧。它是一个用于检索和提取媒体文件元数据的类。你可以使用它来获取视频的每一帧,并将其保存为图片。 示例代码如下: ```java MediaMetadataRetriever retriever = new MediaMetadataRetriever(); retriever.setDataSource("录制的视频文件路径"); Bitmap frame = retriever.getFrameAtTime(时间戳, MediaMetadataRetriever.OPTION_PREVIOUS_SYNC); //将frame保存为图片 ``` 上述代码,通过调用getFrameAtTime()方法,我们可以获取给定时间戳的视频帧。你可以根据自己的需求调整时间戳参数,或根据视频的帧率进行计算。 4. 最后,你可以将获取到的视频帧保存为图片,以便你之后进行操作或展示。你可以使用Bitmap类的compress()方法将Bitmap保存为图片文件,或直接在界面上展示这个Bitmap对象。 上述就是在Android获取录制视频某张图片的方法。希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值