• Android ContentProvider 使用介绍



    contentProvider作为android四大组件其地位也是不可忽视的。contentProvider实现是在Sqlite数据库基础上实现的。其数据存储使用Sqlite实现,上层使用URI进行数据操作,是数据存储变得更简单些。在简化数据存储的同时,contentProvider也提供数据共享功能,数据可以为其它程序提供读写功能。下面看一下具体使用。

    创建数据源DBContent(通常是数据库进行存储)

    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteDatabase.CursorFactory;
    import android.database.sqlite.SQLiteOpenHelper;
    
    public class DBContent extends SQLiteOpenHelper {
    
    	   private final static String DB_NAME = "emcElevator";  
    	   public final static String TABLE_SHUACARD = "shuaCard";  
    	   public final static int DB_VERSION = 1;  
    	   
    	   public final static String SID = "_id";
    	   public final static String CARD_IDNUM="card_id_number";
    	   public final static String CARD_PICTRUE="card_picture_path";
    	   
    	
    	   public DBContent(Context context){
    		   super(context, DB_NAME, null, DB_VERSION); 
    	   }
    	   
    	   public DBContent(Context context, String name, CursorFactory factory,
    			int version) {
    		   super(context, name, factory, version);
    	   }
    
    	@Override
    	public void onCreate(SQLiteDatabase db) {
    		final String sql="CREATE TABLE " + TABLE_SHUACARD + "(" + SID  
                    + " INTEGER PRIMARY KEY AUTOINCREMENT," + 
                    CARD_IDNUM+" TEXT,"+CARD_PICTRUE+" TEXT"+");";
    		db.execSQL(sql);
    	}
    
    	@Override
    	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    		 db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHUACARD);  
    	}  
    	
    
    }
    


    contentProvider实现类:

    import android.content.ContentProvider;
    import android.content.ContentUris;
    import android.content.ContentValues;
    import android.content.UriMatcher;
    import android.database.Cursor;
    import android.database.SQLException;
    import android.database.sqlite.SQLiteDatabase;
    import android.net.Uri;
    import android.text.TextUtils;
    
    public class CardContentProvider extends ContentProvider {
    
        public static final String AUTHORITY = "com.ctsing.provider.shuacarddata";  
        public static final String ReadContentPermission="com.permission.allow.readcontent";
        
        public static final int ITEM = 1;  
        public static final int ITEM_ID = 2;  
        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/" + DBContent.TABLE_SHUACARD;  
        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/" + DBContent.TABLE_SHUACARD;   
        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY  + "/item");  
        //匹配器  
        private static final UriMatcher mMatcher;  
        
        static{
        	mMatcher = new UriMatcher(UriMatcher.NO_MATCH);  
            mMatcher.addURI(AUTHORITY, "item", ITEM);  
            mMatcher.addURI(AUTHORITY, "item/#", ITEM_ID);  
        }
        
        private DBContent dbHelper=null;
    	@Override
    	public boolean onCreate() {
    		dbHelper=new DBContent(getContext());
    		
    		return true;
    	}
    
    	@Override
    	public Cursor query(Uri uri, String[] projection, String selection,
    			String[] selectionArgs, String sortOrder) {
    		 SQLiteDatabase sdb = dbHelper.getReadableDatabase();  
    	        Cursor cur;  
    	        switch (mMatcher.match(uri)) {  
    	        case ITEM:  
    	            cur = sdb.query(DBContent.TABLE_SHUACARD, projection, selection,  
    	                    selectionArgs, null, null, sortOrder);  
    	            break;  
    	        case ITEM_ID:  
    	            long id = ContentUris.parseId(uri);    
    	            cur = sdb.query(DBContent.TABLE_SHUACARD, projection, DBContent.SID  
    	                    + "="  + id  
    	                    + (!TextUtils.isEmpty(selection) ? " AND (" + selection  
    	                            + ")" : ""), selectionArgs, null, null, sortOrder);  
    	            break;  
    	        default:  
    	         //   Log.i("sMatcher.match(uri)", String.valueOf(mMatcher.match(uri)));  
    	            throw new IllegalArgumentException("Query with unknown URI: " + uri);  
    	        }  
    	       /* sdb.close();  
    	        sdb=null;  */
    	        cur.setNotificationUri(getContext().getContentResolver(), uri);  
    	        return cur;  
    	}
    
    	@Override
    	public String getType(Uri uri) {
    		String matchType=null;
    		
    		switch(mMatcher.match(uri)){
    		case ITEM:
    			matchType=CONTENT_TYPE;
    			break;
    		case ITEM_ID:
    			matchType=CONTENT_ITEM_TYPE;
    			break;
    		default:  
                throw new IllegalArgumentException("Unknown URI " + uri);  
    		}
    		return matchType;
    	}
    
    	@Override
    	public Uri insert(Uri uri, ContentValues values) {
    		 SQLiteDatabase db = dbHelper.getWritableDatabase();  
    	      
    	        if (mMatcher.match(uri) != ITEM) {  
    	            db.close();  
    	            db=null;  
    	            throw new IllegalArgumentException("Unknow URI " + uri);  
    	        }  
    	        
    	        long rowId = db.insert(DBContent.TABLE_SHUACARD, DBContent.SID, values);  
    	        if (rowId > 0) {  
    	            Uri noteUri = ContentUris.withAppendedId(CONTENT_URI, rowId);  
    	            this.getContext().getContentResolver().notifyChange(noteUri, null);  
    	            return noteUri;  
    	        }  
    	        db.close();  
    	        db=null;  
    	        throw new SQLException("Failed to insert row into " + uri);  
    	}
    
    	@Override
    	public int delete(Uri uri, String selection, String[] selectionArgs) {
    		 SQLiteDatabase sdb = dbHelper.getWritableDatabase();  
    	        int count;  
    	        switch (mMatcher.match(uri)) {  
    		        case ITEM:  
    		            count = sdb.delete(DBContent.TABLE_SHUACARD, selection, selectionArgs);  
    		            break;  
    		        case ITEM_ID:  
    		            //String id = uri.getPathSegments().get(1);  
    		            long id = ContentUris.parseId(uri);    
    		            count = sdb.delete(DBContent.TABLE_SHUACARD, DBContent.SID  
    		                    + "=" + id  
    		                    + (!TextUtils.isEmpty(selection) ? " AND (" + selection  
    		                            + ")" : ""), selectionArgs);  
    		            break;  
    		        default:  
    		            throw new IllegalArgumentException("Unknown URI " + uri);  
    	        }  
    	        sdb.close();  
    	        sdb=null;  
    	        this.getContext().getContentResolver().notifyChange(uri, null);  
    	        return count;  
    	}
    
    	@Override
    	public int update(Uri uri, ContentValues values, String selection,
    			String[] selectionArgs) {
    		  SQLiteDatabase db = dbHelper.getWritableDatabase();  
    	        int count;  
    	        switch (mMatcher.match(uri)) {  
    		        case ITEM:  
    		            count = db.update(DBContent.TABLE_SHUACARD, values, selection,  
    		                    selectionArgs);  
    		            break;  
    		        case ITEM_ID:  
    		            String id = uri.getPathSegments().get(1);  
    		            count = db.update(DBContent.TABLE_SHUACARD, values, DBContent.SID  
    		                    + "=" + id  
    		                    + (!TextUtils.isEmpty(selection) ? " AND (" + selection  
    		                            + ")" : ""), selectionArgs);  
    		            break;  
    		        default:  
    		            throw new IllegalArgumentException("Unknown URI " + uri);  
    	        }  
    	        db.close();  
    	        db=null;  
    	        this.getContext().getContentResolver().notifyChange(uri, null);  
    	        return count;  
    	}
    
    }
    

     
    配置ContentProvider

    <provider  
            android:name="com.ctsing.content.CardContentProvider"  
            android:authorities="com.ctsing.provider.shuacarddata"
            />  

    单元测试进行功能测试:

    import android.content.ContentResolver;
    import android.content.ContentValues;
    import android.database.Cursor;
    import android.net.Uri;
    import android.test.AndroidTestCase;
    import android.util.Log;
    
    public class TestUse extends AndroidTestCase  {
    
        public void testInsert()throws Exception{   //插入数据    
            Uri uri = Uri.parse("content://" + CardContentProvider.AUTHORITY  + "/item");    
            ContentResolver resolver = this.getContext().getContentResolver();
            
            ContentValues values1 = new ContentValues();    
            values1.put(DBContent.CARD_IDNUM, "1001");    
            values1.put(DBContent.CARD_PICTRUE, "pro/elevater/11.jpeg");    
            resolver.insert(uri, values1);    
            
            ContentValues values2 = new ContentValues(); 
            values2.put(DBContent.CARD_IDNUM, "1002");    
            values2.put(DBContent.CARD_PICTRUE, "pro/elevater/12.jpeg");
            resolver.insert(uri, values2);    
            
            ContentValues values3 = new ContentValues(); 
            values3.put(DBContent.CARD_IDNUM, "1003");    
            values3.put(DBContent.CARD_PICTRUE, "pro/elevater/13.jpeg");  
            resolver.insert(uri, values3);    
            
        }    
       
        public void testUpdate()throws Exception{   //更新id=2的记录  
        	Uri uri = Uri.parse("content://" + CardContentProvider.AUTHORITY  + "/item/2");   
            ContentResolver resolver = this.getContext().getContentResolver();    
            ContentValues values = new ContentValues();    
            values.put(DBContent.CARD_IDNUM, "1012");   
            values.put(DBContent.CARD_PICTRUE, "pro/elevater/102.jpeg"); 
            
            resolver.update(uri, values, null, null);    
        }    
       
        public void testDelete()throws Exception{   //删除id=1的记录    
            Uri uri = Uri.parse("content://" + CardContentProvider.AUTHORITY  + "/item/1"); //删除id=1的记录    
            ContentResolver resolver = this.getContext().getContentResolver();    
            resolver.delete(uri, null, null);    
        }    
          
        public void testQuery()throws Exception{    //查询数据并显示    
            Uri uri = Uri.parse("content://" + CardContentProvider.AUTHORITY  + "/item");    //查询所有记录    
            ContentResolver resolver = this.getContext().getContentResolver();    
            Cursor cursor = resolver.query(uri, null, null, null, null);    
            while(cursor.moveToNext()){    
             //   StudentData person = new StudentData(cursor.getInt(cursor.getColumnIndex("id")),cursor.getString(cursor.getColumnIndex("name")),cursor.getInt(cursor.getColumnIndex("age")));    
               Log.v("ContentProvider", " NO.= "+cursor.getInt((cursor.getColumnIndex("_id")))+
            		   " card_id= "+cursor.getString(cursor.getColumnIndex(DBContent.CARD_IDNUM))+
            		   " pic_path= "+cursor.getString(cursor.getColumnIndex(DBContent.CARD_PICTRUE)));    
            }    
        }   
        
    }
    

     
    这里简单说一下单元测试实现:

    1,在配置文件内声明权限,测试目标工具(在application外部声明):

     <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
        <instrumentation
            android:name="android.test.InstrumentationTestRunner"
            android:targetPackage="com.ctsing.emca" >
        </instrumentation>

        <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >

    注意!在android:targetPackage所指的的包是应用程序包名,而不是测试类所在的包名称。

    在application内部添加使用测试工具:

    <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            
            <uses-library android:name="android.test.runner" />
    

     实现继承AndroidTestCase类,再要测试的方法名称前加test,例如测试Insert方法,则编写测试方法testInsert(),在右侧outline窗口选择要测试功能方法,
    想要测试哪个方法,则在哪个测试方法上右键鼠标,选择Run As,然后再选择Android JUnit Test即可,如果有异常或者错误,左边测试台会显示红色,下边是错误提示。测试通过,测试条为绿色。


    下面在介绍一下Content权限问题,为了数据安全性我们需要添加适当的读写权限

    如果contentProvider要被第三方使用读写则需要添加android:exported="true"属性,设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。默认为false。当被第三方访问时我们可以添加权限来控制局部或者全部数据使用。

    例如设置读取数据权限

    声明权限:

    <permission android:name="com.permission.allow.readcontent"

    android:label="Allow read contentProvider"

    android:protectionLevel="normal" />

    添加权限到contentProvider:

    <provider  
            android:name="com.ctsing.content.CardContentProvider"  
            android:authorities="com.ctsing.provider.shuacarddata"
            android:exported="true"
            android:readPermission="com.permission.allow.readcontent"
            />  
    

    t同样我们还可以设置写入权限,write android:witePermission权限,或者两者兼有permission。数据访问者需要添加权限声明

    <uses-permission android:name="com.permission.allow.readcontent" />,才能正常访问数据内容,这在一定程度上起到保护数据功能。

    部分权限功能:
    实际使用中,provider可以只开放部分URI的权限,例如,我们可以只开放content://com.ctsing.provider.shuacarddata/item路径下权限,不允许访问其他路径,如下声明

    <provider android:name="com.ctsing.content.CardContentProvider"
            android:name="com.ctsing.content.CardContentProvider"  
            android:authorities="com.ctsing.provider.shuacarddata"
            android:exported="true"
            android:readPermission="com.permission.allow.readcontent">
        <path-permission android:pathPrefix="/item" android:readPermission="READ_ITEM_CONTENTPROVIDER" />
    </provider> 
    

     
    除了android:pathPrefix,还可以有android:path和android:pathPatten,例如android:pathPattern="/hello/.*"(注意,通配符*之前有个‘.’)。
    如果要读取content:content://com.ctsing.content.CardContentProvider/item/1则需要声明整个provider的权限com.permission.allow.readcontent或者该路径的权限READ_ITM_CONTENTPROVIDER。


    14.11.20更新,下面说一下数据变化监听问题:

    当数据发生变化时,我们需要知道当数据变化时,希望能够得到通知,在android中已经设计了这种监听是用ContentObserver接口实现。联系人和短信内容发生变化时我们能够实现监听变化就是ContentObserver实现的。上述例子中我已经加入notifyChange方法进行内容变化通知,我们只有实现ContentObserver进行注册监听即可。

    实现数据监听类:

    public class ShuaCardContentObserver extends ContentObserver{
    
    	private Context mContext;
    	final String SHUACARD_ACTION="com.shuaCard.action";
    	final String SHUACARD_DATA="getData";
    	final String SHUACARD_KEYCARDNUM="card_num";
    	final String SHUACARD_KEYCARDADDRESS="card_path";
    	
    	public ShuaCardContentObserver(Handler handler,Context ctx) {
    		super(handler);
    		this.mContext=ctx;
    	}
    	
    	@Override
    	public void onChange(boolean selfChange) {		
    		super.onChange(selfChange);
    	}
    
    	@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    	@Override
    	public void onChange(boolean selfChange, Uri uri) {	
    		super.onChange(selfChange, uri);
    		dealChange();
    	}
    	
    	void dealChange(){
    		Uri uri = Uri.parse("content://" + CardContentProvider.AUTHORITY  + "/item");  
            ContentResolver resolver = mContext.getContentResolver();    
            Cursor cursor = resolver.query(uri, null, null, null, null);    
            List<Map<String,String>> cardList=null;
            
            if(cursor.getCount()>0)
            	cardList=new ArrayList<Map<String,String>>();
            
            while(cursor.moveToNext()){    
            	String cardNum=cursor.getString(cursor.getColumnIndex(DBContent.CARD_IDNUM));
            	String cardAddress=cursor.getString(cursor.getColumnIndex(DBContent.CARD_PICTRUE_PATH));
            	Map<String,String> map=new HashMap<String, String>();
            	map.put(SHUACARD_KEYCARDNUM, cardNum);
            	map.put(SHUACARD_KEYCARDADDRESS, cardAddress);
            	cardList.add(map);
            }
            if(cursor!=null)
            	cursor.close();
            
            if(cardList!=null){		
            	Intent it=new Intent(SHUACARD_ACTION);
            	Bundle b=new Bundle();
            	b.putSerializable(SHUACARD_DATA, (Serializable)cardList);
            	it.putExtras(b);
            	mContext.sendBroadcast(it);
            }
    	}
    


    注册监听:

    @Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		cardChangeObserver=new ShuaCardContentObserver(new android.os.Handler(), this);
    		getContentResolver().registerContentObserver(Uri.parse("content://" + CardContentProvider.AUTHORITY  + "/item"), true,cardChangeObserver);
    	}
    

     
    退出取消监听:

    @Override
    	protected void onDestroy() {
    		if(null!=cardChangeObserver)
    			getContentResolver().unregisterContentObserver(cardChangeObserver);
    		super.onDestroy();
    	}
    

     
    虽然我们可以知道数据发生变化,但是我们并不知道具体发生变化数据具体情况(删除哪些数据或是添加哪些),如何实现这一个需求呢?android并没有实现,这就需要我们自己实现了。有待后续研究,更新中。。。



  • 相关阅读:
    UVA 11198 Dancing Digits
    UVA 10085 The most distant state
    UVA 321 The New Villa
    UVA 10422 Knights in FEN
    poj2876
    poj2895
    poj2914
    poj2892
    poj2941
    LD SDK LDCControlDll 中 CXMLFile的进一步使用方法,建议以后改进
  • 原文地址:https://www.cnblogs.com/happyxiaoyu02/p/6818982.html
Copyright © 2020-2023  润新知