16、四大组件--ContentProvider

一、ContentProvider

1.1、简介

ContentProvider(内容提供者)是Android中的四大组件之一。

ContentProvider分为系统的和自定义的,系统的也就是例如联系人,图片等数据。

内容提供者将一些特定的应用程序数据供给其它应用程序使用。数据可以存储于文件系统、SQLite数据库或其它方式。

内容提供者继承于ContentProvider基类,为其它应用程序取用和存储它管理的数据实现了一套标准方法。

应用程序并不直接调用这些方法,而是使用一个 ContentResolver对象,调用它的方法作为替代。

ContentResolver可以与任意内容提供者进行会话,与其合作来对所有相关交互通讯进行管理。

1.2、ContentProvider

Android提供一些主要数据类型的ContentProvider,比如音频、视频、图片和私人通讯录等,

可在android.provider包下找到一些Android提供的ContentProvider。

通过获得这些ContentProvider可以查询它们包含的数据,当然前提是已获得适当的读取权限。

1.主要方法

image

*如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头。

例:要得到所有person记录的Uri为content://contacts/person,那么返回的MIME类型字符串为"vnd.android.cursor.dir/person"。

*如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头。

例:要得到id为10的person记录的Uri为content://contacts/person/10,那么返回的MIME类型字符串应为"vnd.android.cursor.item/person"。

1.3、ContentResolver

当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver类来完成,

要获取ContentResolver对象,可以使用Context提供的getContentResolver()方法。

ContentResolver contentResolver = getContentResolver();

ContentResolver提供的方法和ContentProvider提供的方法对应的有以下几个方法:

image

1.4、URI

外部程序只需知道内容提供者的Uri路径信息,通过ContentResolver即可调用内容提供者。

b293e503-bf37-483d-8d3b-710370ee5e53

image

1.5、操作短信

public class SmsUtils {
    /** 声明一个接口 , 包含一些回调函数 */
    public interface BackupSmsCallBack {
        /**
         * 短信备份之前调用的方法
         * @param max    短信的总条目个数
         */
        public void beforeSmsBackup(int max);
        /**
         * 当短信备份过程中调用的方法
         * 
         * @param process 当前备份的进度
         */
        public void onSmsBackup(int process);
    }
    /**
     * 备份用户的短信
     * @param context 上下文
     * @param callback 短信备份的接口
     * @param filename 备份后的文件名称
     * @return 是否备份成功
     */
    public static boolean backupSms(Context context,
            BackupSmsCallBack callback, String filename) {
        try {
            ContentResolver resolver = context.getContentResolver();
            Uri uri = Uri.parse("content://sms/");
            File file = new File(Environment.getExternalStorageDirectory(),
                    filename);
            FileOutputStream fos = new FileOutputStream(file);
            XmlSerializer serializer = Xml.newSerializer();
            serializer.setOutput(fos, "utf-8");
            serializer.startDocument("utf-8", true);
            serializer.startTag(null, "info");
            Cursor cursor = resolver.query(uri, new String[] { "address",
                    "date", "body", "type" }, null, null, null);
            int max = cursor.getCount();
            serializer.attribute(null, "total", String.valueOf(max));
            callback.beforeSmsBackup(max);
            int process = 0;
            while (cursor.moveToNext()) {
                serializer.startTag(null, "sms");
                serializer.startTag(null, "address");
                String address = cursor.getString(0);
                serializer.text(address);
                serializer.endTag(null, "address");
                serializer.startTag(null, "date");
                String date = cursor.getString(1);
                serializer.text(date);
                serializer.endTag(null, "date");
                serializer.startTag(null, "body");
                String body = cursor.getString(2);
                serializer.text(body);
                serializer.endTag(null, "body");
                serializer.startTag(null, "type");
                String type = cursor.getString(3);
                serializer.text(type);
                serializer.endTag(null, "type");
                serializer.endTag(null, "sms");
                Thread.sleep(2000);
                process++;
                // pb.setProgress(process);
                // pd.setProgress(process);
                callback.onSmsBackup(process);
            }
            cursor.close();
            serializer.endTag(null, "info");
            serializer.endDocument();
            fos.close();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

1.6、操作联系人

1.准备工作

a)通过DDMS,查看Android模拟器下的com.android.providers.contacts包下的数据库,查看其contact2.db数据库的内容。

wps35D6.tmp5171cade-8c2c-4e7c-8d60-46680e97f69b

b)查看数据库,其中raw_contacts表存放的是联系人条数信息,data表中存放的是raw_contacts中的每一条id对应的具体信息,不同类型的信息由mimetype_id来标识。

raw_contacts表:

wps35E8.tmpb6702ca7-f1d7-4661-b586-1e96828eb7be

data表:

wps35E9.tmp6e49c8ab-7766-46d9-8e0b-3342735d0080

mimetypes表:

wps35F9.tmp3c117825-5143-46f7-b0f0-1b9037878d98

c)打开Android源码,查看packages\providers\路径下的文件,其中ContactsProvider就是联系人的内容提供者。

