Android About ContentProvider

 Contents:

 

ContentProvider Structural Analysis

一、字段部分

 1 public class NotePadProvider extends ContentProvider {
 2  
 3     private static final String TAG = "NotePadProvider";
 4  
 5     private static final String DATABASE_NAME = "note_pad.db";
 6     private static final int DATABASE_VERSION = 2;
 7     private static final String NOTES_TABLE_NAME = "notes";
 8  
 9     private static HashMap<String, String> sNotesProjectionMap;
10     private static HashMap<String, String> sLiveFolderProjectionMap;
11  
12     private static final int NOTES = 1;
13     private static final int NOTE_ID = 2;
14     private static final int LIVE_FOLDER_NOTES = 3;
15  
16     private static final UriMatcher sUriMatcher;

 5,DATABASE_NAME,表征你要使用的数据库名字,这里是note_pad.db,也就是说会在应用程序的数据文件夹里面生成一个名字为note_pad.db的Sqlite数据库文件。

    8,这个map在以后SQLiteQueryBuilder的setProjectionMap方法中会用到,起到的作用就是将数据库中表的名字进行一个映射。刚开始我不是很懂这一部分的意义,后来看sdk文档中有这样一句话:
    The projection map maps from column names that the caller passes into query to database column names. This is useful for renaming columns as well as disambiguating column names when doing joins. For example you could map "name" to "people.name". If a projection map is set it must contain all column names the user may request, even if the key and value are the same.
    这个map是对用户查询操作传入的column名和数据表中存在的column名进行了一个映射,这样一来,有些数据库中抽象的表的column名就可以通过这个projection map实现重新命名,以达到方便开发者做一些类似于数据库表联接(join)操作的目的。一般情况下数据库中的表的column名就已经够清晰了,所以你可以不用再在projection map里面做一些column重命名的操作了——也就是说,你可以将这个map对象中的key和value都设为同样的值(接下来的代码会有体现)。
    10,这个map和刚才那个同样是一个projection map,不同的是它针对的是Notepad的Live Folder。至于什么是Live Folder,请参看Android sdk文档中的Resources中的Live Folders一文。
    12,13,14是三个常量,它们用于等一下会出现的UriMatcher类的addUri()方法。实际上它们是UriMatcher的match()方法的返回值,用于标记Uri的类型。从名字就可以知道,NOTES标记所有的记载(NOTE)所对应的URI返回值,NOTE_ID对应单个NOTE的URI返回值,LIVE_FOLDER_NOTES代表的是Live Folder中的NOTE对应的URI的返回值。它们三个标记了NoteList应用中基本的几种URI,从而在UriMatcher执行match()方法时,便会返回这些相应的int值。
    16,ContentProvider封装的一个UriMatcher实例。该类用于将特定类型的Uri和一些int值进行映射式绑定,然后以后通过该类判断Uri类型时,返回这个绑定的int值,从而可以由此对Uri的类型判断提供更为有效的手段。
二、静态初始化语句块部分
 1 static {
 2         sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 3         sUriMatcher.addURI(NotePad.AUTHORITY, "notes", NOTES);
 4         sUriMatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID);
 5         sUriMatcher.addURI(NotePad.AUTHORITY, "live_folders/notes", LIVE_FOLDER_NOTES);
 6  
 7         sNotesProjectionMap = new HashMap<String, String>();
 8         sNotesProjectionMap.put(Notes._ID, Notes._ID);
 9         sNotesProjectionMap.put(Notes.TITLE, Notes.TITLE);
10         sNotesProjectionMap.put(Notes.NOTE, Notes.NOTE);
11         sNotesProjectionMap.put(Notes.CREATED_DATE, Notes.CREATED_DATE);
12         sNotesProjectionMap.put(Notes.MODIFIED_DATE, Notes.MODIFIED_DATE);
13  
14         // Support for Live Folders.
15         sLiveFolderProjectionMap = new HashMap<String, String>();
16         sLiveFolderProjectionMap.put(LiveFolders._ID, Notes._ID + " AS " +
17                 LiveFolders._ID);
18         sLiveFolderProjectionMap.put(LiveFolders.NAME, Notes.TITLE + " AS " +
19                 LiveFolders.NAME);
20         // Add more columns here for more robust Live Folders.
21     }
    static语句模块经常被用来初始化类里面的字段部分。这里我们的sUriMatcher、sNotesProjectionMap、sLiveFolderProjectionMap都是一次初始化,以后基本上都无需改变的,所以我们就放到这里来进行静态static语句块的初始化了。
    2-5进行sUriMatcher的初始化工作。需要注意的是NotePad.AUTHORITY是本ContentProvider所对应的Uri(这个Uri是整个Android中唯一标识的,由开发人员在AndroidManifest.xml中定义的)。后面类似notes,notes/#,live_folders/notes等便是在这个Uri上添加的部分,用于对应于全部note,单个note以及live folder中的note。在这里初始化了之后,以后调用sUriMatcher的match()方法时,就会返回对应的NOTES,NOTE_ID,LIVE_FOLDER_NOTES等int值,从而表示该Uri对应的种类。
    7-12对sNotesProjectionMap进行了初始化,这里没有对column进行rename。而15-19行则对sLiveFolderProjectionMap初始化时进行了rename

