标签:
内容提供者
应用的数据库是不允许其他应用访问的,内容提供者的作用就是把私有数据暴露给其他应用,通常,是把私有数据库的数据暴露给其他应用。
我们可以通过自定义内容提供者来了解内容提供者的原理
自定义内容提供者步骤:
第一步:我们想写一个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);
标签:
原文地址:http://www.cnblogs.com/jiaowoxiaochen/p/4957198.html