打开清单文件,寻找联系人的内容提供者对应的是哪个java文件。
f76b623a-3c30-49e3-ae21-3a4fcf7cb2f4

打开ContactsProvider2.java文件,查看此内容提供者的uri路径信息
b66db0d8-ba72-4e95-a9df-71a95db4b0cc
操作raw_contacts表的Uri:

content://com.adroid.contacts/raw_contacts

操作data表的Uri:

content://com.adroid.contacts/data

操作数据库表时注意contacts2.db数据库使用了视图,所以操作数据库表时表结构有所改变,注意操作时要操纵的列的列名已经改变。

比如:data表在查询的时候没有mimetype_id,取代的是mimetype

2.操作实例

public static List<ContactInfo> getContactInfos(Context context) {
    // 内容提供者的解析器
    ContentResolver resolver = context.getContentResolver();
    Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
    Uri datauri = Uri.parse("content://com.android.contacts/data");
    Cursor cursor = resolver.query(uri, new String[] { "contact_id" },null, null, null);
    List<ContactInfo> contactInfos = new ArrayList<ContactInfoUtils.ContactInfo>();
    while (cursor.moveToNext()) {
        String id = cursor.getString(0);
        System.out.println("id=" + id);
        if (id != null) {
            ContactInfo info = new ContactInfoUtils().new ContactInfo();
            Cursor datacursor = resolver.query(datauri, new String[] {
                    "data1", "mimetype" }, "raw_contact_id=?",
                    new String[] { id }, null);
            while (datacursor.moveToNext()) {
                String data1 = datacursor.getString(0);
                String mimetype = datacursor.getString(1);
                if ("vnd.android.cursor.item/name".equals(mimetype)) {
                    info.name = data1;// 姓名
                } else if ("vnd.android.cursor.item/phone_v2".equals(mimetype)) {
                    info.phone = data1;// 电话
                } else if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
                    info.email = data1;// 邮箱
                }
            }
            contactInfos.add(info);
        }
    }
    return contactInfos;
}
public class ContactInfo {
    public String name;
    public String email;
    public String phone;
}

1.7、自定义ContentProvider

a) 新建一个类继承ContentProvider来创建内容提供器,并实现六个抽象方法。

public class MyProvider extends ContentProvider {
    // 初始化内容提供器调用。通常完成数据库的创建和升级操作。
    @Override
    public boolean onCreate() {
        return false;
    }
    // 从内容提供器中查询数据,使用uri参数确定查询哪张表。
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {
        return null;
    }
    // 向内容提供器中添加一条数据使用uri参数确定添加哪张表。
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }
    // 从内容提供器中删除数据,使用uri来确定删除哪张表。
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }
    // 更新内容提供器中已有的数据,使用uri参数来确定更新哪张表。
    @Override
    public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {
        return 0;
    }
    // 根据传入的内容URI来返回相应的MIME类型。
    @Override
    public String getType(Uri uri) {
        return null;
    }
}

b) uri的格式上面已经有叙述,接下来我们通过UriMatcher类实现匹配内容URI的功能

public class MyProvider extends ContentProvider {
    
    // 表示访问table1和table2中的所有数据或单条数据
    public static final int TABLE1_DIR = 0;
    public static final int TABLE1_ITEM = 1;
    public static final int TABLE2_DIR = 2;
    public static final int TABLE2_ITEM = 3;
    
    private static UriMatcher mUriMatcher;
    
