为什么需要内容提供者:
第六点就是为什么需要内容提供者
SQLiteDatabase.openDatabase(path, factory, flags)
可以通过这个方法打开一个数据库的文件。但是这个数据库文件必须是其他用户可以使用的权限 -rwx(当前用户权限) rwx(当前用户组权限) rwx其他用户权限)
我们可以使用cursor.getColumnIndex("列名")来返回你指定列名的 索引.
这样的好处就是当我们 不知道索引的情况下 也可以查找出内容。
我们为了实现访问另外一个应用程序中的数据库的内容。可以使用上面的方式。
但是这种方式不是 Android 推荐的方式。
所以我们要使用内容提供者。
————————————————————————————————
内容提供者原理:
————————————————————————————————
实现内容提供者的步骤:
<span style="font-size:18px;">public class AccountProvider extends ContentProvider { //[1]定一个一个uri路径匹配器 private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); private static final int QUERYSUCESS = 0; //ctrl+shift+X 变大写 小写加y private static final int INSERTSUCESS = 1; private static final int UPDATESUCESS = 2; private static final int DELETESUCESS = 3; private MyOpenHelper myOpenHelper; //[2]创建一个静态代码块 在这个里面添加 uri static{ /** * http://www.baidu.com * authority 注意: 和清单文件里面定义的一样 com.itheima.provider/query * */ sURIMatcher.addURI("com.itheima.provider", "query", QUERYSUCESS); sURIMatcher.addURI("com.itheima.provider", "insert", INSERTSUCESS); sURIMatcher.addURI("com.itheima.provider", "update", UPDATESUCESS); sURIMatcher.addURI("com.itheima.provider", "delete", DELETESUCESS); } //当内容提供者初始化 会执行此方法 @Override public boolean onCreate() { //[3]初始化 myopenHelpler 对象 就可以获取到sqlitedatabases对象 我们就可以操作数据库 myOpenHelper = new MyOpenHelper(getContext()); return false; } //这个方法对外暴露的 @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { int code = sURIMatcher.match(uri); if (code ==QUERYSUCESS ) { //说明路径匹配成功 SQLiteDatabase db = myOpenHelper.getReadableDatabase(); //调用query方法 Cursor cursor = db.query("info", projection, selection, selectionArgs, null, null, sortOrder); //发送一条消息 说明说明数据库被操作了 getContext().getContentResolver().notifyChange(uri, null); // db.close(); //小细节 ☆ 这个cursor不能关 return cursor; }else{ //说明路径不匹配 // return null; throw new IllegalArgumentException("哥们 :uri路径不匹配 请检测路径"); } } @Override public String getType(Uri uri) { return null; } @Override public Uri insert(Uri uri, ContentValues values) { int code = sURIMatcher.match(uri); if (code == INSERTSUCESS) { //说明路径匹配成功 SQLiteDatabase db = myOpenHelper.getReadableDatabase(); long insert = db.insert("info", null, values); Uri uri2 = Uri.parse("com.hahaheheheihei/"+insert); if (insert>0) { //发送一条消息 说明说明数据库被操作了 getContext().getContentResolver().notifyChange(uri, null); } db.close();//关闭数据库 return uri2; }else { throw new IllegalArgumentException("姐们 :uri路径不匹配 请检测路径"); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int code = sURIMatcher.match(uri); if (code == DELETESUCESS) { //匹配成功 SQLiteDatabase db = myOpenHelper.getReadableDatabase(); //代表影响的行数 int delete = db.delete("info", selection, selectionArgs); if (delete>0) { //发送一条消息 说明说明数据库被操作了 getContext().getContentResolver().notifyChange(uri, null); } return delete; } return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int code = sURIMatcher.match(uri); if (code == UPDATESUCESS) { //路径匹配成功 SQLiteDatabase db = myOpenHelper.getWritableDatabase(); //代表影响的行数 int update = db.update("info", values, selection, selectionArgs); if(update>0){ //发送一条消息 说明说明数据库被操作了 getContext().getContentResolver().notifyChange(uri, null); } return update; }else{ throw new IllegalArgumentException("大爷:uri路径不匹配 请检测路径"); } } } </span>
<span style="font-size:18px;">public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } //点击按钮 往数据库里面插入一条数据 public void click1(View v){ //因为第一个应用里面的私有的数据库 已经通过内容提供者暴露出来了 所以通过内容解析者去获取数据 Uri uri = Uri.parse("content://com.itheima.provider/insert"); ContentValues values = new ContentValues(); //实际是map //key: 代表列名 value 对应的值 values.put("name", "zhaoliu"); values.put("money", 1000); //插入一条数据 Uri uri2 = getContentResolver().insert(uri, values); System.out.println("uri2:"+uri2); } //点击按钮删除 赵六删掉 public void click2(View v){ //[1]获取内容的解析者 Uri uri = Uri.parse("content://com.itheima.provider/delete"); //[2]代表影响的函数 int delete = getContentResolver().delete(uri, "name=?", new String[]{"zhaoliu"}); Toast.makeText(getApplicationContext(), "删除了"+delete+"行", 1).show(); } //给赵六多点钱 1000元 public void click3(View v){ //[1] 创建uri Uri uri = Uri.parse("content://com.itheima.provider/update"); //[2]获取内容的解析者 ContentValues values = new ContentValues(); values.put("money", "10000000"); int update = getContentResolver().update(uri, values, "name=?",new String[]{"zhaoliu"}); Toast.makeText(getApplicationContext(), "更新了"+update+"行", 1).show(); } //点击按钮 查询第一个应用里面数据库的信息 public void click4(View v){ // 第二种 查询方式 因为第一个应用里面的私有的数据库 已经通过内容提供者暴露出来了 所以通过内容解析者去获取数据 Uri uri = Uri.parse("content://com.itheima.provider/query"); //通过内容解析者获取数据 Cursor cursor = getContentResolver().query(uri, new String[]{"name","money"}, null, null, null); if (cursor!=null) { while(cursor.moveToNext()){ String name = cursor.getString(0); String money = cursor.getString(1); System.out.println("第二个应用:"+name+"---"+money); } } } }</span>
————————————————————————————————
备份短信案例:
当我们看见包名有providers 时候,我们就应该知道 这些内容已经通过内容提供者暴露出来了。我们只要使用内容解析者接收 就可以了。
//点击按钮查询短信内容 然后把短信内容进行备份 public void click(View v) { try { //[1]获取XmlSerializer的实例 XmlSerializer serializer = Xml.newSerializer(); //[2]设置序列化器参数 File file = new File(Environment.getExternalStorageDirectory().getPath(),"smsbackup.xml"); FileOutputStream fos = new FileOutputStream(file); serializer.setOutput(fos, "utf-8"); //[3]写xml文档开头 serializer.startDocument("utf-8", true); //[4]写xml的根节点 serializer.startTag(null, "smss"); //[5]构造uri Uri uri = Uri.parse("content://sms/"); //[6]由于短信的数据库已经通过内容提供者暴露出来 所以我们直接通过内容解析者查询 Cursor cursor = getContentResolver().query(uri, new String[]{"address","date","body"}, null, null, null); while(cursor.moveToNext()){ String address = cursor.getString(0); String date = cursor.getString(1); String body = cursor.getString(2); //[7]写sms节点 serializer.startTag(null, "sms"); //[8]写address节点 serializer.startTag(null, "address"); serializer.text(address); serializer.endTag(null, "address"); //[9]写date节点 serializer.startTag(null, "date"); serializer.text(date); serializer.endTag(null, "date"); //[10]写body节点 serializer.startTag(null, "body"); serializer.text(body); serializer.endTag(null, "body"); serializer.endTag(null, "sms"); } serializer.endTag(null, "smss"); serializer.endDocument(); fos.close(); } catch (Exception e) { e.printStackTrace(); } }
这里的Uri.parse("content://")前面是默认的写法。后面就是在 内容提供者里面定义的。
记住要加权限:android.permission.READ_SMS
android.permission.WRITE_SMS
————————————————————————————————
利用内容提供者插入短信:
————————————————————————————————
读取联系人案例:
记住要加权限:android.permission.READ_CONTACTS
android.permission.WRITE_CONTACTS
注意一点,谷歌工程师设计的联系人删除的时候,只是把raw_contact表 中的raw_contact_id 设置为空。但是 实际的数据并没有删除。所以我们在查询contact_id的时候要判断是否为空.
//查询联系人的工具类 public class QueryContactsUtils { public static List<Contact> queryContacts(Context context){ //[0]创建一个集合 List<Contact> contactLists = new ArrayList<Contact>(); //[1]先查询row_contacts表 的contact_id列 我们就知道一共有几条联系人 Uri uri = Uri.parse("content://com.android.contacts/raw_contacts"); Uri dataUri = Uri.parse("content://com.android.contacts/data"); Cursor cursor = context.getContentResolver().query(uri,new String[]{"contact_id"} , null, null, null); while(cursor.moveToNext()){ String contact_id = cursor.getString(0); if (contact_id!=null) { //创建javabean对象 Contact contact = new Contact(); contact.setId(contact_id); System.out.println("contact_id:"+contact_id); //[2]根据contact_id去查询data表 查询data1列和mimetype_id //☆ ☆ ☆ ☆ 当我们在查询data表的时候 其实查询的是view_data的视图 Cursor dataCursor = context.getContentResolver().query(dataUri, new String[]{"data1","mimetype"}, "raw_contact_id=?", new String[]{contact_id}, null); while(dataCursor.moveToNext()){ String data1 = dataCursor.getString(0); String mimetype = dataCursor.getString(1); //[3]根据mimetype 区分data1列的数据类型 if ("vnd.android.cursor.item/name".equals(mimetype)) { contact.setName(data1); }else if ("vnd.android.cursor.item/phone_v2".equals(mimetype)) { contact.setPhone(data1); }else if ("vnd.android.cursor.item/email_v2".equals(mimetype)) { contact.setEmail(data1); } } //把javabean对象加入到集合中 contactLists.add(contact); } } return contactLists; } }
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); List<Contact> queryContacts = QueryContactsUtils.queryContacts(getApplicationContext()); for (Contact contact : queryContacts) { System.out.println("contat:"+contact); } } }
public void click(View v) { Uri uri = Uri.parse("content://com.android.contacts/raw_contacts"); Uri dataUri = Uri.parse("content://com.android.contacts/data"); //[2]获取name phone email Textutils String name = et_name.getText().toString().trim(); String phone = et_phone.getText().toString().trim(); String email = et_email.getText().toString().trim(); //[2.1]在插入联系人id的时候 先查询一下 row_contact 一共有几条数据 加+1就是联系人的id Cursor cursor = getContentResolver().query(uri, null, null, null, null); int count = cursor.getCount(); int contact_id = count +1; //[3] 先往row_contact表 插入联系人的id (contact_id) ContentValues values = new ContentValues(); values.put("contact_id", contact_id); getContentResolver().insert(uri,values); //[4]在把name phone email 插入到data表 ContentValues nameValues = new ContentValues(); nameValues.put("data1", name); //☆ ☆ ☆ ☆ ☆ 插入的数据要告诉数据库 属于第几条联系人 和 数据类型 nameValues.put("raw_contact_id", contact_id); nameValues.put("mimetype", "vnd.android.cursor.item/name"); getContentResolver().insert(dataUri, nameValues); //[5]把phone号码 插入到data表 ContentValues phoneValues = new ContentValues(); phoneValues.put("data1", phone); phoneValues.put("mimetype", "vnd.android.cursor.item/phone_v2"); phoneValues.put("raw_contact_id", contact_id); getContentResolver().insert(dataUri, phoneValues); //[5]把phone号码 插入到data表 ContentValues emailValues = new ContentValues(); emailValues.put("data1", email); emailValues.put("mimetype", "vnd.android.cursor.item/email_v2"); emailValues.put("raw_contact_id", contact_id); getContentResolver().insert(dataUri, emailValues); }
注意一点:
我们查询的data表,其实是查询view_data表,所以当你去查询mimetype_id的时候
你会发现报错,因为view-data表没有这个数据。 所以我们应该查询mimetype的数据。
还有data表中的raw-contacts_id 就是就是raw-contacts表中的 contacts-id的数据.
————————————————————————————————
内容观察者原理:
注意:内容观察者不是四大组件之一,不需要再清单文件中配置。
————————————————————————————————
内容观察者引用场景:
短信监听