Android开发Android开发

Android 四大组件(三)ContentProvider

2017-07-19  本文已影响0人  朋永

这篇主要介绍下ContentProvider如何实现共享数据、及ContentResolver如何访问其他进程等数据。


ContentProvider共享数据

简介

ContentProvider管理对一组结构化数据的访问。 它们封装了数据,并提供了定义数据安全性的机制。 ContentProvider是将一个进程中的数据与在另一进程中运行的代码相连接的标准接口。

当要访问ContentProvider中的数据时,您可以使用应用程序上下文中的ContentResolver对象作为客户端与提供者进行通信。 ContentResolver对象与Provider对象进行通信,该对象是实现ContentProvider的类的一个实例。提供者对象从客户端接收数据请求,执行请求的操作,并返回结果。

如果您不打算与其他应用程序共享数据,则不需要开发自己的provider程序。但是,您需要自己的provider在您自己的应用程序中提供自定义搜索建议。如果要将应用程序中的复杂数据或文件复制并粘贴到其他应用程序,还需要自己的provider程序。

Android本身包括管理音频,视频,图像和个人联系信息等数据的ContentProvider。有一些限制,任何Android应用程序都可以访问这些提供程序。

在开始构建提供程序之前,请先决定是否需要ContentProvider。如果要提供一个或多个以下功能,则需要构建内容提供程序:

  1. 你想提供复杂的数据或文件到其他应用程序。
  2. 您希望允许用户将应用程序中的复杂数据复制到其他应用程序中。
  3. 您希望使用搜索框架提供自定义搜索建议。

如果使用的完全是你自己应用程序中的SQLite数据库,则并不需要一个ContentProvider。

接下来,按照以下步骤构建ContentProvider:

  1. 为您的数据设计原始存储。ContentProvider以两种方式提供数据:
  1. 定义ContentProvider类及其所需方法的具体实现。
  2. 定义ContentProvider的authority字符串,其内容URI和列名称。如果您希望提供程序的应用程序处理意图,还可以定义intent action,extras data和flags。还要定义要访问数据的应用程序所需的权限。您应该考虑将所有这些值定义为单独的合同类中的常量; 稍后,您可以将此类暴露给其他开发人员。

数据存储这里就不说了,不了解数据库的可以参考我这篇blog:Android 数据存储 (三)SQLite Databases

Content URIs

Content URI是标识provider中的数据的URI。 Content URI包括整个provider(其权限)的符号名称和指向表(路径)的名称。 可选的ID部分指向表中的单个行。每个数据访问方法 ContentProvider都有一个内容URI作为参数; 这允许您确定要访问的表,行或文件。
uri格式:

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

分为三个部分:

内容URI模式使用通配符匹配内容URI:

uri功能丰富,如下
content://com.zpengyong.app.provider/table/1 访问table数据中id为1的记录。
content://com.zpengyong.app.provider/table/1/name 访问table数据中id为1的name字段。

实现ContentProvider类

ContentProvider实例通过处理来自其他应用程序的请求来管理对一组结构化数据的访问。所有形式的访问最终都会调用ContentResolver,然后调用一个具体的方法ContentProvider来获取访问权限。

必需的方法
抽象类ContentProvider定义了您必须实现的六个抽象方法,作为您自己的具体子类的一部分。所有这些方法除了 onCreate()被客户端应用程序调用尝试访问您的ContentProvider。

实现这些方法应该说明如下:

Android系统在启动ContentProvider程序时调用onCreate()。 您应该在此方法中仅执行快速运行的初始化任务,并延迟数据库创建和数据加载,直到provider程序实际接收到对数据的请求。 如果在onCreate()中执行冗长的任务,您将减慢provider的启动速度。 反过来,这将减慢从provider到其他应用程序的响应。
例如,如果使用SQLite数据库,则可以在ContentProvider.onCreate()中创建一个新的SQLiteOpenHelper对象,然后在第一次打开数据库时创建SQL表。 为了方便起见,第一次调用getWritableDatabase()时,它会自动调用SQLiteOpenHelper.onCreate()方法。

