Android ContentProvider使用详解

一.

ContentProvider
1.ContentProvider为存储和获取数据提供了统一的接口

2.使用ContentProvider可以在不同的应用程序之间共享数据

二.

ContentProvider所提供的函数

1.query():查询
2.insert():插入
3.update():更新
4.delete():删除
5.getType():得到数据类型

6.onCreate():创建时的回调函数


实现ContentProvider的过程

1.定义一个CONTENT_URI常量
2.定义一个类,继承ContentProvider
3.实现query,insert,update,delete,getType和onCreate方法

4.在AndroidManifest.xml当中进行声明

定义ContentProvider中所需要的一些基本的常量

package com.lei.contentprovider;

import android.net.Uri;
import android.provider.BaseColumns;

/**
 * Created by renlei on 14-10-15.
 * ContentProvider的一些常量值
 */
public class ConstantContentProvide {
    /**AUTHORIY这里使用的是包名加上ContentProvider子类的全名*/
    public static final String AUTHORIY = "com.lei.contentprovider.ContentProviderTest";
    public static final String DB_NAME = "lei.db";
    public static final class TableMeta implements BaseColumns{
        public static final String TABLE_NAME = "userinfo";
        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORIY + "/"+TABLE_NAME);

        /**
         * 如果操作集合,则必须以vnd.android.cursor.dir开头
         * 如果操作非集合,则必须以vnd.android.cursor.item开头
        */
        public static final String CONTENT_TYPE ="vnd.android.cursor.dir/contentprovidertest.user";
        public static final String CONTENT_TYPE_ITEM ="vnd.android.cursor.item/contentprovidertest.user";
        public static final String USER_NAME = "name";
        public static final String USER_AGE = "age";
        public static final String DEFAULT_SORT_ORDER = "_id desc";

    }
}

构建数据库的类

package com.lei.contentprovider;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

/**
 * Created by renlei on 14-10-15.
 */
public class DBHelp extends SQLiteOpenHelper{

    public DBHelp(Context context){
        super(context,ConstantContentProvide.DB_NAME,null,1);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        Log.d("renlei","create db");
        String sql = "create table " + ConstantContentProvide.TableMeta.TABLE_NAME+ "(" + ConstantContentProvide.TableMeta._ID + " integer," + ConstantContentProvide.TableMeta.USER_NAME + " varchar(20),"+ ConstantContentProvide.TableMeta.USER_AGE +" integer)";
        db.execSQL(sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.d("renlei","upgrade db");
    }
}

核心类,继承类ContentProvider

package com.lei.contentprovider;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

import java.sql.SQLException;
import java.util.HashMap;

/**
 * Created by renlei on 14-10-15.
 */
public class ContentProviderTest extends ContentProvider {
    public static UriMatcher uriMatcher = null;
    public static final int USER_COLLECTION = 1;
    public static final int USER_SINGLE = 2;
    private DBHelp dbHelp;
    public static HashMap<String, String> userMap;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        //把繁琐的地址变成一个标识符
        uriMatcher.addURI(ConstantContentProvide.AUTHORIY, "userinfo", USER_COLLECTION);
        uriMatcher.addURI(ConstantContentProvide.AUTHORIY, "userinfo/#", USER_SINGLE);
    }

    static {
        userMap = new HashMap<String, String>();
        userMap.put(ConstantContentProvide.TableMeta._ID, ConstantContentProvide.TableMeta._ID);
        userMap.put(ConstantContentProvide.TableMeta.USER_NAME, ConstantContentProvide.TableMeta.USER_NAME);
        userMap.put(ConstantContentProvide.TableMeta.USER_AGE, ConstantContentProvide.TableMeta.USER_AGE);
    }