三,一个SQLiteOpenHelper

 1 /**
 2      * This class helps open, create, and upgrade the database file.
 3      */
 4     private static class DatabaseHelper extends SQLiteOpenHelper {
 5  
 6         DatabaseHelper(Context context) {
 7             super(context, DATABASE_NAME, null, DATABASE_VERSION);
 8         }
 9  
10         @Override
11         public void onCreate(SQLiteDatabase db) {
12             db.execSQL("CREATE TABLE " + NOTES_TABLE_NAME + " ("
13                     + Notes._ID + " INTEGER PRIMARY KEY,"
14                     + Notes.TITLE + " TEXT,"
15                     + Notes.NOTE + " TEXT,"
16                     + Notes.CREATED_DATE + " INTEGER,"
17                     + Notes.MODIFIED_DATE + " INTEGER"
18                     + ");");
19         }
20  
21         @Override
22         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
23             Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
24                     + newVersion + ", which will destroy all old data");
25             db.execSQL("DROP TABLE IF EXISTS notes");
26             onCreate(db);
27         }
28     }
29  
30     private DatabaseHelper mOpenHelper;

   这里实现了一个继承于SQLiteOpenHelper的DatabaseHelper内部类。这个类被封装在这里是因为这个ContentProvider本身是和数据库进行打交道的,所以需要一个SQLiteOpenHelper来处理相关关系。具体该类怎么用自己Google吧。

 四,方法部分
