ContentProvider理解

ContentProvider理解

引用:
ContentProvider详解
http://www.cnblogs.com/Coderwei2016/p/6058723.html
android四大组件–ContentProvider详解
http://www.2cto.com/kf/201404/296974.html
摘抄《第一行代码》第 7 章 跨程序共享数据,探究内容提供器

本人很少写博客,大部分也是想起来写一篇,我知道坚持写博客是一个好习惯,既加深了印象,也帮助了那些需要知识的朋友,就像我所知道的一些大神,每次在学习新的知识的时候我也在找他们的文章,说实话,从我学习Android以来CSDN对我帮助最大。

首先我先介绍一下这个博客的主要作用:因为我刚刚在学习Android的四大组件之一的ContentProvider所以想写一片博课加深一下对于ContentProvider的理解

  • ContentProvider的概述
  • ContentResolver
  • URI的详解
  • 自定义URI
  • 读取系统联系人

ContentProvider的概述

内容提供器( Content Provider)主要用于在不同的应用程序之间实现数据共享的功能。
ContentProvider向我们提供了我们在应用程序之前共享数据的一种机制,而我们知道每一个应用程序都是运行在不同的应用程序的,数据和文件在不同应用程序之间达到数据的共享不是没有可能,而是显得比较复杂,而正好Android中的ContentProvider则达到了这一需求,允许一个程序访问另一个程序中的数据,同时还能保证被访数据安全性,使用内容提供器是 Android 实现跨程序共享数据的标准方式。
比如有时候我们需要操作手机里的联系人,手机里的多媒体等一些信息,我们都可以用到这个ContentProvider来达到我们所需。
不同于文件存储和 SharedPreferences 存储中的两种全局可读写操作模式,内容提供器可选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险。

ContentResolver

对于每一个应用程序来说,如果想要访问内容提供器中共享的数据,就一定要借助ContentResolve 类,可以通过 Context 中的 getContentResolver()方法获取到该类的实例。
ContentResolver 中提供了一系列的方法用于对数据进行 CRUD 操作,其中 insert()方法用于添加数据, update()方法用于更新数据, delete()方法用于删除数据, query()方法用于查询数据。