数据存储

数据存储效果:

如果要使用ContentProvider权限来控制对数据的访问,则应将数据存储在内部文件,SQLite数据库或“云”(例如远程服务器)中,并且应保留文件和数据库对你的应用程序是私有的。

权限

即使底层数据是私有的,所有应用程序都可以读取或写入您的ContentProvider程序,因为默认情况下,您的ContentProvider程序没有设置权限。 要更改此设置,请使用<provider>元素的属性或子元素为清单文件中的ContentProvider设置权限。 您可以设置适用于整个ContentProvider程序,特定表格,甚至某些记录或三者的权限。
您的清单文件中包含一个或多个<permission>元素,为您的ContentProvider定义权限。 要使您的ContentProvider程序唯一的权限,请使用Java风格的范围设置为android:name属性。 例如,将读取权限命名为com.example.app.provider.permission.READ_PROVIDER。

以下描述了ContentProvider程序权限的范围,从适用于整个ContentProvider程序的权限开始,然后变得更细。 更细的权限优先于较大范围的权限:

<provider>元素

像Activity和Service组件一样,子类ContentProvider 必须使用该 <provider>元素在其应用程序的清单文件中定义 。Android系统从元素获取以下信息:
权限(android:authorities)
 标识系统中整个提供商的符号名称。
提供者类名( android:name)
 实现的类ContentProvider。该类在“ 实现ContentProvider类 ”一节中有更详细的描述 。
权限
 指定其他应用程序必须具有访问提供程序数据的权限的属性:

启动和控制属性
 这些属性决定了Android系统如何以及何时启动提供程序,提供程序的进程特性以及其他运行时设置:

信息属性
 provider的可选图标和标签:

代码

效果:


ContentProvider 应用

这个是基于之前数据库的demo改的。主界面主要是数据库的相关操作。
ContentProvider代码如下:

package com.zpengyong.app;

import com.zpengyong.app.db.UserSQLiteOpenHelper;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

public class TestProvider extends ContentProvider {
    private static final String TAG = "TestProvider";
    
    private static final String CONTENT_AUTHORITY = "com.zpengyong.app.provider";
    private static final String PATH_USER = "/user";
    private static final int CODE_USER = 1;
    
    private static final Uri URI_USER = Uri.parse("content://com.zpengyong.app.provider/user");
    
    private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    static{
        mUriMatcher.addURI(CONTENT_AUTHORITY, PATH_USER, CODE_USER);        
    }
    private Context mContext;

    private UserSQLiteOpenHelper mUserSQLiteOpenHelper;
    private SQLiteDatabase mDatabase;
    
    private ContentResolver mContentResolver;
    @Override
    public boolean onCreate() {
        mContext = getContext(); //获取上下文
        mUserSQLiteOpenHelper = UserSQLiteOpenHelper.getInstance(mContext); 
        mDatabase = mUserSQLiteOpenHelper.getWritableDatabase();
        mContentResolver = mContext.getContentResolver();
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        int match = mUriMatcher.match(uri);
        Cursor result;
        switch(match){
        case CODE_USER:
            result = mDatabase.query(UserSQLiteOpenHelper.DATABASE_TABLE_USER, 
                    projection, selection, selectionArgs, null, null, sortOrder);
            break;
        default:
            throw new UnsupportedOperationException("query not supported on uri:"+uri);
        }
        return result;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        int match = mUriMatcher.match(uri);
        Uri result;
        switch(match){
        case CODE_USER:
            long id = mDatabase.insert(UserSQLiteOpenHelper.DATABASE_TABLE_USER, null, values);
            result = ContentUris.withAppendedId(uri, id);
            break;
        default:
            throw new UnsupportedOperationException("Insert not supported on uri:"+uri);
        }
        if(result != null){
            //通知数据库的变化
            mContentResolver.notifyChange(URI_USER, null);
        }
        return result;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int match = mUriMatcher.match(uri);
        int result;
        switch(match){
        case CODE_USER:
            result = mDatabase.delete(
                    UserSQLiteOpenHelper.DATABASE_TABLE_USER, 
                    selection, selectionArgs);
            break;
        default:
            throw new UnsupportedOperationException("delete not supported on uri:"+uri);
        }
        if(result > 0){
            mContentResolver.notifyChange(URI_USER, null);
        }
        return result;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        int match = mUriMatcher.match(uri);
        int result;
        switch(match){
        case CODE_USER:
            result = mDatabase.update(
                    UserSQLiteOpenHelper.DATABASE_TABLE_USER, 
                    values, selection, selectionArgs);
            break;
        default:
            throw new UnsupportedOperationException("Update not supported on uri:"+uri);
        }
        if(result > 0){
            mContentResolver.notifyChange(URI_USER, null);
        }
        return result;
    }
}

