【练习】内容提供器二、创建自己的内容提供器并测试

1.什么是ContentProvider

  首先,ContentProvider(内容提供者)是android中的四大组件之一,但是在一般的开发中,可能使用的比较少。 ContentProvider为不同的软件之间数据共享,提供统一的接口。也就是说,如果我们想让其他的应用使用我们自己程序内的数据,就可以使用ContentProvider定义一个对外开放的接口,从而使得其他的应用可以使用咱们应用的文件、数据库内存储的信息。当然,自己开发的应用需要给其他应用共享信息的需求可能比较少见,但是在Android系统中,很多系统自带应用,比如联系人信息,图片库,音频库等应用,为了对其他应用暴露数据,所以就使用了ContentProvider机制。所以,我们还是要学习ContentProvider的基本使用,在遇到获取联系人信息,图片库,音频库等需求的时候,才能更好的实现功能

2.如何定义一个ContentProvider

  Android系统为了让我们更好的对外暴露数据,提供了统一的接口,所以定义了抽象类ContentProvider,因此,如果我们想对外提供数据,我们需要继承ContentProvider,并且实现下面的这几个方法:
onCreate() 当我们的provider初始化时被调用,我们应该在这个方法里面完成部分初始化操作 query() 查询方法,用于给调用者返回数据 insert() 插入操作,用于让外部应用插入数据到内容提供者中 update() 更新操作,用于更新内容提供者的数据 delete() 用于删除数据 getType 返回内容提供者的MIME Type
  上面这些方法,当我们继承自ContentProvider的时候,eclipse会自动的给我们添加,但是这并不代表我们每个方法都需要自定义实现。如果我们只希望给其他应用提供数据,而不允许其他应用修改我们的数据,那么我们只需要实现onCreate(),getType()和query()这三个方法就可以了,其他的三个方法我们可以根据业务需求,实现或者是不实现。

  上面的六个方法几乎每一个方法都会带有Uri这个参数,这个参数也正是调用ContentResolver的调用者传递过来的,所以要对传入的Uri参数进行解析,从而分析出调用者期望访问的表和数据。标准的URI写法如下:

  • content://com.example.app.provider/table1      这个表示的是调用者希望访问的是com.example.app这个应用的table1中的数据,
  • content://com.example.app.provider/table1/1       这个表示的是调用者希望访问的是com.example.app这个应用的table1中的id为1的数据。

  我们还可以使用通配符的方式来匹配这两种格式的内容URI

  • *表示匹配任意长度的任意字符,能匹配任意表的内容的URI格式:         content://com.example.app.provider/*
  • #表示匹配任意长度的数字,能匹配table1中任意一行数据格式:      content://com.example.app.provider/table1/#

需要特别注意的是其中的gettype()方法,他是所有内容提供器都必须提供的一个方法,用于获取uri对象所对应的MIME类型,一个内容uri的MIME类型主要由三个部分组成:

  1. 必须以vnd开头
  2. 如果uri以路径结尾,后接android.cursor.dir/,如果以id结尾,后接android.cursor.item/
  3. 最后接上vnd.<authority>.<path>(authority是在AndroidManifest.xml中注册的时候自定义的)

根据上面的定义

  • 对于content://com.example.app.provider/table1这个内容URI,对应的MIME类型为:vnd.android.cursor.dir/vnd.com.example.app.provider.table1
  • 对于content://com.example.app.provider/table1/1这个内容URI,对应MIME类型为:vnd.android.cursor.item/vnd.com.example.app.provider.table1

