Android四大组件之Content Provider

来源:互联网 发布:节拍器软件 编辑:程序博客网 时间:2024/05/18 00:02

Content Provider简介

1.ContentProvider是android四大组件之一,需要在AndroidManifest.xml中进行配置.2.为了在应用程序之间交换数据,android提供了ContentProvider,是不同应用程序之间进行数据交换的标准API.3.当应用程序需要把自己数据暴露给其他程序时,就可以通过提供的ContentProvider来实现.4.其他程序通过ContentResolver根据Uri去访问操作ContentProvider暴露的数据.

Content Provider开发

  • 实现步骤
1.继承ContentProvider,实现query(),insert(),update()和delete()方法2.在AndroidManifest.xml文件中注册该ContentProvider,指定android:authorities
  • 配置ContentProvider
<application>        <provider         android:name=".xxxProvider"        android:authorities="com.example.provider"        android:exported="true"/></application>

Content Provider使用

  • Uri介绍
Andriod Application Study Note Url 9 uri.jpgscheme:协议,不仅包括传统的http等网络协议,还有content://来表示本地ContentProvider所提供的数据。authority:域名部分,包括host和port,host是主机名称,port是通信端口。Android系统就是由这个部分来找到操作哪个ContentProvider。path:资源路径(数据部分),当访问者需要访问不同资源时,这个部分是动态改变的。
Android为多媒体提供的ContentProvider的Uri如下:MediaStore.Audio.Media.EXTERNAL_CONTENT_URI:存储在外置SD卡上音频内容的URIMediaStore.Audio.Media.INTERNAL_CONTENT_URI:存储在内置SD卡上音频内容的URIMediaStore.Images.Media.EXTERNAL_CONTENT_URI:存储在外置SD卡上图片文件的URIMediaStore.Images.Media.INTERNAL_CONTENT_URI:存储在内置SD卡上图片文件的URIMediaStore.Video.Media.EXTERNAL_CONTENT_URI:存储在外置SD卡上视频内容的URIMediaStore.Video.Media.INTERNAL_CONTENT_URI:存储在内置SD卡上视频内容的URIAndroid系统对联系人管理ContentProvider的几个Uri如下:ContactsContract.Contacts.CONTENT_URI:管理联系人的UriContactsContract.CommonDataKinds.Phone.CONTENT_URI:管理联系人电话的UriContactsContract.CommonDataKinds.Email.CONTENT_URI:管理联系人Email的Uri
  • ContentResolver根据Uri去访问操作ContentProvider数据,ContentResolver方法如下:
insert(Uri url, ContentValues values): 向Uri对应的ContentProvider中插入values对应的数据delete(Uri url, String where, String[] selectionArgs): 删除Uri对应的ContentProvider中where提交匹配的数据update(Uri uri, ContentValues values, String where, String[] selectionArgs): 更新Uri对应的ContentProvider中where提交匹配的数据query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder): 查询Uri对应的ContentResolver中where提交匹配的数据
  • ContentResolver的增删改查方法中会调用ContentProvider对应的增删改查方法,ContentProvider方法如下:
onCreate(): 在创建ContentProvider时调用insert(Uri uri, ContentValues values): 用于添加数据到指定Uri的ContentProvider中delete(Uri uri, String selection, String[] selectionArgs): 用于从指定Uri的ContentProvider中删除数据update(Uri uri, ContentValues values, String selection, String[] selectionArgs): 用于更新指定Uri的ContentProvider中的数据query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder): 用于查询指定Uri的ContentProvider,返回一个CursorgetType(Uri uri): 用于返回指定的Uri中的数据的MIME类型
  • ContentResolver与ContentProvider关系图
Andriod Application Study Note Url 9 1.jpgCRUD(增删改查)方法的第一个参数都是UriUri是ContentResolver和ContentProvider进行数据交换的标识

Content Provider监听

  • 实现步骤
1.继承ContentObserver基类,并重写onChange(boolean selfChange)方法2.通过ContentResolver向指定Uri注册ContentObserver监听器,在不需要时,需要对监听器取消注册

Content Provider应用实例

  • 创建数据库
package com.example.provider;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;public class DatabaseHelper extends SQLiteOpenHelper {    private static final String DATABASE_NAME = "example.db";    private static final int DATABASE_VERSION = 1;    public DatabaseHelper(Context context) {        super(context, DATABASE_NAME, null, DATABASE_VERSION);    }    @Override    public void onCreate(SQLiteDatabase db) {        String sql = "CREATE TABLE user(_id integer primary key autoincrement,name varchar(10),phone varchar(11) NULL)";        db.execSQL(sql);    }    @Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}}
  • 创建ContentProvider 来对数据库进行共享
