Android学习(五)—— 四大组件之Content Provider

本文目录结构:
一、内容提供器简介
二、数据存储方案
三、运行时权限
四、访问其他程序中的数据
五、创建自己的内容提供器

一、内容提供器简介

  • 作为四大组件之一,Content Provider主要用于存储数据和在不同的应用程序之间共享数据。和文件存储、SharedPreferences存储以及SQLite数据库存储不同,Content Provider可以选择只对哪一部分的数据进行共享,从而保证我们的隐私数据不会存在被泄漏的风险,前者的数据只能在本应用程序中使用。(因为文件存储方式存下的文件是在本应用目录下的,其他应用不知道该应用下这个文件的目录,所以说只能在本应用中使用,当然了你知道的话也是可以使用的)
  • 内容提供器的用法一般有两种,一种是使用别人为我们提供的内容提供器,本文第四大点会讲到;另一种则是我们创建自己的内容提供器,提供给别人使用。本文第五大点会讲到。

二、数据存储方案

2.1 文件存储
2.1.1 前言

文件存储是Android中最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理。因此,它比较适合用于存储一些简单的文本数据或二进制数据。
对于存储操作,Context类提供了一个openFileOutput方法。对于读取操作,Context类提供了一个openFileInput方法。两个方法返回的类型分别是FileOutStream和FileInputStream。

2.1.2 使用方法

这里结合一个小案例说明:点击“保存”按钮将输入框的内容存储到文件中,点击“读取”按钮则读取文件中的内容