1 @Override
2     public boolean onCreate() {
3         mOpenHelper = new DatabaseHelper(getContext());
4         return true;
5     }

  该方法用于对ContentProvider中的资源进行初始化,可以知道的是,这些处理是不可能在static语句块中实现的,因为会有一些要求,例如这里我们要传入一个Context实例给DataBaseHelper构造器,这在static语句块中是实现不了的。

 1 @Override
 2     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
 3             String sortOrder) {
 4         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
 5         qb.setTables(NOTES_TABLE_NAME);
 6  
 7         switch (sUriMatcher.match(uri)) {
 8         case NOTES:
 9             qb.setProjectionMap(sNotesProjectionMap);
10             break;
11  
12         case NOTE_ID:
13             qb.setProjectionMap(sNotesProjectionMap);
14             qb.appendWhere(Notes._ID + "=" + uri.getPathSegments().get(1));
15             break;
16  
17         case LIVE_FOLDER_NOTES:
18             qb.setProjectionMap(sLiveFolderProjectionMap);
19             break;
20  
21         default:
22             throw new IllegalArgumentException("Unknown URI " + uri);
23         }
24  
25         // If no sort order is specified use the default
26         String orderBy;
27         if (TextUtils.isEmpty(sortOrder)) {
28             orderBy = NotePad.Notes.DEFAULT_SORT_ORDER;
29         } else {
30             orderBy = sortOrder;
31         }
32  
33         // Get the database and run the query
34         SQLiteDatabase db = mOpenHelper.getReadableDatabase();
35         Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
36  
37         // Tell the cursor what uri to watch, so it knows when its source data changes
38         c.setNotificationUri(getContext().getContentResolver(), uri);
39         return c;
40     }
    这个方法是我们的ContentProvider必须实现的,最重要的方法之一。当我们通过ContentProvider进行查询操作时,这个方法就会被回调,而且返回一个保存了查询结果的Cursor对象
    对于参数:
    uri,查询时传入的uri,它代表了我们要查找的数据源。
    projection,查询时传入的一个字符串数组,通过它来表明我们需要数据表中的哪些row。当查询结束后就会返回包含这些row的结果表。
  selection,所选项以及选取限制。
    selectionArgs,所选项的参数值。
    sortOrder,表示返回的结果集的排列顺序的语句。
    方法体内:
    4,声明了一个SQLiteQueryBuilder对象,该对象利用传入的参数生成一个完整的sqlite查询语句,还可以完成查询工作并返回结果集(Cursor)。
    5,为该SQLiteQueryBuilder对象设置了它查询工作将要针对的表。
    7-23通过一个switch语句,完成了通过uri种类的辨识来生成不同的SQLiteQueryBuilder对象的工作。而这里的uri辨识便用到了我们刚开始声明的UriMatcher对象。其中14行为SQLiteQueryBuilder设置了一个where查询条件。9,13,18则分别为不同的uri设置了不同的projection map。
    27-31设置了查询返回结果的顺序。TextUtils.isEmpty(sortOrder)这一句判断传入的sortOrder是否为空字符串(即"")或者null。如果是的话就将orderBy设置为一个定义好的默认的顺序。否则用传入的顺序。
    34,通过我们之前定义的SQLiteDatabase类来获得一个可读不可写的数据库对象。
    35,通过我们刚刚构造的SQLiteQueryBuilder来进行查询操作,并且返回一个我们即将return的Cursor对象。
    38,让Cursor来监视它所属的那个uri。
    39,返回Cursor c,让外界通过它来获得查询结果。
 1 @Override
 2     public String getType(Uri uri) {
 3         switch (sUriMatcher.match(uri)) {
 4         case NOTES:
 5         case LIVE_FOLDER_NOTES:
 6             return Notes.CONTENT_TYPE;
 7  
 8         case NOTE_ID:
 9             return Notes.CONTENT_ITEM_TYPE;
10  
11         default:
12             throw new IllegalArgumentException("Unknown URI " + uri);
13         }
14     }
    这个方法用于返回相关uri对应的MIME类型。这个类型基本上是自己定义的,而且是会用在Activity的启动判别中去的。需要注意的是,这里又用到了我们之前定义的uri判别工具——sUriMatcher 。

关于getType使用提示:

   <intent-filter android:label="@string/resolve_edit">
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.EDIT" />
                <action android:name="com.android.notepad.action.EDIT_NOTE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    </intent-filter>

      我們很容易看出action和category是很容易匹配的,而我們傳的Uri的數據怎麼匹配呢,這時系統就會去調用你定義的ContentProvider中的getType,取得相關的返回值來和上面的data串進行匹配,當然getType的返回結果你是需要自己去定義的。

