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