接下来是在已经建立好的数据库上进行的,

  1. 添加一个DatabaseProvider类继承ContentProvider,代码如下:

      1 package com.example.databasetest;
      2
      3 import DataBase.MyDataBaseHelper;
      4 import android.content.ContentProvider;
      5 import android.content.ContentValues;
      6 import android.content.UriMatcher;
      7 import android.database.Cursor;
      8 import android.database.sqlite.SQLiteDatabase;
      9 import android.net.Uri;
     10 import android.widget.Switch;
     11
     12 public class DataBaseProvider extends ContentProvider {
     13
     14     public static final int BOOK_DIR = 0 ;//访问BOOK表中所有数据
     15     public static final int BOOK_ITEM = 1 ;//访问BOOK表中单条数据
     16     public static final int CATEGORY_DIR = 2 ;//访问CATEGORY表中所有数据
     17     public static final int CATEGORY_ITEM = 3 ;//访问CATEGORY表中单条数据
     18
     19     public static final String AUTHORITY = "com.example.databasetest.provider";//定义静态常量AUTHORITY,以便后边使用
     20
     21     private static UriMatcher uriMatcher;
     22
     23     private MyDataBaseHelper dbHelper;
     24
     25     //静态代码块:
     26     //对UriMatcher进行初始化操作,将期望匹配的集中URI格式添加了进去
     27     static
     28     {
     29         uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
     30         uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
     31         uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
     32         uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
     33         uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
     34     }
     35
     36     //创建了一个MyDataBaseHelper的实例,然后返回true表示内容提供器初始化成功,这时数据库就完成了创建或者升级操作
     37     @Override
     38     public boolean onCreate() {
     39         dbHelper = new MyDataBaseHelper(getContext(),"BookStore.db",null,2);
     40         return true;
     41     }
     42
     43     @Override
     44     public Cursor query(Uri uri, String[] projection, String selection,
     45             String[] selectionArgs, String sortOrder) {
     46         // 查询数据
     47         SQLiteDatabase db  = dbHelper.getReadableDatabase();        //获取SQLiteDatabase实例
     48         Cursor cursor = null;                                        //定义光标
     49
     50         switch(uriMatcher.match(uri))                                //根据传入的Uri判断用户要访问的是哪张表
     51         {
     52             case BOOK_DIR:
     53                 cursor = db.query("BOOK",projection,selection,selectionArgs,null,null,sortOrder);
     54                 break;
     55             case BOOK_ITEM:
     56
     57                 //uri的getPathSegments方法,它会将uri权限之后的部分以/符号进行分割,并把分割后的结果放入到一个字符串列表
     58                 //那个列表的0位置存放的是路径,1位置存放的是id
     59
     60                 String bookid = uri.getPathSegments().get(1);
     61                 cursor = db.query("BOOK",projection,"id = ?",new String[]{bookid},null,null,sortOrder);
     62                 break;
     63             case CATEGORY_DIR:
     64                 cursor = db.query("CATEGORY",projection,selection,selectionArgs,null,null,sortOrder);
     65                 break;
     66             case CATEGORY_ITEM:
     67                 String categoryid = uri.getPathSegments().get(1);
     68                 cursor = db.query("CATEGORY",projection,"id = ?",new String[]{categoryid},null,null,sortOrder);
     69                 break;
     70         }
     71
     72
     73         return cursor;
     74     }
     75
     76
     77     //根据响应的规则,再根据uri判断返回特定的值以便获取MIME类型
     78     @Override
     79     public String getType(Uri uri) {
     80         // 获取mime类型
     81         switch (uriMatcher.match(uri)) {
     82         case BOOK_DIR:
     83             return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book";
     84         case BOOK_ITEM:
     85             return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.book";
     86         case CATEGORY_DIR:
     87             return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.category";
     88         case CATEGORY_ITEM:
     89             return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.category";
     90         }
     91         return null;
     92     }
     93
     94     @Override
     95     public Uri insert(Uri uri, ContentValues values) {
     96         // 添加数据
     97         SQLiteDatabase db = dbHelper.getWritableDatabase();                //获取SQLiteDatabase实例
     98         Uri uriReturn = null ;                                            //因为insert方法要求返回一个能够表达这条新数据的uri,所以定义uri便于接收后边的uri
     99         switch(uriMatcher.match(uri))
    100         {
    101         case BOOK_DIR:
    102         case BOOK_ITEM:
    103             long newBookId = db.insert("Book", null, values);
    104             uriReturn = Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);//使用Uri的parse方法将内容URI解析成Uri对象赋值给uriReturn
    105             break;
    106         case CATEGORY_DIR:
    107         case CATEGORY_ITEM:
    108             long newCategoryId = db.insert("Category", null, values);
    109             uriReturn = Uri.parse("content://"+AUTHORITY+"/category/"+newCategoryId);
    110             break;
    111         default:
    112             break;
    113         }
    114         return uriReturn;
    115     }
    116
    117     @Override
    118     public int update(Uri uri, ContentValues values, String selection,
    119             String[] selectionArgs) {
    120         //更新数据
    121         SQLiteDatabase db = dbHelper.getWritableDatabase();                //获取SQLiteDatabase实例
    122         int updatedRows = 0;                                            //受影响更新的行数会作为返回值返回,用于接收返回值
    123         switch (uriMatcher.match(uri)) {                                //判断用户想要访问哪张表中的数据
    124         case BOOK_DIR:
    125             updatedRows = db.update("Book", values, selection, selectionArgs);//执行更新操作
    126             break;
    127         case BOOK_ITEM:
    128             String bookId = uri.getPathSegments().get(1);
    129             updatedRows = db.update("Book", values, "id = ?", new String[]{bookId});
    130             break;
    131
    132         case CATEGORY_DIR:
    133             updatedRows = db.update("Category", values, selection, selectionArgs);
    134             break;
    135         case CATEGORY_ITEM:
    136             String categoryId = uri.getPathSegments().get(1);
    137             updatedRows = db.update("Category", values, "id = ?", new String[]{categoryId});
    138             break;
    139
    140         default:
    141             break;
    142         }
    143         return updatedRows;
    144     }
    145
    146     @Override
    147     public int delete(Uri uri, String selection, String[] selectionArgs) {
    148         //删除数据
    149         SQLiteDatabase db = dbHelper.getWritableDatabase();                //获取SQLiteDatabase数据库实例
    150         int deletedRows = 0;                                            //定义为了获取删除行数
    151         switch (uriMatcher.match(uri)) {                                //根据传入的uri判断要访问那张表
    152         case BOOK_DIR:
    153             deletedRows = db.delete("Book",selection, selectionArgs);
    154             break;
    155         case BOOK_ITEM:
    156             String bookId = uri.getPathSegments().get(1);
    157             deletedRows = db.delete("Book", "id = ?", new String[]{bookId});
    158             break;
    159
    160         case CATEGORY_DIR:
    161             deletedRows = db.delete("Category",  selection, selectionArgs);
    162             break;
    163         case CATEGORY_ITEM:
    164             String categoryId = uri.getPathSegments().get(1);
    165             deletedRows = db.delete("Category",  "id = ?", new String[]{categoryId});
    166             break;
    167
    168         default:
    169             break;
    170         }
    171         return deletedRows;
    172     }
    173 }

    DataBaseProvider

  2. 把自定义的内容提供器注册<provider>内的内容表示对内容提供器进行注册,<provider>必须放在应用的<application>下面,<name>属性指定该类的全名;<authorities>指定该内容提供器的权限,<exported>指定是否可以被其他程序访问;

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     3     package="com.example.databasetest"
     4     android:versionCode="1"
     5     android:versionName="1.0" >
     6     <uses-sdk
     7         android:minSdkVersion="14"
     8         android:targetSdkVersion="21" />
     9
    10     <application
    11         android:allowBackup="true"
    12         android:icon="@drawable/ic_launcher"
    13         android:label="@string/app_name"
    14         android:theme="@style/AppTheme" >
    15         <activity
    16             android:name=".MainActivity"
    17             android:label="@string/app_name" >
    18             <intent-filter>
    19                 <action android:name="android.intent.action.MAIN" />
    20
    21                 <category android:name="android.intent.category.LAUNCHER" />
    22             </intent-filter>
    23         </activity>
    24
    25         <provider
    26             android:name="com.example.databasetest.DataBaseProvider"
    27             android:authorities="com.example.databasetest.provider"
    28             android:exported="true">
    29         </provider>
    30
    31     </application>
    32 </manifest> 
  3. 创建测试,新建项目ProviderTest,用于访问刚才的项目首先编写布局文件

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:id="@+id/LinearLayout1"
     4     android:layout_width="match_parent"
     5     android:layout_height="match_parent"
     6     android:orientation="vertical"
     7     android:paddingBottom="@dimen/activity_vertical_margin"
     8     android:paddingLeft="@dimen/activity_horizontal_margin"
     9     android:paddingRight="@dimen/activity_horizontal_margin"
    10     android:paddingTop="@dimen/activity_vertical_margin"
    11     tools:context="com.example.providertest.MainActivity" >
    12
    13     <Button
    14         android:id="@+id/add_data"
    15         android:layout_width="match_parent"
    16         android:layout_height="wrap_content"
    17         android:text="Add To Book" />
    18
    19     <Button
    20         android:id="@+id/query_data"
    21         android:layout_width="match_parent"
    22         android:layout_height="wrap_content"
    23         android:text="Query From Book" />
    24
    25     <Button
    26         android:id="@+id/update_data"
    27         android:layout_width="match_parent"
    28         android:layout_height="wrap_content"
    29         android:text="Update Book" />
    30
    31     <Button
    32         android:id="@+id/delete_data"
    33         android:layout_width="match_parent"
    34         android:layout_height="wrap_content"
    35         android:text="Delete From Book" />
    36
    37 </LinearLayout>

    activity_main.xml

  4. 然后修改MainActivity.java中的代码,完成访问其他程序

      1 package com.example.providertest;
      2
      3 import android.app.Activity;
      4 import android.content.ContentValues;
      5 import android.database.Cursor;
      6 import android.net.Uri;
      7 import android.os.Bundle;
      8 import android.util.Log;
      9 import android.view.Menu;
     10 import android.view.MenuItem;
     11 import android.view.View;
     12 import android.view.View.OnClickListener;
     13 import android.widget.Button;
     14
     15 public class MainActivity extends Activity {
     16
     17     private String newId;//用于存放刚操作数据的id
     18
     19     @Override
     20     protected void onCreate(Bundle savedInstanceState) {
     21         super.onCreate(savedInstanceState);
     22         setContentView(R.layout.activity_main);
     23         Button addData = (Button) findViewById(R.id.add_data);
     24         Button queryData = (Button) findViewById(R.id.query_data);
     25         Button updateData = (Button) findViewById(R.id.update_data);
     26         Button deleteData = (Button) findViewById(R.id.delete_data);
     27         addData.setOnClickListener(new OnClickListener() {
     28
     29             @Override
     30             public void onClick(View v) {
     31                 //添加数据
     32                 Uri uri  = Uri.parse("content://com.example.databasetest.provider/book");//将内容URI解析成Uri对象,
     33
     34                 //把要添加的数据都放到ContentValues的对象values中
     35                 ContentValues values = new ContentValues();
     36                 values.put("name", "A Clash of Kings");
     37                 values.put("author", "George Martin");
     38                 values.put("pages", 1040);
     39                 values.put("price", 55.55);
     40
     41                 //当前Activity的getContentResolver()的insert方法插入数据
     42                 Uri newUri = getContentResolver().insert(uri, values);
     43                 //newUri的getPathSegments().get(1)取到新插入数据的id,
     44                 newId = newUri.getPathSegments().get(1);
     45
     46             }
     47         });
     48
     49         queryData.setOnClickListener(new OnClickListener() {
     50
     51             @Override
     52             public void onClick(View v) {
     53                 //查询数据
     54                 Uri uri = Uri.parse("content://com.example.databasetest.provider/book");//将内容URI解析成Uri对象
     55                 Cursor cursor = getContentResolver().query(uri, null, null, null, null);//调用getContentResolver的query方法把取到的值放在游标Cursor中
     56
     57                 //取出cursor中的数据
     58                 if (cursor!=null) {
     59                     while (cursor.moveToNext()) {
     60                         String name = cursor.getString(cursor.getColumnIndex("name"));
     61                         String author = cursor.getString(cursor.getColumnIndex("author"));
     62                         int pages = cursor.getInt(cursor.getColumnIndex("pages"));
     63                         double price = cursor.getDouble(cursor.getColumnIndex("price"));
     64
     65                         Log.d("MainActivity", "book name is "+ name);
     66                         Log.d("MainActivity", "book author is "+ author);
     67                         Log.d("MainActivity", "book pages is "+ pages);
     68                         Log.d("MainActivity", "book price is "+ price);
     69                     }
     70                 }
     71                 cursor.close();//关闭游标
     72             }
     73         });
     74
     75         updateData.setOnClickListener(new OnClickListener() {
     76
     77             @Override
     78             public void onClick(View v) {
     79                 // 更新数据
     80                 Uri uri = Uri.parse("content://com.example.databasetest.provider/book/"+newId);//将内容URI解析成Uri对象
     81
     82                 //将要更新的内容放到values中
     83                 ContentValues values = new ContentValues();
     84                 values.put("name", "A storm of Swords");
     85                 values.put("pages", 1216);
     86                 values.put("price", 24.05);
     87
     88                 //执行更新操作
     89                 getContentResolver().update(uri, values, null, null);
     90             }
     91         });
     92
     93         deleteData.setOnClickListener(new OnClickListener() {
     94
     95             @Override
     96             public void onClick(View v) {
     97                 // 删除数据
     98                 Uri uri = Uri.parse("content://com.example.databasetest.provider/book/"+newId);//将内容URI解析成Uri对象
     99                 getContentResolver().delete(uri, null, null);//调用getContentResolver()的delete方法删除指定的数据
    100             }
    101         });
    102     }
    103
    104 }

    MainActivity.java

