ContentProvider是Android四大組件之一,它為存儲和獲取數(shù)據(jù)提供統(tǒng)一的接口。即將應(yīng)用程序的數(shù)據(jù)提供給外部程序使用的一種手段和方法。最大特點是可以實現(xiàn)不同的應(yīng)用程序之間共享數(shù)據(jù)。
對于ContentProvider來說,無論數(shù)據(jù)的來源是什么留储,他都認為是一種表,它會把數(shù)據(jù)組織成表格咙轩。
兩種使用情景:1.A應(yīng)用訪問B應(yīng)用获讳,2A應(yīng)用訪問系統(tǒng)應(yīng)用(通訊錄)
1A應(yīng)用訪問B應(yīng)用:(主要是實現(xiàn)定義一個ContentProvider的操作)
(1)自定義一個類繼承于ContentProvider,并且實現(xiàn)它的方法:注意操作步驟
想要建立文件的地方->右鍵->new->other->Content Provider->修改名稱,填寫唯一的uri->完成活喊。
public class OutContentProvider extends ContentProvider {
public OutContentProvider() {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Implement this to handle requests to delete one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public String getType(Uri uri) {
// TODO: Implement this to handle requests for the MIME type of the data
// at the given URI.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO: Implement this to handle requests to insert a new row.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public boolean onCreate() {
// TODO: Implement this to initialize your content provider on startup.
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO: Implement this to handle query requests from clients.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO: Implement this to handle requests to update one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
}
然后看一下manifest.xml:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<provider
android:name=".OutContentProvider"
android:authorities="com.stone.mycontentprovider.outcp"
android:enabled="true"
android:exported="true"></provider>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
當然也可以先自定義個類丐膝,然后繼承ContentProvider,實現(xiàn)他的方法,然后配置mainfest.xml帅矗。
ok偎肃,下一步。
(2)由于這是一個簡單的使用步驟浑此,所以就簡單的按照sqlite數(shù)據(jù)庫來寫累颂,當然數(shù)據(jù)的來源還可以是其他的任何的一種,這里只是舉個例子
由于ContentProvider的創(chuàng)建是在應(yīng)用第一次打開的時候就創(chuàng)建的凛俱,所以為了方便吧數(shù)據(jù)也在ContentProvider中創(chuàng)建了
SQLiteDatabase sqLiteDatabase;
@Override
public boolean onCreate() {
// TODO: Implement this to initialize your content provider on startup.
//創(chuàng)建數(shù)據(jù)庫
Log.e("###", "onCreate");
SQLiteOpenHelper helper = new SQLiteOpenHelper(getContext(), "outcp.db", null, 1) {
@Override
public void onCreate(SQLiteDatabase db) {
//創(chuàng)建表格
String sql = "create table demo_table(_id integer primary key autoincrement," +
"name varchar(20)," +
"age integer," +
"gender varchar(2))";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
};
sqLiteDatabase = helper.getReadableDatabase();
//返回true紊馏,這樣才能會返回數(shù)據(jù)
return true;
}
注意返回值改成true。
(3)insert()方法:增加數(shù)據(jù)蒲犬,在OutContentProvider中
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO: Implement this to handle requests to insert a new row.
// throw new UnsupportedOperationException("Not yet implemented");
Long itemId = 0l;
try {
itemId = sqLiteDatabase.insertOrThrow("demo_table", null, values);
} catch (Exception e) {
Log.e("###", e.getLocalizedMessage());
}
Log.e("###", "調(diào)用了out中的insert方法");
//表名朱监,非空列,值,對于sqlite數(shù)據(jù)庫原叮,添加完成之后會返回一個Long的id
//uri這個uri就是另外的程序調(diào)用contentprovider的時候傳過來的uri赫编,如果傳回去那么跟調(diào)用程序的uri沒有區(qū)別也沒有意義
// 所以用ContentUris把uri和itemid整合一下,成為一個新的Uri奋隶,返回回去擂送。
return ContentUris.withAppendedId(uri, itemId);
}
注意:這里的 try {}catch((Exception e){} 可以不寫,insertOrThrow可以改成insert达布。另外ContentUris是處理uri的一個類团甲, ContentUris.withAppendedId(uri, itemId)是吧uri追加一個id作為返回值,為了方便調(diào)用者獲取到添加成功的狀態(tài)和id黍聂。
(4)insert()方法的調(diào)用:這里需要新建一個其他的項目,在調(diào)用項目的合適的位置寫一個調(diào)用方法身腻。這里寫了一個textview的點擊監(jiān)聽事件:
Uri mUri = Uri.parse("content://com.stone.mycontentprovider.outcp");
ContentResolver resolver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//獲取ContentResolver對象
resolver = getContentResolver();
}
public void insertTv(View view) {
ContentValues values = new ContentValues();
values.put("name", MyUtils.getChineseName());
values.put("age", MyUtils.getNum(10, 60));
values.put("gender", MyUtils.getGender());
Uri reUri = resolver.insert(mUri, values);
Long reId = ContentUris.parseId(reUri);
Toast.makeText(MainActivity.this, reId + "", Toast.LENGTH_LONG).show();
}
注意:這里的uri就是ContentProvider提供者的insert方法返回的我們添加了id的那個uri产还,同樣的我們使用ContentUris這個類的 ContentUris.parseId(reUri);方法拿到添加成功的數(shù)據(jù)的id。
(5)query()查詢方法:
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO: Implement this to handle query requests from clients.
// throw new UnsupportedOperationException("Not yet implemented");
Cursor cursor = sqLiteDatabase.query("demo_table", projection, selection, selectionArgs, null, null, sortOrder);
return cursor;
}
(6)調(diào)用查詢方法:
SimpleCursorAdapter simpleCursorAdapter;
ListView mListView;
public void queryTv(View view) {
// 根據(jù)contentprovider中的查詢方法:Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder)
//rui,查詢哪一列嘀趟,查詢條件脐区,條件之,排序
Cursor cursor = resolver.query(mUri, null, null, null, null);
//這里的listview的adapter用到SimpleCursorAdapter
simpleCursorAdapter = new SimpleCursorAdapter(this,
R.layout.layout_adapter_cursor,
cursor,
new String[]{"_id", "name", "age", "gender"},
new int[]{R.id.item_id, R.id.item_name, R.id.item_age, R.id.item_gender},
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
mListView.setAdapter(simpleCursorAdapter);
}
注意這里直接用的SimpleCursorAdapter其中的參數(shù)分別是:SimpleCursorAdapter(context,layout,cursor,鍵名的字符串數(shù)組她按,對應(yīng)的控件的id牛隅,標志).關(guān)于SimpleCursorAdapter有一篇博客可以看一下
(7)delete():刪除方法
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Implement this to handle requests to delete one or more rows.
// throw new UnsupportedOperationException("Not yet implemented");
int result = sqLiteDatabase.delete("demo_table", selection, selectionArgs);
return result;
}
(8)調(diào)用刪除方法:
int delResult = resolver.delete(mUri, "_id=?", new String[]{itemId});
if (delResult > 0) {
Toast.makeText(MainActivity.this, "刪除成功", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "刪除灰白", Toast.LENGTH_LONG).show();
}
(9)修改updata()方法
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO: Implement this to handle requests to update one or more rows.
// throw new UnsupportedOperationException("Not yet implemented");
int upReslut = sqLiteDatabase.update("demo_table", values, selection, selectionArgs);
return upReslut;
}
(10)調(diào)用修改方法:
ContentValues upValues = new ContentValues();
upValues.put("name", MyUtils.getChineseName());
upValues.put("age", MyUtils.getNum(10, 60));
upValues.put("gender", MyUtils.getGender());
int upResult=resolver.update(mUri,upValues,"_id=?",new String[]{itemId});
if (upResult>0){
Toast.makeText(MainActivity.this, "修改成功", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "修改灰白", Toast.LENGTH_LONG).show();
}
ok,以上記錄的是設(shè)置ContentProvider的方法酌泰,和相應(yīng)的方法的調(diào)用然后再看一下關(guān)于Uri的解析媒佣。
Uri解析有兩種方法,一種是使用UriMatcher解析陵刹,一種是Uri自帶的解析默伍。
(一)UriMatcher解析:事先制定好匹配規(guī)則,當調(diào)用ContentProvide中的方法時,利用制定好的匹配規(guī)則匹配uri也糊,根據(jù)不同的uri給出不同的處理:以刪除為例在oncreat中制定規(guī)則:
UriMatcher matcher;
@Override
public boolean onCreate() {
// TODO: Implement this to initialize your content provider on startup.
//闖將數(shù)據(jù)庫
Log.e("###", "onCreate");
//匹配規(guī)則:
//UriMatcher.NO_MATCH:無法匹配
matcher = new UriMatcher(UriMatcher.NO_MATCH);
matcher.addURI("com.stone.mycontentprovider.outcp", "democp", 1000);
matcher.addURI("com.stone.mycontentprovider.outcp", "democp/child", 1001);
matcher.addURI("com.stone.mycontentprovider.outcp", "democp/#", 1002);//后面是數(shù)字
matcher.addURI("com.stone.mycontentprovider.outcp", "democp/*", 1003);//任意字符
//返回true炼蹦,這樣才能會返回數(shù)據(jù)
return true;
}
注意:其中的democp可以使任意的字符串,也可以沒有狸剃。
刪除方法:
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Implement this to handle requests to delete one or more rows.
// throw new UnsupportedOperationException("Not yet implemented");
int result = 0;
int code = matcher.match(uri);
switch (code) {
case 1000:
Log.e("###match","匹配到的路徑是"+uri.toString());
break;
case 1001:
Log.e("###match","匹配到的路徑是"+uri.toString());
break;
case 1002:
Log.d("###match","匹配到路徑為"+uri.toString()+"任意數(shù)字的內(nèi)容");
break;
case 1003:
Log.i("###match","匹配到路徑為"+uri.toString()+"任意字符的內(nèi)容");
break;
default:
Log.e("###match","執(zhí)行刪除數(shù)據(jù)庫內(nèi)容的方法");
result = sqLiteDatabase.delete("demo_table", selection, selectionArgs);
break;
}
return result;
}
那么調(diào)用刪除方法:
resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/democp"),null,null);
resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/democp/child"),null,null);
resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/democp/123"),null,null);
resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/democp/090"),null,null);
resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/democp/89ii"),null,null);
resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/democp/ab90"),null,null);
resolver.delete(Uri.parse("content://com.stone.mycontentprovider.outcp/123/ab90"),null,null);
運行結(jié)果:
E/###match: 匹配到的路徑是content://com.stone.mycontentprovider.outcp/democp
E/###match: 匹配到的路徑是content://com.stone.mycontentprovider.outcp/democp/child
D/###match: 匹配到路徑為content://com.stone.mycontentprovider.outcp/democp/123任意數(shù)字的內(nèi)容
D/###match: 匹配到路徑為content://com.stone.mycontentprovider.outcp/democp/090任意數(shù)字的內(nèi)容
I/###match: 匹配到路徑為content://com.stone.mycontentprovider.outcp/democp/89ii任意字符的內(nèi)容
I/###match: 匹配到路徑為content://com.stone.mycontentprovider.outcp/democp/ab90任意字符的內(nèi)容
E/###match: 執(zhí)行刪除數(shù)據(jù)庫內(nèi)容的方法
(二)Uri自帶的解析方法:以添加為例:
調(diào)用添加方法:
Uri othUri=resolver.insert(Uri.parse("content://com.stone.mycontentprovider.outcp/anypath?name="+MyUtils.getChineseName()
+"&age="+MyUtils.getNum(10, 60)
+"&gender="+ MyUtils.getGender()),new ContentValues());
注意其中的anypath可以使任意的字符串掐隐,也可以不要。
MyContentProvider中的添加方法:
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO: Implement this to handle requests to insert a new row.
// throw new UnsupportedOperationException("Not yet implemented");
Long itemId = 0l;
if (values.size()>0){
try {
itemId = sqLiteDatabase.insertOrThrow("demo_table", null, values);
} catch (Exception e) {
Log.e("###", e.getLocalizedMessage());
}
}else {
String authory=uri.getAuthority();
Log.e("###authory",authory);
String path=uri.getPath();
Log.e("###path",path);
String query=uri.getQuery();
Log.e("###query",query);
String name=uri.getQueryParameter("name");
Log.e("###name",name);
String age=uri.getQueryParameter("age");
Log.e("###age",age);
String gender=uri.getQueryParameter("gender");
Log.e("###gender",gender);
values.put("name", name);
values.put("age",age);
values.put("gender",gender);
itemId = sqLiteDatabase.insertOrThrow("demo_table", null, values);
}
Log.e("###", "調(diào)用了out中的insert方法");
//表名钞馁,非空列虑省,值,對于sqlite數(shù)據(jù)庫,添加完成之后會返回一個Long的id
//uri這個uri就是另外的程序調(diào)用contentprovider的時候傳過來的uri指攒,如果傳回去那么跟調(diào)用程序的uri沒有區(qū)別也沒有意義
// 所以用ContentUris把uri和itemid整合一下慷妙,成為一個新的Uri,返回回去允悦。
return ContentUris.withAppendedId(uri, itemId);
}
運行結(jié)果:
2020-07-23 20:07:33.190 15996-16123/com.stone.mycontentprovider E/###: 調(diào)用了out中的insert方法
2020-07-23 20:07:33.246 15996-16123/com.stone.mycontentprovider E/###authory: com.stone.mycontentprovider.outcp
2020-07-23 20:07:33.246 15996-16123/com.stone.mycontentprovider E/###path: /anypath
2020-07-23 20:07:33.247 15996-16123/com.stone.mycontentprovider E/###query: name=咸桂&age=45&gender=男
2020-07-23 20:07:33.247 15996-16123/com.stone.mycontentprovider E/###name: 咸桂
2020-07-23 20:07:33.248 15996-16123/com.stone.mycontentprovider E/###age: 45
2020-07-23 20:07:33.248 15996-16123/com.stone.mycontentprovider E/###gender: 男
2020-07-23 20:07:33.256 15996-16123/com.stone.mycontentprovider E/###: 調(diào)用了out中的insert方法
兩種添加方法都執(zhí)行了膝擂,所以打印了兩邊“” 調(diào)用了out中的insert方法“”。
ok關(guān)于解析就簡單記錄這些東西隙弛。下面簡單說一下調(diào)用系統(tǒng)ContentProvider的問題:
(一)讀取短信息:基本分三個步驟:
(1)獲取ContentResolver
ContentResolver contentResolver = getContentResolver();
(2)查詢短信息
Uri smsUri = Uri.parse("content://sms/");
Cursor smsCoursor = contentResolver.query(smsUri, null, null, null, null);
(3)遍歷smsCoursor 架馋,得到每一條的數(shù)據(jù)
//遍歷所有的行
while (smsCoursor.moveToNext()) {
//參數(shù)是列索引,遍歷所有的列
for (int i = 0; i < smsCoursor.getColumnCount(); i++) {
Log.e("###", smsCoursor.getString(i));//此時獲取到的是每一條的所有內(nèi)容
String address=cursor.getString(cursor.getColumnIndex("address"));//地址
String body=cursor.getString(cursor.getColumnIndex("body"));//短信內(nèi)容
String dateString=cursor.getString(cursor.getColumnIndex("date"));//日期
}
}
(4)注意點:
"content://sms/":短信箱
"content://sms/inbox":收件箱
"content://sms/sent":發(fā)件箱
"content://sms/draft":草稿箱
(5)權(quán)限不要忘了動態(tài)申請全闷,manifest也要注冊叉寂。
<uses-permission android:name="android.permission.READ_SMS"/>
(6)還有我的紅米手機獲取不到短信息,查了資料說是沒有權(quán)限总珠,現(xiàn)在還沒有解決屏鳍,希望各位看官能給個指導(dǎo)意見和方法。華為的可以局服。
(二)讀取聯(lián)系人:通訊錄中的姓名和其他內(nèi)容是由不同的contentprovider提供的钓瞭。也就是說姓名和其他內(nèi)容是不同的表,姓名是主表淫奔,其他內(nèi)容是從表山涡,主表中的主鍵會在從表中作為外鍵使用
(1)權(quán)限:動態(tài)申請
<uses-permission android:name="android.permission.READ_CONTACTS"/>
(2)讀取數(shù)據(jù)
ContentResolver resolver;
resolver = getContentResolver();
//先讀取聯(lián)系人信息
Cursor cCursor = resolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
while (cCursor.moveToNext()) {
// ContactsContract.Contacts.DISPLAY_NAME//姓名
// ContactsContract.Contacts._ID//id
//根據(jù)讀取到的聯(lián)系人信息遍歷Cursor獲取每一個聯(lián)系人的姓名和id
String name = cCursor.getString(cCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String mId = cCursor.getString(cCursor.getColumnIndex(ContactsContract.Contacts._ID));
Log.e("###name", name + " ");
Log.e("###mId", mId + " ");
//根據(jù)id獲取對應(yīng)的聯(lián)系人的其他信息
String selections = ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?";//查詢條件
Cursor phoneCursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, selections, new String[]{mId}, null);
//變美麗其他信息
while (phoneCursor.moveToNext()) {
//獲取電話號碼
String phoneNum = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
name += " " + phoneNum;
}
//這個時候的log打印的就是姓名和聯(lián)系電話
Log.e("###name", name + " ");
}
(三)添加聯(lián)系人:上面我們知道了聯(lián)系人姓名和電話是在兩個表中,所以寫入聯(lián)系人也需要添加兩次:
(1)權(quán)限:WRITE_CONTACTS唆迁,動態(tài)的你懂的
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
(2)寫入聯(lián)系人
//1.往一個contentprovider中插入一條空數(shù)據(jù)鸭丛,獲取新生成的id
ContentValues values = new ContentValues();
Uri uri = resolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
long id = ContentUris.parseId(uri);//獲取到剛剛生成的id
//2.利用剛剛獲取到的id分別組合姓名和電話號碼往另外一個contentprovider中插入數(shù)據(jù)
//插入姓名
values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,MyUtils.getChineseName());//插入的姓名
values.put(ContactsContract.Data.RAW_CONTACT_ID,id);//指定和姓名關(guān)聯(lián)的id
values.put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);//設(shè)置該行數(shù)據(jù)的類型
resolver.insert(ContactsContract.Data.CONTENT_URI,values);//插入操作
//插入電話
values.clear();
values.put(ContactsContract.Data.RAW_CONTACT_ID,id);//指定和電話關(guān)聯(lián)的id
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER,"13788883333");//插入的電話
values.put(ContactsContract.CommonDataKinds.Phone.TYPE,ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);//插入的電話的類型
values.put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);//設(shè)置該行數(shù)據(jù)的類型
resolver.insert(ContactsContract.Data.CONTENT_URI,values);//插入操作