但在程序中你也可以自己知道data的類型,就直接匹配了:intent.setType(type);

 1 @Override
 2     public Uri insert(Uri uri, ContentValues initialValues) {
 3         // Validate the requested uri
 4         if (sUriMatcher.match(uri) != NOTES) {
 5             throw new IllegalArgumentException("Unknown URI " + uri);
 6         }
 7  
 8         ContentValues values;
 9         if (initialValues != null) {
10             values = new ContentValues(initialValues);
11         } else {
12             values = new ContentValues();
13         }
14  
15         Long now = Long.valueOf(System.currentTimeMillis());
16  
17         // Make sure that the fields are all set
18         if (values.containsKey(NotePad.Notes.CREATED_DATE) == false) {
19             values.put(NotePad.Notes.CREATED_DATE, now);
20         }
21  
22         if (values.containsKey(NotePad.Notes.MODIFIED_DATE) == false) {
23             values.put(NotePad.Notes.MODIFIED_DATE, now);
24         }
25  
26         if (values.containsKey(NotePad.Notes.TITLE) == false) {
27             Resources r = Resources.getSystem();
28             values.put(NotePad.Notes.TITLE, r.getString(android.R.string.untitled));
29         }
30  
31         if (values.containsKey(NotePad.Notes.NOTE) == false) {
32             values.put(NotePad.Notes.NOTE, "");
33         }
34  
35         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
36         long rowId = db.insert(NOTES_TABLE_NAME, Notes.NOTE, values);
37         if (rowId > 0) {
38             Uri noteUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_URI, rowId);
39             getContext().getContentResolver().notifyChange(noteUri, null);
40             return noteUri;
41         }
42  
43         throw new SQLException("Failed to insert row into " + uri);
44     }
    这个方法用于我们的ContentProvider进行insert操作。参数uri为我们即将进行insert操作的数据源。initialValues则是我们传入的要insert到数据库中去的信息。该方法返回我们insert进去的条目所对应的uri。
    4-6进行异常判断,如果传入的uri不是我们想要的则抛出异常。
    8,声明了一个本地的ContentValues,用于接下来的initialValues处理。
    9-13,如果initialValues为null,则本地生成一个ContentValues对象,否则使用initialValues。
    15,通过方法调用获得了一个时间信息,将它保存到Long对象now里面去。now作为时间特性将会保存到数据库中的CREATED_DATE和MODIFIED_DATE项目中去。
    18-33,通过containsKey方法来判断我们的values里面是否包含了某一项条目,有则无动作,没有则通过本地获得信息并且放到values里面去。
    35,获得一个可读可写的数据库对象。
    36,执行insert操作,并且把insert方法返回的我们insert进去的项的_id保存在rowId中。
    37,通过rowId来生成一个即将返回的uri。

 1 @Override
 2     public int delete(Uri uri, String where, String[] whereArgs) {
 3         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
 4         int count;
 5         switch (sUriMatcher.match(uri)) {
 6         case NOTES:
 7             count = db.delete(NOTES_TABLE_NAME, where, whereArgs);
 8             break;
 9  
10         case NOTE_ID:
11             String noteId = uri.getPathSegments().get(1);
12             count = db.delete(NOTES_TABLE_NAME, Notes._ID + "=" + noteId
13                     + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
14             break;
15  
16         default:
17             throw new IllegalArgumentException("Unknown URI " + uri);
18         }
19  
20         getContext().getContentResolver().notifyChange(uri, null);
21         return count;
22     }
23  
24     @Override
25     public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
26         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
27         int count;
28         switch (sUriMatcher.match(uri)) {
29         case NOTES:
30             count = db.update(NOTES_TABLE_NAME, values, where, whereArgs);
31             break;
32  
33         case NOTE_ID:
34             String noteId = uri.getPathSegments().get(1);
35             count = db.update(NOTES_TABLE_NAME, values, Notes._ID + "=" + noteId
36                     + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
37             break;
38  
39         default:
40             throw new IllegalArgumentException("Unknown URI " + uri);
41         }
42  
43         getContext().getContentResolver().notifyChange(uri, null);
44         return count;
45     }
46 }
    delete方法和update方法则针对的是数据库中的删除条目和更新条目的操作看懂之前的代码后这里没有难度,所以就不解释了。
 
