zoukankan      html  css  js  c++  java
  • Android基础(五) ContentProvider 内容提供者

    一、ContentProvider

        1.什么是ContentProvider

        内容提供者ContentProvider是Android的四大组件之一。它可以把应用程序内的数据对外进行共享,在共享的时候,通过方法封装了访问数据的代码,提高了安全性,不需要直接暴露数据。

        应用程序可以对指定的内容提供者注册一个观察者,内容提供者在数据修改之后可以通知观察者,观察者即可实时监听数据的修改。

        2.创建ContentProvider

        需要定义一个类继承ContentProvider,并重写其中的抽象方法。

        需要在清单文件的<application>节点下生命<provider>节点,定义name和authorities属性来指定类和地址

    在清单文件中注册

        3.访问ContentProvider

        通过Context对象获取一个ContentResolver对象,该对象可以对一个指定的Uri调用增删改查的方法,这就是执行了ContentProvider的增删改查。

        Uri需要以content://开头,后面加上对应的authorities中的地址。

        4.增删改查

        在ContentProvider的query()、insert()、delete()、update()方法中可以根据传入的参数来访问数据库(也可以访问其他数据)。

        ContentUris类可以解析Uri后的id(使用ContentUris.parseId(Uri)方法),也可以给一个Uri追加上id(ContentUris.withAppendedId(uri,id))。

        UriMatcher类可以用来识别Uri,创建对象之后,先调用addUri()方法添加一些可以识别的Uri,然后在增删改查方法中调用match()方法来匹配参数Uri,会得到预先定义的结果码。

    public class SQLiteProvider extends ContentProvider {
    	private MyOpenHelper helper;// 该类中会多次使用到MyOpenHelper,所以定义为成员变量
    	private UriMatcher matcher;
    	//定义静态变量,用于标识表名,增强程序的可读性
    	private static final int PERSON_ID=0;
    	private static final int PERSON=1;
    	private static final int USER=2;
    
    	public boolean onCreate() {
    		System.out.println("onCreate");
    		
    		/*
    		 * 创建完对象才会创建Context,所以getContext()不能在成员变量处使用,
    		 * 也不能在构造函数中使用(构造函数执行时,对象还未创建完毕)
    		 */
    		helper = new MyOpenHelper(getContext());
    		matcher = new UriMatcher(UriMatcher.NO_MATCH);//创建一个匹配器,用来识别传入的Uri,分辨表名
    		//给匹配器添加Uri,以备在增删改查中识别
    		matcher.addURI("com.xxx.SQLiteProvider", "person", PERSON);
    		matcher.addURI("com.xxx.SQLiteProvider", "user", USER);
    		matcher.addURI("com.xxx.SQLiteProvider", "person/#", PERSON_ID);
    		return true;
    	}
    
    	public Cursor query(Uri uri, String[] projection, String selection,
    			String[] selectionArgs, String sortOrder) {
    		SQLiteDatabase db = helper.getReadableDatabase();
    		switch (matcher.match(uri)) {
    			case PERSON_ID:
    				selection="id="+ContentUris.parseId(uri);//截取uri中的id,作为查询的条件
    			case PERSON:
    				return db.query("person", projection, selection, selectionArgs, null, null, sortOrder);
    			case USER :
    				return db.query("user", projection, selection, selectionArgs, null, null, sortOrder);
    			default:
    				throw new IllegalArgumentException("不能识别的uri:"+uri);
    		}
    		
    	}
    
    	public Uri insert(Uri uri, ContentValues values) {
    		SQLiteDatabase db = helper.getWritableDatabase();
    		switch (matcher.match(uri)) {
    		case PERSON:
    			long id = db.insert("person", "id", values);
    			getContext().getContentResolver().notifyChange(uri, null);//通知指定的Uri数据已修改
    			db.close();
    			return ContentUris.withAppendedId(uri, id);//将id拼接在uri后面,返回
    		default:
    			throw new IllegalArgumentException("不能识别的uri:"+uri);
    		}
    	}
    
    	public int delete(Uri uri, String selection, String[] selectionArgs) {
    		SQLiteDatabase db = helper.getWritableDatabase();
    		switch (matcher.match(uri)) {
    		case PERSON_ID:
    			selection="id="+ContentUris.parseId(uri);
    		case PERSON:
    			int count = db.delete("person", selection, selectionArgs);
    			if(count!=0)
    				getContext().getContentResolver().notifyChange(uri, null);
    			db.close();
    			return count;
    		default:
    			throw new IllegalArgumentException("不能识别的uri:"+uri);
    		}
    	}
    
    	public int update(Uri uri, ContentValues values, String selection,
    			String[] selectionArgs) {
    		SQLiteDatabase db = helper.getWritableDatabase();
    		switch (matcher.match(uri)) {
    		case PERSON_ID:
    			selection="id="+ContentUris.parseId(uri);
    		case PERSON:
    			int count = db.update("person", values, selection, selectionArgs);
    			if(count!=0){
    				getContext().getContentResolver().notifyChange(uri, null);
    			}
    			db.close();
    			return count;
    		default:
    			throw new IllegalArgumentException("不能识别的uri:"+uri);
    		}
    	}
    
    	public String getType(Uri uri) {
    		switch (matcher.match(uri)) {
    		case PERSON_ID:
    			return "vnd.android.cursor.item/person";//mimetype,单条person
    		case PERSON:
    			return "vnd.android.cursor.dir/person";//mimetype,多条person
    		default:
    			throw new IllegalArgumentException("不能识别的uri:"+uri);
    		}
    		
    	}
    }
    
    <provider android:name=".provider.SQLiteProvider" android:authorities="com.xxx.SQLiteProvider"></provider>

    5.ContentObserver

        在其他应用中可以对ContentProvider注册一个ContentObserver,当数据被修改的时候,会执行ContentObserver中的onChange()方法。

        在数据修改后,ContentProvider中必须调用notifyChange()方法,ContentObserver才会收到数据修改的通知。

    public class MainActivity extends Activity {
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            
            //给10.SQLite中的ContentProvider注册一个ContentObserver
            Uri uri=Uri.parse("content://com.xxx.SQLiteProvider");
            getContentResolver().registerContentObserver(uri, true, new MyObserver());//第二个参数决定,子集改变时是否发出通知
            System.out.println("register");
        }
    
        private class MyObserver extends ContentObserver{
    
    		public MyObserver() {
    			super(new Handler());//在主线程中创建的handler,onChange()方法就在主线程中运行
    		}
    
    		@Override
    		public void onChange(boolean selfChange) {
    			System.out.println("收到数据修改的通知");
    		}
        }
    }

    二、短信的监听

        1.原理

        短信数据是储存在一个数据库中的,这个数据库对外提供了ContentProvider,我们对ContentProvider注册观察者,就可以监听数据的变化

        2.下载代码获取Uri

        通过https://github.com/android下载源代码, 清单文件中获取uri

        短信和彩信的数据库都在com.android.providers.telephony中,Type=1为收短信 Type=2为发短信

    public class MainActivity extends Activity {
    
            @Override
            public void onCreate(Bundle savedInstanceState) {
                    super.onCreate(savedInstanceState);
                    setContentView(R.layout.main);
    
                    //注册观察者,监听短信
                    final Uri uri = Uri.parse("content://sms");
                    getContentResolver().registerContentObserver(uri, true, new ContentObserver(new Handler()) {
                @Override
                public void onChange(boolean selfChange) {
                    //查询出第一条记录
                    Cursor c=getContentResolver().query(uri, new String[]{"address","date","body","type"}, null, null, "_id DESC LIMIT 1");
                    if(c.moveToNext()){
                        String address = c.getString(0);
                        long time = c.getLong(1);
                        DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM);
                        String date = df.format(time);
                        String body = c.getString(2);
                        int type = c.getInt(3);
                        System.out.println(date+" "+(type==1?'收':'发')+" "+address+" "+body);
                    }
                }
    
            });
    
            }
    }

    三、读写联系人

        1.原理

        联系人数据也是存储在数据库中的,并且对外提供了ContentProvider,我们可以通过ContentProvider查询或者写入数据。

        2.查询

        先查raw_contacts表得到id,每一个id对应一个联系人;再用id查询data表,data表中就是联系人的各项数据。

        3.插入

        由于需要执行多次插入操作,先插入raw_contacts表的id,再插入data表中的数据,而且这些操作要么都成功,要么都失败。

        这就需要使用ContentResolver的applyBatch()方法来实现,对指定的路径进行一组操作。

    public class ContactTest extends AndroidTestCase {
    	private Uri rawContactsUri = Uri.parse("content://com.android.contacts/raw_contacts");
    	private Uri dataUri = Uri.parse("content://com.android.contacts/data");
    
    	public void testRead() {
    		ContentResolver resolver = getContext().getContentResolver();
    
    		// 查询raw_contacts表, 获取所有的id
    		Cursor rawContactsCursor = resolver.query(rawContactsUri, new String[] { "_id" }, null, null, null);
    		while (rawContactsCursor.moveToNext()) {
    			int id = rawContactsCursor.getInt(0); 
    
    			// 把id作为查询条件, 查询data表
    			Cursor dataCursor = resolver.query(dataUri, new String[] { "data1", "mimetype" }, "raw_contact_id=?", new String[] { id + "" }, null);
    			while (dataCursor.moveToNext()) {
    				String data1 = dataCursor.getString(0);
    				String mimetype = dataCursor.getString(1);
    				if ("vnd.android.cursor.item/name".equals(mimetype))
    					System.out.println("姓名: " + data1);
    				else if ("vnd.android.cursor.item/phone_v2".equals(mimetype))
    					System.out.println("电话: " + data1);
    				else if ("vnd.android.cursor.item/email_v2".equals(mimetype))
    					System.out.println("邮箱: " + data1);
    			}
    		}
    	}
    	
    	public void testWrite() {
    		ContentResolver resolver = getContext().getContentResolver();
    		ContentValues values = new ContentValues();
    		
    		// 向raw_contacts表中插入一个id(自动生成)
    		Uri resultUri = resolver.insert(rawContactsUri, values);
    		long id = ContentUris.parseId(resultUri);
    		
    		// 用刚刚插入的id作为raw_contact_id列的值, 向data表中插入3条数据
    		values.put("raw_contact_id", id);
    		values.put("mimetype", "vnd.android.cursor.item/name");
    		values.put("data1", "FLX");
    		resolver.insert(dataUri, values);
    		
    		values.put("mimetype", "vnd.android.cursor.item/phone_v2");
    		values.put("data1", "18600056789");
    		resolver.insert(dataUri, values);
    		
    		values.put("mimetype", "vnd.android.cursor.item/email_v2");
    		values.put("data1", "lkp@itcast.cn");
    		resolver.insert(dataUri, values);
    	}
    	
    	public void testWriteBatch() throws Exception {
    		// 创建4个ContentProviderOperation, 代表4次insert操作
    		ContentProviderOperation operation1 = ContentProviderOperation.newInsert(rawContactsUri) //
    				.withValue("_id", null) //
    				.build();
    		ContentProviderOperation operation2 = ContentProviderOperation.newInsert(dataUri) //
    				.withValueBackReference("raw_contact_id", 0) // 用同组的0号操作得到的返回值作为值插入
    				.withValue("mimetype", "vnd.android.cursor.item/name") //
    				.withValue("data1", "ZZH") //
    				.build();
    		ContentProviderOperation operation3 = ContentProviderOperation.newInsert(dataUri) //
    				.withValueBackReference("raw_contact_id", 0) // 用同组的0号操作得到的返回值作为值插入
    				.withValue("mimetype", "vnd.android.cursor.item/phone_v2") //
    				.withValue("data1", "18600098765") //
    				.build();
    		ContentProviderOperation operation4 = ContentProviderOperation.newInsert(dataUri) //
    				.withValueBackReference("raw_contact_id", 0) // 用同组的0号操作得到的返回值作为值插入
    				.withValue("mimetype", "vnd.android.cursor.item/email_v2") //
    				.withValue("data1", "zzh@itcast.cn") //
    				.build();
    		
    		// 将4个操作对象装入集合
    		ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
    		Collections.addAll(operations, operation1, operation2, operation3, operation4);
    		
    		ContentResolver resolver = getContext().getContentResolver();
    		resolver.applyBatch("com.android.contacts", operations);
    	}
    
    }
    
  • 相关阅读:
    块设备驱动框架分析(一)
    LIN总线协议
    LCD驱动分析(三)时序分析
    str_shuffle — 随机打乱一个字符串
    str_replace — 子字符串替换
    str_repeat — 重复一个字符串
    str_pad — 使用另一个字符串填充字符串为指定长度
    str_getcsv — 解析 CSV 字符串为一个数组
    ltrim — 删除字符串开头的空白字符(或其他字符)
    lcfirst — 使一个字符串的第一个字符小写
  • 原文地址:https://www.cnblogs.com/chenchong/p/3034184.html
Copyright © 2011-2022 走看看