• <Android基础> (七)内容提供器


    第七章 内容提供器

    7.1 内容提供器(Content Provider)

    主要应用于在不同的应用程序之间实现数据共享功能。允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。

    7.2 运行时权限

    7.2.1 Android权限机制

    普通权限:不会直接威胁到用户的安全和隐私的权限,系统会自动进行授权

    危险权限:可能会触及用户隐私或者对设备安全性造成影响的权限,对于这部分权限,必须要手动点击授权才可以,否则程序就无法使用相应的功能。

    7.2.2 在程序运行时申请权限

    新建RuntimePermissionTest项目,使用CALL_PHONE权限,编写拨打电话功能的时候需要声明。

    1.Android 6.0之前

    a.activity_main.xml中加入Button

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <Button
            android:id="@+id/make_call"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="make call"/>
    </LinearLayout>

    b.MainActivity.java中注册事件

     Button makeCall = (Button)findViewById(R.id.make_call);
            makeCall.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                       try{
                               Intent intent = new Intent(Intent.ACTION_CALL);
                               intent.setData(Uri.parse("tel:10086"));
                               startActivity(intent);
                        }catch(SecurityException e){
                                e.printStackTrace();  
            });

    构建了一个隐式Intent

    c.增加AndroidManifest.xml中的权限

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.sog.runtimepermissiontest">
    
        <uses-permission android:name="android.permission.CALL_PHONE"/>
    
        <application

    低于6.0的系统都是可以正常运行的,当在6.0或更高版本系统的手机上运行,打印日志会发现需要进行权限处理。

    2. 6.0系统以上的处理

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button makeCall = (Button)findViewById(R.id.make_call);
            makeCall.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                   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();
                   }
                }
            });
        }
        public void call(){
            try{
                Intent intent = new Intent(Intent.ACTION_CALL);
                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, "You denied the permission", Toast.LENGTH_SHORT).show();
                    }
                    break;
                default:
            }
        }
    }

    onCreate()方法中,借助ContextCompat.checkSelfPermission()方法判断是否用户已经授权了,接收两个参数,第一个为Context,第二个参数为具体的权限名,然后将使用方法的返回值和PackageManager.PERMISSION_GRANTED作比较,相等就表示已经授权,否则就为没授权。

    授权了就call(),没有授权就调用requestPermissions()方法,接收三个参数,第一个参数为Activity的实例,第二个参数为String数组,要把申请的权限名传入,第三个参数为请求码,传入1

    Button makeCall = (Button)findViewById(R.id.make_call);
            makeCall.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                   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();
                   }
                }
            });

    调用完了requestPermissions()方法之后,系统会弹出一个权限申请的对话框,用户选择同意或者拒绝,不论什么结果,都会回调到onReqPermissionsResult()方法中

        @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, "You denied the permission", Toast.LENGTH_SHORT).show();
                    }
                    break;
                default:
            }
        }

     7.3 访问其他程序中的数据

    内容提供器的用法:

    1.使用现有的内容提供器来读取和操作相应程序中的数据

    2.创建自己的内容提供器给程序的数据提供外部访问的接口

    7.3.1 ContentResolver的基本用法

    访问内容提供器中共享的数据,就一定要借助ContentResolve类,可以通过Context中的getContentResolver()方法获取该实例。

    ContentResolver中提供了一系列的方法用于对数据进行CRUD操作,insert()用于添加数据,update()用于更新数据,delete()用于删除数据,query()用于查询数据。

    CRUD操作的参数为Uri参数,内容URI给内容提供器中的数据建立了唯一的标识符,由两部分组成:authority和path。

    authority一般为程序包名,path则为用于对一应用程序中不同的表做区分的,最后要在字符串头部加上协议声明,如:

    content://com.example.app.provider/table1

    content://com.example.app.provider/table2

    用Uri.parse()方法进行解析为Uri对象

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

    然后就可以利用该Uri对象来查询table1表中的数据了

    Cursor cursor = getContentResolver().query(
        uri,
        projection,
        selection,
        selectionArgs,
        sortOrder);

    通过Cursor对象来进行增删改查操作

    1.查

    遍历Cursor所有行,通过移动游标的方式,再取出每一行中相应列的数据。

    if(cursor != null){
        while(cursor.moveToNext()){
                String column1 = cursor.getString(cursor.getColumnIndex("column1"));
                int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
        }
        cursor.close();      
    }

    2.增

    向table1中添加数据

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

    3.清空column1值,update

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

    4.删除

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

    7.3.2 读取系统联系人

    新建项目ContactsTest,在activity_main中添加ListView

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.example.song.contactstest.MainActivity">
    
        <ListView
            android:id="@+id/contacts_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

    修改MainActivity中的代码

    import android.content.pm.PackageManager;
    import android.database.Cursor;
    import android.provider.ContactsContract;
    import android.support.annotation.NonNull;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.content.ContextCompat;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.widget.ArrayAdapter;
    import android.widget.ListView;
    import android.widget.Toast;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.jar.Manifest;
    
    public class MainActivity extends AppCompatActivity {
        ArrayAdapter<String> adapter;
        List<String> contactsList = 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);
            adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contactsList);
            contactsView.setAdapter(adapter);
            //权限设置
            if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED){
                ActivityCompat.requestPermissions(this, new String[]{ android.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));
                        contactsList.add( displayName + "
    " + 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,"You denied the permission", Toast.LENGTH_SHORT).show();
                    }
                    break;
                default:
            }
        }
    }

    重点在于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));
                        contactsList.add( displayName + "
    " + number );
                    }
                }
                adapter.notifyDataSetChanged();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(cursor != null){
                    cursor.close();
                }
            }
        }

    使用ContentResolver的query()方法来查询系统的联系人数据,没有调用Uri.parse()方法来解析Uri字符串,ContactsContract.CommomDataKinds.Phone类做好了封装

      String displayName =cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
      String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));  

    将拼接好的数据添加到ListView的数据源里,并通知刷新ListView。

    最后记得要关闭Cursor对象

    在AndroidManifest.xml中加入权限声明

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

    运行程序

    7.4 创建自己的内容提供器

    7.4.1 创建内容提供器的步骤

    新建一个类来继承ContentProvider的方式来创建一个自己的内容提供器

    import android.content.ContentProvider;
    import android.content.ContentValues;
    import android.database.Cursor;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.CancellationSignal;
    import android.support.annotation.Nullable;
    
    public class MyProvider extends ContentProvider{
        @Override
        public boolean onCreate() {
            return false;
        }
    
        @Nullable
        @Override
        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
            return null;
        }
    
     
    
        @Nullable
        @Override
        public Uri insert(Uri uri, ContentValues contentValues) {
            return null;
        }
    
        @Override
        public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
            return 0;
        }
    
        @Override
        public int delete(Uri uri, String s, String[] strings) {
            return 0;
        }
    
        @Nullable
        @Override
        public String getType(Uri uri) {
            return null;
        }
    }

    重写了6个方法

    1.onCreate()

    初始化内容提供器的时候调用,通常会完成对数据库的创建和升级等操作,返回true表示初始化成功,返回false则表示失败。

    2.query()

    从内容提供器中查询数据。使用Uri参数来确定查询哪张表,projection参数用于确定查询哪些列,selection和selectionArgs参数用于约束查询哪些行,sortOrder参数用于对结果进行排序,查询的结果存放在Cursor对象中返回。

    3.insert()

    向内容提供器中添加一条数据。使用uri参数来确定要添加的表,待添加的数据保存在values参数中。添加完成后,返回一个用于表示这条新纪录的Uri。

    4.update()

    更新内容提供器中以有的数据。使用uri参数来确定更新哪一张表中的数据,新数据保存在values中,selections和selectionArgs参数用于约束更新那些行,受影响的行数将作为返回值返回。

    5.delete()

    从内容提供器中删除数据。使用uri参数来确定删除哪一张表中的数据,selections和selectionArgs参数用于约束删除哪些行,被删除的行数将作为返回值返回。

    6.getType()

    根据传入的内容URI来返回相应的MIME类型。

    同时,可以在URI内容后面添加通配符

    数字:表示id    content://com.example.app.provider/table1/1

    *:表示匹配任意长度的任意字符    content://com.example.app.provider/*

    #:表示匹配任意长度的数字    content://com.example.app.provide/table1/#

    借助UriMatch类可以实现匹配内容Uri的功能,UriMatch类提供了一个addURI()方法,接受3个参数,可以分别传入authority、path和一个自定义代码。当调用match()方法时,可以将一个Uri对象传入,返回值是某个能够匹配这个Uri对象所对应的自定义代码,利用该代码,就可以判断出调用方期望访问的是表中哪个数据了。

    eg:

    更改类MyProvider中的代码

    public class MyProvider extends ContentProvider{
        public static final int TABLE1_DIR = 0;
    
        public static final int TABLE1_ITEM = 1;
    
        public static final int TABLE2_DIR = 2;
    
        public static final int TABLE2_ITEM = 3;
    
        public static UriMatcher uriMatcher;
    
        static{
            uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
            uriMatcher.addURI("com.example.app.provider","table1",TABLE1_DIR);
            uriMatcher.addURI("com.example.app.provider","table1/#",TABLE1_ITEM);
            uriMatcher.addURI("com.example.app.provider","table2",TABLE2_DIR);
            uriMatcher.addURI("com.example.app.provider","table2/#",TABLE2_ITEM);
        }
      
      ......
      
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    switch (uriMatcher.match(uri)){
    case TABLE1_DIR:
    break;
    case TABLE1_ITEM:
    break;
    case TABLE2_DIR:
    break;
    case TABLE2_ITEM:
    break;
    default:
    break;
    }
    return null;
    }
      .....
    }
     

    TABLE1_DIR表示访问table1表中的所有数据,TABLE1_ITEM表示访问table1表中的单条数据;TABLE2_DIR表示访问table2表中的所有数据,TABLE2_ITEM表示访问Table2中的单条数据。

    除此之外,还有一种getType()方法,是所有内容提供器都必须提供的一个方法,用于获取Uri对象所对应的MIME类型。

    一个内容URI对应的MIME字符串由3部分组成:

    1)必须以vnd开头

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

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

    所以,对于content://com.example.app.provider/table1,对应的MIME类型为:

    vnd.android.cursor.dir/vnd.com.example.app.provider.table1

    对于content://com.example/app.provider/table1/1,对应的MIME类型为:

    vnd.android.cursor.item/vnd.com.example.app.provider.table1

    所以对MyProvider中的方法还可以继续完善为:

      @Override
        public String getType(Uri uri) {
            switch(uriMatcher.match(uri)){
                case TABLE1_DIR:
                    return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";
                case TABLE1_ITEM:
                    return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";
                case TABLE2_DIR:
                    return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2";
                case TABLE2_ITEM:
                    return "vnd.android.cursor.item/vnd.com.example.app.provider.table2"
            }
            return null;
        }

    7.4.2 实现跨程序数据共享

  • 相关阅读:
    冒泡 希尔 快速 插入 堆 基数
    排序总结
    软件工程(齐治昌-谭庆平-宁洪)
    Java简单计算器
    插入排序
    Android中theme.xml与style.xml的区别
    activity theme parent 属性浅析
    xml中不能直接添加ViewGroup
    Java中对象的上转型对象
    Android原理View、ViewGroup
  • 原文地址:https://www.cnblogs.com/HarSong13/p/10710412.html
Copyright © 2020-2023  润新知