package com.example.provider;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.net.Uri;public class UserProvider extends ContentProvider {    private DatabaseHelper mHelper;    private static final UriMatcher MARCHER = new UriMatcher(UriMatcher.NO_MATCH);    private static final int USERS = 1;    private static final int USER = 2;    private static final String AUTHORITY = "com.example.provider";    private static final String DATABASE_TABLE = "user";    /*    UriMatcher工具类提供了2个方法用来确定内容提供者实际能处理的Uri:      1>.void addRUI(String authority,String path,int code):用于向UriMathcher对象注册Uri.authority和path组合成一个Uri,而code则代表该Uri对应的标识符.      2>.int match(Uri uri):根据前面注册的Uri来判断指定uri对应的标识符,如果找不到匹配的标识码就返回-1.    */    static {        MARCHER.addURI(AUTHORITY, "user", USERS);        MARCHER.addURI(AUTHORITY, "user/#", USER);    }    // 该方法用于返回当前Uri所代表数据的MIME类型    // 如果操作的数据数据集合类型,MIME类型字符串应该以vnd.android.cursor.dir/开头    // 如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头    @Override    public String getType(Uri uri) {        switch (MARCHER.match(uri)) {            case USERS:                return "vnd.android.cursor.dir/user";            case USER:                return "vnd.android.cursor.item/user";            default:                throw new IllegalArgumentException("Wrong Uri!");        }    }    @Override    public boolean onCreate() {        mHelper = new DatabaseHelper(getContext());        return true;    }    @Override    public Uri insert(Uri uri, ContentValues values) {        SQLiteDatabase db = mHelper.getWritableDatabase();        long _id = db.insert(DATABASE_TABLE, null, values);        if (_id > 0)            sendNotifyChange(uri);        return ContentUris.withAppendedId(uri, _id);    }    @Override    public int delete(Uri uri, String selection, String[] selectionArgs) {        SQLiteDatabase db = mHelper.getWritableDatabase();        int count = db.delete(DATABASE_TABLE, selection + "=?", selectionArgs);        if (count > 0)            sendNotifyChange(uri);        return count;    }    @Override    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {        SQLiteDatabase db = mHelper.getWritableDatabase();        int count = db.update(DATABASE_TABLE, values, selection + "=?", selectionArgs);        if (count > 0) {            String name = values.getAsString("name");            sendNotifyChange(Uri.withAppendedPath(uri, name));        }        return count;    }    @Override    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,            String sortOrder) {        SQLiteDatabase db = mHelper.getReadableDatabase();        Cursor mCursor = db.query(DATABASE_TABLE, projection, selection + "=?", selectionArgs,                null, null, sortOrder);        return mCursor;    }    private void sendNotifyChange(Uri uri) {        getContext().getContentResolver().notifyChange(uri, null);    }}
  • 配置ContentProvider
<application        <provider            android:name="com.example.provider.UserProvider"            android:authorities="com.example.provider"            android:exported="true" />    </application>
  • 使用Content Provider操作并监听数据
package com.example.providerdemo;import android.app.Activity;import android.content.ContentResolver;import android.content.ContentValues;import android.content.Context;import android.database.ContentObserver;import android.database.Cursor;import android.net.Uri;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.widget.EditText;import android.widget.Toast;public class MainActivity extends Activity {    private static final Uri USER_URI = Uri.parse("content://com.example.provider/user");    private ContentResolver mResolver;    private Context mContext;    private EditText name, phone;    private Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            if (msg.what == 0x123) {                show("Date Changed!");            }        }    };    private ContentObserver observer = new ContentObserver(mHandler) {        @Override        public void onChange(boolean selfChange) {            mHandler.sendEmptyMessage(0x123);        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mResolver = getContentResolver();        mContext = MainActivity.this;        name = (EditText) findViewById(R.id.name);        phone = (EditText) findViewById(R.id.phone);        Uri uri = Uri.withAppendedPath(USER_URI, "xiaoming");        mResolver.registerContentObserver(uri, false, observer);    }    @Override    protected void onDestroy() {        super.onDestroy();        mResolver.unregisterContentObserver(observer);    }    public void insert(View view) {        ContentValues values = new ContentValues();        values.put("name", name.getText().toString());        values.put("phone", phone.getText().toString());        Uri result = mResolver.insert(USER_URI, values);        if (result != null) {            show("Insert success!");        }    }    public void delete(View view) {        String whereClause = "name";        String[] whereArgs = {name.getText().toString()};        int result = mResolver.delete(USER_URI, whereClause, whereArgs);        if (result > 0) {            show("Delete success!");        }    }    public void update(View view) {        ContentValues values = new ContentValues();        values.put("name", name.getText().toString());        values.put("phone", phone.getText().toString());        String whereClause = "name";        String[] whereArgs = {name.getText().toString()};        int result = mResolver.update(USER_URI, values, whereClause, whereArgs);        if (result > 0) {            show("Update success!");        }    }    public void query(View view) {        String[] columns = {"_id", "name", "phone"};        String whereClause = "name";        String[] whereArgs = {name.getText().toString()};        Cursor result = mResolver.query(USER_URI, columns, whereClause, whereArgs, null);        if (result.moveToFirst()) {            String mPhone = result.getString(result.getColumnIndex("phone"));            show("query success. Phone:" + mPhone);        }    }    private void show(String str) {        Toast.makeText(mContext, str, Toast.LENGTH_SHORT).show();    }}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical" >    <EditText        android:id="@+id/name"        android:layout_width="match_parent"        android:layout_height="wrap_content" />    <EditText        android:id="@+id/phone"        android:layout_width="match_parent"        android:layout_height="wrap_content" />    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="insert"        android:text="Insert" />    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="delete"        android:text="Delete" />    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="update"        android:text="Update" />    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="query"        android:text="Query" /></LinearLayout>