//保存数据到文件中
     private void saveFile(String content) {
        FileOutputStream fos = null;
        BufferedWriter bw = null;
        try {
            //第一参数是指定文件的名称,这里的名字不可以包含路径,因为所有文件都是存储在/data/data//files/目录下的
            //第二个参数是文件的操作模式,主要有两种模式可以选:MODE_PRIVATE(默认)和MODE_APPEND。
            //前者表示文件名字相同时会覆盖掉之前的内容,而后者表示会在后面追加内容
            fos = openFileOutput("myFile", Context.MODE_APPEND);
            bw = new BufferedWriter(new OutputStreamWriter(fos));
            bw.write(content);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bw != null) {
                    bw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

//从文件中读取数据
    private String readFile() {
        FileInputStream fis = null;
        BufferedReader br = null;
        StringBuilder builder = new StringBuilder();
        try {
            //只接收一个参数,即要打开的文件名
            fis = openFileInput("myFile");
            br = new BufferedReader(new InputStreamReader(fis));
            String flag;
            while((flag = br.readLine()) != null){
                builder.append(flag);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return builder.toString();
    }
2.2 SharedPreferences存储
2.2.1 前言

与文件存储不同,SharedPreferences是用键值对的形式存储数据的。也就是说,当我们保存一条数据的时候,需要给这条数据提供一个对应的键,取的时候也是根据这个键取出来。

2.2.2 将数据存储到SharedPreferences中

要想用SharedPreferences存储数据,我们需要先获取SharedPreferences对象。Android主要提供了三种方法来得到该对象。

  • Context类中的getSharedPreferences()方法
    该方法接收两个参数,第一个是指定SharedPreferences文件的名称,该文件都是存放在/data/data//shared_prefs/目录下的。第二个是指定操作模式,目前只有MODE_PRIVATE这一种该模式可选,它也是默认的,与传入0效果是一样的。
  • Activity类中的getPreferences()方法
    该方法与上一个方法类似,不过它只接收一个参数,即操作模式。SharedPreferences文件的名称为当前活动的类名。
  • PreferenceManager类中的getDefaultSharedPreferences()方法
    这是一个静态方法,它接收一个Context参数,并自动使用当前应用程序的包名作为前缀来命名。

拿到SharedPreferences对象之后就可以向SharedPreferences文件存储数据了,步骤如下:
① 首先,调用SharedPreferences对象的edit方法来获取一个SharedPreferences.Editor对象。
② 然后,向SharedPreferences.Editor对象添加数据,比如添加一个int型数据则可以用putInt()方法,字符串则是putString()方法,以此类推。
③ 最后,调用apply或者commit方法将数据提交即可。

SharedPreferences.Editor editor = getSharedPreferences("myShared",MODE_PRIVATE).edit();
editor.putString("content",content);
editor.putInt("id",2016100341);
editor.putBoolean("flag",true);
editor.apply();
2.2.3 从SharedPreferences中读取数据
SharedPreferences preferences = getSharedPreferences("myShared",MODE_PRIVATE);
//该方法的第一个参数是存数据时写的键值,第二个参数是获取不到这个键对应的值时的默认返回的内容。
String result = preferences.getString("content","error");
int num = preferences.getInt("id",0);
boolean flag = preferences.getBoolean("flag",true);
2.2.4 注意

(1)细心的朋友们可能会注意到提交数据的时候不是有两种该方法提交吗?那它们有什么区别呢?
① apply无返回值,commit有(boolean)
② apply的话是将修改原子提交到内存,而后异步真正提交到硬件,commit是同步提交到硬件,所以相比之下数据量大的时候apply的效率会更高一点。
③ apply不会有任何的失败提示。

(2)当两个Editor在修改preferences时,最后一个调用apply或者commit的才会生效。
源码注释:
Note that when two editors are modifying preferences at the same time, the last one to call commit wins.
Note that when two editors are modifying preferences at the same time, the last one to call apply wins.

2.3 SQLite数据库存储
2.3.1 前言
  • SQLite是一款轻量级的数据库,运算速度非常快,占用资源很少,通常只需要几百KB的内存就够了,因而非常适合在移动设备上使用。SQLite不仅支持标准的SQL语法,还遵循了数据库的ACID事务。
  • 整体思路:
    首先,自定义类继承SQLiteOpenHelper,重写onCreateonUpgrade方法。
    然后,实例化该类,调用getReadableDatabasegetWritableDatabase方法拿到SQLiteDatabase对象,这样就创建完数据库了。
    接着,通过ContentValues的put,clear等方法将数据放进ContentValues中。
    最后,SQLiteDatabase对象调用insert、update、delete、query方法做增删查改操作(查询的话则用到cursor)。
  • 这是一些初始化信息,下面的这个功能都是在这个的基础上实现的。
public class Main5Activity extends AppCompatActivity implements View.OnClickListener {

    private Button btn_create,btn_upgrade,btn_insert,
            btn_delete,btn_select,btn_update;

    private MySQLiteHelper helper;
    private SQLiteDatabase db;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main5);

        helper = new MySQLiteHelper(this,"BookStore.db",null,1);

        btn_create = findViewById(R.id.btn_create);//创建数据库
        btn_upgrade = findViewById(R.id.btn_upgrade);//升级数据库
        btn_insert = findViewById(R.id.btn_insert);//插入数据
        btn_delete = findViewById(R.id.btn_delete);//删除数据
        btn_select = findViewById(R.id.btn_select);//查询数据
        btn_update = findViewById(R.id.btn_update);//更新数据

        btn_create.setOnClickListener(this);
        btn_upgrade.setOnClickListener(this);
        btn_insert.setOnClickListener(this);
        btn_delete.setOnClickListener(this);
        btn_select.setOnClickListener(this);
        btn_update.setOnClickListener(this);
    }
2.3.2 创建数据库
//第一,自定义类继承SQLiteOpenHelper ,重写onCreate和onUpgrade方法
public class MySQLiteHelper extends SQLiteOpenHelper {

    private Context mContext;
    public static final String CREATE_BOOK = "create table Book (" +
            "id integer primary key autoincrement, " +
            "author text, " +
            "price real, " +
            "pages integer, " +
            "name text)";

    public MySQLiteHelper(Context context,String name,SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;
    }

    //该方法用于创建数据库,如果已存在则不会再创建
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        Toast.makeText(mContext, "create succeeded", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

//activity里面的点击事件
case R.id.btn_create:
      //getWritableDatabase与getReadableDatabase方法的区别在于,当数据库不可写入的时候(如磁盘空间已满),getReadableDatabase方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase方法则将抛出异常
     db = helper.getWritableDatabase();
     break;
2.3.3 升级数据库

重新写好MySQLiteHelper之后,只需在new MySQLiteHelper的最后一个参数里面传入一个比之前大的数,再调用getWritableDatabase或getReadableDatabase方法即可升级数据库了。

2.3.4 增删查改
//增加数据
case R.id.btn_insert:
    ContentValues values = new ContentValues();
    //组装第一条数据
    values.put("name","LongSh1z");
    values.put("author","anthony");
    values.put("pages",454);
    values.put("price",16.8);
    //第一个参数是要插入的表的名称,
    //第二个用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,
    //第三个参数是要插入的数据
    db.insert("Book",null,values); //向数据库插入数据
    values.clear();//清空values
    //组装第二条数据
    values.put("name","haha");
    values.put("author","David");
    values.put("pages",65);
    values.put("price",15);
    db.insert("Book",null,values); //向数据库插入数据
break;

//删除数据
case R.id.btn_delete:
    //第一个参数是表名
    //第二和三个是要删除数据的条件,比如这里的意思就是要删除pages大于100的记录
    db.delete("Book","pages > ?",new String[]{"100"});
break;

//查询数据
case R.id.btn_select:
    //除了第一个参数是表名之外,其他的都是约束条件
    Cursor cursor = db.query("Book", null, null, null, null, null, null, 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"));
            Log.i("MainActivity", "name: " + name);
            Log.i("MainActivity", "author: " + author);
            Log.i("MainActivity", "pages: " + pages);
      }
      cursor.close();//记得关闭cursor,不然会造成内存泄漏
break;

//更改数据
case R.id.btn_update:
        ContentValues values1 = new ContentValues();
        values1.put("price",10.47);
        //第一个参数是表名,第二和第三是约束条件
        db.update("Book",values1,"name = ?",new String[]{"haha"});
break;
2.4 注意

① 其实,也可以直接用SQL语句来完成增删查改的功能:

db.execSQL("insert into Book (name,author,pages,price) values (?,?,?,?)", 
            new String[]{ "john" , "Smith", "123", "7.5"});
db.execSQL("delete from Book where pages > ?" , new String[]{ "400" });
db.rawQuery("select * from Book" , null);
db.execSQL("update Book set price = ? " , new String[]{ "32" });

② SQLite的事务处理是如何做的?
SQLite在做CRUD操作时都默认开启了事务,然后把SQL语句翻译成对应的SQLiteStatement,并调用其相应的CRUD方法,此时整个操作还是在roolback journal这个临时文件上进行,只有操作顺利完成才能更新.db数据库,否则会回滚。

③ 怎么使用SQLite更好的做批量操作
使用SQLiteDatabase的beginTransaction方法开启一个事务,将批量操作SQL语句转化成SQLiteStatement,并进行批量操作,结束后endTransaction

db.beginTransaction();
try{
    db.execSQL(...);
    db.execSQL(...);
    db.setTransactionSuccessful();
}catch(...){
    ...
}finally{
    db.endTransaction();
}

④ 怎么删除表中的一个字段
SQLite数据库只允许增加表字段而不允许修改和删除表字段,只能采取复制表的思想,即创建一个新表保留原表想要的字段,再将原表删除。

⑤ 使用SQLite时有什么优化操作

  • 使用事务做批量处理
  • 及时关闭cursor,避免内存泄露
  • 耗时操作异步化,数据库的操作属于本地IO,通常比较耗时,建议放入异步线程中处理
  • ContentValues的容量调整,ContentValues内部采用HashMap来存储数据,ContentValues初始容量为8,扩容时翻倍。因此建议提前估量要填入的数据,设置合理的初始化容量,避免不必要的扩容操作。
  • 使用索引加快检索速度,对于查询操作量较大,业务对查询要求较大的推荐使用索引

三、运行时权限

3.1 前言

运行时权限是Android开发团队在Android 6.0(API 23)系统中引入的功能,用户不需要在安装软件的时候一次性授权所有申请的权限,而是可以在软件的使用过程中再对某一项权限申请授权,这样可以更好的保护了用户的安全和隐私。

3.2 分类

Android现在将所有权限分成了两类,一类是普通权限。另一类是危险权限。

第三类特殊权限就不讨论了,如下:
SYSTEM_ALERT_WINDOW 系统级别对话框
WRITE_SETTINGS 修改系统设置

  • 普通权限
    它指的是那些不会直接威胁到用户安全和隐私的权限,对于这部分权限,系统会自动帮我们授权,而不需要用户再去手动授权了。除了危险权限之外,剩余的都是普通权限。
  • 危险权限
    它表示那些可能会威胁到用户安全和隐私的权限,对于这部分权限,需要用户手动去授权才可以。
    该部分权限如下(9组24个权限):
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
 
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL
 
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
 
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
 
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
 
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION
 
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
 
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
 
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
3.3 注意

上述的每一个危险权限都属于对应的一个权限组,我们在进行运行时权限处理时使用的是权限名,但是用户一旦授权了,那么该权限所在权限组中的所有其他权限也会同时被授权。

3.4 小案例

比如我们点击按钮之后要实现打电话的功能,因为电话权限属于危险权限,所以我们在6.0以上的手机运行时需要先询问用户是否同意授权。

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main6);

        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //先检查打电话权限是否已经被授权
                //第一个参数是context,第二个是需要授权的权限
                if (ContextCompat.checkSelfPermission(Main6Activity.this, Manifest.permission.CALL_PHONE) 
                                                      != PackageManager.PERMISSION_GRANTED){
                    //如果该权限还未授权,系统会弹出一个申请权限的对话框
                    //不管用户选择哪项,都会调用下面重写的onRequestPermissionsResult方法
                    ActivityCompat.requestPermissions(Main6Activity.this,
                                                      new String[]{ Manifest.permission.CALL_PHONE },1);
                }else {
                    call();
                }
            }
        });
    }

    private void call(){
        //打电话
        Intent intent = new Intent(Intent.ACTION_CALL);
        intent.setData(Uri.parse("tel:10086"));
        startActivity(intent);
    }

    @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){
                    call();
                }else {
                    Toast.makeText(this, "您已拒绝该权限,请前往设置开启!", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }

四、访问其他程序中的数据

4.1 前言

Android系统中自带的电话簿、短信、媒体库等程序都提供了这样的内容提供器供外界访问。

4.2 ContentResolver
  • 每个应用程序要想访问内容提供器中的数据,就一定要借助ContentResolver类,可以通过Context中的getContentResolver方法来获取。ContentResolver类提供了一系列方法用于对数据进行CRUD操作,insert方法用于添加数据,update方法用于更新数据,delete方法用于删除数据,query方法用于查询数据。它和SQLiteDatabase很像,只不过它不接收表名,而是换成了Uri参数。
  • Uri包括两部分:authority和path。authority命名为 包名.provider,path命名为 表名 ,所以一个标准的Uri可以为content://com.example.testspecial.provider/table1

除此之外,我们还可以在这个Uri的后面加上一个id,如下所示
content://com.example.testspecial.provider/table1/1
它表示调用方希望访问的是com.example.testspecial这个应用的table1表中id为1的数据。

我们得到内容Uri字符串之后,还需要将其转化为Uri对象才可以作为参数传入。

Uri uri = Uri.parse("content://com.example.testspecial.provider/table1");
4.2.1 增加数据
ContentValues values = new ContentValues();
values.put("column1" , "text");
values.put("column2" , 1);
getContentResolver().insert(uri , values);
4.2.2 删除数据
getContentResolver().delete(uri , "column2 = ? " , new String[]{ "1" });
4.2.3 查询数据
Cursor cursor = getContentResolver().query(
                uri,  //该参数是一个Uri对象
                projection,  //该参数指定查询的列名
                selection,  //该参数指定where的约束条件
                sortOrder);  //该参数指定查询结果的排序方式
if(cursor != null){
    while(cursor.moveToNext()){
        String column1 = cursor.getString( cursor.getColumnIndex("column1") );
        int column2 = cursor.getInt( cursor.getColumnIndex("column2") );
    }
    cursor.close();
}
4.2.4 修改数据

比如我们把column1的数据改为"changed",我们可以:

ContentValues values = new ContentValues();
values.put("column1" , "changed");
getContentResolver().update(uri , values, "column1 = ? and column2 = ? ", new String[]{ "text" , "1" });
4.3 小案例

这里我们实现读取手机里联系人的功能

public class Main6Activity extends AppCompatActivity {

    private ListView listView;
    ArrayAdapter adapter;
    List contactsList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main6);

        listView = findViewById(R.id.listView);
        adapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,contactsList);
        listView.setAdapter(adapter);

        //先检查权限是否已经被授权
        if (ContextCompat.checkSelfPermission(Main6Activity.this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(Main6Activity.this, new String[]{Manifest.permission.READ_CONTACTS}, 1);
        } else {
            readContacts();
        }
    }

    private void readContacts() {
        Cursor cursor = null;
        try {
            //ContactsContract.CommonDataKinds.Phone类已经帮我们封装好了,提供了一个CONTENT_URI常量,它就是一个Uri对象
            cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null);
            if (cursor != null){
                while (cursor.moveToNext()){
                    String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                    String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                    contactsList.add(name + "\n" + number);
                }
                //通知listView数据已改变
                adapter.notifyDataSetChanged();
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (cursor != null) cursor.close();
        }
    }

    @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;
        }
    }
}