    static{
        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        mUriMatcher.addURI("cn.legend.provider", "table1", TABLE1_DIR);
        mUriMatcher.addURI("cn.legend.provider", "table1/#", TABLE1_ITEM);
        mUriMatcher.addURI("cn.legend.provider", "table2", TABLE2_DIR);
        mUriMatcher.addURI("cn.legend.provider", "table2/#", TABLE2_ITEM);
        
    }
    @Override
    public boolean onCreate() {
        return false;
    }
    
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {
        switch (mUriMatcher.match(uri)) {
        case TABLE1_DIR:
            // 查询table1表中的所有数据
            break;
        case TABLE1_ITEM:
            // 查询table1表中的单条数据
            break;
        case TABLE2_DIR:
            // 查询table2表中的所有数据
            break;
        case TABLE2_ITEM:
            // 查询table2表中的单条数据
            break;
        }
        return null;
    }
    
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        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;
    }
    
    @Override
    public String getType(Uri uri) {
        switch (mUriMatcher.match(uri)) {
        case TABLE1_DIR:
            return "vnd.android.cursor.dir/vnd.cn.legend.provider.table1";
        case TABLE1_ITEM:
            
            return "vnd.android.cursor.dir/vnd.cn.legend.provider.table1";
        case TABLE2_DIR:
            
            return "vnd.android.cursor.dir/vnd.cn.legend.provider.table2";
        case TABLE2_ITEM:
            
            return "vnd.android.cursor.dir/vnd.cn.legend.provider.table2";
        }
        return null;
    }
}

c)上述只是以查询为例,增删改查都大同小异,只有getType()方法比较特殊,它是获取uri对象所对应的MIME类型,MIME字符串由三部分组成:

    1. 必须以vnd开头。

    2. 如果URI以路径结尾,则后接 android.cursor.dir/,如果URI以id结尾则后接 android.cursor.item/。

    3. 最后接上 vnd.<authority>.<path>。

对于content://cn.legend.provide/table1这个URI所对应MIME:

vnd.android.cursor.dir/vnd.cn.legend.provider.table1

对于content://cn.legend.provider/table1/1这个URI所对应MIME:

vnd.android.cursor.item/vnd.cn.legend.provider.table1

1.8、ContentObserver

ContentObserver的使用类似与设计模式中的观察者模式,ContentObserver是观察者,被观察的ContentProvider是被观察者。

当被观察者ContentProvider的数据发生了增删改的变化,就会及时的通知给ContentProvider,ContentObsserver做出相应的处理。

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         
        // 注册观察者Observser
        this.getContentResolver().registerContentObserver(Uri.parse("content://sms"),true,new SMSObserver(new Handler()));
    }
 
    private final class SMSObserver extends ContentObserver {
        public SMSObserver(Handler handler) {
            super(handler);
        }
        @Override
        public void onChange(boolean selfChange) {
            Cursor cursor = MainActivity.this.getContentResolver().query(
                    Uri.parse("content://sms/inbox"), null, null, null, null);
            while (cursor.moveToNext()) {
                StringBuilder sb = new StringBuilder();
                sb.append("address=").append(
                        cursor.getString(cursor.getColumnIndex("address")));
                sb.append(";subject=").append(
                        cursor.getString(cursor.getColumnIndex("subject")));
                sb.append(";body=").append(
                        cursor.getString(cursor.getColumnIndex("body")));
                sb.append(";time=").append(
                        cursor.getLong(cursor.getColumnIndex("date")));
                System.out.println("--------has Receivered SMS::" + sb.toString());
            }
        }
    }
}

这些知识contentObserver的基本使用,更细节的使用方式待后续补充。

1.9、观察者模式

ad019a4c-1db5-461d-aed3-7a9705ad6b94

原生的方式一般不推荐使用:有两个弊端

  • 继承Observer的话,观察者只能作为它的子类。
  • update中的data数据类型不明确。

自定义观察者模式:

a)在被观察者中将定义MyObserver接口,并将被观察者对象和数据传递进接口的方法update()。

b)在被观察者中造一个集合,来表示观察者的集合。

c)在被观察者中暴漏添加观察者的方法和删除观察者的方法。

d)在被观察者中定义notifyXXX(Data data)方法,并将被观察者中的数据传递进去。

  并对观察者集合进行迭代,迭代过程中一一调用MyObserver中的方法,并将当前对象和数据传递进去。

e) 那么当被观察者中数据改变时,我们就可以调用notifyXXX(Data data)方法传入数据了。

f) 给观察者实现MyObserver接口,并重写其中的update()方法。

g)将被观察者定义为单例模式,在观察者中得到被观察者的实例后,去添加和销毁监听器即可。

转载于:https://www.cnblogs.com/pengjingya/p/5508775.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值