美文网首页
Android第一行代码(十):内容提供器

Android第一行代码(十):内容提供器

作者: 飞行员suke | 来源:发表于2017-03-29 09:46 被阅读0次

    内容提供器(ContentProvider)主要用于不同的程序之间实现数据共享的功能,并保证被访数据的安全性。使用内容提供器是Android是实现跨程序共享数据的标准方式。

    内容提供器的用法一般有两种:

    1. 使用现有的内容提供器来读取和操作相应程序中的数据
    2. 创建自己的内容提供器给我们程序的数据提供外部访问的接口

    想访问内容提供器中共享的数据,要借助工具类ContentResolver类。可通过Context中的getContentResolver()方法获取该类的实例。
    ContentResolver类中提供了一系列方法对数据进行CRUD操作:

    1. insert()用于添加数据.
    2. update()用于更新数据.
    3. delete()用于删除数据.
    4. query()用于查询数据.

    使用步骤:

    1. ContentResolver中的增删改查方法都不接收表名参数,而使用一个Uri参数代替,这个参数称为内容URI.
      内容URI为内容提供器的数据建立了唯一的标识符.
      Uri由authority和path组成。其中authority为“包名.provider”,path为表名。标准的Uri如下:

       content://com.example.app.provider/table1  
      
    2. 获取的URI字符串需要解析为URI对象:

       Uri uri = Uri.parse("content://com.example.app.provider/table1");
      
    3. 利用Uri对象查询数据

       Cursor cursor = getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
       if (cursor != null) {
         while (cursor.moveToNext()) {
           String column1 = cursor.getString(cursor.getColumnIndex("column1"));
           int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
         } 
         cursor.close();
       }
      

    填加:

    ContentValues values = new ContentValues();
    values.put("column1", "text");
    values.put("column2", 1);
    getContentResolver().insert(uri, values);
    

    修改:

    ContentValues values = new ContentValues();
    values.put("column1", "");
    getContentResolver().update(uri, values, "column1 = ? and column2 = ?", newString[] {"text", "1"});
    

    删除:

    getContentResolver().delete(uri, "column2 = ?", new String[] { "1" });
    

    实例:用内容提供器读取系统联系人

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <ListView
            android:id="@+id/contacts_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        </ListView>
    </LinearLayout>
    

    MainActivity

    public class MainActivity extends AppCompatActivity {
    
        ArrayAdapter<String> adapter;
        List<String> contactList = new ArrayList<>();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //获取ListView实例
            ListView contactsView = (ListView)findViewById(R.id.contacts_view);
            //生成适配器,提供参数 上下文、ListView子项Item、ListView中数据
            adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,contactList);
            //给ListView设置适配器
            contactsView.setAdapter(adapter);
    
            //判断用户是否已经授权执行读取通讯录的操作,借助ContextCompat.checkSelfPermission()方法
            if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED){
            /*调用运行时权限的处理逻辑,即在程序运行时请求用户授权,这里请求READ_CONTACTS的权限
            参数1:Activity实例; 参数2:请求权限的数组; 参数3:请求码(要唯一值)*/
                ActivityCompat.requestPermissions(this,new String[]{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));
    
                        contactList.add(displayName + "\n" + number);
                    }
                    //刷新ListView
                    adapter.notifyDataSetChanged();
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                //一定要close cursor
                if(cursor != null)
                    cursor.close();
            }
        }
    
    //运行时请求权限弹出选择框 选择的回调方法
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        //requestCode就是请求时传入的唯一值请求码
            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();
                    }
            }
        }
    }
    

    Manifest.xml

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.contactstest">
        //最重要的是在Manifest文件中,声明读取系统联系人的权限!!!
        <uses-permission android:name="android.permission.READ_CONTACTS"/>
        <application
            ...
        </application>
    </manifest>
    

    创建自己的内容提供器

    可以通过新建一个类去继承ContentProvider的方式来创建一个自己的内容提供器。
    在项目中右击程序包->New->Other->Content Provider可新建内容提供器。
    需要重写以下6个方法:

    1. onCreate():初始化内容提供器时调用,用于数据库的创建和升级等操作返回true则初始化成功,false则失败.只有ContentResolver尝试访问程序中的数据时,内容提供器才会被初始化.
    2. query():使用 uri 参数来确定查询哪张表, projection 参数用于确定查询哪些列,selection 和 selectionArgs 参数用于约束查询哪些行,sortOrder 参数用于对结果进行排序, 查询的结果存放在 Cursor 对象中返回.
    3. insert():向内容提供器中添加一条数据,使用 uri 参数来确定要添加到的表,待添加的数据保存在 values 参数中,添加完成后,返回一个用于表示这条新记录的 URI.
    4. update():更新内容提供器中已有的数据.使用 uri 参数来确定更新哪一张表中的数据,新数据保存在 values 参数中,,selection 和 selectionArgs 参数用于约束更新哪些行,,受影响的行数将作为返回值返回.
    5. delete():从内容提供器中删除数据.使用 uri 参数来确定删除哪一张表中的数据,和 selectionArgs 参数用于约束删除哪些行, 被删除的行数将作为返回值返回.
    6. getType():根据传入的内容 URI 来返回相应的 MIME 类型.

    URI写法:

    访问com.example.app这个app中的table1表
    content://com.example.app.provider/table1
    访问com.example.app这个app中table1表中id为1的数据
    content://com.example.app.provider/table1/1
    

    通配符匹配:

    *: 表示匹配任意长度的任意字符串
    #: 表示匹配任意长度的数据
    content://com.example.app.provider/* 能匹配任意表的内容
    content://com.example.app.provider/table1/#  能匹配table1 表中任意一行数据的内容
    

    需要借助UriMatcher类来 实现匹配URI的功能
    UriMatcher提供了addURI(),接受三个参数(权限、路径、自定义代码).
    UriMatcher还提供了match()方法,返回一个匹配这个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.example.a123.databasetest.provider";
        private static UriMatcher uriMatcher;
        private  MyDatabaseHelper dbHelper;
    
        static {
            //获得UriMatcher实例
            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 Cursor query(Uri uri, String[] projection, String selection,
                            String[] selectionArgs, String sortOrder) {
            SQLiteDatabase db = dbHelper.getReadableDatabase();
            Cursor cursor = null;
            //match()方法,返回对应的自定义代码
            switch(uriMatcher.match(uri)){
                case BOOK_DIR:
                    cursor = db.query("Book",projection,selection,selectionArgs,null,null,sortOrder);
                    break;
    
                case BOOK_ITEM:
                    //getPathSegments(),将内容URI 权限之后的部分以"/"符号进行分割,得到字符串列表
                    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);
            }
            return cursor;
        }
        
    }
    

    getType()方法,用于获取uri对象所对应的MIME类型。一个内容URI所对应的MIME字符串主要由3部分组成

    1. 必须以vnd开头

    2. 如果内容 URI 以路径结尾,则后接 android.cursor.dir/,如果内容 URI 以 id 结尾,则后接 android.cursor.item/

    3. 最后接上 vnd.<authority>.<path>

       @Override
       public String getType(Uri uri) {
           switch (uriMatcher.match(uri)){
               case BOOK_DIR:
                   return "vnd.android.cursor.dir/vnd.com.example.a123.databasetest.provider.book";
               case BOOK_ITEM:
                   return "vnd.android.cursor.item/vnd.com.example.a123.databasetest.provider.book";
               case CATEGORY_DIR:
                   return "vnd.android.cursor.dir/vnd.com.example.a123.databasetest.provider.category";
               case CATEGORY_ITEM:
                   return "vnd.android.cursor.item/vnd.com.example.a123.databasetest.provider.category";
           }
           return null;
       }
      

    然后在项目中Manifest.xml注册

    <provider
        android:name=".DatabaseProvider"
        android:authorities="com.example.a123.databasetest.provider"
        android:enabled="true"
        android:exported="true">
    </provider>
    

    这样别的程序就能通过Uri来共享数据了!

         Uri uri = Uri.parse("content://com.example.a123.databasetest.provider/book");
        ContentValues values = new ContentValues();
        values.put("name","A Clash of Kings");
        values.put("author","George Martin");
        values.put("pages",1040);
        values.put("price",55.55);
        Uri newUri = getContentResolver().insert(uri,values);

    相关文章

      网友评论

          本文标题:Android第一行代码(十):内容提供器

          本文链接:https://www.haomeiwen.com/subject/bxkeottx.html