第7章 探究内容提供者

作者: 追梦小乐 | 来源:发表于2018-04-30 19:25 被阅读5次

    本系列学习笔记第7章

    前言

    打算把android基本知识点写一个系列,旨在把android基础书,例如《Android 第一行代码 第2版》、《爱上android》、《疯狂android讲义》等书的一些知识点记录一下,会持续更新内容,为了方便自己复习,也希望可以帮助到大家!

    1、内容提供器的简介

    内容提供者 Content Provider 主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另外一个程序中的数据,同时还能保证被访问数据的安全性,因为它可以选择对那一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险。

    目前,使用内容提供者是Android实现跨程序共享数据的标准方式。

    2、运行时权限

    Android 6.0系统中引用了运行时权限这个功能,更好的保护了用户的安全和隐私。

    2.1 Android权限机制详解

    运行时权限功能指的是:用户不需要在安装软件的时候一次性授权所有申请的权限,而是可以在软件的使用过程中再对某一项权限申请进行授权。例如,在某个应用中,我选择拒绝了某一个权限,但是我还是可以使用该应用的其它功能。

    android将所有的权限归成了两类:
    1)普通权限
    指的是那些不会直接威胁到用户的安全和隐私的权限,对于这部分权限申请,系统会自动帮我们进行授权,而不用用户去手动操作了。
    2)危险权限
    指的是那些可能会触及用户隐私或者对设备安全性造成影响的权限,比如获取设备联系人信息、定位设备的地理位置等,这些权限的申请,必须要由用户手动点击授权才可以。

    危险权限分组

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

    2.2 在程序运行时申请权限(以CALL_PHONE权限为例)

    AndroidManifest.xml文件

        <uses-permission android:name="android.permission.CALL_PHONE" />
    

    activity_main.xml文件

    <?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"
        android:orientation="vertical"
        tools:context="com.example.runtimepermissiontest.MainActivity">
    
        <Button
            android:id="@+id/btn_call"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="30sp"
            android:text="打电话"
            />
    
    </LinearLayout>
    

    MainActivity.java文件

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button btnCall = (Button) findViewById(R.id.btn_call);
            btnCall.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(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 {
                        call();
                    }
                }
            });
        }
    
        private void call(){
            try {
                //直接打电话
                Intent intent = new Intent(Intent.ACTION_CALL);
    
    //        跳转到拨号界面
    //        Intent intent = new Intent(Intent.ACTION_DIAL);
    
                intent.setData(Uri.parse("tel:10086"));
                startActivity(intent);
            } catch (SecurityException e) {
                e.printStackTrace();
            }
        }
    
        @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;
                default:
                    break;
            }
        }
    }
    
    
    image.png
    image.png
    image.png image.png

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

    内容提供器的用法一般有两种:
    1)使用现有的内容提供器来读取和操作相应程序中的数据
    2)创建自己的内容提供器给我们程序的数据提供外部访问接口,例如电话簿、短信、媒体库等程序都提供了类似的外部接口

    3.1 ContentPesolver的基本用法

    1)内容URI的认识
    增删改查方法都是不接收表名参数的,而是使用一个Uri参数代替,这个参数就称为内容URI,它给内容提供器中的数据建立了唯一标识符,主要是由俩部分组成:authority 和 path
    2)authority
    用于对不同的应用程序做区分的,为了避免冲突,都会采用程序包名的方式进行命名。比如一个程序的包名是com.example.app,那么该程序对应的authority就可以命名为com.example.app.provider
    3)path
    用于对同一个应用程序中不同的表做区分的,通常都会添加到authority的后面,比如某个程序的数据库里存在俩张表:table1 和 table2,这时path就可以分别名为/table1 和 /table2
    4)标准内容URI的写法
    content://com.example.app.provider/table1
    content://com.example.app.provider/table2

    5)解析成Uri

            Uri uri = Uri.parse("content://com.example.runtimepermissiontest.provider/table1");
    

    6)查询数据(query()方法)

            /**
             * Uri uri,                指定查询某个应用程序下的某一张表
             * String[] projection,    指定查询的列名
             * String selection,       指定where的约束条件
             * String[] selectionArgs, 为where中的占位符提供具体的值
             * String sortOrder        指定查询结果的排序方式
             */
            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();
            }
        }
    

    7)添加数据(insert()方法)

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

    8)更新数据(update()方法)

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

    8)删除数据(delete()方法)

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

    3.2 读取系统联系人

    清单文件添加权限

     <uses-permission android:name="android.permission.READ_CONTACTS" />
    
    image.png

    activity_main.xml 布局

    <?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"
        android:orientation="vertical"
        tools:context="com.example.runtimepermissiontest.MainActivity">
    
    
        <ListView
            android:id="@+id/lv_contacts_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        </ListView>
    
    </LinearLayout>
    

    MainActivity.java文件

    public class MainActivity extends AppCompatActivity {
    
        private List<String> contactsList = new ArrayList<>();
        private ArrayAdapter<String> adapter;
        private ListView lvContacts;
        private Cursor cursor;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            lvContacts = (ListView) findViewById(R.id.lv_contacts_view);
    
            adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, contactsList);
    
            lvContacts.setAdapter(adapter);
    
            if (ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED){
                ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CONTACTS},1);
            }else {
                readContacts();
            }
    
        }
    
        private void readContacts() {
            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));
                        contactsList.add(displayName + "\n"+number);
                    }
                    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;
                default:
                    break;
            }
        }
    }
    
    

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

    内容URI的格式主要有两种:
    1)以路径结尾就表示期望访问该表中所有的数据
    content://com.example.app.provider/table1
    2)以id结尾就表示期望访问该表中拥有相应id的数据
    content://com.example.app.provider/table1/1

    可以使用通配符的方式来分别匹配这俩种格式的内容URI,规则如下:
    1):表示匹配任意长度的任意字符
    content:://com.example.app.provider/

    表示一个能够匹配table1表中任意一行数据内容URI格式

    2)#:表示匹配任意长度的数字
    content:://com.example.app.provider/table1/#
    表示一个能够匹配任意表内容URI格式

    4.1 创建内容提供器的步骤

    public class MyProvider extends ContentProvider {
    
        /**
         * 初始化内容提供器的时候调用,通常会在这里完成对数据库的创建和升级等操作,
         * 返回TRUE表示内容提供器初始化成功,返回false则表示初始化失败。
         * 只有当存在ContentResolver尝试访问我们程序的数据时,内容提供器才会被初始化
         * @return
         */
        @Override
        public boolean onCreate() {
            return false;
        }
    
        /**
         * 从内容提供器中查询数据
         * @param uri      查询哪一张表
         * @param strings  确定查询那些列
         * @param s        用于约束查询那些行
         * @param strings1
         * @param s1       用于对结果进行排序
         * @return
         */
        @Nullable
        @Override
        public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
            return null;
        }
    
        /**
         * 插入数据
         * @param uri
         * @param contentValues
         * @return
         */
        @Nullable
        @Override
        public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
            return null;
        }
    
    
        /**
         * 删除数据
         * @param uri
         * @param s
         * @param strings
         * @return
         */
        @Override
        public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
            return 0;
        }
    
    
        /**
         * 更新数据
         * @param uri
         * @param contentValues
         * @param s
         * @param strings
         * @return
         */
        @Override
        public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
            return 0;
        }
    
        /**
         * 根据传入的内容URI来返回相应的MIME类型
         * 
         * 一个内容URI对应的MIME字符串主要有3部分组成:
         * 1) 必须以vnd开头
         * 
         * 2)如果内容URI以路径结尾,则接android.cursor.dir/,
         *    如果内容URI以id结尾,则接android.cursor.item/
         *    
         * 3) 最后接上vnd .<authority>.<path>
         * @param uri
         * @return
         */
        @Nullable
        @Override
        public String getType(@NonNull Uri uri) {
            return null;
        }
    }
    
    

    4.2 实现跨程序数据共享

    4.2.1 要提供访问接口的应用

    AndroidManifest.xml文件

            <provider
                android:name=".MyContentProvider"
                android:authorities="com.example.mydatabasehelper.provider"
                android:enabled="true"
                android:exported="true"></provider>
    

    MyDatabaseHelper.java文件

    public class MyDatabaseHelper extends SQLiteOpenHelper {
    
        private static final String CREATE_BOOK = "create table Book ("
                +"id integer primary key autoincrement, "
                +"author text, "
                +"price real, "
                +"pages integer, "
                +"name text)"
                ;
    
        private static final String CREATE_CATEGORY = "create table Category ("
                +"id integer primary key autoincrement, "
                +"category_name text, "
                +"category_code integer)"
                ;
    
        private Context mContext;
    
        public MyDatabaseHelper(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);
            db.execSQL(CREATE_CATEGORY);
            Toast.makeText(mContext, "数据库创建成功!", Toast.LENGTH_SHORT).show();
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int i, int i1) {
            db.execSQL("drop table if exists Book");
            db.execSQL("drop table if exists Category");
            onCreate(db);
        }
    }
    

    MyContentProvider.java文件

    public class MyContentProvider 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.mydatabasehelper.provider";
    
        private static UriMatcher uriMatcher;
    
        private MyDatabaseHelper dbHelper;
    
        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);
        }
    
        public MyContentProvider() {
        }
    
        @Override
        public boolean onCreate() {
            dbHelper = 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 = dbHelper.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;
                default:
                    break;
            }
            return cursor;
        }
    
    
        @Override
        public Uri insert(Uri uri, ContentValues values) {
            // 添加数据
            SQLiteDatabase db = dbHelper.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 = dbHelper.getWritableDatabase();
            int updatedRows = 0;
            switch (uriMatcher.match(uri)) {
                case BOOK_DIR:
                    updatedRows = db.update("Book", values, selection, selectionArgs);
                    break;
                case BOOK_ITEM:
                    String bookId = uri.getPathSegments().get(1);
                    updatedRows = db.update("Book", values, "id = ?", new String[] { bookId });
                    break;
                case CATEGORY_DIR:
                    updatedRows = db.update("Category", values, selection, selectionArgs);
                    break;
                case CATEGORY_ITEM:
                    String categoryId = uri.getPathSegments().get(1);
                    updatedRows = db.update("Category", values, "id = ?", new String[] { categoryId });
                    break;
                default:
                    break;
            }
            return updatedRows;
        }
    
        @Override
        public int delete(Uri uri, String selection, String[] selectionArgs) {
            // 删除数据
            SQLiteDatabase db = dbHelper.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;
                default:
                    break;
            }
            return deletedRows;
        }
    
        @Override
        public String getType(Uri uri) {
            switch (uriMatcher.match(uri)) {
                case BOOK_DIR:
                    return "vnd.android.cursor.dir/vnd.com.example.mydatabasehelper. provider.book";
                case BOOK_ITEM:
                    return "vnd.android.cursor.item/vnd.com.example.mydatabasehelper. provider.book";
                case CATEGORY_DIR:
                    return "vnd.android.cursor.dir/vnd.com.example.mydatabasehelper. provider.category";
                case CATEGORY_ITEM:
                    return "vnd.android.cursor.item/vnd.com.example.mydatabasehelper. provider.category";
            }
            return null;
        }
    
    }
    
    
    4.2.2 访问接口的应用

    activity_main.xml文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <Button
            android:id="@+id/add_data"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Add To Book" />
    
        <Button
            android:id="@+id/query_data"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Query From Book" />
    
        <Button
            android:id="@+id/update_data"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Update Book" />
    
        <Button
            android:id="@+id/delete_data"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Delete From Book" />
    
    </LinearLayout>
    

    MainActivity.java文件

    public class MainActivity extends AppCompatActivity {
    
        private String newId;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button addData = (Button) findViewById(R.id.add_data);
            addData.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // 添加数据
                    Uri uri = Uri.parse("content://com.example.mydatabasehelper.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);
                    newId = newUri.getPathSegments().get(1);
                }
            });
            Button queryData = (Button) findViewById(R.id.query_data);
            queryData.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // 查询数据
                    Uri uri = Uri.parse("content://com.example.mydatabasehelper.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.d("MainActivity", "book name is " + name);
                            Log.d("MainActivity", "book author is " + author);
                            Log.d("MainActivity", "book pages is " + pages);
                            Log.d("MainActivity", "book price is " + price);
                        }
                        cursor.close();
                    }
                }
            });
            Button updateData = (Button) findViewById(R.id.update_data);
            updateData.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // 更新数据
                    Uri uri = Uri.parse("content://com.example.mydatabasehelper.provider/book/" + newId);
                    ContentValues values = new ContentValues();
                    values.put("name", "A Storm of Swords");
                    values.put("pages", 1216);
                    values.put("price", 24.05);
                    getContentResolver().update(uri, values, null, null);
                }
            });
            Button deleteData = (Button) findViewById(R.id.delete_data);
            deleteData.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // 删除数据
                    Uri uri = Uri.parse("content://com.example.mydatabasehelper.provider/book/" + newId);
                    getContentResolver().delete(uri, null, null);
                }
            });
        }
    
    }
    

    相关文章

      网友评论

        本文标题:第7章 探究内容提供者

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