Android开发笔记之:深入理解Cursor相关的性能问题

当数据库中存有大量数据的时候,用Cursor查询时要注意,有可能引发性能问题。数据库查询出来的Cursor都会由一个CursorWindow来进行数据管理,包括内存空间的申请和数据的填充。CursorWindow对Cursor中的内容大小有限制,限制为1024*1024也就是1M,换句话说Cursor中数据的大小不能超过1M,如果超过1M会引发如下的错误:


代码如下:

08-23 05:48:31.838: DEBUG/Cursor(1805): skip_rows row 149
08-23 05:48:31.844: ERROR/CursorWindow(1805): need to grow: mSize = 1048576, size = 11499, freeSpace() = 7397, numRows = 80
08-23 05:48:31.844: ERROR/CursorWindow(1805): not growing since there are already 80 row(s), max size 1048576
08-23 05:48:31.844: ERROR/Cursor(1805): Failed allocating 11499 bytes for blob at 228,7
08-23 05:48:31.849: DEBUG/Cursor(1805): finish_program_and_get_row_count row 12
08-23 05:48:31.851: DEBUG/browser/BrowserBookmarkFoldersAdapter(1805): getView()-Position:149
08-23 05:48:32.019: DEBUG/Cursor(1805): skip_rows row 148

这表明CursorWindow中的空闲空间已经不足,已经在申请新的空间,但似乎申请失败。这个错误有时候会导致查询得到的Cursor为null,有时候不会引发太严重的问题。但是它会引起性能上的问题,不停的申请空间会占用大量的CPU时间,从而导致整个手机变卡。特别是在ListView或GridView中绑定的Cursor,会导致无法滑动,或者滑动变的十分的卡。用Android2.3的原生Browser,打开其中的历史记录,当有超过200条历史记录时,不停的滑动,特别是由下向上滑时会变的十分的卡,而对于其书签,如果条目超过100,且每个都有缩略图时,滑动会变得特别的卡,甚至都打不开,就是这个原因。
这个问题没有根本的解法,这是Android系统的限制,唯一可行的就是想办法避免,也就是尽可能让Cursor的大小 小于1M,以下是可行的方法:
1. 只查询需要的字段
这个特别重要,根据UI显示的需要,或者实际的需要查询需要的字段。就是一定要给ContentResolver.query(uri, projection)第二个参数PROJECTION,如果这个参数为null,那么就会查询表中所有的字段,那么当条数一增加Cursor的大小 会增长很快。Browser中历史记录的原因就是它在query的时候查询了所有的字段,其数所库中保存了favicon和thumbnail二进制文件,因此当包含了这二个字段时,Cursor的容量很容易就达到了限制。
2. 二进制文件不要存在数据库中
数据库仅适用于保存一些较短文字,整数,布尔,浮点数等一些,易于查询和操作的轻量级的数据,目的也是在于快速搜索和查询。对于像图片,较长的文字(如文章)等大数据,最好直接以文件形式存储在硬盘中,然后在数据库保存它们的访问路径。对于像favicon这样的小图片也可以考虑存在数据库中,但是像对于thumbnail的图片就不明智,除非整个应用在数量上有限制(比如只有几十或百级)否则很容易在查询的时候达到1M的限制。
3. 对于特别大量的数据超过5000级或万级或十万级或百万级的要分段查询
无论表中的一条记录数据量如何的小,当条数达到5000级或者万级或者更多的时候,还是会达到1M的限制,这时就需要分段查询,比如每次查询500个,或者1000个。另外,如果是要做展示用,这么多数据一下子出来,用户也不方便查看。
【实例】Android2.3书签,历史记录和最多访问三个页面当数据量达到300左右时,就会出现滑动很卡的现象,特别是由下向上滑动时,特别的卡,会狂打出:


代码如下:

08-23 05:48:31.844: ERROR/CursorWindow(1805): need to grow: mSize = 1048576, size = 11499, freeSpace() = 7397, numRows = 80
08-23 05:48:31.844: ERROR/CursorWindow(1805): not growing since there are already 80 row(s), max size 1048576
08-23 05:48:31.844: ERROR/Cursor(1805): Failed allocating 11499 bytes for blob at 228,7

这样的LOG。而书签似乎都没有办法打开和滑动,其特别的卡。
究其原因就是它们在查询的时候都用了同一个字段集Browser.HISTORY_PROJECTION这个是把bookmarks表中的所有字段都 查询出来。书签,历史记录和最多访问虽是三个不同的展示页,但它们的数据是相同的都是来自bookmarks表。Bookmarks表中存有_id,title,url,bookmark,favicon,touch_icon,thumbnail等字段,其中favicon和thumbnail是二进制图片数据(byte[])。Browser.HISTORY_PROJECTION里面包含了所有的字段,当然也包含了favicon和thumbnail,所以当条目一旦达到200多时,Cursor就会达到其1M的限制,因此会导致性能下降,滑动变卡。

事实上对于历史记录和最多访问二个页面来讲thumbnail和touch_icon根本就没有用到,它只需要_id,title,url,bookmark和favicon;对于书签页,也仅是在GRID时才用到thumbnail。所以,只需要把查询时的字段集Browser.HISTORY_PROJECTION中的THUMBNAIL去掉,即可以解决滑动变卡。

(0)