    @Override
    public boolean onCreate() {
        dbHelp = new DBHelp(getContext());
        Log.d("renlei", "onCreate");
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        switch (uriMatcher.match(uri)){
            case USER_COLLECTION:
                qb.setTables(ConstantContentProvide.TableMeta.TABLE_NAME);
                qb.setProjectionMap(userMap);
                break;
            case USER_SINGLE:
                qb.setTables(ConstantContentProvide.TableMeta.TABLE_NAME);
                qb.setProjectionMap(userMap);
                qb.appendWhere(ConstantContentProvide.TableMeta._ID+"="+uri.getPathSegments().get(1));
                break;
        }
        String orderBy ;
        if (TextUtils.isEmpty(sortOrder)){
            orderBy = ConstantContentProvide.TableMeta.DEFAULT_SORT_ORDER;
        }else {
            orderBy = sortOrder;
        }
        SQLiteDatabase db = dbHelp.getWritableDatabase();
        Cursor c = qb.query(db,projection,selection,selectionArgs,null,null,orderBy);
        c.setNotificationUri(getContext().getContentResolver(), uri);
        Log.d("renlei","query");
        return c;
    }

    @Override
    public String getType(Uri uri) {
        Log.d("renlei", "getType");
        switch (uriMatcher.match(uri)) {
            case USER_COLLECTION:
                return ConstantContentProvide.TableMeta.CONTENT_TYPE;
            case USER_SINGLE:
                return ConstantContentProvide.TableMeta.CONTENT_TYPE_ITEM;
            default:
                throw new IllegalArgumentException("Unknown URI" + uri);
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.d("renlei", "insert");
        SQLiteDatabase db = dbHelp.getWritableDatabase();
        long rowId = db.insert(ConstantContentProvide.TableMeta.TABLE_NAME,null,values);
        if (rowId>0){
            Uri insertUserUri = ContentUris.withAppendedId(ConstantContentProvide.TableMeta.CONTENT_URI,rowId);
            getContext().getContentResolver().notifyChange(insertUserUri,null);
            return insertUserUri;
        }
        try {
            throw new SQLException("Failed to insert row into" + uri);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}

测试activity

package com.lei.contentprovider;

import android.app.ActionBar;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.lei.ContentProvider.R;

public class MyActivity extends Activity {
    Button insertButton;
    Button queryBtn;
    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        insertButton = (Button)findViewById(R.id.insertButton);
        queryBtn = (Button)findViewById(R.id.queryButton);
        insertButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ContentValues values = new ContentValues();
                values.put(ConstantContentProvide.TableMeta.USER_NAME,"renlei");
                values.put(ConstantContentProvide.TableMeta.USER_AGE,23);
                Uri uri = getContentResolver().insert(ConstantContentProvide.TableMeta.CONTENT_URI,values);
                Log.d("renlei", "activityoncreate");
            }
        });

        queryBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Cursor c = getContentResolver().query(ConstantContentProvide.TableMeta.CONTENT_URI,null,null,null,null);
                while (c.moveToNext()){
                    Log.d("renlei",c.getString(c.getColumnIndex(ConstantContentProvide.TableMeta.USER_NAME))+c.getString(c.getColumnIndex(ConstantContentProvide.TableMeta.USER_AGE)));
                }
            }
        });
    }
}

测试布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <Button
            android:id="@+id/insertButton"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="插入"/>

    <Button
            android:id="@+id/queryButton"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="查询"/>

</LinearLayout>

manifest中的配置

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.lei.ContentProvider"
          android:versionCode="1"
          android:versionName="1.0">
    <uses-sdk android:minSdkVersion="19"/>
    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
        <activity android:name="com.lei.contentprovider.MyActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <provider android:authorities="com.lei.contentprovider.ContentProviderTest"
                  android:name="com.lei.contentprovider.ContentProviderTest"
                  android:exported="true"
                />
    </application>
</manifest>

三.ContentProvider的权限问题(此处借鉴了别人的文章)

访问其他应用的content provider

我们在ProPermission中提供了一个content provider,成为PrivProvider,然后在ProPermissionClient中对调用这个provider接口。在ProPermission的AndroidManifest.xml中,对provider声明如下:

<provider android:name=".PrivProvider"
    android:authorities="cn.wei.flowingflying.propermission.PrivProvider"
    android:exported="true" />

