如何使用ContentResolver

如何使用ContentResolver,第1张

如果想公开自己的数据,那么可有两种办法:

1创建自己的ContentProvider,需要继承ContentProvider类

2如果你的数据和已存在的ContentProvider数据结构一致,可以将数据写到已存在的ContentProvider中

当然前提是获取写该ContentProvider的权限比如把OA中的成员通讯信息加入到系统的联系人ContentProvider中

ContentProvider基础

所有ContentProvider都需要实现相同的接口,用于查询ContentProvider并返回数据也包括增加、修改和删除数据

步骤:

1获得一个ContentResolver的实例,可通过Activity的成员方法getContentResovler()方法:

ContentResolver cr = thisgetContentResolver();

ContentResolver实例带的方法可实现找到指定的ContentProvider并获取到ContentProvider的数据

ContentResolver的查询过程开始,Android系统将确定查询所需的具体ContentProvider,确认它是否启动并运行它

android系统负责初始化所有的ContentProvider,不需要用户自己去创建实际上,ContentProvider的用户都不可能直接访问到ContentProvider实例,只能通过ContentResolver在中间代理

2数据模型

ContentProvider展示数据类似一个单个数据库

其中:

每行有个带唯一值的数字字段,名为_ID,可用于对表中指定记录的定位

ContentProvider返回的数据结构,是类似JDBC的ResultSet,在android中,是Cursor对象

URI,每个ContentProvider定义一个唯一的公开的URI,用于指定到它的数据集

一个ContentProvider可以包含多个数据集(可以看作多张表),这样,就需要有多个URI与每个数据集对应

这些URI要以这样的格式开头:

content://

表示这个URI指定一个ContentProvider

如果你想创建自己的ContentProvider,最好把自定义的URI设置为类的常量,这样简化别人的调用,并且以后如果更新URI也很容易

android定义了CONTENT_URI常量用于URI,如:androidproviderContactsPhonesCONTENT_URI

2查询ContentProvider

要想使用一个ContentProvider,需要以下信息:

定义这个ContentProvider的URI,返回结果的字段名称,这些字段的数据类型

如果需要查询ContentProvider数据集的特定记录(行),还需要知道该记录的ID的值

构建查询

查询就是输入URI等参数,其中URI是必须的,其他是可选的,如果系统能找到URI对应的ContentProvider将返回一个Cursor对象

可以通过ContentResolverquery()或者ActivitymanagedQuery()方法

两者的方法参数完全一样,查询过程和返回值也是相同的

区别是,通过ActivitymanagedQuery()方法,不但获取到Cursor对象,而且能够管理Cursor对象的生命周期

比如当Activity暂停(pause)的时候,卸载该Cursor对象,当Activity Restart的时候重新查询另外,也可以对一个没有处于Activity管理的Cursor对象做成被Activity管理的,通过调用ActivitystartManaginCursor()方法

类似这样:

Cursor cur = managedQuery(myPerson,null,null,null,null);

其中第一个参数myPerson是Uri类型实例

如果需要查询的是指定行的记录,需要用_ID值,比如ID值为23,URI将是类似:

content:///23

android提供了方便的方法,让开发者不需要自己拼接上面这样的URI,比如类似:

Uri myPerson = ContentUriswithAppendedId(PeopleCONTENT_URI,23);

或者:

Uri myPerson = UriwithAppendedPath(PeopleCONTENT_URI,"23");

二者的区别是一个接收整数类型的ID值,一个接收字符串类型

其他几个参数:

names,可以为null,表示取数据集的全部列,或者声明一个String数组,数组中存放列名称,比如:People_ID一般列名都在该ContentProvider中有常量对应;

针对返回结果的过滤器,格式类似于SQL中的WHERE子句,区别是不带WHERE关键字,如果返回null表示不过滤,比如name=;

前面过滤器的参数,是String数组,是针对前面条件中占位符的值;

排序参数,类似SQL的ORDER BY字句,不过不需要写ORDER BY部分,比如name desc,如果不排序,可输入null

返回值是Cursor对象,游标位置在第一条记录之前

下面实例适用于android 20及以上版本,从android通讯录中得到姓名字段:

java代码:

Cursor cursor = getContentResolver()query(ContactsContractCommonDataKindsPhoneCONTENT_URI,null,null,null,null);

读取返回的数据

如果在查询的时候使用到ID,那么返回的数据只有一条记录在其他情况下,一般会有多条记录和JDBC的ResultSet类似,需要 *** 作游标遍历结果集,在每行,再通过列名获取到列的值,可以通过getString()、getInt()、getFloat()等方法获取值

比如类似下面:

java代码:

while(cursormoveToNext()) {

builderappend(cursorgetString(cursorgetColumnIndex(ContactsContractCommonDataKindsPhoneDISPLAY_NAME)))append("-");

}

和JDBC中不同,没有直接通过列名获取列值的方法,只能先列名获取到列的整型索引值,然后再通过该索引值定位获取列的值

编辑数据

可以通过ContentProvider实现以下编辑功能:

增加新的记录:

在已经存在的记录中增加新的值、批量更新已经存在的多个记录、删除记录

所有的编辑功能都是通过ContentResolver的方法实现一些ContentProvider对权限要求更严格一些,需要写的权限,如果没有会报错

增加记录

要想增加记录到ContentProvider,首先,要在ContentValues对象中设置类似map的键值对,在这里,键的值对应ContentProvider中的列的名字,键值对的值,是对应列希望的类型

然后,调用ContentResolverinsert()方法,传入这个ContentValues对象,和对应ContentProvider的URI即可返回值是这个新记录的URI对象这样你可以通过这个URI获得包含这条记录的Cursor对象

比如:

java代码:

ContentValues values = new ContentValues();

valuesput(PeopleNAME,"Abraham Lincoln");

Uri uri = getContentResolver()insert(PeopleCONTENT_URI, values);

在原有记录上增加值

如果记录已经存在,可在记录上增加新的值,或者编辑已经存在的值

首先要找到原来的值对象,然后要清除原有的值,然后像上面增加记录一样即可:

java代码:

Uri uri = UriwithAppendedPath(PeopleCONTENT_URI, "23");

Uri phoneUri = UriwithAppendedPath(uri, PeoplePhonesCONTENT_DIRECTORY);

valuesclear();

valuesput(PeoplePhonesTYPE, PeoplePhonesTYPE_MOBILE);

valuesput(PeoplePhonesNUMBER, "1233214567");

getContentResolver()insert(phoneUri, values);

批量更新值

批量更新一组记录的值,比如NY改名为Eew York可调用ContentResolverupdate()方法

删除记录

如果是删除单个记录,调用ContentResolverdelete()方法,URI参数,指定到具体行即可

如果是删除多个记录,调用ContentResolverdelete()方法,URI参数指定Contentprovider即可,并带一个类似SQL的WHERE子句条件这里和上面类似,不带WHERE关键字

创建自己的ContentProvider

创建contentprovider,需要设置存储系统大多数ContentProvider使用文件或者SQLite数据库,不过你可以用任何方式存储数据android提供SQLiteOpenHelper帮助开发者创建和管理SQLiteDatabase

继承ContentProvider,提供对数据的访问在manifest文件中声明ContentProvider继承ContentProvider类

必须定义ContentProvider类的子类,需要实现如下方法:

java代码:

query()

insert()

update()

delete()

getType()

onCreate()

在实现子类的时候,还有一些步骤可以简化ContentProvider客户端的使用:

定义public static final Uri常量,名称为CONTENT_URI:

java代码:

public static final Uri CONTENT_URI = Uriparse("content://comexamplecodelabtransportationprovider");

如果有多个表,它们也是使用相同的CONTENT_URI,只是它们的路径部分不同

声明ContentProvider

创建ContentProvider后,需要在manifest文件中声明,android系统才能知道它,当其他应用需要调用该ContentProvider时才能创建或者调用它

语法类似:

<provider android:name="comeasymorsecpMyContentProvider"

android:authorities="comeasymorsecpmycp">

</provider>

android:name要写ContentProvider继承类的全名

android:authorities要写和CONTENT_URI常量的B部分

转载,仅供参考,祝你愉快,。