四,小结
    在我的理解看来,ContentProvider是对各种数据源进行统一包装并外露的工具——不只是我们这里所用到的典型的数据库式的数据源,其他如文件等数据源也可以通过ContentProvider来包装并实现对其他有需求的消费者的数据供应。
   查询Content Provider的方法有两个:ContentResolver的query() 和 Activity 对象的 managedQuery(),二者接收的参数均相同,返回的都是Cursor 对象,唯一不同的是 使用managedQuery 方法可以让Activity 来管理 Cursor 的生命周期。 
    被管理的Cursor 会在 Activity进入暂停状态的时候调用自己的 deactivate 方法自行卸载,而在Activity回到运行状态时会调用自己的requery 方法重新查询生成的Cursor对象。如果一个未被管理的Cursor对象想被Activity管理,可以调用Activity的 startManagingCursor方法来实现。 

What is URI?

分为A,B,C,D 4个部分:

A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;"content://"

B:URI的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它      必须是一个完整的、小写的 类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包.      类的名称 ;"content://com.android.calendar" (系统日历的URI)

C:路径,URI下的某一个Item,就像网站一样,主网页下包含很多小网页。这里通俗的讲就是你要操作的数据库中表的名      字,或者你也可以自己定义,记得在使用的时候保持一致就ok了;"content://com.android.calendar/calendars"

D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部;                            "content://com.android.calendar/calendars/#" #表示数据id(#代表任意数字)

     "content://com.android.calendar/calendars/*" *来匹配任意文本

 ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分
parseId(uri)方法用于从路径中获取ID部分

 Query Attribute,SQL UNION 

一是:按名字查询;二是:多表查询。

我的理解:其实ContentProvider的机制很随意,它就类似于一个服务器一样,你把uri传来,只要按照特定的方式,它就能给你特定的功能,我觉得这个机制自由又方便。

其实这两个功能主要通过修改query就可以,完整的ContentProvider代码如下:

package com.ianc.lilyprovider;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.util.Log;
public class LilyProvider extends ContentProvider {
    final static String TABLE_NAME = "customer";
    final static String PRODUCT_TABLE = "shoplist";
    private static final String  DATABASE_NAME = "lily.db";
    private static String AUTHORITY = "com.ianc.lilyprovider";
    private static final int DATABASE_VERSION = 1;
    