相关推荐

  • Android开发框架之自定义ZXing二维码扫描界面并解决取景框拉伸问题

    先给大家展示下效果图: 扫描内容是下面这张,二维码是用zxing库生成的 由于改了好几个类,还是去年的事都忘得差不多了,所以只能上这个类的代码了,主要就是改了这个CaptureActivity.java package com.zxing.activity; import java.io.IOException; import java.util.Vector; import android.app.Activity; import android.content.Intent; import

  • Android拍照保存在系统相册不显示的问题解决方法

    可能大家都知道我们保存相册到Android手机的时候,然后去打开系统图库找不到我们想要的那张图片,那是因为我们插入的图片还没有更新的缘故,先讲解下插入系统图库的方法吧,很简单,一句代码就能实现 复制代码 代码如下: MediaStore.Images.Media.insertImage(getContentResolver(), mBitmap, "", ""); 通过上面的那句代码就能插入到系统图库,这时候有一个问题,就是我们不能指定插入照片的名字,而是系统给了我

  • Android Studio的中文乱码问题解决方法

    Android Studio安装后发现所有的中文,不管是界面上的还是输出的log中的中文都变成小框框  可以肯定是字体的问题 解决:菜单File->settings对话框,切换到Appearance标签  选择override default fonts by, 先随便选择一个中文字体(PS:小框框都是中文字 呵呵),保存,重启软件,即可.

  • Android动态添加View的问题解决方法

    后台代码 复制代码 代码如下: private void ChangeView()    {        ly.removeAllViews();        LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);        View layout = inflater.inflate(R.layout.grid,null);        GridView gridview

  • Android中fragment嵌套fragment问题解决方法

    都说fragment好用,duang~~,又遇到问题了,记录一下,分享给遇到这个问题的同学! 1.fragment嵌套fragment时出现getActivity()为null activity A嵌套fragment B,B嵌套fragment C,C跳转到activity D,当activity D被finish掉之后,C中容易爆出getActivity为空.如果你的activity被回收了,那你需要在bundle中保存一下fragment信息,我的解决方法:fragment实例化之后会到a

  • Android程序启动时出现黑屏问题的解决方法

    本文实例讲述了Android程序启动时出现黑屏问题的解决方法.分享给大家供大家参考,具体如下: 关于黑屏: 默认的情况下,程序启动时,会有一个黑屏的时期,原因是,首个activity会加载一些数据,比如初始化列表数据.向服务器发送请求获取数据等等. 去除方法: 1.在style里面添加一个style: <style name="ContentOverlay"parent="@android:style/Theme.Light"> <itemname

  • Fedora14下android开发: eclipse与ibus确有冲突的问题分析

    今天笔记本上开发再次遇到这个问题,经过确认eclipse indigo版本和ibus1.3.11确实有冲突.具体表现是eclipse里按ctrl+空格,输入法死活不出来.但ibus在其他地方确用的好好的.解决方法是:参照我之前的<解决Fedora14下eclipse进行android开发,ibus提示没有输入窗口的方法详解>安装fcitx,值得注意的是在"然后点系统--输入法--将fcitx选为默认输入法"这个步骤之后,重启电脑,会自动弹出这个图片: 在ubuntu下暂未发

  • Android开发笔记之:ListView刷新顺序的问题详解

    背景一个典型的ListView,每个Item显示一个TextView,代表一个Task,需要实现二个编辑方式:一个是用CheckBox来标识任务已经完成,另一个要实现的编辑是删除任务.对于完成的CheckBox就直接放在布局中就可,但对于删除不想使用ContextMenu来实现编辑,对于像iOS中那样的列表,它的删除都是通过对列表中每个项目的手势来触发.这个实现起来并不难,可以用一个ViewSwitcher,Checkbox和删除按扭是放入其中,让ViewSwitcher来控制显示哪一个,正常情

  • Android开发中遇到端口号占用问题解决方法

    Android开发的时候经常遇到端口号被占用的问题,经常使程序无法运行,很烦人.我总结了一个很好的方法,非常实用.方法如下: (1):方法1: 第一步:1:netstat -ano | findstr "5037" 第二步:2:TASKLIST | findstr "9292" (2):方法2: 首先进入目录下:E: 复制代码 代码如下: adb kill-server adb start-server 如下图所示:

  • Android开发常见问题总结

    本文总结分析了Android开发常见问题.分享给大家供大家参考,具体如下: 1. 在ui中使用scrollview之后,假如复写activity的onkeydown或onkeyup事件,若果焦点在scrollview上,则这两个事件是不触发的,对于ontouch事件也是一样.解决题目的方法是 scrollview.setOnKeyListener(); 2. mediaplay在使用时,添加的一些事件诸如setOnCompletionListener等,没有起到作用,不执行,原因是由于之前没有设

  • android开发环境遇到adt无法启动的问题分析及解决方法

    开始研究android开发,搭建开发环境的时候就出了问题--果然是好事多磨~ 安装了jdk,配置环境变量,安装了完整版的adt.创建了helloworld程序,启动的时候就报错 "Please ensure that adb is correctly located at 'D:\adt-bundle-windows-x86_64\sdk\platform-tools\adb.exe' and can be executed." 网上查了大量的资料,群里问了很多人,都说是配置环境有问题

  • 在android开发中尽量不要使用中文路径的问题详解

    在开发过程中发现,有些软件对中文路径支持不大好,如果使用Uri.fromFile转换中文路径为uri的时候,有些软件可能会识别不出来导致功能异常,已知的有两个应用:1.腾讯微博的分享功能:2.酷派D530下调用系统摄像头拍照. 如果非要用中文路径,可以采用下面的方式: String path = getCameraTempFilePath(),; //有些系统摄像头对中文路径支持不好,通过Uri.fromFile编码之后反而有问题,先手动加上吧 //                 Uri ur

  • Android 异步获取网络图片并处理导致内存溢出问题解决方法

    测试环境为Adnroid 2.1以上. 1.AndroidManifest.xml 权限配置: 添加互联网访问权限: 复制代码 代码如下: <uses-permission android:name="android.permission.INTERNET" /> 2.异步图片类 ImageDownloadTask 复制代码 代码如下: import java.io.ByteArrayOutputStream; import java.io.IOException; imp

随机推荐