Android类-ContentProvider

1 提供程序定义

ContentResolver 方法可提供持续存储的基本“CRUD”(创建、检索、更新和删除)功能。 
客户端应用进程中的 ContentResolver 对象和拥有提供程序的应用中的 ContentProvider 对象可自动处理跨进程通信。
注:要访问提供程序,您的应用通常需要在其清单文件中请求特定权限。

2 访问提供程序

通过query(Uri,projection,selection,selectionArgs,sortOrder)来获取字词及列表。 客户端的 ContentResolver.query()会调用提供程序定义的ContentProvider.query()。
- 内容Uri : 内容 URI 是用于在提供程序中标识数据的 URI。内容 URI 包括整个提供程序的符号名称(其权限)和一个指向表的名称(路径)。
Note: Uri和Uri.Buillder类 包含构造URI对象的便利方法。ContentUris包括一些将ID值追加到URI后的方法。例如:
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4); //从用户字典中检索 _ID 为 4 的行
在检索到一组行后想要更新或删除其中某一行时通常会用到 ID 值。
例子:字词表的完整URI是content://user_dictionary/words,协定类表达是 UserDictionary.Words.CONTENT_URI

3 提供程序数据类型

- 整型,长整型(长),浮点型,长浮点型(双倍)
- 二进制大型对象(BLOB)
提供程序还会维护其定义的每个内容URI的MIME。我们可以通过MINE类型信息选择处理类型。例如:联系人提供程序中的 ContactsContract.Data 表会使用 MIME 类型来标记每行中存储的联系人数据类型

3.1 协定类 :

帮助应用使用内容 URI、列名称、 Intent 操作以及内容提供程序的其他功能的常量。例如:联系人提供程序的 ContactsContract 也是一个协定类

3.2 MIME 类型引用 : 内容提供程序可以返回标准 MIME 媒体类型和/或自定义 MIME 类型字符串。

    MIME (Multipurpose Internet Mail Extensions) : 多用途互联网邮件扩展
    格式:type/subtype : 例如:众所周知的 MIME 类型 text/html 具有 text 类型和 html 子类型。
    - 自定义MIME类型
自定义MIME类型字符串的类型值始终是:vnd.android.cursor.dir(多行)或vnd.android.cursor.item(单行)
例如,当联系人应用为电话号码创建行时,它会在行中设置以下 MIME 类型: vnd.android.cursor.item/phone_v2(子类型值是 phone_v2)
假设提供程序包括列车时刻表。提供程序的授权是com.example.trains,并包含表Line1,Line2,Line3。
在响应表Line1的内容URI content://com.example.trains/Line1 时,提供程序会返回MIME类型是 vnd.android.cursor.dir/vnd.example.line1
在响应表Line2中第5行的内容URI content://com.example.trains/Line2/5 时,提供程序会返回MIME类型是 vnd.android.cursor.item/vnd.example.line2

4 提供程序访问的替代形式

 - 批量访问:您可以通过 ContentProviderOperation 类中的方法创建一批访问调用,然后通过 ContentResolver.applyBatch() 应用它们。 
- 异步查询:您应该在单独线程中执行查询。执行此操作的方式之一是使用 CursorLoader 对象。 加载器指南中的示例展示了如何执行此操作。 
- 通过 Intent 访问数据:尽管您无法直接向提供程序发送 Intent,但可以向提供程序的应用发送 Intent,后者通常具有修改提供程序数据的最佳配置。
    Demo: 通过Intent修改通讯录 

5 创建内容提供程序

5.1 设计数据存储

- 储存结构化数据: Android 系统包括一个 SQLite 数据库 API。SQLiteOpenHelper 类可帮助您创建数据库,SQLiteDatabase 类是用于访问数据库的基类。
- 存储文件数据:
- 使用基于网络的数据,请使用 java.net 和 android.net 中的类
Note:使用二进制大型对象 (BLOB) 数据类型存储大小或结构会发生变化的数据。 例如,您可以使用 BLOB 列来存储协议缓冲区或 JSON 结构。

5.2 设计和实现权限

 该权限充当其 Android 内部名称。例如,如果您的 Android 软件包名称为 com.example.<appname>,则应为提供程序授予权限 com.example.<appname>.provider 。
如下描述提供程序权限的作用域,从使用整个程序开始,然后细化。细化的权限优先作用域较大的权限:
- 统一读写提供程序级别权限
    一个同时控制对整个提供程序读取和写入访问的权限,通过 <provider> 元素的 android:permission 属性指定。
- 单独的读取和写入提供程序级别权限
    针对整个提供程序的读取权限和写入权限。您可以通过 <provider> 元素的 android:readPermission 属性和 android:writePermission 属性 指定它们。它们优先于 android:permission 所需的权限。    
- 路径级别权限 
    针对提供程序中内容 URI 的读取、写入或读取/写入权限。您可以通过 <provider> 元素的 <path-permission> 子元素指定您想控制的每个 URI。