五、创建自己的内容提供器

5.1 前言

整体思路:
首先,自定义类继承ContentProvider。
然后,重写onCreate、query、insert、update、delete、getType方法。
最后,记得在AndroidManifest文件中注册该content provider。

5.2 实例
public class MyProvider extends ContentProvider {

    public static final int BOOK_DIR = 0; //代表访问Book表的所有数据
    public static final int BOOK_ITEM = 1; //代表访问Book表的单条数据
    public static final int CATEGORY_DIR = 2;
    public static final int CATEGORY_ITEM = 3;

    public static final String AUTHORITY = "com.example.testspecial.provider";

    private static UriMatcher uriMatcher; //UriMatcher类可以实现匹配Uri的功能

    private MySQLiteHelper sqLiteHelper;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY,"book",BOOK_DIR); //如果uriMatcher.match(uri)匹配这个URI,就返回BOOK_DIR
        uriMatcher.addURI(AUTHORITY,"book/#",BOOK_ITEM);
        uriMatcher.addURI(AUTHORITY,"category",CATEGORY_DIR);
        uriMatcher.addURI(AUTHORITY,"category/#",CATEGORY_ITEM);
    }

    public MyProvider() {

    }

    //初始化内容提供器的时候调用,通常会在这里完成对数据库的创建和升级等操作,返回true表示初始化成功,反之失败。
    @Override
    public boolean onCreate() {
        // TODO: Implement this to initialize your content provider on startup.
        sqLiteHelper = new MySQLiteHelper(getContext(),"BookStore.db",null,2);
        return true;
    }

    //从内容提供器中查询数据,查到的内容放在cursor中返回
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // TODO: Implement this to handle query requests from clients.
        SQLiteDatabase db = sqLiteHelper.getReadableDatabase();
        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;
        }
        return cursor;
    }

    //根据传入的内容URI来返回相应的MIME类型。
    //一个内容URI所对应的MIME字符串主要由三部分组分。
    //Android对这三个部分做了以下格式规定:必须以vnd开头;如果内容URI以路径结尾,
    //则后接android.cursor.dir/,如果内容URI以id结尾,则后接android.cursor.item/;
    //最后接上vnd.< authority>.< path>
    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.testspecial.provider.book";
            case BOOK_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.testspecial.provider.book";
            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.testspecial.provider.category";
            case CATEGORY_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.testspecial.provider.category";
        }
        return null;
    }

    //向内容提供器中添加一条数据,返回一个用于表示这条新纪录的URI
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
        SQLiteDatabase db = sqLiteHelper.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;
        }
        return uriReturn;
    }

    //从内容提供器中删除数据,返回受影响的行数
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        SQLiteDatabase db = sqLiteHelper.getWritableDatabase();
        int deletedRows = 0;
        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
                deletedRows = db.delete("Book",selection,selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                deletedRows = db.delete("Book","id = ?",new String[]{bookId});
                break;
            case CATEGORY_DIR:
                deletedRows = db.delete("Category",selection,selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                deletedRows = db.delete("Category","id = ?",new String[]{categoryId});
                break;
        }
        return deletedRows;
    }

    //更新内容提供器中已有的数据,返回受影响的行数
    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
        SQLiteDatabase db = sqLiteHelper.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;
        }
        return updateRows;
    }
}

