Android之 ContentProvider内容提供者详解

一 四大组件

1.1 Activity组件,它一个单独的窗口,程序流程都必须在Activity中运行。

1.2 service组件,用于在后台完成用户指定的操作。

1.3 content provider组件,会为所有的应用准备一个内容窗口,并且保留数据库、文件。

1.4 broadcast receiver组件,是程序之间传递信息时的一种机制,作用就是接收或者发送通知。

二 content provider介绍

2.1 ContentProvider 主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另外一个程序的数据,同时还能保证被访问数据的安全性

2.2 Content Provider 不同于 文件存储 和 ShardPreferences存储 全局可读写操作模式,它可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险。

2.3 Content Provider 的用法一般有两种:

  • 使用现有的内容提供器来读取和操作相应程序中的数据,比如系统的通讯录,系统短信信息;
  • 创建自己的内容提供器给我们程序的数据提供外部访问接口,即自定义内容提供者

2.4 如果一个应用程序通过内容提供器对其数据提供了外部访问接口,那么任何其他的应用程序就都可以对这部分数据进行访问。 例如:系统中自带的电话簿、短信、媒体库等。

2.5 Content Provider 的使用和 SQLite 使用非常相似,提供增删改查数据,两者的区别:

  • SQLite可以为应用程序创建完全封装的关系数据库。使用这些数据库可以存储和管理复杂的、结构化的应用程序数据。Android数据库存储在设备上的/data/data/<pacage_name>/databases文件夹中。所有的数据库都是私有的,只能被创建它们的应用程序访问。
  • Content Provider提供了一种基于使用content:://模式的简单URI寻址模型来发布和使用数据的接口。它们允许将应用层从底层数据层中分离出来,通过抽象底层数据源使应用程序不必依赖于某个数据源。

三 content provider 使用

3.1 常用方法:

  • onCreate():当提供者被启动时调用。
  • query():该方法从客户端接受请求,结果是返回指针(Cursor)对象。
  • insert():该方法向内容提供者插入新的记录。
  • delete():该方法从内容提供者中删除已存在的记录。
  • update():该方法更新内容提供者中已存在的记录。
  • getType():该方法为给定的URI返回元数据类型。

3.2 查询构造函数,返回Cursor方便下一步遍历数据

Cursor cursor = getContentResolver().query(
		 Uri uri, 
         String[] projection,
         String selection, 
         String[] selectionArgs,
         String sortOrder
);    
  • uri:查询路径,如查询某个应用下的一张表,结构一般是content://包名.provider/表名。比如 Uri uri = Uri.parse("content://com.example.app.provider/table1")。对应sqlite中的 “ from tableName ” 
  • projection:查询指定列名,对应sqlite中的 “ select column ” 
  • selection:约束条件,对应sqlite的中 “ where  column = value ” 
  • selectionArgs:为where的占位符附上具体的值,对应sqlite的中 execute("select * from table where id= ? ",(100) )
  • sortOrder:查询结果的排序方式,对应sqlite中的 “ order by column ” 

3.3  插入数据,返回uri路径

Uri uri = getContentResolver().insert(
        Uri uri, 
        ContentValues values); 
  • uri:查询路径,表名
  • ContentValues:是一个类似map的键值对,使用put可以添加数据,如values.put("列名","值");

3.4 更新数据,返回更新条数

int count= getContentResolver().update(
	Uri uri,
	ContentValues values,
    String where, 
	String[] selectionArgs);

  • uri:查询路径,表名
  • ContentValues:是一个类似map的键值对,使用put可以添加数据,如values.put("列名","值");
  • selection:约束条件,对应sqlite的中 “ where  column = value ” 
  • selectionArgs:为where的占位符附上具体的值,对应sqlite的中 execute("select * from table where id= ? ",(100) )

3.5 删除数据,返回改变条数

int count = getContentResolver().delete(
	 Uri url, 
	 String where,
	 String[] selectionArgs);
  • uri:查询路径,表名
  • 约束条件,对应sqlite的中 “ where  column = value ” 
  • selectionArgs:为where的占位符附上具体的值,对应sqlite的中 execute("select * from table where id= ? ",(100) )

四  content provider调用系统数据

4.1 获取通讯录数据:

清单文件需要注册通讯录查询修改权限

<!--允许应用程序写出到联系人-->
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<!--允许应用程序读取到联系人-->
<uses-permission android:name="android.permission.READ_CONTACTS"/>	

然后动态请求权限,查询通讯录数据 

public class MainActivity extends AppCompatActivity {

    ArrayAdapter<String> adapter;
    List<String> contactsList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ListView contactsView = findViewById(R.id.contacts_view);
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, contactsList);
        contactsView.setAdapter(adapter);
        // 判断当前是否有权限
        if (ContextCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.READ_CONTACTS}, 1);
        } else {
            readContacts();
        }
    }

    /**
     * 读取通信录数据
     */
    private void readContacts() {
        Cursor cursor = null;
        try {
            cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
            if (cursor != null) {
                while (cursor.moveToNext()) {
                    String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                    String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                    contactsList.add(displayName + "\n" + number);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 1:
                // 结果是授予权限则读取联系人列表
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    readContacts();
                    // 为赋予权限则弹出提示框
                } else {
                    Toast.makeText(this, "you denied the permission", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
        }
    }
}

4.2 插入通讯录数据

private void insertData(){
	String name = "张三";
	String phone = "123456789101";

	ContentValues values = new ContentValues();
	Uri uri = getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, values);
	long rawContentID = ContentUris.parseId(uri);
	values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContentID);
	values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
	values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, name);
	values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);

	getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);

}

