Android应用程序组件Content Provider应用实例(2)

简介:
   第一点是我们在ArticlesProvider类的内部中定义了一个DBHelper类,它继承于SQLiteOpenHelper类,它用是用辅助我们操作数据库的。使用这个DBHelper类来辅助操作数据库的好处是只有当我们第一次对数据库时行操作时,系统才会执行打开数据库文件的操作。拿我们这个例子来说,只有第三方应用程序第一次调用query、insert、update或者delete函数来操作数据库时,我们才会真正去打开相应的数据库文件。这样在onCreate函数里,就不用执行打开数据库的操作,因为这是一个耗时的操作,而在onCreate函数中,要避免执行这些耗时的操作。
        第二点是设置URI匹配规则。因为我们是根据URI来操作数据库的,不同的URI对应不同的操作,所以我们首先要定义好URI匹配规则,这样,当我们获得一个URI时,就能快速地判断出要如何去操作数据库。设置URI匹配规则的代码如下所示:
  1. private static final UriMatcher uriMatcher;  
  2. static {  
  3.       uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);  
  4.       uriMatcher.addURI(Articles.AUTHORITY, "item", Articles.ITEM);  
  5.       uriMatcher.addURI(Articles.AUTHORITY, "item/#", Articles.ITEM_ID);  
  6.       uriMatcher.addURI(Articles.AUTHORITY, "pos/#", Articles.ITEM_POS);  
  7. }  
        在创建UriMatcher对象uriMatcher时,我们传给构造函数的参数为UriMatcher.NO_MATCH,它表示当uriMatcher不能匹配指定的URI时,就返回代码UriMatcher.NO_MATCH。接下来增加了三个匹配规则,分别是content://shy.luo.providers.articles/item、content://shy.luo.providers.articles/item/#和content://shy.luo.providers.articles/pos/#,它们的匹配码分别是Articles.ITEM、Articles.ITEM_ID和Articles.ITEM_POS,其中,符号#表示匹配任何数字。
        第三点是SQLiteQueryBuilder的使用。在query函数中,我们使用SQLiteQueryBuilder来辅助数据库查询操作,使用这个类的好处是我们可以不把数据库表的字段暴露出来,而是提供别名给第三方应用程序使用,这样就可以把数据库表内部设计隐藏起来,方便后续扩展和维护。列别名到真实列名的映射是由下面这个HashMap成员变量来实现的:
  1. private static final HashMap<String, String> articleProjectionMap;  
  2. static {  
  3.       articleProjectionMap = new HashMap<String, String>();  
  4.       articleProjectionMap.put(Articles.ID, Articles.ID);  
  5.       articleProjectionMap.put(Articles.TITLE, Articles.TITLE);  
  6.       articleProjectionMap.put(Articles.ABSTRACT, Articles.ABSTRACT);  
  7.       articleProjectionMap.put(Articles.URL, Articles.URL);  
  8. }  
       在上面的put函数中,第一个参数表示列的别名,第二个参数表示列的真实名称。在这个例子中,我们把列的别名和和真实名称都设置成一样的。
 
       第四点是数据更新机制的使用。执行insert、update和delete三个函数时,都会导致数据库中的数据发生变化,所以这时候要通过调用ContentResolver接口的notifyChange函数来通知那些注册了监控特定URI的ContentObserver对象,使得它们可以相应地执行一些处理,例如更新数据在界面上的显示。在query函数中,最终返回给调用者的是一个Cursor,调用者获得这个Cursor以后,可以通过它的deleteRow或者commitUpdates来执行一些更新数据库的操作,这时候也要通知那些注册了相应的URI的ContentObserver来作相应的处理,因此,这里在返回Cursor之前,要通过Cursor类的setNotificationUri函数来把当前上下文的ContentResolver对象保存到Curosr里面去,以便当通过这个Cursor来改变数据库中的数据时,可以通知相应的ContentObserver来处理。不过这种用法已经过时了,即不建议通过这个Cursor来改变数据库的数据,要把Cursor中的数据看作是只读数据。这里调用Cursor类的setNotificationUri函数还有另外一个作用,我们注意到它的第二个参数uri,对应的是Cursor中的内容,当把这个uri传给Cursor时,Cursor就会注册自己的ContentObserver来监控这个uri对应的数据的变化。一旦这个uri对应的数据发生变化,这个Cursor对应的数据就不是再新的了,这时候就需要采取一些操作来更新内容了。
         第五点我们实现了ContentProvider的call函数。这个函数是一个未公开的函数,第三方应用程序只有Android源代码环境下开发,才能使用这个函数。设计这个函数的目的是什么呢?我们知道,当我们需要从Content Provider中获得数据时,一般都是要通过调用它的query函数来获得的,而这个函数将数据放在Cursor中来返回给调用者。以前面一篇文章 Android应用程序组件Content Provider简要介绍和学习计划 中,我们提到,Content Provider传给第三方应用程序的数据,是通过匿名共享内存来传输的。当要传输的数据量大的时候,使用匿名共享内存来传输数据是有好处的,它可以减入数据的拷贝,提高传输效率。但是,当要传输的数据量小时,使用匿名共享内存来作为媒介就有点用牛刀来杀鸡的味道了,因为匿名共享内存并不是免费的午餐,系统创建和匿名共享内存也是有开销的。因此,Content Provider提供了call函数来让第三方应用程序来获取一些自定义数据,这些数据一般都比较小,例如,只是传输一个整数,这样就可以用较小的代价来达到相同的数据传输的目的。
        至此,ArticlesProvider的源代码就分析完了,下面我们还要在AndroidManifest.xml文件中配置这个ArticlesProvider类才能正常使用。AndroidManifest.xml文件的内容如下所示:
 
 
  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"   
  2.         package="shy.luo.providers.articles">   
  3.     <application android:process="shy.luo.process.article"   
  4.         android:label="@string/app_label"   
  5.         android:icon="@drawable/app_icon">   
  6.         <provider android:name="ArticlesProvider"   
  7.             android:authorities="shy.luo.providers.articles"   
  8.             android:label="@string/provider_label"   
  9.             android:multiprocess="false">   
  10.         </provider>   
  11.     </application>   
  12.  

 
 
  1. </ manifest >   

        在配置Content Provider的时候,最重要的就是要指定它的authorities属性了,只有配置了这个属性,第三方应用程序才能通过它来找到这个Content Provider。这要需要注意的,这里配置的authorities属性的值是和我们前面在Articles.java文件中定义的AUTHORITY常量的值是一致的。另外一个属性multiprocess是一个布尔值,它表示这个Content Provider是否可以在每个客户进程中创建一个实例,这样做的目的是为了减少进程间通信的开销。这里我们为了减少不必要的内存开销,把属性multiprocess的值设置为false,使得系统只能有一个Content Provider实例存在,它运行在自己的进程中。在这个配置文件里面,我们还可以设置这个Content Provider的访问权限,这里我们为了简单起见,就不设置权限了。有关Content Provider的访问权限的设置,可以参考官方文档 http://developer.android.com/guide/topics/manifest/provider-element.html
 
        这个应用程序使用到的字符串资源定义在res/values/strings.xml文件中,它的内容如下所示:
 
 
  1. <?xml version="1.0" encoding="utf-8"?>   
  2. <resources>   
  3.     <string name="app_label">Articles Storage</string>   
  4.     <string name="provider_label">Articles</string>   
  5.  

 
 
  1. </ resources >   

 
        由于Content Provider类型的应用程序是没有用户界面的,因此,我们不需要在res/layout目录下为程序准备界面配置文件。
 
        程序的编译脚本Android.mk的内容如下所示:
 
 
  1. LOCAL_PATH:= $(call my-dir)   
  2. include $(CLEAR_VARS)   
  3.    
  4. LOCAL_MODULE_TAGS :optional   
  5.    
  6. LOCAL_SRC_FILES := $(call all-subdir-java-files)   
  7.    
  8. LOCAL_PACKAGE_NAME :ArticlesProvider   
  9.    
  10. include $(BUILD_PACKAGE)   
 
     下面我们就可以参照 如何单独编译Android源代码中的模块 一文来编译和打包这个应用程序了:
  1. USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/ArticlesProvider      
  2. USER-NAME@MACHINE-NAME:~/Android$ make snod   
        这样,打包好的Android系统镜像文件system.img就包含我们这里所创建的ArticlesProvider应用程序了。
        前面说过,在Articles.java文件中定义的常量是要给第三方应用程序使用的,那么我们是不是直接把这个源文件交给第三方呢?这样就显得太不专业了,第三方拿到这个文件后,还必须要放在shy/luo/providers/articles目录下或者要把这个Articles类所在的package改掉才能正常使用。正确的做法是把编译好的Articles.java文件打包成一个jar文件交给第三方使用。编译ArticlesProvider这个应用程序成功后,生成的中间文件放在out/target/common/obj/APPS/ArticlesProvider_intermediates目录下,我们进入到这个目录中,然后执后下面的命令把Articles.class文件提取出来:
  1. USER-NAME@MACHINE-NAME:~/Android/out/target/common/obj/APPS/ArticlesProvider_intermediates$ jar -xvf classes.jar shy/luo/providers/articles/Articles.class  
 
        然后再单独打包这个Articles.class文件:
  1. USER-NAME@MACHINE-NAME:~/Android/out/target/common/obj/APPS/ArticlesProvider_intermediates$ jar -cvf ArticlesProvider.jar ./shy  
        这样,我们得到的ArticlesProvider.jar文件就包含了Articles.java这个文件中定义的常量了,第三方拿到这个文件后,就可以开发自己的应用程序来访问我们在ArticlesProvider这个Content Provider中保存的数据了。接下来我们就介绍调用这个ArticlesProvider来获取数据的第三方应用程序Article。        2. Article应用程序的实现
        首先是参照前面的ArticlesProvider工程,在packages/experimental目录下建立工程文件目录Article。这个应用程序的作用是用来管理ArticlesProvider应用程序中保存的文章信息的,因此,它需要获得相应的Content Provider接口来访问ArticlesProvider中的数据。我们首先在工程目录Article下面创建一个libs目录,把上面得到的ArticlesProvider.jar放在libs目录下,后面我们在编译脚本的时候,再把它引用到工程上来。下面我们就开始分析这个应用程序的实现。
        这个应用程序的主界面MainActivity包含了一个ListView控件,用来显示从ArticlesProvider中得到的文章信息条目,在这个主界面上,可以浏览、增加、删除和更新文章信息。当需要增加、删除或者更新文章信息时,就会跳到另外一个界面ArticleActivity中去执行具体的操作。为了方便开发,我们把每一个文章信息条目封装成了一个Article类,并且把与ArticlesProvider进交互的操作都通过ArticlesAdapter类来实现。下面介绍每一个类的具本实现。
        下面是Article类的实现,它实现在src/shy/luo/Article.java文件中:
 
 
  1. package shy.luo.article;   
  2.    
  3. public class Article {   
  4.         private int id;   
  5.         private String title;   
  6.         private String abs;   
  7.         private String url;   
  8.    
  9.         public Article(int id, String title, String abs, String url) {   
  10.                 this.id = id;   
  11.                 this.title = title;   
  12.                 this.abs = abs;   
  13.                 this.url = url;   
  14.         }   
  15.    
  16.         public void setId(int id) {   
  17.                 this.id = id;   
  18.         }   
  19.    
  20.         public int getId() {   
  21.                 return this.id;   
  22.         }   
  23.    
  24.         public void setTitle(String title) {   
  25.                 this.title = title;   
  26.         }   
  27.    
  28.         public String getTitle() {   
  29.                 return this.title;   
  30.         }   
  31.    
  32.         public void setAbstract(String abs) {   
  33.                 this.abs = abs;   
  34.         }   
  35.    
  36.         public String getAbstract() {   
  37.                 return this.abs;   
  38.         }   
  39.    
  40.         public void setUrl(String url) {   
  41.                 this.url = url;   
  42.         }   
  43.    
  44.         public String getUrl() {   
  45.                 return this.url;   
  46.         }   
  47. }   
 




