android之数据库和Content Provider(二)

建立Content Provider

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

做为4大组件之一,建立一个新的Content Provider须要继承一个抽象类ContentProvider: android

public class MyContentProvider extends ContentProvider 数据库

 

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

你须要重写onCreate方法去初始化底层数据源,一样还有重写query,update,delete,insert和getType这些方法,使Content Resolver能够与数据进行交互。 框架

 

注册Content Provider

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

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

每一个Content Provider的authority必须是惟一的,因此定义基础URI一般以包名做为路径。如:
com.<CompanyName>.provider.<ApplicationName>
this

例子: 
.net

<provider android:name=”.MyContentProvider” 
            android:authorities=”com.paad.skeletondatabaseprovider”/>
 

公布你的Content ProviderURI地址

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

如:

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

 

相似与上述这例子表示请求表中全部的记录,而:

content://com.paad.skeletondatabaseprovider/elements/5
表示某行记录。(接数字)

全部这些形式都支持你去访问你的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 ... ] 
  }

}
相关文章
相关标签/搜索