private void readContacts() {
        Cursor cursor = null;
        try {
            // 查询联系人数据
            cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                    null, null, null, null);
            while (cursor.moveToNext()) {
                // 获取联系人姓名
                String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                // 获取联系人手机号
                String number = cursor.getString(cursor
                        .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                contactsList.add(displayName + "\n" + number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

URI的详解

Uri的定义:通用资源标识符(Universal Resource Identifier),Uri代表要操作的数据,Android 上每种可用的资源,包括图像、视频、联系人等都可以用Uri来表示。Uri的组成一般分成三部分:访问资源的命名机制、存放资源的主机名、资源自身的名称,由路径表示。

URI图解

A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;”content://” B:URI 的标识,用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称 C:路径(path),通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就可以了;”content://com.bing.provider.myprovider/tablename” D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; “content://com.bing.provider.myprovider/tablename/#” #表示数据id。

PS:
路径(path)可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:
1、要操作person表中id为10的记录,可以构建这样的路径:/person/10
2、要操作person表中id为10的记录的name字段, person/10/name
3、要操作person表中的所有记录,可以构建这样的路径:/person
4、要操作xxx表中的记录,可以构建这样的路径:/xxx
5、当然要操作的数据不一定来自数据库,也可以是文件、xml或网络等其他存储方式,如下:
要操作xml文件中person节点下的name节点,可以构建这样的路径:/person/name
6、如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:Uri uri = Uri.parse(“content://com.bing.provider.personprovider/person”)

自定义URI

引用代码

首先在App1中创建一个数据库,用SQLiteOpenHelper

public class MyOpenHelper extends SQLiteOpenHelper {

    public MyOpenHelper(Context context){
        super(context,"Account.db",null,1);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table info(_id integer primary key autoincrement, name varchar(20),money varchar(10))");
        db.execSQL("insert into info(name, money) values(?,?)",new String[]{"张三","5000"});
        ContentValues cv = new ContentValues();
        cv.put("name","李四");
        cv.put("money","6000");
        db.insert("info",null,cv);
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

然后用过ContentProvider把数据库共享出来,首先看代码:

public class MyProvider extends ContentProvider {
    //定义一个Uri路径匹配器   (匹配不成功的时候会返回一个NO_MATCH返回一个-1)
    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int QUERYSUCESS = 0;
    private static final int INSERTSUCESS = 1;
    private static final int UPADTESUCESS = 2;
    private static final int DELETESUCESS = 3;
    //创建一个静态代码块,在这里添加Uri
    static {
        /**
         * authority 注意:和清单文件里面定义的一样 com.coderwei.provider/query
         */
        sURIMatcher.addURI("com.coderwei.provider","query",QUERYSUCESS );
        sURIMatcher.addURI("com.coderwei.provider","insert",INSERTSUCESS );
        sURIMatcher.addURI("com.coderwei.provider","update",UPADTESUCESS );
        sURIMatcher.addURI("com.coderwei.provider","delete",DELETESUCESS );
    }

    private MyOpenHelper myOpenHelper;

    @Override
    public boolean onCreate() {
        //初始化MyOpenHelper
        myOpenHelper = new MyOpenHelper(getContext());
        return false;
    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        if (sURIMatcher.match(uri)==QUERYSUCESS){
            //说明路径匹配成功
            SQLiteDatabase db = myOpenHelper.getReadableDatabase();
            Cursor cursor = db.query("info",projection,selection,selectionArgs,null,null,sortOrder);
            //db.close();
            //这里不能关闭Cursor
            return cursor;
        }else {
            throw  new IllegalArgumentException("uri路径不匹配");
        }
    }

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

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        if (sURIMatcher.match(uri)==INSERTSUCESS){
            SQLiteDatabase db = myOpenHelper.getReadableDatabase();
            long insert = db.insert("info",null,values);
            Uri uri1 = Uri.parse("com.coderwei.insert/"+insert);
            //db.close();
            return uri1;
        }else {
            throw  new IllegalArgumentException("uri路径不匹配");
        }
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        if (sURIMatcher.match(uri)==DELETESUCESS){
            SQLiteDatabase db = myOpenHelper.getReadableDatabase();
            int delete = db.delete("info",selection,selectionArgs);
            //db.close();
            return delete;
        }else {
            throw  new IllegalArgumentException("uri路径不匹配");
        }
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        if (sURIMatcher.match(uri)== UPADTESUCESS){
            SQLiteDatabase db = myOpenHelper.getReadableDatabase();
            int update = db.update("info",values,selection,selectionArgs);
            //db.close();
            return update;
        }else {
            throw  new IllegalArgumentException("uri路径不匹配");
        }
    }
}

代码很长,但是很简单,主要把上面四个增删改查方法实现就行,上面的db.close()关闭的时候报错了,所以就注释掉了,关不关都可以。

Insert方法返回的是一个Url对象,直接自己写一个就行了。

<provider
            android:authorities="com.coderwei.provider"
            android:exported="true"
            android:name=".MyProvider">
 </provider>

android:authorities 自己随便给一个字符串,
android:exported=”true”一定要写上这句,这句话表示可以被另一个Application的组件启动,没写可能会报错。
sURIMatcher.addURI(“com.coderwei.provider”,”query”,QUERYSUCESS );中的com.coderwei.provider就是这个字符串。

然后就是来到App2了,先看代码:
记得在Manifest.xml中配置ContentProvider:

public class MainActivity extends Activity  {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void click1(View v){
        doQuery();
    }
    public void click2(View v){
        doInsert();
    }
    public void click3(View v){
        doDelete();
    }
    public void click4(View v){
        doUpdate();
    }

    private void doQuery(){
        Uri uri = Uri.parse("content://com.coderwei.provider/query");
        Cursor cursor = getContentResolver().query(uri,new String[]{"name","money"},null,null,null);

        if (cursor!=null){
            while (cursor.moveToNext()){
                String name = cursor.getString(0);
                String money = cursor.getString(1);
                System.out.println("name:"+name+"  money: "+money);
            }
        }
    }

    private void doInsert(){
        Uri uri = Uri.parse("content://com.coderwei.provider/insert");
        ContentValues cv = new ContentValues();
        cv.put("name","王五");
        cv.put("money","800");
        Uri uri1 = getContentResolver().insert(uri,cv);
        System.out.println("插入uri1:"+uri1);
    }

    private void doDelete(){
        Uri uri = Uri.parse("content://com.coderwei.provider/delete");
        int delete = getContentResolver().delete(uri,"name=?",new String[]{"王五"});
        System.out.print("刪除了: "+delete);
    }

    private void doUpdate(){
        Uri uri = Uri.parse("content://com.coderwei.provider/update");
        ContentValues cv = new ContentValues();
        cv.put("money","9000");
        int update = getContentResolver().update(uri,cv,"name=?",new String[]{"王五"});
        System.out.println("更新了:"+update);
    }
}

里面就是四个增删改查方法,就Query稍微麻烦一点,查询的结果返回一个Cursor对象,然后把这个Cursor对象的内容取出来就行了,

读取系统联系人

布局文件,这里我们希望读取出来的联系人信息能够在 ListView 中
显示

activity_main.xml 文件

<LinearLayout   
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <ListView
        android:id="@+id/contacts_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </ListView>
</LinearLayout>

简单起见, LinearLayout 里就只放置了一个 ListView。接着修改 MainActivity 中的代码,
如下所示:

public class MainActivity extends Activity {
    ListView contactsView;
    ArrayAdapter<String> adapter;
    List<String> contactsList = new ArrayList<String>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        contactsView = (ListView) findViewById(R.id.contacts_view);
        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contactsList);
        contactsView.setAdapter(adapter);
        readContacts();
    }

    private void readContacts() {
        Cursor cursor = null;
        try {
            // 查询联系人数据
            cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                    null, null, null, null);
            while (cursor.moveToNext()) {
                // 获取联系人姓名
                String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                // 获取联系人手机号
                String number = cursor.getString(cursor
                        .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                contactsList.add(displayName + "\n" + number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

}


onCreate()方法中,我们首先获取了 ListView 控件的实例,并给它设置好了适配器,
然后就去调用 readContacts()方法。下面重点看下 readContacts()方法,可以看到,这里使用
了 ContentResolver 的 query()方法来查询系统的联系人数据。不过传入的 Uri 参数怎么有些奇
怪啊,为什么没有调用 Uri.parse()方法去解析一个内容 URI 字符串呢?这是因为
ContactsContract.CommonDataKinds.Phone类已经帮我们做好了封装,提供了一个CONTENT_URI
常量,而这个常量就是使用 Uri.parse()方法解析出来的结果。接着我们对 Cursor 对象进行遍
历,将联系人姓名和手机号这些数据逐个取出,联系人姓名这一列对应的常量是
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,联系人手机号这一列对应的常
量是 ContactsContract.CommonDataKinds.Phone.NUMBER。两个数据都取出之后,将它们进
行拼接,并且中间加上换行符,然后将拼接后的数据添加到 ListView 里。最后千万不要忘记
将 Cursor 对象关闭掉
这样就结束了吗?还差一点点,读取系统联系人也是需要声明权限的,因此修改
AndroidManifest.xml 中的代码,如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.contactstest"
android:versionCode="1"
android:versionName="1.0" >
……
<uses-permission android:name="android.permission.READ_CONTACTS" />
……
</manifest>

加入了 android.permission.READ_CONTACTS 权限,这样我们的程序就可以访问到系统
的联系人数据了,现在才算是大功告成。

Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成格式丰富的HTML页面。 —— [ 维基百科 ]

1.

目录

[TOC]来生成目录:


http://www.cnblogs.com/Coderwei2016/p/6058723.html
http://www.2cto.com/kf/201404/296974.html


  1. 本人部分摘抄,请勿见怪.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值