然后我们在另外一个项目中访问上面的content provider。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn_insert,btn_delete,btn_select,btn_update;
    private String newId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btn_insert = findViewById(R.id.btn_insert);
        btn_delete = findViewById(R.id.btn_delete);
        btn_select = findViewById(R.id.btn_select);
        btn_update = findViewById(R.id.btn_update);
        btn_insert.setOnClickListener(this);
        btn_delete.setOnClickListener(this);
        btn_select.setOnClickListener(this);
        btn_update.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_insert:
                Uri uri1 = Uri.parse("content://com.example.testspecial.provider/book");
                ContentValues values = new ContentValues();
                values.put("name","oiu");
                values.put("author","wef");
                values.put("pages",15);
                Uri newUri = getContentResolver().insert(uri1,values);
                newId = newUri.getPathSegments().get(1);
                break;
            case R.id.btn_delete:
                Uri uri2 = Uri.parse("content://com.example.testspecial.provider/book/"+newId);
                getContentResolver().delete(uri2,null,null);
                break;
            case R.id.btn_select:
                Log.i("MainActivity", "onClick: ");
                Uri uri = Uri.parse("content://com.example.testspecial.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"));
                        Log.i("MainActivity", "name: "+name+
                                "\n author: "+author+
                                "\n pages: "+pages);
                    }
                    cursor.close();
                }
                break;
            case R.id.btn_update:
                Uri uri3 = Uri.parse("content://com.example.testspecial.provider/book/"+newId);
                ContentValues values1 = new ContentValues();
                values1.put("name","changedName");
                values1.put("author","changedAuthor");
                values1.put("pages",67);
                getContentResolver().update(uri3,values1,null,null);
                break;
        }
    }
}

你可能感兴趣的:(Android学习(五)—— 四大组件之Content Provider)