Android学习随笔(13)------内容提供器

来源:互联网 发布:网络教学英语作文优点 编辑:程序博客网 时间:2024/06/07 12:47

学习流程来自《第一行代码》(第二版)
Content Provider主要用于在不同应用之间实现跨程序数据共享。

Android运行时权限

Android在6.0系统中加入了运行时权限功能,将所有权限归为了两类 :
1. 普通权限 :系统自动帮我们授权
2. 危险权限 :需要用户手动授权 一共9组,24个权限。一旦用户同意授权,那么该权限组中所有的其他权限也会同时被授权。

在程序运行时申请权限

activity_main.xml

<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>

点击按钮触发打电话的逻辑。

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);    // 6.0以下版本运行 权限 代码        makeCall.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                try {                    Intent intent = new Intent(Intent.ACTION_CALL);    // 系统内置的打电话的动作                    intent.setData(Uri.parse("tel:10086"));                    startActivity(intent);    // 需要在AndroidManifest.xml中声明                } catch (SecurityException e) {                    e.printStackTrace();                }            }        });    }}

会要求我们申请权限
AndroidManifest.xml

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

运行,在6.0版本以上的手机是会报错的
Exler
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 view) {                if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {    // 判断用户是不是已经授权checkSelfPermission(context, 具体权限) 相等就已经授权                    // ContextCompat.checkSelfPermission,主要用于检测某个权限是否已经被授予,方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。当返回DENIED就需要进行申请授权了。                    ActivityCompat.requestPermissions(MainActivity.this, new String[]{ Manifest.permission.CALL_PHONE}, 1);    // 申请权限 这是一个异步方法用于向用户申请授权                    //Activity activity,String[] permissions,int requestCode                    // Activity实例, String数组 申请权限名(是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权), 请求码(唯一值,主要用于回调时候的检测)                } else {                    call();                }            }        });    }    private 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, String[] permissions, int[] grantResults) {    // 授权结果封装在grantResults参数中 处理申请回掉 int requestCode        switch (requestCode) {    // 对于权限的申请结果,首先验证requestCode定位到你的申请,然后验证grantResults对应于申请的结果,这里的数组对应于申请时的第二个权限字符串数组。            // 如果你同时申请两个权限,那么grantResults的length就为2,分别记录你两个权限的申请结果            case 1: // ActivityCompat.requestPermissions()方法中的第三个参数                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {    // 申请结果是通过                    call();                } else {                    Toast.makeText(this, "You denied the permission", Toast.LENGTH_LONG).show();                }                break;            default:                break;        }    }}

Exler
点击允许,再次点击Make Call
Exler
可以在设置中对授予的权限进行关闭。

访问其他程序的数据

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

利用ContentResolver类(getContentResolver()方法获得)
内容提供器的唯一标识符是内容URI参数(由两部分组成authority和path,Content://com.example.app.provider/table1)

读取系统联系人

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    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"        /></LinearLayout>

利用ListView 来显示从手机中读取的联系人信息

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 contactsView =(ListView) findViewById(R.id.contacts_view);        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1, contactsList);        contactsView.setAdapter(adapter);    // 配置ListView        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() {        Cursor cursor = null;        try {            cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);  // 查询联系人数据(联系人的URI,指定查询的列名,指定where的约束条件,为where中的占位符提供具体的值,指定查询结果的排序方式)            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, String[] permissions, 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_LONG).show();                }                break;            default:                break;        }    }}

添加读取联系人的权限

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

Exler

Exler

创建自己的内容提供器

通过新建类继承ContentProvider来创建一个内容提供器。

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.yezhou.example.com.databasetest.provider";    private static UriMatcher uriMatcher;    private MyDatabaseHelper dbHelper;    static {    // 初始化操作,将期望匹配的几种uri格式添加        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);        uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);        uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);        uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);    }    public DatabaseProvider() {    }    @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) {    // 查询数据        // TODO: Implement this to handle query requests from clients.        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);    // 将内容URI权限之后的部分以"/"分割 0位置路径 1位置就是id                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 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 = dbHelper.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) {    // 用于获取Uri对象所对应的MIME类型        // 必须以vnd开头    如果内容URI以路径结尾,则后接android.cursor.dir/,如果内容URI以id结尾,则后接android.cursor.item/        // 最后接上vnd.authority.path        switch (uriMatcher.match(uri)) {            case BOOK_DIR:                return "vnd.android.cursor.dir/vnd.com.yezhou.example.com.databasetest.provider.book";            case BOOK_ITEM:                return "vnd.android.cursor.item/vnd.com.yezhou.example.com.databasetest.provider.book";            case CATEGORY_DIR:                return "vnd.android.cursor.dir/vnd.com.yezhou.example.com.databasetest.provider.category";            case CATEGORY_ITEM:                return "vnd.android.cursor.item/vnd.com.yezhou.example.com.databasetest.provider.category";            default:                break;        }        return null;    }}
<provider    android:name=".DatabaseProvider"    android:authorities="com.yezhou.example.com.databasetest.provider"    android:enabled="true"</provider>    <!--用于对DatabaseProvider进行注册,name 类名,-->

我们自己的内容提供器创建好了。
新建一个Android Project

<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>
public class MainActivity extends AppCompatActivity {    // 用于读取DatabaseTest应用数据库中的数据    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 view) {                Uri uri = Uri.parse("content://com.yezhou.example.com.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", 22.85);                Uri newUri = getContentResolver().insert(uri, values);                newId = newUri.getPathSegments().get(1);    // 将id取出            }        });        Button queryData = (Button) findViewById(R.id.query_data);        queryData.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {    // 查询数据                Uri uri =  Uri.parse("content://com.yezhou.example.com.databasetest.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("admin", "book name is " + name);                        Log.d("admin", "book name is " + author);                        Log.d("admin", "book name is " + pages);                        Log.d("admin", "book name is " + price);                    }                    cursor.close();                }            }        });        Button updateData = (Button) findViewById(R.id.update_data);        updateData.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Uri uri =  Uri.parse("content://com.yezhou.example.com.databasetest.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 view) {                Uri uri = Uri.parse("content://com.yezhou.example.com.databasetest.provider/book/" + newId);                getContentResolver().delete(uri, null, null);    // newId必须有值            }        });    }}

新项目就可以访问到之前项目中的数据了。

第三方ContentURI,匹配手机上所有app的Provider Authority,找到App,通过UriMatcher再次过滤,不匹配丢弃,匹配相应请求。


此博文为个人学习笔记,仅供个人学习使用,希望对大家有帮助。

原创粉丝点击