4.3 根据用户名更新手机号信息 

private void updateData(){
	String name_update = "张三";
	String phone_update = "123456789101";

	Long rawContactId = 0L;
	ContentValues valuesUpdate= new ContentValues();
	valuesUpdate.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone_update);

	Cursor cursorUpdate = getContentName(name_update);
	if (cursorUpdate.moveToFirst()) {
		rawContactId = cursorUpdate.getLong(0);
	}
	getContentResolver().update(ContactsContract.Data.CONTENT_URI, valuesUpdate, "raw_contact_id=?", new String[]{rawContactId + ""});
	cursorUpdate.close();
}

//根据名字查询
private Cursor getContentName(String name_search) {
	String[] query_all = new String[]{
			ContactsContract.CommonDataKinds.Identity.RAW_CONTACT_ID, //用户id
			ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, //联系人姓名
			ContactsContract.CommonDataKinds.Phone.NUMBER //联系人电话
	};

	String selections = ContactsContract.Contacts.DISPLAY_NAME + "=?";
	String[] selection_args = new String[]{name_search};
	Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, query_all, selections, selection_args, null);
	return cursor;
}

4.4 删除通讯录信息

private void deleteData() {
	String name1 = "张三";
	int count = getContentResolver().delete(ContactsContract.RawContacts.CONTENT_URI, ContactsContract.Contacts.DISPLAY_NAME + "=?", new String[]{name1});
	if (count > 0) {
		Toast.makeText(this, "删除成功!", Toast.LENGTH_SHORT).show();
	}
}

五 自定义content provider

5.1  系统为我们提供了固定的表名,列名,不能在修改表结构。如果我们需要自己写一个内容提供者给外部程序使用,还是要借助SQLite数据库进行自定义数据的增删改查。

5.2 如下示例,自定义contentprovider

public class PersonProvider extends ContentProvider {
 
	private DBHelper mHelper;
	
	private static final int PERSON_DIR = 0;
	private static final int PERSON_ITEM = 1;
 
	private static final String tag = "PersonProvider";
	
	private static UriMatcher mMatcher;
	
	static{
		// 添加匹配规则
		mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		mMatcher.addURI("com.example.contentproviderdemo.PersonProvider.provider", "person", PERSON_DIR);
		mMatcher.addURI("com.example.contentproviderdemo.PersonProvider.provider", "person/#", PERSON_ITEM);
	}
	
	@Override
	public boolean onCreate() {
		mHelper = new DBHelper(getContext(), "test.db");
		return true;
	}
 
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		Cursor cursor = null;
		SQLiteDatabase db = mHelper.getReadableDatabase();
		
		switch (mMatcher.match(uri)) {
		case PERSON_DIR:
			Log.d(tag, "PERSON_DIR");
			cursor = db.query("person", projection, selection, selectionArgs, null, null, sortOrder);
			break;
		case PERSON_ITEM:
			Log.d(tag, "PERSON_ITEM");
			// 获取id.内部是把uri是"/"进行分割了,所以获取get(1)就是id值。
			String personID = uri.getPathSegments().get(1);
			cursor = db.query("person", projection, "_id = ?", new String[]{personID}, null, null, sortOrder);
			break;
		default:
			Log.d(tag, "Invalid uri");
			break;
		}
		return cursor;
	}
 
	@Override
	public String getType(Uri uri) {
		String result = null;
 
		switch (mMatcher.match(uri)) {
		case PERSON_DIR:
			result = "vnd.android.cursor.dir/com.example.contentproviderdemo.PersonProvider.provider.person";
			break;
		case PERSON_ITEM:
			result = "vnd.android.cursor.item/com.example.contentproviderdemo.PersonProvider.provider.person";
			break;
		default:
			break;
		}
		
		return result;
	}
 
	@Override
	public Uri insert(Uri uri, ContentValues values) {
		Uri result = null;
		SQLiteDatabase db = mHelper.getWritableDatabase();
		
		switch (mMatcher.match(uri)) {
		case PERSON_DIR:
			long id = db.insert("person", null, values);
			String uriString = "content://com.example.contentproviderdemo.PersonProvider.provider/person/" + id;
			result = Uri.parse(uriString);
			break;
		default:
			break;
		}
		return result;
	}
 
	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		SQLiteDatabase db = mHelper.getWritableDatabase();
		int result = 0;
		
		switch (mMatcher.match(uri)) {
		case PERSON_DIR:
			result = db.delete("person", selection, selectionArgs);
			break;
		case PERSON_ITEM:
			String personID = uri.getPathSegments().get(1);
			result = db.delete("person", "_id = ?", new String[]{personID});
			break;
		default:
			break;
		}
		
		return result;
	}
 
	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		SQLiteDatabase db = mHelper.getWritableDatabase();
		int result = 0;
		
		switch (mMatcher.match(uri)) {
		case PERSON_DIR:
			result = db.update("person", values, selection, selectionArgs);
			break;
		case PERSON_ITEM:
			String personID = uri.getPathSegments().get(1);
			result =  db.update("person", values, "_id = ?", new String[]{personID});
			break;
		default:
			break;
		}
		
		return result;
	}
 
}

5.3 如果其它程序能够访问,还需要在清单文件注册自定义的内容提供者

<provider 
	android:name="com.dinghe.test.PersonProvider"
	android:authorities="com.dinghe.test.PersonProvider.provider"
	android:exported="true"
/>

六 总结

内容提供者大部分场景还是调用系统的资源,比如通讯录,短信,通话记录等,还有Android7.0版本以后访问文件也需要添加内容提供者,自定义数据的访问还是主要通过数据库方案。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值