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);
    

      

  • 相关阅读:
    Oracle之配置节点间相互信任机制测试
    记crt 在windows与linux服务器之间利用ftp进行文件的上传下载
    虚拟机防火墙的相关设置
    Linux下载安装mysql5.7教程
    linux下使用Oracle常用命令
    Linux的常用命令
    ORA-01078: failure in processing system parameters LRM-00109: could not open parameter file '/u01/app/oracle/product/19.2.0/db_1/dbs/initsanshi.ora'报错
    Linux下创建Oracle19C的数据库实例
    redhat7.6Linux安装Oracle19C完整版教程
    Python模拟登录的几种方法
  • 原文地址:https://www.cnblogs.com/jiaowoxiaochen/p/4957198.html
Copyright © 2011-2022 走看看