    DatabaseHelper mDbHelper;
    static UriMatcher sUriMatcher;
    private static final int USER = 1;
    private static final int USER_NAME = 2;
    private static final int MULITABLE = 3;
    private static final int SHOPLIST = 4;
    static{
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(AUTHORITY, TABLE_NAME, USER);
        sUriMatcher.addURI(AUTHORITY, PRODUCT_TABLE, SHOPLIST);
        sUriMatcher.addURI(AUTHORITY, TABLE_NAME+"/"+LilyUser.UserColumns.NAME+"/*", USER_NAME);//search special user by name
        sUriMatcher.addURI(AUTHORITY, TABLE_NAME+"/*", MULITABLE);
    }
    class DatabaseHelper extends SQLiteOpenHelper {
        public DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
        @Override
        public void onCreate(SQLiteDatabase db) {
            Log.i("lily","LilyProvider-->DatabaseHelper-->onCreate");
            db.execSQL("Create table " + TABLE_NAME + "( " +
                    LilyUser.UserColumns._ID+ " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                    LilyUser.UserColumns._COUNT + " INTEGER,"+
                    LilyUser.UserColumns.NAME + " TEXT," +
                    LilyUser.UserColumns.PASSWORD +" TEXT" +
                    ");");
            db.execSQL("Create table " + PRODUCT_TABLE + "( " +
                    LilyUser.ShopListColumns._ID+ " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                    LilyUser.ShopListColumns._COUNT + " INTEGER,"+
                    LilyUser.ShopListColumns.USER_ID+" INTEGER,"+
                    LilyUser.ShopListColumns.PRODUCT + " TEXT," +
                    LilyUser.ShopListColumns.DATE +" TEXT" +
                    ");");
        }
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.i("lily","LilyProvider-->DatabaseHelper-->onUpgrade");
            db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME+";");
            db.execSQL("DROP TABLE IF EXISTS "+PRODUCT_TABLE+";");
            onCreate(db);
        }
        
    }
    @Override
    public int delete(Uri arg0, String arg1, String[] arg2) {
        Log.i("lily","LilyProvider-->delete");
        SQLiteDatabase db = mDbHelper.getWritableDatabase();
        int rownum = db.delete(TABLE_NAME, arg1, arg2);
        return rownum;
    }
    @Override
    public String getType(Uri uri) {
        Log.i("lily","LilyProvider-->getType");
        return null;
    }
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.i("lily","LilyProvider-->insert");
        if (sUriMatcher.match(uri) == USER){
            SQLiteDatabase db = mDbHelper.getWritableDatabase();
            db.insert(TABLE_NAME, null, values);
        }
        else if (sUriMatcher.match(uri) == SHOPLIST){
            SQLiteDatabase db = mDbHelper.getWritableDatabase();
            db.insert(PRODUCT_TABLE, null, values);
        }
    
        return null;
    }
    @Override
    public boolean onCreate() {
        Log.i("lily","LilyProvider-->onCreate");
        mDbHelper = new DatabaseHelper(this.getContext());
        return false;
    }
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        Log.i("lily","LilyProvider-->query");
        Log.i("lily","uri = "+uri);
        Log.i("lily","sUriMatcher.match(uri)="+sUriMatcher.match(uri));
        Cursor c = null;
        if (sUriMatcher.match(uri) == USER){
            SQLiteDatabase db = mDbHelper.getReadableDatabase();
            c = db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
        }else if (sUriMatcher.match(uri) == USER_NAME){
            
            // format:authority/table/column/value-->0:table 1:column 2:value
            String name = uri.getPathSegments().get(2);// get name value
            Log.i("lily","query table:"+uri.getPathSegments().get(0)+" column:"+uri.getPathSegments().get(1)+" value:"+uri.getPathSegments().get(2));
            SQLiteDatabase db = mDbHelper.getReadableDatabase();
            if (selection != null){
                if (selection.length()>0){
                    selection += "AND "+LilyUser.UserColumns.NAME+" like "+name;
                }
            }
            c = db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
        }else if (sUriMatcher.match(uri) == MULITABLE){
            // format:authority/table1/table2
            String table1 = uri.getPathSegments().get(0);
            String table2 = uri.getPathSegments().get(1);
            
            Log.i("lily","table1 = "+table1);
            Log.i("lily","table2 = "+table2);
            
            if (table1.equalsIgnoreCase(TABLE_NAME)){
                if (table2.equals(PRODUCT_TABLE)){
                    SQLiteDatabase db = mDbHelper.getReadableDatabase();
                    if (selection != null){
                        if (selection.length()>0){
                            selection += "AND "+LilyUser.UserColumns._ID+" = "+LilyUser.ShopListColumns.USER_ID;
                        }
                        else{
                            selection = LilyUser.UserColumns._ID+" = "+LilyUser.ShopListColumns.USER_ID;
                        }    
                    }
                    else
                    {
                        selection = TABLE_NAME + "." +LilyUser.UserColumns._ID+" = "+PRODUCT_TABLE + "." + LilyUser.ShopListColumns.USER_ID;
                    }
                    c = db.query(table1+" cross join "+table2, projection, null, selectionArgs, null, null, sortOrder);
                }
            }
        }
    
        return c;
    }
    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        Log.i("lily","LilyProvider-->update");
        SQLiteDatabase db = mDbHelper.getWritableDatabase();
        int rownum = db.update(TABLE_NAME, values, selection, selectionArgs);
        return rownum;
    }
}

这个里面定义Uri格式的时候一定要小心,不然出错了死都查不出来。

下面是使用查询的方法:

