七 内容提供器(ContentProvider)
7.1 介绍
- 内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。目前,使用内容提供器是Android实现跨程序共享数据的标准方式。
- 不同于文件存储和SharedPreferences存储中的两种全局可读写操作模式,内容提供器可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险。
7.2 运行时权限
- Android的权限机制并不是什么新鲜事物,从系统的第一个版本开始就已经存在了。但其实之前Android的权限机制在保护用户安全和隐私等方面起到的作用比较有限,尤其是一些大家都离不开的常用软件,非常容易店大欺客。
- 为此,Android开发团队在Android 6.0系统中引用了运行时权限这个功能,从而更好地保护了用户的安全和隐私,那么本节我们就来详细学习一下这个6.0系统中引入的新特性。
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
7.3 动态申请权限
<uses-permission android:name="android.permission.CALL_PHONE" />
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="打电话"
android:onClick="call"
/>
</LinearLayout>
package com.shu;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void call(View view){
if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CALL_PHONE},1);
}else {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode){
case 1:
if(grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}
else {
Toast.makeText(this,"权限未通过",Toast.LENGTH_LONG).show();
}
break;
default:
}
}
}
- 因此,第一步就是要先判断用户是不是已经给过我们授权了,借助的ContextCompat.checkSelfPermission()方法。
- checkSelfPermission()方法接收两个参数,第一个参数是Context,这个没什么好说的,第二个参数是具体的权限名,比如打电话的权限名就是Manifest.permission.CALL_PHONE,然后我们使用方法的返回值和PackageManager. PERMISSION_GRANTED做比较,相等就说明用户已经授权,不等就表示用户没有授权。
- 如果已经授权的话就简单了,直接去执行拨打电话的逻辑操作就可以了,这里我们把拨打电话的逻辑封装到了call()方法当中。如果没有授权的话,则需要调用ActivityCompat. requestPermissions()方法来向用户申请授权,requestPermissions()方法接收3个参数,第一个参数要求是Activity的实例,第二个参数是一个String数组,我们把要申请的权限名放在数组中即可,第三个参数是请求码,只要是唯一值就可以了,这里传入了1。
7.4 ContentResolver
- 对于每一个应用程序来说,如果想要访问内容提供器中共享的数据,就一定要借助Content-Resolver类,可以通过Context中的getContentResolver()方法获取到该类的实例。
- Content-Resolver中提供了一系列的方法用于对数据进行CRUD操作,其中insert()方法用于添加数据,update()方法用于更新数据,delete()方法用于删除数据,query()方法用于查询数据。
- 不同于SQLiteDatabase, ContentResolver中的增删改查方法都是不接收表名参数的,而是使用一个Uri参数代替,这个参数被称为内容URI。内容URI给内容提供器中的数据建立了唯一标识符,它主要由两部分组成:authority和path。
- authority是用于对不同的应用程序做区分的,一般为了避免冲突,都会采用程序包名的方式来进行命名。
content://com.android.contacts/data/phones
手机联系人url
查询方法
cursor=getContentResolver().query();
public final Cursor query( Uri uri,
String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
return query(uri, projection, selection, selectionArgs, sortOrder, null);
}
![epub_26211889_207.jpg](https://img-blog.csdnimg.cn/img_convert/7b3423a379f8bfcdeb42b782fbdab64e.png#clientId=ubda6a572-d1fe-4&crop=0&crop=0&crop=1&crop=1&from=drop&id=u8be2f858&margin=[object Object]&name=epub_26211889_207.jpg&originHeight=272&originWidth=1280&originalType=binary&ratio=1&rotation=0&showTitle=false&size=41739&status=done&style=none&taskId=u7e351c8b-f4d1-42af-ba49-a43c2732732&title=)
private void read(){
Cursor cursor = null;
try {
System.out.println("xxx读取url:"+ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
cursor=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
if(cursor!=null){
while (cursor.moveToNext()){
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String phone = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
listViews.add(name+"\n"+phone);
}
adapter.notifyDataSetChanged();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(cursor!=null){
cursor.close();
}
}
}
插入数据 将待添加的数据组装到ContentValues中,然后调用ContentResolver的insert()方法,将Uri和ContentValues作为参数传入即可。
public void add(){
System.out.println("xxx读取url:"+ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
ContentValues contentValues=new ContentValues();
contentValues.put("admin",10086);
getContentResolver().insert(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,contentValues)
}
修改数据
public void update(){
System.out.println("xxx读取url:"+ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
ContentValues contentValues=new ContentValues();
contentValues.put("admin","");
getContentResolver().update(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,contentValues,"admin=?",new String[]{"10001"});
}
删除数据
public void delete(){
System.out.println("xxx读取url:"+ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
ContentValues contentValues=new ContentValues();
contentValues.put("admin","");
getContentResolver().delete(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,"admin=?",new String[]{"10001"});
}
7.5 读取通讯录
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstActivity">
<ListView
android:id="@+id/listview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
package com.shu;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class FirstActivity extends AppCompatActivity {
ArrayAdapter<String> adapter;
List<String> listViews=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
ListView listView = (ListView) findViewById(R.id.listview);
adapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1, listViews);
listView.setAdapter(adapter);
if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(FirstActivity.this,new String[]{Manifest.permission.READ_CONTACTS},1);
}else{
read();
}
}
private void read(){
Cursor cursor = null;
try {
System.out.println("xxx读取url:"+ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
cursor=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
if(cursor!=null){
while (cursor.moveToNext()){
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String phone = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
listViews.add(name+"\n"+phone);
}
adapter.notifyDataSetChanged();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(cursor!=null){
cursor.close();
}
}
}
public void add(){
System.out.println("xxx读取url:"+ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
ContentValues contentValues=new ContentValues();
contentValues.put("admin",10086);
getContentResolver().insert(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,contentValues);
}
public void update(){
System.out.println("xxx读取url:"+ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
ContentValues contentValues=new ContentValues();
contentValues.put("admin","");
getContentResolver().update(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,contentValues,"admin=?",new String[]{"10001"});
}
public void delete(){
System.out.println("xxx读取url:"+ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
ContentValues contentValues=new ContentValues();
contentValues.put("admin","");
getContentResolver().delete(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,"admin=?",new String[]{"10001"});
}
@Override
public void onRequestPermissionsResult(int requestCode,String[] permissions, int[] grantResults) {
switch (requestCode){
case 1:
if(grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
read();
}
else {
Toast.makeText(this,"权限未通过",Toast.LENGTH_LONG).show();
}
break;
default:
}
}
}
7.6 自定义内容提供器
方推荐的方式就是使用内容提供器,可以通过新建一个类去继承ContentProvider的方式来创建一个自己的内容提供器。ContentProvider类中有6个抽象方法,我们在使用子类继承它的时候,需要将这6个方法全部重写。
package com.shu;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class MyProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
-
onCreate()初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级等操作,返回true表示内容提供器初始化成功,返回false则表示失败。 -
query()从内容提供器中查询数据。使用uri参数来确定查询哪张表,projection参数用于确定查询哪些列,selection和selectionArgs参数用于约束查询哪些行,sortOrder参数用于对结果进行排序,查询的结果存放在Cursor对象中返回。 -
insert()向内容提供器中添加一条数据。使用uri参数来确定要添加到的表,待添加的数据保存在values参数中。添加完成后,返回一个用于表示这条新记录的URI。 -
update()更新内容提供器中已有的数据。使用uri参数来确定更新哪一张表中的数据,新数据保存在values参数中,selection和selectionArgs参数用于约束更新哪些行,受影响的行数将作为返回值返回。 -
delete()从内容提供器中删除数据。使用uri参数来确定删除哪一张表中的数据,selection和selectionArgs参数用于约束删除哪些行,被删除的行数将作为返回值返回。 -
getType()根据传入的内容URI来返回相应的MIME类型,是所有的内容提供器都必须提供的一个方法,用于获取Uri对象所对应的MIME类型。一个内容URI所对应的MIME字符串主要由3部分组成,Android对这3个部分做了如下格式规定。必须以vnd开头。如果内容URI以路径结尾,则后接android.cursor.dir/,如果内容URI以id结尾,则后接android.cursor.item/。 最后接上vnd..
。
content://com.shu.provider/table1
vnd.android.cursor.dir/vnd.com.shu. provider.table1
7.7 实战
先定义一个内容提供器完成对数据的操作
package com.shu;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
public class DatabaseProvider extends ContentProvider {
public static final int BOOK_DIR = 0;
public static final int BOOK_ITEM = 1;
public static final int CATEGORY_DIR = 2;
public static final int CATEGORY_ITEM = 3;
public static final String AUTHORITY = "com.shu.databasesave.provider";
private static final UriMatcher uriMatcher;
private MyDatabaseHelper helper;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}
@Override
public boolean onCreate() {
helper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = helper.getWritableDatabase();
Cursor cursor = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
cursor = db.query("Book", projection, selection, selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
cursor = db.query("Book", projection, "id = ?", new String[]{bookId}, null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query("Category", projection, selection, selectionArgs, null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query("Category", projection, "id = ?", new String[]{categoryId}, null, null, sortOrder);
break;
default:
break;
}
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = helper.getWritableDatabase();
Uri uriReturn = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("Book", null, values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("Category", null, values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" + newCategoryId);
break;
default:
break;
}
return uriReturn;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase db = helper.getWritableDatabase();
int updateRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
updateRows = db.update("Book", values, selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updateRows = db.update("Book", values, "id = ?", new String[]{bookId});
break;
case CATEGORY_DIR:
updateRows = db.update("Category", values, selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updateRows = db.update("Category", values, "id = ?", new String[]{categoryId});
break;
default:
break;
}
return updateRows;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = helper.getWritableDatabase();
int deleteRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
deleteRows = db.delete("Book", selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deleteRows = db.delete("Book", "id = ?", new String[]{bookId});
break;
case CATEGORY_DIR:
deleteRows = db.delete("Category", selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deleteRows = db.delete("Category", "id = ?", new String[]{categoryId});
break;
default:
break;
}
return deleteRows;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.shu.databasesave.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.shu.databasesave.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.shu.databasesave.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.com.shu.databasesave.provider.category";
}
return null;
}
}
package com.shu;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class MyDatabaseHelper extends SQLiteOpenHelper {
private Context context;
public static final String CREATE_BOOK = "create table Book(" +
"id integer primary key autoincrement," +
"author text," +
"price real," +
"pages integer," +
"name text)";
public static final String CREATE_CATEGORY = "create table Category(" +
"id integer primary key autoincrement," +
"category_name text," +
"category_code integer)";
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
this.context = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
}
<provider
android:name=".DatabaseProvider"
android:authorities="com.shu.databasesave.provider"
android:enabled="true"
android:exported="true">
</provider>
再定义一个测试
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add Data To Book"
android:textAllCaps="false"
android:textSize="18sp"
android:textStyle="bold" />
<Button
android:id="@+id/btn_query"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Query Data From Book"
android:textAllCaps="false"
android:textSize="18sp"
android:textStyle="bold" />
<Button
android:id="@+id/btn_update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Update Book"
android:textAllCaps="false"
android:textSize="18sp"
android:textStyle="bold" />
<Button
android:id="@+id/btn_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Delete Data From Book"
android:textAllCaps="false"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
package com.example.providertest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
private Button btn_add, btn_query, btn_update, btn_delete;
private String newId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
btn_add = (Button) findViewById(R.id.btn_add);
btn_query = (Button) findViewById(R.id.btn_query);
btn_update = (Button) findViewById(R.id.btn_query);
btn_delete = (Button) findViewById(R.id.btn_delete);
btn_add.setOnClickListener(this);
btn_query.setOnClickListener(this);
btn_update.setOnClickListener(this);
btn_delete.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_add:
add();
Toast.makeText(this, "add successfully", Toast.LENGTH_SHORT).show();
break;
case R.id.btn_query:
query();
Toast.makeText(this, "query successfully", Toast.LENGTH_SHORT).show();
break;
case R.id.btn_update:
update();
Toast.makeText(this, "update successfully", Toast.LENGTH_SHORT).show();
break;
case R.id.btn_delete:
delete();
Toast.makeText(this, "delete successfully", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
private void add() {
Uri uri = Uri.parse("content://com.shu.databasesave.provider/book");
ContentValues cv = new ContentValues();
cv.put("name", "A Clash of Kings");
cv.put("author", "George Martin");
cv.put("pages", 1040);
cv.put("price", 22.85);
Uri newUri = getContentResolver().insert(uri, cv);
newId = newUri.getPathSegments().get(1);
}
private void query() {
Uri uri = Uri.parse("content://com.shu.databasesave.provider/book");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
double price = cursor.getDouble(cursor.getColumnIndex("price"));
Log.e(TAG, "book name is " + name);
Log.e(TAG, "book author is " + author);
Log.e(TAG, "book pages are " + pages);
Log.e(TAG, "book price is " + price);
}
cursor.close();
}
}
private void update() {
Uri uri = Uri.parse("content://com.shu.databasesave.provider/book/" + newId);
ContentValues cv = new ContentValues();
cv.put("name", "A Storm of Swords");
cv.put("pages", 1216);
cv.put("price", 24.05);
getContentResolver().update(uri, cv, null, null);
}
private void delete() {
Uri uri = Uri.parse("content://com.shu.databasesave.provider/book/" + newId);
getContentResolver().delete(uri, null, null);
}
}
|