赞
踩
我们会经常发现app中有这样的功能:
是否允许读取系统联系人?这时你点击是,然后发现你手机通讯录里面存的联系人信息竟然被你新安装的app读取到了。
由于不同的app运行在不同的进程中,这不就是进程间的通信了吗? 没错ContentProvider是安卓提供的用于不同app(进程)之间进行共享数据。
除了系统联系人外、手机信息、日程信息、这些系统的app都提供有对外的ContentProvider。接下来就来总结下ContentProvider。
我们对ContentProvider也有所了解了,知道他可以对外暴露一些想要暴露的数据,供其他进程(app)来共享这些数据。然而ContentReslover又是啥鬼?其实ContentReslover也是安卓提供的一个类,我们要想获得ContentProvider提供的数据就要通过ContentReslover这个类来实现。
ContentProvider和数据库类似有一套增删除改查。ContentProvider吧一些数据暴露出来提供了增删改查操作,我们通过ContentReslover就可以对通讯录app这些暴露的数据进行增删改查。
//对象获取
ContentResolver resolver = getContentResolver();
//增删改查 //1 增 final Uri insert(Uri url, ContentValues values)//Inserts a row into a table at the given URL. //2 删 final int delete(Uri url, String where, String[] selectionArgs) //Deletes row(s) specified by a content URI. //3 改 final int update(Uri uri, ContentValues values, String where, String[] selectionArgs)//Update row(s) in a content URI. // 4 查 final Cursor query(Uri uri, String[] projection, Bundle queryArgs, CancellationSignal cancellationSignal) final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) //Query the given URI, returning a Cursor over the result set.
可以看到上面的这些方法是不是似曾相识?没错SQLiteDataBase也是这样来完成curd的。只不过SQLiteDataBase接受的参数为表名,这里变成了URI对象。而且这里的uri为内容URI。
内容uri给内容提供器中的数据建立了唯一标识。内容uri的写法:
content://authority/path
其实authority就是ContentProvider注册时的authority字符串:
public class MyProvider extends ContentProvider {
//authority strings from manifest file
private static final String authority = "com.example.contentresloverdemo.provider";
// 暴漏两张表
public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + authority + "book");
public static final Uri USER_CONTENT_URI = Uri.parse("content://" + authority + "user");
...
}
<!-- you can also add custom permission for safe here,-->
<provider
android:process=":provider"
android:exported="true"
android:authorities="com.example.contentresloverdemo.provider"
android:name=".provider.MyProvider">
</provider>
比如我们想暴露两张表的数据,表分别为book、user这时,内容uri就可以写为:
content://com.example.contentresloverdemo.provider.book
content://com.example.contentresloverdemo.provider.user
如果只使用表名,系统无法知道我们要访问哪个app中的表,而通过URI就能精确定位程序。uri对象获得方式:
Uri uri1 = Uri.parse("content://com.example.administrator.messangerdemo/table1");
final Cursor query(Uri uri,
String[] projection,
String selection,
String[] selectionArgs,
String sortOrder)
我们通过ContentReslover来读取手机联系人。由于xml文件就一个TextView不在给出,直接上段简单的代码:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); checkPermision(); } /** * 权限检测 */ @TargetApi(Build.VERSION_CODES.O) private void checkPermision() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 1); } else { readContacts(); } } /** * 读取联系人 */ @RequiresApi(api = Build.VERSION_CODES.O) private void readContacts() { StringBuffer sb = new StringBuffer(); //sb String name = null; //联系人姓名 String number = null;// 手机号 Cursor cursor = null; cursor = getContentResolver().query( //简单模拟 实际放线程中操作(查询也是耗时) ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null); if (cursor != null) { while (cursor.moveToNext()) { name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); sb.append("联系人:") .append(name) .append("手机号:") .append(number) .append("\n"); } } ((TextView) findViewById(R.id.tv_text)).setText(sb.toString()); } @RequiresApi(api = Build.VERSION_CODES.O) @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case 1: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { readContacts(); } else { Toast.makeText(this, "权限否定", Toast.LENGTH_SHORT).show(); } break; } } }
把上面的代码简单的copy运行下就完成了简单的IPC,读取手机联系人成功,是不是感觉特别easy。哈哈接下来我们就分析下涉及的知识点:
其实就是个安卓6.0新增的特性,需要时申请权限。
安卓数据库查询返回的结果集(query方法返回值类型)常见方法:
(1)行列相关:
(2)遍历Cursou数据常用:
返回值代表是否移动成功,有指定行就返回true,没有就返回false
(3)取数据常用
根据列名获得列所在的索引这点我们明白,但是根据列的索引获得数据我们就有点迷惑了,这里不是应该返回数据集合吗?一列数据的。其实Cursor就是个结果集。我们只需要不断的移动指针让他指向不同的行,这时指针指的行与我们指定的列综合起来就确定了某行某列的数据。
上文中的Uri直接使用了这个系统字符串常量,嗯???其实这个是google规定好的系统电话的内容uri类似的还有Email,photo(联系人照片)这些系统都有封装,我们使用时查下就好啦。
系统app的ContentProvider提供的数据我们可以查询到,下面就总结下自己app的ContentProvider提供共享数据。
(1)自定义类继承ContentProvider,实现要实现的方法。
(2)提供数据库,完成要暴露表的初始化工作。
使用SQLiteOpenHelper操作原生数据库暴露数据
(1)数据库
/** * Created by sunnyDay on 2019/6/28 12:00 */ public class DbOpenHelper extends SQLiteOpenHelper { private static final String DB_NAME = "book_provider_db"; public static final String BOOK_TABLE_NAME = "book"; public static final String USER_TABLE_NAME = "user"; private static final int DB_VERSION = 1; // book table private static final String CREATE_BOOK_TABLE = "create table if not exists " + BOOK_TABLE_NAME + "(id integer primary key,name text)"; // user table private static final String CREATE_USER_TABLE = "create table if not exists " + USER_TABLE_NAME + "(id integer primary key,name text,sex integer)"; public DbOpenHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_USER_TABLE); db.execSQL(CREATE_BOOK_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO nothing // call this method when db version to upgrade } }
(2)ContentProvider
/** * Created by sunnyDay on 2019/6/28 11:39 */ public class MyProvider extends ContentProvider { private static final String authority = "com.example.contentresloverdemo.provider"; //authority strings from manifest file public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + authority + "book"); public static final Uri USER_CONTENT_URI = Uri.parse("content://" + authority + "user"); private static final int BOOK_URI_CODE = 0; private static final int USER_URI_CODE = 1; // uri 和code 建立联系 。建立联系后我们可以根据uri的到code值,根据code值 的到数据表名称 private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); //uriMatcher 对象 参数为UriMatcher 内部提供的常量 static { uriMatcher.addURI(authority, "book", BOOK_URI_CODE);// 代表 BOOK_CONTENT_URI uriMatcher.addURI(authority, "user", USER_URI_CODE); // 代表 USER_CONTENT_URI } private SQLiteDatabase mDB; /** * 根据 uri 获得表名 */ private String getTableName(Uri uri) { String tableName = null; switch (uriMatcher.match(uri)) { //int match(uri) case BOOK_URI_CODE: tableName = DbOpenHelper.BOOK_TABLE_NAME; break; case USER_URI_CODE: tableName = DbOpenHelper.USER_TABLE_NAME; break; default: break; } return tableName; } @Override public boolean onCreate() { mDB = new DbOpenHelper(getContext()).getReadableDatabase(); mDB.execSQL("insert into book values (3,'android')"); mDB.execSQL("insert into book values (4,'java')"); mDB.execSQL("insert into user values (1,'tom',20)"); mDB.execSQL("insert into user values (2,'kate',20)"); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { String tableName = getTableName(uri);//获得用户想要访问的表名 if (tableName == null) { throw new ArithmeticException("un support uri:" + uri); } return mDB.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null); } @Override public String getType(Uri uri) { return null; } @Override public Uri insert(Uri uri, ContentValues values) { String tableName = getTableName(uri);//获得用户想要访问的表名 if (tableName == null) { throw new ArithmeticException("un support uri:" + uri); } mDB.insert(tableName,null,values); getContext().getContentResolver().notifyChange(uri,null); return uri; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { String tableName = getTableName(uri);//获得用户想要访问的表名 if (tableName == null) { throw new ArithmeticException("un support uri:" + uri); } int count = mDB.delete(tableName,selection,selectionArgs); if (count>0){ getContext().getContentResolver().notifyChange(uri,null); } return count; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { String tableName = getTableName(uri);//获得用户想要访问的表名 if (tableName == null) { throw new ArithmeticException("un support uri:" + uri); } int row = mDB.update(tableName,values,selection,selectionArgs); if (row>0){ getContext().getContentResolver().notifyChange(uri,null); } return row; } }
(3)MainActivity测试
/** * 自定义 内容提供者逻辑处理 */ @RequiresApi(api = Build.VERSION_CODES.O) private void customProvider() { queryBook(); // 查询book表 queryUser();// 查询user 表 } @RequiresApi(api = Build.VERSION_CODES.O) private void queryUser() { Uri uri = Uri.parse("content://com.example.contentresloverdemo.provider/user"); Cursor cursor = getContentResolver().query(uri, null, null, null); if (null != cursor){ while(cursor.moveToNext()){ String userName = cursor.getString(cursor.getColumnIndex("name")); String userid = cursor.getString(cursor.getColumnIndex("id")); Log.i("aaa", "customProvider: "+userid+userName); } } } @RequiresApi(api = Build.VERSION_CODES.O) private void queryBook() { Uri uri = Uri.parse("content://com.example.contentresloverdemo.provider/book"); Cursor cursor = getContentResolver().query(uri, null, null, null); if (null != cursor){ while(cursor.moveToNext()){ String bookName = cursor.getString(cursor.getColumnIndex("name")); String bookid = cursor.getString(cursor.getColumnIndex("id")); Log.i("aaa", "customProvider: "+bookid+bookName); } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。