private void queryNameUri() {
        Uri uri = Uri.withAppendedPath(LilyUser.User.CONTENT_URI, "name/lily");//content://com.ianc.lilyprovider/customer/name/lily
        String[] projection = {LilyUser.UserColumns._ID, LilyUser.UserColumns.NAME, LilyUser.UserColumns.PASSWORD};
        String selection = null;
        String [] selectionArgs = null;
        String sortOrder = null;
        Cursor cursor = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
        if (cursor == null){
            Log.i("lily","cursor == null");
            return;
        }
        if (cursor.moveToFirst()){
            Log.i("lily","*********************************************");
            String id;
            String name;
            String password;
            do{
                id = cursor.getString(cursor.getColumnIndex(LilyUser.UserColumns._ID));
                Log.i("lily","id = "+id);
                
                name = cursor.getString(cursor.getColumnIndex(LilyUser.UserColumns.NAME));
                Log.i("lily","name = "+name);
                
                password = cursor.getString(cursor.getColumnIndex(LilyUser.UserColumns.PASSWORD));
                Log.i("lily","password = "+password);
                
                Log.i("lily","*********************************************");
            }while(cursor.moveToNext());
        }
        else{
            Log.i("lily","no result");
        }
        cursor.close();
    }

多表查询:

private void queryMultiTable() {
        String[] projection = {"customer."+LilyUser.UserColumns._ID, "customer."+LilyUser.UserColumns.NAME, "customer."+LilyUser.UserColumns.PASSWORD, 
                "shoplist."+LilyUser.ShopListColumns.USER_ID, "shoplist."+LilyUser.ShopListColumns.PRODUCT, "shoplist."+LilyUser.ShopListColumns.DATE};
        String selection = null;
        String [] selectionArgs = null;
        String sortOrder = null;
        Uri uri = Uri.withAppendedPath(LilyUser.User.CONTENT_URI, "shoplist");
        Cursor cursor = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
        if (cursor == null){
            Log.i("lily","queryMultiTable()-->cursor == null");
            return;
        }
        if (cursor.moveToFirst()){
            Log.i("lily","*********************************************");
            String id;
            String name;
            String password;
            String user_id;
            String product;
            String date;
            do{
                id = cursor.getString(cursor.getColumnIndex(LilyUser.UserColumns._ID));
                Log.i("lily","id = "+id);
                
                name = cursor.getString(cursor.getColumnIndex(LilyUser.UserColumns.NAME));
                Log.i("lily","name = "+name);
                
                password = cursor.getString(cursor.getColumnIndex(LilyUser.UserColumns.PASSWORD));
                Log.i("lily","password = "+password);
                
                user_id = cursor.getString(cursor.getColumnIndex(LilyUser.ShopListColumns.USER_ID));
                Log.i("lily","user_id = "+user_id);
                
                product = cursor.getString(cursor.getColumnIndex(LilyUser.ShopListColumns.PRODUCT));
                Log.i("lily","product = " + product);
                
                date = cursor.getString(cursor.getColumnIndex(LilyUser.ShopListColumns.DATE));
                Log.i("lily","date = " + date);
                
                Log.i("lily","*********************************************");
            }while(cursor.moveToNext());
        }
        else{
            Log.i("lily","no result");
        }
        cursor.close();
    }

接下来是LilyUser这个说明类的代码:

package com.ianc.lilyprovider;
import android.net.Uri;
import android.provider.BaseColumns;
public class LilyUser {
    
    public static class User{
        public static final Uri CONTENT_URI = Uri.parse("content://com.ianc.lilyprovider/" + "customer");
    }
    public static class UserColumns implements BaseColumns{    
        public static String NAME = "name";
        public static String PASSWORD = "password";
    }
    
    public static class ShopList{
        public static final Uri CONTENT_URI = Uri.parse("content://com.ianc.lilyprovider/" + "shoplist");
    }
    public static class ShopListColumns implements BaseColumns{    
        public static String USER_ID = "user_id";
        public static String PRODUCT = "product";
        public static String DATE = "date";
    }
}
原文地址:https://www.cnblogs.com/qiengo/p/2517657.html