- 临时权限 
    要想启用临时权限,请设置 <provider> 元素的 android:grantUriPermissions 属性,或者向您的 <provider> 元素添加一个或多个 <grant-uri-permission> 子元素。
    要向应用授予临时访问权限, Intent 必须包含 FLAG_GRANT_READ_URI_PERMISSION 和/或 FLAG_GRANT_WRITE_URI_PERMISSION 标志。它们通过 setFlags() 方法进行设置。

5.3 元素

     与 Activity 和 Service 组件类似,必须使用 <provider> 元素在清单文件中为其应用定义 ContentProvider 的子类。

5.4 Demo:创建一个自己的内容提供程序

UI 界面:
  • 截图如下:
    这里写图片描述
  • 功能介绍:
    Demo 1:通过Intent方式和CotentResolver接口访问和更新日历提供程序。
    Demo 2:通过ContentResolver接口查询/插入/更新/删除自定义的提供程序,提供程序使用的是google Robotium里面提供的RobotiumNotepad项目。
  • 代码:
public class ContentProviderActivity extends Activity {

    Button mIntentGetButton, mContentP_GetButton, mContentP_SetButton;
    Button mQueryButton,mInsertButton,mUpdateButton,mDeleteButton;
    EditText mEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_contentprovider);


        mIntentGetButton = (Button)findViewById(R.id.intentgetbutton);
        mIntentGetButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                //通过Intent方式获取数据,不需要增加权限
                Calendar beginTime = Calendar.getInstance();
                beginTime.set(2012, 1, 19, 7, 30);
                long startMillis = beginTime.getTimeInMillis();

                Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
                builder.appendPath("time");

                ContentUris.appendId(builder, startMillis);

                Intent intent = new Intent(Intent.ACTION_VIEW).setData(builder.build());
                startActivity(intent);
            }
        });

        mContentP_GetButton = (Button)findViewById(R.id.contentR_getbutton);
        mContentP_GetButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                //通过contentResolver方式增加数据,需要在Manifest里面增加WRITE_CALENDAR权限
                QueryCalenderData();


            }
        });

        mContentP_SetButton = (Button)findViewById(R.id.contentR_setbutton);
        mContentP_SetButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                //通过contentResolver方式增加数据,需要在Manifest里面增加WRITE_CALENDAR权限
                updateCalenderData();
            }
        });

        //访问NotePad中定义的Content Provider
        mEditText = (EditText)findViewById(R.id.editText);

        mQueryButton = (Button)findViewById(R.id.contentprovider_query_button);
        mQueryButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                //通过contentResolver方式访问,需要在Manifest里面增加权限
                QueryNotepadData();
            }
        });

        mInsertButton = (Button)findViewById(R.id.contentprovider_insert_button);
        mInsertButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Uri mUri = NoteColumns.CONTENT_URI;
                String title = mEditText.getText().toString();
                String note = "Use this note for working items";
                long now = System.currentTimeMillis();
                long createDate = now;
                long updateData = now;
                InsertNotepadData(mUri,title,note,createDate,updateData);
            }
        });

        mUpdateButton = (Button)findViewById(R.id.contentprovider_update_button);
        mUpdateButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                String title =mEditText.getText().toString();   
                UpdateNotepadData(title);

            }
        });

        mDeleteButton = (Button)findViewById(R.id.contentprovider_delete_button);
        mDeleteButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                String title =mEditText.getText().toString();           
                DeleteNotepadData(title);           
            }
        });
    }

    //---------------------------using in Notepad Demo
    private static final String[] NOTEPAD_PROJECTION = new String[] {
        NoteColumns._ID, // 0
        NoteColumns.TITLE, // 1
    };


    private void DeleteNotepadData(String title){
        Cursor cur = null;
        ContentResolver cr = getContentResolver();
        Uri uri = NoteColumns.CONTENT_URI;
        String selection = "TITLE=?";
        String[] selectionArgs = new String[]{title};
        cur = cr.query(uri, NOTEPAD_PROJECTION, selection, selectionArgs, null);
        long updateID;
        int n = 0;
        while(cur.moveToNext()){
            updateID = cur.getLong(0);
            Uri uriDelete = ContentUris.withAppendedId(NoteColumns.CONTENT_URI, updateID);
            getContentResolver().delete(uriDelete, null, null);
            n++;
        }
        Toast.makeText(ContentProviderActivity.this, "Delete items : " + n, Toast.LENGTH_SHORT).show();
    }

    private void UpdateNotepadData(String title){
        Cursor cur = null;
        ContentResolver cr = getContentResolver();
        Uri uri = NoteColumns.CONTENT_URI;
        String selection = "TITLE=?";
        String[] selectionArgs = new String[]{title};
        cur = cr.query(uri, NOTEPAD_PROJECTION, selection, selectionArgs, null);
        long updateID;
        if(cur.moveToFirst()){
            updateID = cur.getLong(0);
        }else{
            Toast.makeText(ContentProviderActivity.this, "Do not find item with title : " + title, Toast.LENGTH_SHORT).show();
            return;
        }

        ContentValues values = new ContentValues();
        values.put(NoteColumns.TITLE, "unkown");
        Uri uriUpdate = ContentUris.withAppendedId(uri, updateID);
        int row = getContentResolver().update(uriUpdate, values, null, null);

        Toast.makeText(ContentProviderActivity.this, "Update row :" + updateID + "\n id = " + updateID, Toast.LENGTH_SHORT).show();
    }

    private void InsertNotepadData(Uri uri,String title, String note, long createDate, long updateDate){
        ContentValues values = new ContentValues();
        values.put(NoteColumns.TITLE, title);
        values.put(NoteColumns.NOTE, note);
        values.put(NoteColumns.CREATED_DATE, createDate);
        values.put(NoteColumns.MODIFIED_DATE, updateDate);
        getContentResolver().insert(uri, values);
        Toast.makeText(ContentProviderActivity.this, "Insert item that title is : " + title + "  successfully", Toast.LENGTH_SHORT).show();
    }

    private void QueryNotepadData(){
        Cursor cur = null;
        ContentResolver cr = getContentResolver();
        Uri uri = NoteColumns.CONTENT_URI;
        String selection = "";
        String[] selectionArgs = new String[]{};

        cur = cr.query(uri, NOTEPAD_PROJECTION, selection, selectionArgs, null);


        String output = "";

        while(cur.moveToNext()){
            long mID = cur.getLong(0);
            String mTitle = cur.getString(1);
            output = output + "\n mTitle : " + mTitle + " mID : " + mID;
        }

        Toast.makeText(this, "Total number is" + cur.getCount() + "\n" + output , Toast.LENGTH_SHORT).show();
    }

    //----------------------------
    public static final String[] EVENT_PROJECTION = new String[]{
            Calendars._ID, //0
            Calendars.ACCOUNT_NAME, //1
            Calendars.CALENDAR_DISPLAY_NAME, //2
            Calendars.OWNER_ACCOUNT //3
    };

    private void QueryCalenderData(){
        Cursor cur = null;
        ContentResolver cr = getContentResolver();
        Uri uri = Calendars.CONTENT_URI;
        String selection = "";
        String[] selectionArgs = new String[]{};

        cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);


        String output = null;

        while(cur.moveToNext()){
            long calID = cur.getLong(0);
            String accountName = cur.getString(1);
            String calendarDisplayName = cur.getString(2);
            String ownerAccount = cur.getString(3);
            output = output + "\ncalendarDisplayName : " + calendarDisplayName + " calID : " + calID;
        }

        Toast.makeText(this, "Total number is" + cur.getCount() + "\n" + output , Toast.LENGTH_SHORT).show();

    }

    private void updateCalenderData(){
        long calID = 2;
        ContentValues values = new ContentValues();
        values.put(Calendars.CALENDAR_DISPLAY_NAME, "lewi's Calendar");
        Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID);
        int rows = getContentResolver().update(updateUri, values, null, null);
        Toast.makeText(this, "Rows updated :" + rows, Toast.LENGTH_SHORT).show();
    }

}
  • 代码分析:
    自定义的NotePadProvider通过SQLiteDatabase来保存数据。
    通过在NotePadProvider中加入打印日志分析,自定义的ContentProvider的启动时机是当有client去调用的时候。第一次会初始化DatabaseHelper类,之后直接和数据库交互。

  • 问题描述:
    希望NotePadProvider能够提供不同权限的接口,但是访问时, 提供权限不够。在csdn论坛里面发帖询问,暂时无人回答:http://bbs.csdn.net/topics/391843256

6 Android自带提供程序

- Calendar Provider : http://developer.android.com/intl/zh-cn/guide/topics/providers/calendar-provider.html
- Contacts Provider: http://developer.android.com/intl/zh-cn/guide/topics/providers/contacts-provider.html
- Storage Access Framework(SAF) :http://developer.android.com/intl/zh-cn/guide/topics/providers/document-provider.html

7 Reference:

http://developer.android.com/intl/zh-cn/reference/android/content/ContentProvider.html 官方api文档
http://developer.android.com/intl/zh-cn/guide/topics/providers/content-providers.html 中文文档
http://www.android-doc.com/reference/android/provider/package-summary.html 访问Android提供的 content provider的类接口(协定类)
http://developer.android.com/intl/zh-cn/guide/topics/providers/contacts-provider.html 联系人提供程序
http://developer.android.com/intl/zh-cn/reference/android/Manifest.permission.html permission类(提供了具体权限的常量)
http://developer.android.com/intl/zh-cn/guide/topics/manifest/provider-element.html Provider元素

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值