至此,完成了自定义内容提供器并且实现被其它程序访问;并且经过这次总结,自己对内容提供器有了一个全新的认识;

时间: 2024-11-08 23:37:17

【练习】内容提供器二、创建自己的内容提供器并测试的相关文章

其它内容选项----第二章:创建和管理内容

一个弹出式窗口将出现在其中您可以更新文本不留主页 (见图2-8).修改编辑器中的文本,然后单击保存按钮.新版本 您保存后的网页上会自动出现.这是执行快速文本的好方法 触摸起坐,你已经发现了或固定的错误. 图2-8.对网页内容编辑 如果进行一些修改,喜欢全屏幕编辑器界面中,单击铅笔图标(见 图2-7),然后选择编辑选项.这是因为点击任何内容项的标题来查看同一 内容详细页(即内容项目作为一个独立的页面的完整渲染),然后选择编辑选项卡 内容的标题下方,就像我们以前一样. 其它内容选项 在内容编辑表格的

Android学习笔记(二十)——自定义内容提供器

//此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 如果我们想要实现跨程序共享数据的功能,官方推荐的方式就是使用内容提供器,可以通过新建一个类去继承 ContentProvider 的方式来创建一个自己的内容提供器: 一.继承ContentProvider的六个方法: ContentProvider 类中有六个抽象方法,我们需要使用子类去继承它,并重写六个方法,我们先来认识这六个类.新建 MyProvider继承自 ContentProvide,代码如下所示: 1 p

