Android四大组件之ContentProvider

内容提供者

应用的数据库是不允许其他应用访问的,内容提供者的作用就是把私有数据暴露给其他应用,通常,是把私有数据库的数据暴露给其他应用。


我们可以通过自定义内容提供者来了解内容提供者的原理

  自定义内容提供者步骤:  

      第一步:我们想写一个myOpenHelper继承SQLiteOpenHelper,来创建一个数据库

      第二步 :写myContentProviderjava类继承ContentProvider的内容提供者类,重写增删改查的方法。 内容提供者需要在清单文件中注册

      具体代码:

//内容提供者需要在清单文件中注册
<provider android:name="com.xiaochen.neirong.myContentProvider" //指定具体类 android:authorities="com.xiaochen.people"      //自定义authorities 其他应用吐过这个值找到对应此内容提供者 android:exported="true"></provider>      //值为true表示该组件的一个实例,可以运行给所有的用户

  

public class PersonProvider extends ContentProvider {

	private MyOpenHelper oh;
	SQLiteDatabase db;

	//创建uri匹配器对象,在谷歌源码中设置为静态
	static UriMatcher um = new UriMatcher(UriMatcher.NO_MATCH); //NO_MATCH 表示不匹配任何路径时返回码 是-1
	//检测其他用户传入的uri与匹配器定义好的uri中,哪条匹配
	static {
		um.addURI("com.xiaochen.people", "person", 1);//content://com.itheima.people/person  如果匹配 返回1
		um.addURI("com.xiaochen.people", "teacher", 2);//content://com.itheima.people/teacher 如果匹配 返回2
		um.addURI("com.xiaochen.people", "person/#", 3);//content://com.itheima.people/person/4 #代表携带任意数字 这里一般作为id携带 *代表携带任意文本
	}
	
	//内容提供者创建时调用
	@Override
	public boolean onCreate() {
		oh = new MyOpenHelper(getContext());
		db = oh.getWritableDatabase();
		return false;
	}
    //提供查查服务
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		Cursor cursor = null;
		if(um.match(uri) == 1){
			cursor = db.query("person", projection, selection, selectionArgs, null, null, sortOrder, null);
		}
		else if(um.match(uri) == 2){
			cursor = db.query("teacher", projection, selection, selectionArgs, null, null, sortOrder, null);
		}
		else if(um.match(uri) == 3){
			//把uri末尾携带的数字取出来
			long id = ContentUris.parseId(uri);
			cursor = db.query("person", projection, "_id = ?", new String[]{id + ""}, null, null, sortOrder, null);
		}
		else{//抛出非法参数异常
			throw new IllegalArgumentException("uri又有问题哟亲么么哒");
		}
		return cursor;
	}

	@Override
	public String getType(Uri uri) {
		if(um.match(uri) == 1){
			return "vnd.android.cursor.dir/person";
		}
		else if(um.match(uri) == 3){
			return "vnd.android.cursor.item/person";
		}
		return null;
	}

	//此方法供其他应用调用,用于往people数据库里插数据
	//values:由其他应用传入,用于封装要插入的数据
	//uri:内容提供者的主机名,也就是地址
	@Override
	public Uri insert(Uri uri, ContentValues values) {
		//使用uri匹配器匹配传入的uri
		if(um.match(uri) == 1){
			db.insert("person", null, values);
			
			//发送数据改变的通知
			//uri:通知发送到哪一个uri上,所有注册在这个uri上的内容观察者都可以收到这个通知
			getContext().getContentResolver().notifyChange(uri, null);
		}
		else if(um.match(uri) == 2){
			db.insert("teacher", null, values);
			
			getContext().getContentResolver().notifyChange(uri, null);
		}
		else{
			throw new IllegalArgumentException("uri有问题哟亲么么哒");
		}
		return uri;
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		int i = db.delete("person", selection, selectionArgs);
		return i;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		int i = db.update("person", values, selection, selectionArgs);
		return i;
	}

}

  

  我们在其他应用中访问自定义内容提供者

    

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

