练习内容提供器二创建自己的内容提供器并测试
Posted csschn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了练习内容提供器二创建自己的内容提供器并测试相关的知识,希望对你有一定的参考价值。
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类型主要由三个部分组成:
- 必须以vnd开头
- 如果uri以路径结尾,后接android.cursor.dir/,如果以id结尾,后接android.cursor.item/
- 最后接上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
接下来是在已经建立好的数据库上进行的,
- 添加一个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 }
- 把自定义的内容提供器注册<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>
- 创建测试,新建项目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>
- 然后修改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 }
至此,完成了自定义内容提供器并且实现被其它程序访问;并且经过这次总结,自己对内容提供器有了一个全新的认识;
以上是关于练习内容提供器二创建自己的内容提供器并测试的主要内容,如果未能解决你的问题,请参考以下文章