本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966939,如需转载请自行联系原作者
目录
相关文章
|
22天前
|
存储 Android开发 开发者
深入理解安卓应用开发的核心组件
【10月更文挑战第8天】探索Android应用开发的精髓,本文带你了解安卓核心组件的奥秘,包括Activity、Service、BroadcastReceiver和ContentProvider。我们将通过代码示例,揭示这些组件如何协同工作,构建出功能强大且响应迅速的应用程序。无论你是初学者还是资深开发者,这篇文章都将为你提供新的视角和深度知识。
|
25天前
|
数据可视化 Android开发 开发者
安卓应用开发中的自定义View组件
【10月更文挑战第5天】在安卓应用开发中,自定义View组件是提升用户交互体验的利器。本篇将深入探讨如何从零开始创建自定义View,包括设计理念、实现步骤以及性能优化技巧,帮助开发者打造流畅且富有创意的用户界面。
58 0
|
25天前
|
XML 前端开发 Java
安卓应用开发中的自定义View组件
【10月更文挑战第5天】自定义View是安卓应用开发的一块基石,它为开发者提供了无限的可能。通过掌握其原理和实现方法,可以创造出既美观又实用的用户界面。本文将引导你了解自定义View的创建过程,包括绘制技巧、事件处理以及性能优化等关键步骤。
|
1月前
|
测试技术 数据库 Android开发
深入解析Android架构组件——Jetpack的使用与实践
本文旨在探讨谷歌推出的Android架构组件——Jetpack,在现代Android开发中的应用。Jetpack作为一系列库和工具的集合,旨在帮助开发者更轻松地编写出健壮、可维护且性能优异的应用。通过详细解析各个组件如Lifecycle、ViewModel、LiveData等,我们将了解其原理和使用场景,并结合实例展示如何在实际项目中应用这些组件,提升开发效率和应用质量。
38 6
|
2月前
|
存储 开发框架 数据可视化
深入解析Android应用开发中的四大核心组件
本文将探讨Android开发中的四大核心组件——Activity、Service、BroadcastReceiver和ContentProvider。我们将深入了解每个组件的定义、作用、使用方法及它们之间的交互方式,以帮助开发者更好地理解和应用这些组件,提升Android应用开发的能力和效率。
139 5
|
2月前
|
缓存 搜索推荐 Android开发
安卓应用开发中的自定义View组件实践
【9月更文挑战第10天】在安卓开发领域,自定义View是提升用户体验和实现界面个性化的重要手段。本文将通过一个实际案例,展示如何在安卓项目中创建和使用自定义View组件,包括设计思路、实现步骤以及可能遇到的问题和解决方案。文章不仅提供了代码示例,还深入探讨了自定义View的性能优化技巧,旨在帮助开发者更好地掌握这一技能。
|
3月前
|
XML 搜索推荐 Android开发
安卓开发中的自定义View组件实践
【8月更文挑战第30天】探索Android世界,自定义View是提升应用界面的关键。本文以简洁的语言带你了解如何创建自定义View,从基础到高级技巧,一步步打造个性化的UI组件。
|
3天前
|
编解码 Java Android开发
通义灵码:在安卓开发中提升工作效率的真实应用案例
本文介绍了通义灵码在安卓开发中的应用。作为一名97年的聋人开发者,我在2024年Google Gemma竞赛中获得了冠军,拿下了很多项目竞赛奖励,通义灵码成为我的得力助手。文章详细展示了如何安装通义灵码插件,并通过多个实例说明其在适配国际语言、多种分辨率、业务逻辑开发和编程语言转换等方面的应用,显著提高了开发效率和准确性。
|
2天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
12 5
|
1天前
|
缓存 数据库 Android开发
安卓开发中的性能优化技巧
【10月更文挑战第29天】在移动应用的海洋中,性能是船只能否破浪前行的关键。本文将深入探讨安卓开发中的性能优化策略,从代码层面到系统层面,揭示如何让应用运行得更快、更流畅。我们将以实际案例和最佳实践为灯塔,引领开发者避开性能瓶颈的暗礁。
9 3