Android内容提供器——创建自己的内容提供器

创建自己的内容提供器很简单,只需要新建一个类继承ContentProvider类,并重写其中的6个抽象方法即可.(需要全部重写) 一 URI的写法 一个标准的内容URI写法有两种: 1.路径结尾,表示访问表中所有内容: content://com.example.app.provider/table1 // * 可以代表任意长度的任意字符,所以也可以写成下面这样 content://com.example.app.provider/* 2.id结尾,表示访问表中特定id的内容: content:

Android4.2.2多媒体架构MediaPlay的创建过程分析(二):解析器的创建

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email: [email protected] 在上一文中,我们分析到setDataSource_pre()函数最终实际返回的是StagefrightPlayer类(class StagefrightPlayer : public MediaPlayerInterface). 1 .继续分析setDataSource 函数: // now set data source setDataSource

搭建rtmp直播流服务之4:videoJS/ckPlayer开源播放器二次开发(播放rtmp、hls直播流及普通视频)

前面几章讲解了使用nginx-rtmp搭建直播流媒体服务器,以及使用ffmpeg推流到nginx-rtmp服务器,java通过命令行调用ffmpeg实现推流服务,后端的事情到这里就已经全部完成了. 本章讲一下播放器的选用和二次开发,前端的播放器虽然有flex(flash)可以用,但是很遗憾flex接触的并不多,学习成本又太高,那么基于第三方开源的flash播放器二次开发就显得很有必要. 一.几种播放器选择 那么播放器,哪些已经不再更新的和收费的,这里不会介绍,只介绍两种轻量级的开源播放器. 1.