//点击插入按钮,拿到内容提供者,将数据插入数据库
    public void insert(View v){
    	//,我们想要使用内容提供者的话,通过拿到ContentResolver
    	ContentResolver cr = getContentResolver();
     //
    	ContentValues values = new ContentValues();
    	values.put("name", "赵帅哥");
//    	values.put("money", "13000");
    	//url:内容提供者的主机名
    	//values:要插入的数据
    	cr.insert(Uri.parse("content://com.xiaochen.people/teacher"), values);
    	
    }
    
    public void delete(View v){
    	ContentResolver cr = getContentResolver();
    	int i = cr.delete(Uri.parse("content://com.itheima.people"), "name = ?", new String[]{"小志"});
    	System.out.println(i);
    }
    public void update(View v){
    	ContentResolver cr = getContentResolver();
    	ContentValues values = new ContentValues();
    	values.put("name", "sb志");
    	int i = cr.update(Uri.parse("content://com.itheima.people"), values, "name = ?", new String[]{"大志"});
    	System.out.println(i);
    }
 
   
    public void select(View v){
    	ContentResolver cr = getContentResolver();
    	Cursor cursor = cr.query(Uri.parse("content://com.itheima.people/person/4"), null, null, null, null);
    	while(cursor.moveToNext()){
    		String name = cursor.getString(1);
    		String money = cursor.getString(2);
    		System.out.println(name + ";" + money);
    	}
    }
    
}

  


我们来操作系统的内容提供者

 我们通过内容提供者来获取系统短信,备份系统短信和插入系统短信。

 获取系统短信,备份系统短信

    找到系统的短信数据库 文件在data/data/com.android.provides.teltphony/databases/mmssms.db 

    导出来之后会发现有很多表,我们需要关注sms表

    sms表中关注四个字段

      body:短信内容
      address:短信的发件人或收件人号码(跟你聊天那哥们的号码)
      date:短信时间
      type:1为收到,2为发送

在获取系统短信的时候我们需要查看安卓源码,来忽的短信数据库内容提供者的主机名和路径

    查看主机名 系统自带内容提供者的路径packages/providers/TelephonyProvider  在清单文件中可以查看到主机名 发现主句名是sms ,同时可以看到权限 读写短信的权限  

    查看路径  在对应的java类中可以看到匹配规则,查看路径

    代码 我们通过内容提供者获取到系统的短信,拿到短信后封装到javabean中,备份短信然后利用xml序列化器,将数据存放到xml文件中

      读取系统短信和往sd卡中写入数据需要两个权限

      <uses-permission android:name="android.permission.READ_SMS"/>
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