Content Provider实现原理

  • Content Provider启动流程
第一次访问Content Provider时启动
Andriod Application Study Note Url 9 SequenceDiagram1.jpg
Andriod Application Study Note Url 9 SequenceDiagram2.jpg
Andriod Application Study Note Url 9 SequenceDiagram3.jpg
  • Content Provider数据共享原理
原理概要1.Content Provider组件将要传输的共享数据抽象为一个游标2.Content Provider组件通过Binder进程间通信机制来突破应用程序为边界的权限控制3.以匿名共享内存作为数据传输媒介,从而提供了一种高效的数据共享方式
概要流程xxActivity组件在请求xxProvider组件返回信息之前,首先在当前应用程序创建一个CursorWindow对象,CursorWindow内部包含了一块匿名共享内存。通过Binder进程间通信机制将所创建的CursorWindow对象(连同它内部的匿名共享内存)传递给xxProvider组件。xxProvider组件获得了xxActivity组件发送过来的CursorWindow对象之后,就会创建一个SQLiteCursor对象通过调用setWindow将CursorWindow对象保存在父类的成员变量mWindow中。SQLiteCursor对象创建完成之后,xxProvider组件就会将xxActivity组件所请求的数据保存在这个SQLiteCursor对象中实际上是保存在与它所关联的CursorWindow对象内部的一块匿名共享内存中xxActivity可以访问这块匿名共享内存,因此可以通过这块内存来获取xxProvider组件返回给它的数据。SQLiteCursor对象并不是一个Binder本地对象,xxProvider组件不能直接将它返回给xxActivity组件使用。xxProvider组件首先会创建一个CursorToBulkCursorAdaptor对象,用来适配前面创建的SQLiteCursor对象,并将这个对象保存在mCursor中然后将CursorToBulkCursorAdaptor对象返回给xxActivity组件。xxActivity组件收到xxProvider组件返回数据之前,除了创建CursorWindow对象外,还会创建一个BulkCursorToCursorAdaptor对象并将CursorWindow对象保存在它的父类AbstractWindowedCursor的成员变量mWindow中。xxActivity组件收到xxProvider组件返回的CursorToBulkCursorAdaptor对象之后,实际上获得的是CursorToBulkCursorAdaptor的代理对象并将它保存BulkCursorToCursorAdaptor对象的mBulkCursor中,这时候xxActivity组件就可以通过这个BulkCursorToCursorAdaptor对象来读取xxProvider组件返回的数据了。
SQLiteCursor类实现关系图Andriod Application Study Note Url 9 ClassDiagram.jpg注意:CursorWindow包含了一块匿名共享内存
Content Provider 数据共享模型Andriod Application Study Note Url 9 ContentProvider-asm.jpg注意:CursorWindow引用了同一块匿名共享内存
实现原理结构图Andriod Application Study Note Url 9 ContentProvider-cp-yl.jpg
  • Content Provider数据监听原理
原理概要1.ContentProvider组件的数据更新通知机制类似于Android系统的广播机制,都是一种消息发布和订阅的事件驱动模型。2.内容观察者ContentObserver负责接收数据更新通知,ContentProvider组件负责发送数据更新通知。3.内容观察者ContentObserver在接收到通知之前,必须要注册到ContentService中,通过URI来描述需要接收什么样的数据更新通知。
注册ContentObserverAndriod Application Study Note Url 9 SequenceDiagram4.jpg注意:1.真正注册到ContentService中的并不是一个ContentObserver,而是与这个ContentObserver所关联的binder本地对象Transport(ContentObserver contentObserver)2.mRootNode保存内容观察者
发送数据改变通知Andriod Application Study Note Url 9 SequenceDiagram5.jpg
回调onChange方法Andriod Application Study Note Url 9 ContentObserver.jpg
数据监听结构图Andriod Application Study Note Url 9 ContentObserver2.jpg
1 0
原创粉丝点击