前言:事先说明:在实际应用中这种做法设计需要各位读者自己设计,本文只提供一种思想。准备工作:安装后本地数redis服务器,使用mysql数据库,事先插入1000万条数据,可以参考我之前的文章插入数据,这里不再细说。我大概的做法是这样的,编码使用多线程访问我的数据库,在访问数据库前先访问redis缓存没有的话在去查询数据库,需要注意的是redis最大连接数最好设置为300,不然会出现很多报错。

贴一下代码吧

12345678910111213141516171819202122232425package select; import redisclientsjedisJedisPool;import redisclientsjedisJedisPoolConfig; public class SelectFromMysql {       public static void main(String[] args) {            JedisPool pool;                       JedisPoolConfig config = new JedisPoolConfig();//创建redis连接池            // 设置最大连接数,-1无限制            configsetMaxTotal(300);            // 设置最大空闲连接            configsetMaxIdle(100);            // 设置最大阻塞时间,记住是毫秒数milliseconds            configsetMaxWaitMillis(100000);            // 创建连接池            pool = new JedisPool(config, "127001", 6379,200000);          for (int i =9222000; i <=9222200; i++) {//这里自己设置用多少线程并发访问                String teacherName=StringvalueOf(i);                new ThreadToMysql(teacherName, "123456",pool)start();                              }     }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081package select; import javasqlConnection;import javasqlDriverManager;import javasqlResultSet;import javasqlSQLException;import javasqlStatement; import redisclientsjedisJedis;import redisclientsjedisJedisPool; public class ThreadToMysql extends Thread {    public String teacherName;    public String password;    public JedisPool pool;    public ThreadToMysql(String teacherName, String password,JedisPool pool) {//构造函数传入要查询登录的老师姓名和密码                 thisteacherName=teacherName;        thispassword=password;        thispool=pool;    }         public void run() {         Jedis jedis = poolgetResource();         Long startTime=SystemcurrentTimeMillis();//开始时间        if (jedisget(teacherName)!=null) {             Long entTime=SystemcurrentTimeMillis();//开始时间            Systemoutprintln(currentThread()getName()+" 缓存得到的结果: "+jedisget(teacherName)+" 开始时间:"+startTime+"  结束时间:"+entTime+"  用时:" +(entTime-startTime)+"ms");            poolreturnResource(jedis);            Systemoutprintln("释放该redis连接");        } else {                   String url = "jdbc:mysql://127001/teacher";          String name = "commysqljdbcDriver";          String user = "root";          String password = "123456";         Connection conn = null;         try {            ClassforName(name);            conn = DriverManagergetConnection(url, user, password);//获取连接             connsetAutoCommit(false);//关闭自动提交,不然conncommit()运行到这句会报错        } catch (ClassNotFoundException e1) {            e1printStackTrace();        } catch (SQLException e) {            eprintStackTrace();        }        if (conn!=null) {                        String sql="select t_name from test_teacher where t_name='"+teacherName+"' and t_password='"+password+"' ";//SQL语句            String t_name=null;            try {                Statement stmt=conncreateStatement();                ResultSet rs=stmtexecuteQuery(sql);//获取结果集                if (rsnext()) {                    t_name=rsgetString("t_name");                    jedisset(teacherName, t_name);                    Systemoutprintln("释放该连接");                }                conncommit();                stmtclose();                connclose();            } catch (SQLException e) {                eprintStackTrace();            }finally {                poolreturnResource(jedis);                Systemoutprintln("释放该连接");            }                Long end=SystemcurrentTimeMillis();                Systemoutprintln(currentThread()getName()+"  数据库得到的查询结果:"+t_name+"   开始时间:"+startTime+"  结束时间:"+end+"  用时:"+(end-startTime)+"ms");                                  } else {            Systemoutprintln(currentThread()getName()+"数据库连接失败:");        }                      }    }     }

我的数据库表数据是这样的。可以看到我的t_name是1-10000000,密码固定123456利用循环创建线程很好做传入循环的次数作为查询的t_name就行了

采用redis缓存替换加索引的方案

1在200并发访问下:

第一次访问结果:由于第一次访问缓存不存在该数据,速度很慢

最慢90多秒

运行第二次访问后(redis数据库已存在数据)的结果:

最慢700多毫秒

2当我尝试1000线程并发访问时redis直接挂掉,原因在于reids缓存并没有要查找的数据,就从数据库查找,1000个线程同时并发访问数据库等待时间太长了,造成redis连接等待超时(就算把redis的超时等待时间设置为100分钟也没用,会报redis连接被拒绝的错误)

3当我利用循环事先把100万条数据插入redis缓存服务器后,在1万个线程并发访问测试下只需要5~6秒就拿到了查询结果,效率出奇的快,而且没有报任何错

4在3的条件下我把并发线程提升到100万个时,测试在百万并发条件下查询性能,发现完全没有压力,每个线程也是几毫秒就能查到结果,这个时候限制我速度的就是电脑CPU了。我的测试电脑是4核的,处理100万个线程起来比较慢,下面是截图,运行到50多万个线程的时候我就停止了运行

好了,以上都是数据库查询的字段没有加索引直接利用redis缓存查找的

而且有个弊端,百万级的并发访问需要事先把数据放到缓存中,在实际中并不科学(因为并不知道那些是热点数据),下面来看看如何使用索引加缓存的效果

1给t_name和t_password字段加组合索引

我们来看看在有索引且redis缓存事先没有数据的时候,创建100万个线程并发访问的结果

没问题,这样就完成了百万级别下的并发访问,但是这样我的程序创建线程很慢,因为我的电脑4核CPU的(但是要创建100万个线程),这个时候就是硬件设备的性能了,在设备硬件性能足够的条件下是没问题的

以下是我的总结:

1我的优化方案中只有两种,一种是给查询的字段加组合索引。另一种是给在用户和数据库中增加缓存

2添加索引方案:面对1~2千的并发是没有压力的,在往上则限制的瓶颈就是数据库最大连接数了,在上面中我用show global status like 'Max_used_connections’查看数据库可以知道数据库最大响应连接数是5700多,超过这个数tomcat直接报错连接被拒绝或者连接已经失效

3缓存方案:在上面的测试可以知道,要是我们事先把数据库的千万条数据同步到redis缓存中,瓶颈就是我们的设备硬件性能了,假如我们的主机有几百个核心CPU,就算是千万级的并发下也可以完全无压力,带个用户很好的。

4索引+缓存方案:缓存事先没有要查询的数据,在一万的并发下测试数据库毫无压力,程序先通过查缓存再查数据库大大减轻了数据库的压力,即使缓存不命中在一万的并发下也能正常访问,在10万并发下数据库依然没压力,但是redis服务器设置最大连接数300去处理10万的线程,4核CPU处理不过来,很多redis连接不了。我用show global status like 'Max_used_connections'查看数据库发现最大响应连接数是388,这么低所以数据库是不会挂掉的。

5使用场景:a几百或者2000以下并发直接加上组合索引就可以了。b不想加索引又高并发的情况下可以先事先把数据放到缓存中,硬件设备支持下可解决百万级并发。c加索引且缓存事先没有数据,在硬件设备支持下可解决百万级并发问题。d不加索引且缓存事先没有数据,不可取,要80多秒才能得到结果,用户体验极差。

6原理:其实使用了redis的话为什么数据库不会崩溃是因为redis最大连接数为300,这样数据库最大同时连接数也是300多,所以不会挂掉,至于redis为什么设置为300是因为设置的太高就会报错(连接被拒绝)或者等待超时(就算设置等待超时的时间很长也会报这个错)。

最后说明:本文不代表实际应用开发场景,更多的是提供一种思想,一种解决方案,如有错误,请指正,谢谢

rs用完得关闭呀,rsclose();

while(rsnext())

{

stmtexecuteUpdate("insert into btlbnzc01 (loancode) values ('"+rsgetString(17)+"')");

Systemoutprintln("1");

}

rsclose();

在while循环外加个rsclose();

我已经给你掩饰了 别加错地方

以上就是关于如何使用ContentResolver全部的内容,包括:如何使用ContentResolver、redis怎么加索引、java 提示Operation not allowed after ResultSet closed等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/9539099.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-29
下一篇2023-04-29

发表评论

登录后才能评论

评论列表(0条)

    保存