博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
android之数据库和Content Provider(二)
阅读量:6733 次
发布时间:2019-06-25

本文共 15169 字,大约阅读时间需要 50 分钟。

hot3.png

创建Content Provider

Content Provider为公布数据提供了一个接口,别的APP使用Content Resolver来使用该接口所提供的数据。

作为4大组件之一,创建一个新的Content Provider需要继承一个抽象类ContentProvider:

public class MyContentProvider extends ContentProvider

 

就像之前描述的数据库的Contract/Helper类,也同样最好是去包含静态数据库常量---尤其是列名等。

你需要重写onCreate方法去初始化底层数据源,同样还有重写query,update,delete,insert和getType这些方法,使Content Resolver可以与数据进行交互。

 

注册Content Provider

像Activity,Service,Content Provider,必须在Mainfest中注册。使用一个provider标签:包含一个name和authorities属性。

authorities属性 去定义Provider的authority的基础URI。Content Provider的authority代表一个数据库。

每个Content Provider的authority必须是唯一的,所以定义基础URI通常以包名作为路径。如:

com.<CompanyName>.provider.<ApplicationName>

例子: 

 

公布你的Content ProviderURI地址

每个Content Provider应该使用公有静态常量 CONTENT_URI ,使其更容易被访问。

如:

public static final Uri CONTENT_URI =    Uri.parse(“content://com.paad.skeletondatabaseprovider/elements”);

 

类似与上述这例子表示请求表中所有的记录,而:

表示某行记录。(接数字)

所有这些形式都支持你去访问你的Provider。这么做最简单方式是去使用UriMatcher,一个非常有用得类,用来解析URI和决定它的格式。

// Create the constants used to differentiate between the different URI // requests. private static final int ALLROWS = 1;  private static final int SINGLE_ROW = 2;private static final UriMatcher uriMatcher;// Populate the UriMatcher object, where a URI ending in //‘elements’ will correspond to a request for all items,  // and ‘elements/[rowID]’ represents a single row. static {     uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);     uriMatcher.addURI(“com.paad.skeletondatabaseprovider”,                                   “elements”, ALLROWS);     uriMatcher.addURI(“com.paad.skeletondatabaseprovider”,                                   “elements/#”, SINGLE_ROW); }

 

你可能使用类似的技术去暴露某个Content Provider更多的URI,这些URI可以代表不同的数据集,或是不同的表。

 

创建Content Provider的数据库

private MySQLiteOpenHelper myOpenHelper;@Override public boolean onCreate() {        // Construct the underlying database.        // Defer opening the database until you need to perform        // a query or transaction.        myOpenHelper = new MySQLiteOpenHelper(getContext(),           MySQLiteOpenHelper.DATABASE_NAME, null,           MySQLiteOpenHelper.DATABASE_VERSION);     return true; }

 

注意:Content Provider的onCreate是在主线程中执行的,数据库一旦打开后,若程序还在运行,没必要马上又把它关掉,这出于效率问题。你可能担心资源问题,其实事实是系统如果真需要额外资源,你的APP会被杀死,然后相关的数据库也会被关。

 

实现Content Provider查询

为了支持Content Provider相关的查询,你必须实现query和getType方法。Content Resolver使用这些方法去获取低沉的数据而无需关心它的细节和实现。

注意:UriMatcher对象通常用来完善事务和查询的请求,SQLite Query Builder更方便作为执行基于行查询的帮手。

例子:实现Content Provider查询的框架代码

@Override public Cursor query(Uri uri, String[] projection, String selection,        String[] selectionArgs, String sortOrder) {       // Open the database.        SQLiteDatabase db;        try {          db = myOpenHelper.getWritableDatabase();        } catch (SQLiteException ex) {          db = myOpenHelper.getReadableDatabase();        }       // Replace these with valid SQL statements if necessary.        String groupBy = null;        String having = null;       // Use an SQLite Query Builder to simplify constructing the        // database query.        SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();       // If this is a row query, limit the result set to the passed in row.        switch (uriMatcher.match(uri)) {          case SINGLE_ROW :             String rowID = uri.getPathSegments().get(1);             queryBuilder.appendWhere(KEY_ID + “=” + rowID);          default: break;        }       // Specify the table on which to perform the query. This can        // be a specific table or a join as required.        queryBuilder.setTables(MySQLiteOpenHelper.DATABASE_TABLE);       // Execute the query.        Cursor cursor = queryBuilder.query(db, projection, selection,             selectionArgs, groupBy, having, sortOrder);       // Return the result Cursor.        return cursor; }

 

实现查询后,你必须还要指定一个MIME类型去鉴定返回的数据。重写getType方法,返回一个唯一描述你数据的字符串。

此类型返回必须包含2种形式,其一为单个条目,另外一个为所有的条目:

Single item:

vnd.android.cursor.item/vnd.<companyname>.<contenttype>

All items:

vnd.android.cursor.dir/vnd.<companyname>.<contenttype>

 

例子:

@Override public String getType(Uri uri) {               // Return a string that identifies the MIME type               // for a Content Provider URI               switch (uriMatcher.match(uri)) {                  case ALLROWS:                    return “vnd.android.cursor.dir/vnd.paad.elemental”;                  case SINGLE_ROW:                    return “vnd.android.cursor.item/vnd.paad.elemental”;                  default:                    throw new IllegalArgumentException(“Unsupported URI: “ +                                                                  uri);               } }

 

Content Provider事务

像query方法,当然还有delete,insert,update方法,实现后,由Content Resolver调用,从而其它APP就可以使用。

当执行修改数据集的事务,比较好的方式是去调用Content Resolver的notifyChange方法。这个方法会通知所有内容观察者。

怎么用? 直接看一些框架代码:

@Override public int delete(Uri uri, String selection, String[] selectionArgs) {     // Open a read / write database to support the transaction.     SQLiteDatabase db = myOpenHelper.getWritableDatabase();    // If this is a row URI, limit the deletion to the specified row.     switch (uriMatcher.match(uri)) {       case SINGLE_ROW :         String rowID = uri.getPathSegments().get(1);         selection = KEY_ID + “=” + rowID              + (!TextUtils.isEmpty(selection) ?              “ AND (“ + selection + ‘)’ : “”);       default: break;     }    // To return the number of deleted items you must specify a where     // clause. To delete all rows and return a value pass in “1”.     if (selection == null)       selection = “1”;    // Perform the deletion.     int deleteCount = db.delete(MySQLiteOpenHelper.DATABASE_TABLE,       selection, selectionArgs);    // Notify any observers of the change in the data set.     getContext().getContentResolver().notifyChange(uri, null);    // Return the number of deleted items.     return deleteCount; }@Override public Uri insert(Uri uri, ContentValues values) {     // Open a read / write database to support the transaction.     SQLiteDatabase db = myOpenHelper.getWritableDatabase();    // To add empty rows to your database by passing in an empty     // Content Values object you must use the null column hack     // parameter to specify the name of the column that can be     // set to null.     String nullColumnHack = null;    // Insert the values into the table     long id = db.insert(MySQLiteOpenHelper.DATABASE_TABLE,         nullColumnHack, values);    // Construct and return the URI of the newly inserted row.     if (id > -1) {        // Construct and return the URI of the newly inserted row.        Uri insertedId = ContentUris.withAppendedId(CONTENT_URI, id);       // Notify any observers of the change in the data set.         getContext().getContentResolver().notifyChange(insertedId, null);       return insertedId;      }     else        return null; }@Override public int update(Uri uri, ContentValues values, String selection,             String[] selectionArgs) {         // Open a read / write database to support the transaction.           SQLiteDatabase db = myOpenHelper.getWritableDatabase();// If this is a row URI, limit the deletion to the specified row.  switch (uriMatcher.match(uri)) {         case SINGLE_ROW :               String rowID = uri.getPathSegments().get(1);                selection = KEY_ID + “=” + rowID                 + (!TextUtils.isEmpty(selection) ?                  “ AND (“ + selection + ‘)’ : “”);               default: break; }// Perform the update.  int updateCount = db.update(MySQLiteOpenHelper.DATABASE_TABLE,  values, selection, selectionArgs);// Notify any observers of the change in the data set.  getContext().getContentResolver().notifyChange(uri, null);        return updateCount;  }

 

Tip:ContentUris类包含一个withAppendedId方法,此方法很方便得帮助构建一个附加指定行ID的Uri。

 

Content Provider 存储文件

以前提到过,数据库里保存文件,通常建议保存的是路径->一个合适的Uri.

通常表中文件类型的数据列名取名_data形式。

重写openFile方法

@Override public ParcelFileDescriptor openFile(Uri uri, String mode)   throws FileNotFoundException {  // Find the row ID and use it as a filename.   String rowID = uri.getPathSegments().get(1);  // Create a file object in the application’s external   // files directory.   String picsDir = Environment.DIRECTORY_PICTURES;   File file =      new File(getContext().getExternalFilesDir(picsDir), rowID);  // If the file doesn’t exist, create it now.   if (!file.exists()) {      try {        file.createNewFile();      } catch (IOException e) {        Log.d(TAG, “File creation failed: “ + e.getMessage());      }   }  // Translate the mode parameter to the corresponding Parcel File   // Descriptor open mode.   int fileMode = 0;   if (mode.contains(“w”))      fileMode |= ParcelFileDescriptor.MODE_WRITE_ONLY;   if (mode.contains(“r”))      fileMode |= ParcelFileDescriptor.MODE_READ_ONLY;   if (mode.contains(“+”))      fileMode |= ParcelFileDescriptor.MODE_APPEND;  // Return a Parcel File Descriptor that represents the file.   return ParcelFileDescriptor.open(file, fileMode); }

 

完整的Content Provider 实现框架代码:

public class MyContentProvider extends ContentProvider {             public static final Uri CONTENT_URI =                Uri.parse(“content://com.paad.skeletondatabaseprovider/elements”);             // Create the constants used to differentiate between              // the different URI requests.              private static final int ALLROWS = 1;              private static final int SINGLE_ROW = 2;             private static final UriMatcher uriMatcher;             // Populate the UriMatcher object, where a URI ending              // in ‘elements’ will correspond to a request for all              // items, and ‘elements/[rowID]’ represents a single row.              static {               uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);               uriMatcher.addURI(“com.paad.skeletondatabaseprovider”,                 “elements”, ALLROWS);               uriMatcher.addURI(“com.paad.skeletondatabaseprovider”,                 “elements/#”, SINGLE_ROW);              }// The index (key) column name for use in where clauses. public static final String KEY_ID = “_id”;// The name and column index of each column in your database. // These should be descriptive. public static final String KEY_COLUMN_1_NAME = “KEY_COLUMN_1_NAME”; // TODO: Create public field for each column in your table.// SQLite Open Helper variable private MySQLiteOpenHelper myOpenHelper;@Override public boolean onCreate() {   // Construct the underlying database.   // Defer opening the database until you need to perform   // a query or transaction.   myOpenHelper = new MySQLiteOpenHelper(getContext(),        MySQLiteOpenHelper.DATABASE_NAME, null,        MySQLiteOpenHelper.DATABASE_VERSION);  return true; }@Override public Cursor query(Uri uri, String[] projection, String selection,      String[] selectionArgs, String sortOrder) {   // Open the database.   SQLiteDatabase db = myOpenHelper.getWritableDatabase();  // Replace these with valid SQL statements if necessary.   String groupBy = null;   String having = null;  SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();   queryBuilder.setTables(MySQLiteOpenHelper.DATABASE_TABLE);  // If this is a row query, limit the result set to the   // passed in row.   switch (uriMatcher.match(uri)) {      case SINGLE_ROW :        String rowID = uri.getPathSegments().get(1);        queryBuilder.appendWhere(KEY_ID + “=” + rowID);      default: break;   }  Cursor cursor = queryBuilder.query(db, projection, selection,        selectionArgs, groupBy, having, sortOrder);  return cursor; }@Override public int delete(Uri uri, String selection, String[] selectionArgs) {           // Open a read / write database to support the transaction.           SQLiteDatabase db = myOpenHelper.getWritableDatabase();        // If this is a row URI, limit the deletion to the specified row.          switch (uriMatcher.match(uri)) {               case SINGLE_ROW :                   String rowID = uri.getPathSegments().get(1);                    selection = KEY_ID + “=” + rowID                          + (!TextUtils.isEmpty(selection) ?                            “ AND (“ + selection + ‘)’ : “”);                  default: break;          }        // To return the number of deleted items, you must specify a where         // clause. To delete all rows and return a value, pass in “1”.        if (selection == null)            selection = “1”;      // Execute the deletion.       int deleteCount = db.delete(MySQLiteOpenHelper.DATABASE_TABLE,              selection, selectionArgs);     // Notify any observers of the change in the data set.       getContext().getContentResolver().notifyChange(uri, null);      return deleteCount; }@Override public Uri insert(Uri uri, ContentValues values) {                // Open a read / write database to support the transaction.                SQLiteDatabase db = myOpenHelper.getWritableDatabase();               // To add empty rows to your database by passing in an empty                // Content Values object, you must use the null column hack                // parameter to specify the name of the column that can be                // set to null.                String nullColumnHack = null;               // Insert the values into the table                long id = db.insert(MySQLiteOpenHelper.DATABASE_TABLE,                     nullColumnHack, values);               if (id > -1) {                  // Construct and return the URI of the newly inserted row.                  Uri insertedId = ContentUris.withAppendedId(CONTENT_URI, id);                 // Notify any observers of the change in the data set.                  getContext().getContentResolver().notifyChange(insertedId, null);  return insertedId;      }      else        return null;   }  @Override   public int update(Uri uri, ContentValues values, String selection,      String[] selectionArgs) {     // Open a read / write database to support the transaction.      SQLiteDatabase db = myOpenHelper.getWritableDatabase();     // If this is a row URI, limit the deletion to the specified row.      switch (uriMatcher.match(uri)) {        case SINGLE_ROW :          String rowID = uri.getPathSegments().get(1);          selection = KEY_ID + “=” + rowID               + (!TextUtils.isEmpty(selection) ?                  “ AND (“ + selection + ‘)’ : “”);        default: break;      }     // Perform the update.      int updateCount = db.update(MySQLiteOpenHelper.DATABASE_TABLE,        values, selection, selectionArgs);     // Notify any observers of the change in the data set.      getContext().getContentResolver().notifyChange(uri, null);     return updateCount;   }  @Override   public String getType(Uri uri) {      // Return a string that identifies the MIME type      // for a Content Provider URI      switch (uriMatcher.match(uri)) {        case ALLROWS:          return “vnd.android.cursor.dir/vnd.paad.elemental”;        case SINGLE_ROW:          return “vnd.android.cursor.item/vnd.paad.elemental”;        default:          throw new IllegalArgumentException(“Unsupported URI: “ + uri);      }   }  private static class MySQLiteOpenHelper extends SQLiteOpenHelper {      // [ ... SQLite Open Helper Implementation ... ]   }}

 

转载于:https://my.oschina.net/wangjunhe/blog/110230

你可能感兴趣的文章
AIX 5L学习总结2
查看>>
linux shell 查看进程的可执行程序路径
查看>>
今天我终于搞明白的HSRP 的操作命令和原理
查看>>
资料集合
查看>>
cookie注入&中转注入笔记
查看>>
Linux常用命令大全-toolfk程序员在线工具网
查看>>
springmvc+mybatis+sql server实现简单登录功能【转】
查看>>
Linux禁Ping的方法
查看>>
ssh不断线的配置方法
查看>>
CentOS后台运行和关闭、查看后台任务命令
查看>>
Mysql学习总结(11)——MySql存储过程与函数
查看>>
ALTER EXTSEQNO must be performed on each corresponding downstream reader
查看>>
生产环境linux服务器系统安全配置
查看>>
我的友情链接
查看>>
MySql中 delimiter 详解
查看>>
浏览器history操作实现一些功能
查看>>
你那么喜欢看”干货“,是因为你根本不想下功夫。
查看>>
Introduction to ASP.NET MVC 4
查看>>
20172303 2017-2018-2 《程序设计与数据结构》结对编程项目-四则运算 第二周
查看>>
程序员需要具备哪些素质
查看>>