zoukankan      html  css  js  c++  java
  • 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);
    

      

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 阮小二买彩票
    Java实现 蓝桥杯VIP 算法提高 传染病控制
    Java实现 蓝桥杯VIP 算法提高 传染病控制
    Java实现 蓝桥杯VIP 算法提高 传染病控制
    Java实现 蓝桥杯VIP 算法提高 传染病控制
    Java实现 蓝桥杯VIP 算法提高 传染病控制
    Java实现 蓝桥杯VIP 算法提高 企业奖金发放
    Java实现 蓝桥杯VIP 算法提高 企业奖金发放
    让程序后台隐藏运行
    只要你喜欢,并且可以养家糊口,就是好的
  • 原文地址:https://www.cnblogs.com/jiaowoxiaochen/p/4957198.html
Copyright © 2011-2022 走看看