public class MainActivity extends Activity {
    //定义集合封装短信
	List<Message> smsList;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        smsList = new ArrayList<Message>();
    }


    public void click(View v){
    	//访问内容提供者获取短信
    	ContentResolver cr = getContentResolver();
    	//						短信内容提供者的主机名 不写路径获取到所有短信
    	Cursor cursor = cr.query(Uri.parse("content://sms"), new String[]{"address", "date", "body", "type"}, 
    			null, null, null);
    	while(cursor.moveToNext()){
    		String address = cursor.getString(0);
    		long date = cursor.getLong(1);
    		String body = cursor.getString(2);
    		String type = cursor.getString(3);
    		Message sms = new Message(body, type, address, date);
    		smsList.add(sms);
    	}
    }
    
    public void click2(View v){
    	XmlSerializer xs = Xml.newSerializer();
    	File file = new File("sdcard/sms.xml");
    	FileOutputStream fos;
		try {
			fos = new FileOutputStream(file);
			xs.setOutput(fos, "utf-8");
			
			xs.startDocument("utf-8", true);
			xs.startTag(null, "message");
			
			for (Message sms : smsList) {
				xs.startTag(null, "sms");
				
				xs.startTag(null, "body");
				xs.text(sms.getBody());
				xs.endTag(null, "body");
				
				xs.startTag(null, "date");
				xs.text(sms.getDate() + "");
				xs.endTag(null, "date");
				
				xs.startTag(null, "type");
				xs.text(sms.getType());
				xs.endTag(null, "type");
				
				xs.startTag(null, "address");
				xs.text(sms.getAddress());
				xs.endTag(null, "address");
				
				xs.endTag(null, "sms");
			}
			
			xs.endTag(null, "message");
			xs.endDocument();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
    
}

  插入系统短信

    读写短信权限  

        <uses-permission android:name="android.permission.WRITE_SMS"/>
        <uses-permission android:name="android.permission.READ_SMS"/>

 public void click(View v){
    
    	        ContentResolver cr = getContentResolver();
    	    	ContentValues values = new ContentValues();
    	    	values.put("address", 138438);
    	    	values.put("type", 1);
    	    	values.put("date", System.currentTimeMillis());
    	    	values.put("body", "我是马云");
    	    	cr.insert(Uri.parse("content://sms"), values);
    }

  


获取系统联系人

    联系人数据库 找到系统的短信数据库 文件在data/data/com.android.provides.contacts/databases/contacts2.db 

    关注三个表 

    raw_contacts表:

      contact_id:联系人id
    data表:联系人的具体信息,一个信息占一行
       data1:信息的具体内容
       raw_contact_id:联系人id,描述信息属于哪个联系人
       mimetype_id:描述信息是属于什么类型
     mimetypes表:通过mimetype_id到该表查看具体类型

    查看主机名 系统自带内容提供者的路径packages/providers/contactsProvides 在清单文件中可以查看到主机名 发现主句名是com.android.comtacts,同时可以看到权限 读写短信的权限  

    查看路径  在对应的java类中可以看到匹配规则,查看路径

  需要权限: <uses-permission android:name="android.permission.READ_CONTACTS"/>

 public void click(View v){
    	//通过内容提供者访问联系人数据库
    	ContentResolver cr = getContentResolver();          //查询raw_contacts                      //查询contact_id字段
    	Cursor cursorContactId = cr.query(Uri.parse("content://com.android.contacts/raw_contacts"), new String[]{"contact_id"}, null, null, null);
    
    while(cursorContactId.moveToNext()){ //获取联系人id String contactId = cursorContactId.getString(0);    //查询data表                //更具联系人id查询联系人信息 Cursor cursorData = cr.query(Uri.parse("content://com.android.contacts/data"), new String[]{"data1", "mimetype"}, //这里直接传入mimetype它会直接去mimetypes表中查询具体类型 "raw_contact_id = ?", new String[]{contactId}, null); //获取所有字段的名字 // String[] names = cursorData.getColumnNames(); // for (String string : names) { // System.out.println(string); // } Contact con = new Contact(); while(cursorData.moveToNext()){ String data1 = cursorData.getString(0); String mimetype = cursorData.getString(1); //通过mimetype的判断,把data1存入对应的属性 if("vnd.android.cursor.item/email_v2".equals(mimetype)){ con.setEmail(data1); } else if("vnd.android.cursor.item/phone_v2".equals(mimetype)){ con.setPhone(data1); } else if("vnd.android.cursor.item/name".equals(mimetype)){ con.setName(data1); } } System.out.println(con.toString()); } } }

 插入联系人 

插入联系人需要两个权限

<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>

  public void click(View v){
    	ContentResolver cr = getContentResolver();
    	//先查询raw_contacts表,获取最新联系人的主键,然后主键+1,就是要插入的联系人的id  这里通过主键拿 因为如果在手机上中将一个联系人删除 只是将contact_id设为null值 联系人具体信息在data表中还是存在的 如果拿到最后一个是null 给null加1这样是不行的
    	Cursor cursorContactId = cr.query(Uri.parse("content://com.android.contacts/raw_contacts"), new String[]{"_id"}, null, null, null);
    	//默认联系人id就是1
    	int contact_id = 1;
    	if(cursorContactId.moveToLast()){
    		//拿到主键
    		int _id = cursorContactId.getInt(0);
    		//主键+1,就是要插入的联系人id
    		contact_id = ++_id;
    	}
    	
    	ContentValues values = new ContentValues();
    	values.put("contact_id", contact_id);
    	//把联系人id插入raw_contacts数据库
    	cr.insert(Uri.parse("content://com.android.contacts/raw_contacts"), values);
    	
    	values.clear();
    	values.put("data1", "二bi");
    	values.put("mimetype", "vnd.android.cursor.item/name");
    	values.put("raw_contact_id", contact_id);
    	cr.insert(Uri.parse("content://com.android.contacts/data"), values);
    	
    	values.clear();
    	values.put("data1", "1344567");
    	values.put("mimetype", "vnd.android.cursor.item/phone_v2");
    	values.put("raw_contact_id", contact_id);
    	cr.insert(Uri.parse("content://com.android.contacts/data"), values);
    }
    
}

  


内容观察者:

    当数据库数据改变时,内容提供者会发出通知,在内容提供者的uri上注册一个内容观察者,就可以收到数据改变的通知

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //注册一个内容观察者,监听短信数据库内容的改变
        ContentResolver cr = getContentResolver();
        //uri:监听哪个uri上的内容提供者的通知
        //notifyForDescendents:如果是true,那么只要以content://sms开头的uri的数据改变,都能收到通知,比如content://sms/inbox
        cr.registerContentObserver(Uri.parse("content://sms"), true, new MyObserver(new Handler()));
    }
    
    class MyObserver extends ContentObserver{

		public MyObserver(Handler handler) {
			super(handler);
			// TODO Auto-generated constructor stub
		}
    	
		//收到数据改变的通知,此方法调用
		@Override
		public void onChange(boolean selfChange) {
			// TODO Auto-generated method stub
			super.onChange(selfChange);
			System.out.println("短信数据库改变");
		}	
    }  
}

  

在自定义内容提供者中,发送通知的代码

ContentResolver cr = getContext().getContentResolver();
		//发出通知,所有注册在这个uri上的内容观察者都可以收到通知
		cr.notifyChange(uri, null);

  

原文地址:https://www.cnblogs.com/jiaowoxiaochen/p/4957198.html