ContentResolver访问共享数据

先看效果图:


ContentResolver 应用

该应用通过ContentResolver访问另一个应用ContentProvider中的共享数据,并就行删除、添加、更改操作。监听数据库等变化更新UI界面。

1 访问数据库:

private void refreshList() {
    // 查询数据,更新到listview上
    Cursor cursor = mContentResolver.query(URI_USER, null, null, null, "modifyTime desc");
    if (null != cursor) {
        List<UserBean> userList = new ArrayList<UserBean>();
        while (cursor.moveToNext()) {
            UserBean user = new UserBean();
            user.set_id(cursor.getLong(cursor.getColumnIndex(COL_ID)));
            user.setName(cursor.getString(cursor.getColumnIndex(COL_NAME)));
            user.setPwd(cursor.getString(cursor.getColumnIndex(COL_PWD)));
            user.setModifyTime(cursor.getLong(cursor.getColumnIndex(COL_TIME)));
            userList.add(user);
        }
        mUserDataList = userList;
        // 刷新listview
        mAdapter.notifyDataSetChanged();
        cursor.close();
    }
}

2 添加数据

ContentValues values = new ContentValues();
values.put(COL_NAME, name);
values.put(COL_PWD, pwd);
values.put(COL_TIME, System.currentTimeMillis());
// 添加数据
Uri uri = mContentResolver.insert(URI_USER, values);

3 删除数据

// 删除数据 返回值表示删除的行数
int result = mContentResolver.delete(URI_USER, "_id = ?", new String[] { id + "" });

4 修改数据

String name = nameEt.getText().toString();
String pwd = pwdEt.getText().toString();
if (pwd.length() > 0 || name.length() > 0) {
    long id = mUserDataList.get(position).get_id();
    ContentValues values = new ContentValues();
    if (name.length() > 0) {
        values.put(COL_NAME, name);
    } else {
        values.put(COL_PWD, pwd);
    }
    values.put(COL_TIME, System.currentTimeMillis());
    // 修改数据 返回值表示修改的行数
    int result = mContentResolver.update(URI_USER, values, "_id = ?", new String[] { id + "" });
    Log.i(TAG, "update id=" + id + ",result=" + result);
}

5 监听数据库变化

mContentObserver = new MyContentObserver(new Handler());
mContentResolver = mContext.getContentResolver();
//监听数据库的变化
mContentResolver.registerContentObserver(URI_USER, true, mContentObserver);

class MyContentObserver extends ContentObserver {
    public MyContentObserver(Handler handler) {
        super(handler);
    }

    // 数据库变化会回调该方法
    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        Log.i(TAG, "onChange selfChange=" + selfChange);
        refreshList();
    }
}

6 取消监听数据库变化

// 取消监听数据库的变化
mContentResolver.unregisterContentObserver(mContentObserver);

相关文章:
Android 四大组件(一)Activity
Android 四大组件(二)Service

欢迎关注朋永的简书!


欢迎扫一扫关注我的微信公众号,不定期推送优质技术文章:

欢迎关注
上一篇 下一篇

猜你喜欢

热点阅读