android:exported属性非常重要。这个属性用于指示该服务是否能够被其他应用程序组件调用或跟它交互。如果设置为true,则能够被调用或交互,否则不能。设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。如果content provider允许其他应用调用,即允许其他进程调用,需要将该属性设置为true。如果,我们设置该属性,会报下面的错误:

Provider的读写权限

Provider可以提供读权限,写权限,或者权限,例如:

<permission android:name="wei.permission.READ_CONTENTPROVIDER"
    android:label="Allow read content provider"
    android:protectionLevel="normal" /> 

    ……

<provider android:name=".PrivProvider"
    android:authorities="cn.wei.flowingflying.propermission.PrivProvider"
    android:readPermission="wei.permission.READ_CONTENTPROVIDER"
    android:exported="true" >
</provider>

设置写权限属性为android:writePermission,读写权限为android:permission。我们仍使用ProPermissionClient来访问provider接口,则出现权限错误:

我们在ProPermissionClient的AndroidManifest.xml中加上权限声明即可:

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

Provider的URI权限

前面,我们定义的整个provider的权限,但实际使用中,provider可以只开放部分URI的权限,例如本例,我们可以只开发content://cn.wei.flowingflying.propermission.PrivProvider/hello路径下权限,不允许访问其他路径,如下声明:

<provider android:name=".PrivProvider"
    android:authorities="cn.wei.flowingflying.propermission.PrivProvider"
    android:readPermission="wei.permission.READ_CONTENTPROVIDER"
    android:exported="true" >
    <path-permission android:pathPrefix="/hello" android:readPermission="READ_HELLO_CONTENTPROVIDER" />
</provider>

我们可以针对其中某个或某部分URI,单独进行权限设置。除了android:pathPrefix,还可以有android:path和android:pathPatten,例如android:pathPattern="/hello/.*"(注意,通配符*之前有个‘.’)。

在ProPermissionClient如果要读取content:content://cn.wei.flowingflying.propermission.PrivProvider/hello/1则需要声明整个provider的权限wei.permission.READ_CONTENTPROVIDER或者该路径的权限READ_HELLO_CONTENTPROVIDER。

Provider的granting

全局granting

ProPermissionClient具有读取content provider的权限,它去调用另一个应用C的activity,例子中这个另一个应用C为ProPermissionGrant,但是这个例子没有读取content provider的权限,ProPermissionClient可以将自己的权限通过intent传递给应用C,让其也具有访问content provider的权限。

对于应用A,相关的content provider为:

<provider android:name=".PrivProvider"
     android:authorities="cn.wei.flowingflying.propermission.PrivProvider"
     android:readPermission="wei.permission.READ_CONTENTPROVIDER"

     android:grantUriPermissions="true"
     android:exported="true" />

对于应用B,其具有wei.permission.READ_CONTENTPROVIDER的权限,而应用C不具备,应用B通过intent调用应用C的代码如下:

Intent intent = new Intent(this,ReadProvider.class);
intent.setClassName("com.example.propermissiongrant", "com.example.propermissiongrant.MainActivity");
intent.setData(Uri.parse("content://cn.wei.flowingflying.propermission.PrivProvider/world/1"));

intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);  //传递权限
startActivity(intent);

所调用的C的activity具备访问content provider的权限。

如果我们将provider的属性android:grantUriPermissions设置为false,则不允许通过接受传递的权限方式进行访问,即B所调用的C的activity不能读content provider,报错如下:

部分URI的granting

有时候,我们只希望部分的URI允许grant权限访问,而不是开放整个provider,如下:

<provider android:name=".PrivProvider"
     android:authorities="cn.wei.flowingflying.propermission.PrivProvider"
     android:readPermission="wei.permission.READ_CONTENTPROVIDER"
     android:exported="true" > 
     <grant-uri-permission android:pathPrefix="/hello" />
</provider>

我们将之允许前缀为hello的部分URI访问。一旦我们设置了grant-uri-permission,则全局的android:grantUriPermissions属性将无效,无论设置true还是flase,也都是只允许grant-uri-permission是声明的部分uri可以被grant权限访问。



下载链接:http://download.csdn.net/detail/renlei0109/8044325



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值