IOS设计模式之二(门面模式,装饰器模式)

本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns. 由 @krq_tiger(http://weibo.com/xmuzyq)翻译,如果你发现有什么错误,请与我联系谢谢. 门面(Facade)模式(译者注:facade有些书籍译为门面,有些书籍译为外观,此处译为门面) 门面模式针对复杂的子系统提供了单一的接口,不需要暴漏一些列的类和API给用户,你仅仅暴漏一个简单统一的API. 下面的图解释了这个概念: 这个API的使用者

java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessController的checkPerssiom方法,访问控制器AccessController的栈检查机制又遍历整个 PerssiomCollection来判断具体拥有什么权限一旦发现栈中一个权限不允许的时候抛出异常否则简单的返回,这个过程实际上比我的描述要复杂 得多,这里我只是简单的一句带过,因为这

搭建rtmp直播流服务之4:videojs和ckPlayer开源播放器二次开发(播放rtmp、hls直播流及普通视频)

前面几章讲解了使用 nginx-rtmp搭建直播流媒体服务器; ffmpeg推流到nginx-rtmp服务器; java通过命令行调用ffmpeg实现推流服务; 从数据源获取,到使用ffmpeg推流,再到nginx-rtmp发布流,后端的服务到这里就已经全部完成了. 如果这里的流程没走通,那么这里的播放器也是没办法播放实时流的. 本章讲一下播放器的选用和二次开发,前端的播放器虽然有flex(flash)可以用,但是很遗憾flex接触的并不多,学习成本又太高,那么基于第三方开源的flash播放器二

QT开发(二十一)——QT布局管理器

QT开发(二十一)--QT布局管理器 一.布局管理器简介 QT中使用绝对定位的布局方式无法自适应窗口的变化. QT中提供了对界面组件进行布局管理的类,用于对界面组件进行管理,能够自动排列窗口中的界面组件,窗口大小变化后自动更新界面组件的大小. QLayout是QT中布局管理器的抽象基类,通过对QLayout的继承,实现了功能各异且互补的布局管理器. 布局管理器不是界面组件,而是界面组件的定位策略. 任意容器类型的组件都可以指定布局管理器. 同一个布局管理器管理中的组件拥有相同的父组件,在设置布局