美文网首页
ContentProvider的总结

ContentProvider的总结

作者: fastcv | 来源:发表于2019-06-23 20:01 被阅读0次

    前言

    ContentProvider作为Android中四大组件之一,它的中文意思是内容提供者,主要是用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另外一个程序中的数据,同时还能保证被访数据的安全性。是Android提供给上层的一个组件,主要用于实现数据访问的统一管理和数据共享。这里的数据管理是通过定义统一的访问接口来完成,如增删改查。同时,它采用了类似Internet的URL机制,将数据以URI的形式来标识,这样其他App就可以采用一套标准的URI规范来访问同一处数据,而不用关心具体的实现细节。比如我们的APP去访问通讯录,日历,短信等。(结合大佬们的博客总结的,如有侵权,麻烦联系我删除此文章)

    基本用法

    1、ContentProvider提供了一套通用的数据访问接口,我们只需要继承它,然后实现它的抽象方法。

    public class ContactProvider extends ContentProvider {
        //做一些初始化的操作
        @Override
        public boolean onCreate() {
            return false;
        }
    
        @Nullable
        @Override
        public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
            return null;
        }
    
        //处理请求的Uri
        @Nullable
        @Override
        public String getType(@NonNull Uri uri) {
            return null;
        }
    
        @Nullable
        @Override
        public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
            return null;
        }
    
        @Override
        public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
            return 0;
        }
    
        @Override
        public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
            return 0;
        }
    }
    

    这些接口貌似跟数据库的访问接口类似。没错,ContentProvider就是专门来管理数据库中的数据的。

    2、每个想访问内容提供者的应用程序都需要依靠这个ContentResolver类,可以通过getContentResolver()方法获取到它的实例,它提供了一系列的方法用于对数据进行CRUD操作,其中,insert()方法用于添加数据,Update()方法用于更新数据,delete()用于删除数据,query()方法用于查询数据。ContentProvider就好比一个server,其他的App在访问这个ContentProvider的时候都必须先获得一个Server的Client才行,这里的ContentResolver就是Android提供给开发者使用来得到一个ContentProvider Client的管理工具类。

    注意的是:ContentResolver中的CRUD操作方法都不是接收表名,而是使用一个Uri参数代替,这个参数被称为内容URI,它由两部分组成:authority和path。Authority是用于对不同的应用程序做区分的。Path是用于对同一应用程序中不同的两张表。如:content://com.example.app.privoder/table1

    3、另外需要操作安卓内部其他应用数据库的话,我们还需要去实现SQLiteOpenHelper

    public class Helper extends SQLiteOpenHelper {
        public Helper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
            super(context, name, factory, version);
        }
    
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
           //数据库升级时调用
        }
    }
    

    实例一:获取联系人

    第一步:新建一个类表示我们要得到的联系人对象

    public class ContactInfo {
        private String name;
        private String phone;
        private String email;
        private String qq;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getPhone() {
            return phone;
        }
        public void setPhone(String phone) {
            this.phone = phone;
        }
        public String getEmail() {
            return email;
        }
        public void setEmail(String email) {
            this.email = email;
        }
        public String getQq() {
            return qq;
        }
        public void setQq(String qq) {
            this.qq = qq;
        }
        @Override
        public String toString() {
            return "ContactInfo [name=" + name + ", phone=" + phone + ", email="
                    + email + ", qq=" + qq + "]";
        }
    }
    

    第二步:新建一个获取联系人列表的方法类

    public class ContactUtils {
    
        public static ArrayList<ContactInfo> getContact(Context context) {
            ArrayList<ContactInfo> list = new ArrayList<>();
            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);
            while (cursor.moveToNext()) {
    
                String id = cursor.getString(0);
                System.out.println("00"+id);
                if (id != null) {
                    ContactInfo info = 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.setName(data1);
                        } else if ("vnd.android.cursor.item/im".equals(mimetype)) {
                            info.setQq(data1);
                        } else if ("vnd.android.cursor.item/email_v2"
                                .equals(mimetype)) {
                            info.setEmail(data1);
                        } else if ("vnd.android.cursor.item/phone_v2"
                                .equals(mimetype)) {
                            info.setPhone(data1);
                        }
                    }
                    datacursor.close();
                    list.add(info);
                }
            }
            cursor.close();
            return list;
        }
    }
    

    第三步:运行查看结果

    ArrayList<ContactInfo> contactlist = ContactUtils.getContact(this);
            LogUtil.log("info" + contactlist.size());
    
    2019-06-23 18:43:13.248 21155-21155/sayhallo.cn.ilikeandroid E/LOG_: info162
    

    实例二:操作其他APP的数据库

    第一步:新建一个类继承SQLiteOpenHelper提供共享数据库

    public class Helper extends SQLiteOpenHelper {
    
        public static final String DATABASE_NAME = "test.db";
        public static final int DATABASE_VERSION = 1;
        public static final String SQL_CREATE_TABLE = "create table account (_id integer primary key autoincrement,name varchar(20),number varchar(20))";
    
    
        public Helper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(SQL_CREATE_TABLE);
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
        }
    }
    

    第二步:新建一个类继承ContentProvider,实现接口提供调用者使用

    public class ContactProvider extends ContentProvider {
    
        private static final int SUCCESS = 1;
    
        /** 判断Uri规则 */
        static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        static {
            mUriMatcher.addURI("com.exmple.text", "account", SUCCESS);    //uri规则可自己定义,但一定和清单文件一直
        }
    
        @Override
        public boolean onCreate() {
            return false;
        }
    
        /** 增删改查为空实现 */
        @Override
        public Cursor query(Uri uri, String[] projection, String selection,
                            String[] selectionArgs, String sortOrder) {
            int code = mUriMatcher.match(uri); // 判断Uri是否合法
            if (code == SUCCESS) {
                LogUtil.log("查询数据");
                Helper helper = new Helper(getContext());
                SQLiteDatabase db = helper.getReadableDatabase();
                return db.query("account", projection, selection, selectionArgs,
                        null, null, sortOrder);
            } else {
                throw new IllegalArgumentException("路径不正确");
            }
        }
    
        @Override
        public String getType(Uri uri) {
            return null;
        }
    
        @Override
        public Uri insert(Uri uri, ContentValues values) {
            int code = mUriMatcher.match(uri);
            if (code == SUCCESS) {
                LogUtil.log("添加数据");
                Helper helper = new Helper(getContext());
                SQLiteDatabase db = helper.getWritableDatabase();
                db.insert("account", null, values);
                getContext().getContentResolver().notifyChange(uri, null); // 内容观察者检测数据库是否更改
            } else {
                throw new IllegalArgumentException("路径不正确");
            }
            return null;
        }
    
        @Override
        public int delete(Uri uri, String selection, String[] selectionArgs) {
            int code = mUriMatcher.match(uri);
            if (code == SUCCESS) {
                LogUtil.log("删除数据");
                Helper helper = new Helper(getContext());
                SQLiteDatabase db = helper.getWritableDatabase();
                db.delete("account", selection, selectionArgs);
                getContext().getContentResolver().notifyChange(uri, null);     // 内容观察者检测数据库是否更改
            } else {
                throw new IllegalArgumentException("路径不正确");
            }
            return 0;
        }
    
        @Override
        public int update(Uri uri, ContentValues values, String selection,
                          String[] selectionArgs) {
            int code = mUriMatcher.match(uri);
            if (code == SUCCESS) {
                LogUtil.log("更新数据");
                Helper helper = new Helper(getContext());
                SQLiteDatabase db = helper.getWritableDatabase();
                db.update("account", values, selection, selectionArgs);
                getContext().getContentResolver().notifyChange(uri, null);    // 内容观察者检测数据库是否更改
            } else {
                throw new IllegalArgumentException("路径不正确");
            }
            return 0;
    
        }
    }
    

    配置文件中添加provider节点:

            <!-- 注册内容提供者数据 -->
            <provider
                android:name="com.example.provider.BackDoor"
                android:authorities="com.exmple.text" >
            </provider>
    

    第三步:其他APP调用此接口

    /**
         * 利用后门程序 添加一条数据
         */
        public void insert(View view) {
            ContentResolver resolver = getContentResolver();
            Uri uri = Uri.parse("content://com.exmple.text/account");
            ContentValues values = new ContentValues();
            values.put("name", "zhangsan");
            values.put("number", 10000);
            resolver.insert(uri, values);
        }
    
        /**
         * 利用后门程序 删除一条数据
         */
        public void delete(View view) {
            ContentResolver resolver = getContentResolver();
            Uri uri = Uri.parse("content://com.exmple.text/account");
            resolver.delete(uri, "name=?", new String[] { "zhangsan" });
        }
    
        /**
         * 利用后门程序 修改数据
         */
        public void update(View view) {
            ContentResolver resolver = getContentResolver();
            Uri uri = Uri.parse("content://com.exmple.text/account");
            ContentValues values = new ContentValues();
            values.put("number", 20000);
            resolver.update(uri, values, "name=?", new String[] { "zhangsan" });
        }
    
        /**
         * 利用后门程序 查询数据
         */
        public void query(View view) {
            ContentResolver resolver = getContentResolver();
            Uri uri = Uri.parse("content://com.exmple.text/account");
            Cursor cursor = resolver.query(uri, new String[] { "name", "number" },
                    null, null, null);
            while (cursor.moveToNext()) {
                String name = cursor.getString(0);
                float number = cursor.getFloat(1);
                Log.e("LOG_","name:" + name + "----" + "number:" + number);
            }
            cursor.close();
        }
    

    第四步:运行查看结果

    2019-06-23 19:11:26.080 25084-25099/sayhallo.cn.ilikeandroid E/LOG_: 添加数据
    2019-06-23 19:11:27.437 25084-25099/sayhallo.cn.ilikeandroid E/LOG_: 添加数据
    2019-06-23 19:11:29.301 25084-25099/sayhallo.cn.ilikeandroid E/LOG_: 更新数据
    2019-06-23 19:11:36.005 25084-25099/sayhallo.cn.ilikeandroid E/LOG_: 查询数据
    2019-06-23 19:11:36.019 25495-25495/sayhallo.cn.server E/LOG_: name:zhangsan----number:20000.0
    2019-06-23 19:11:36.020 25495-25495/sayhallo.cn.server E/LOG_: name:zhangsan----number:20000.0
    2019-06-23 19:11:41.251 25084-25099/sayhallo.cn.ilikeandroid E/LOG_: 查询数据
    2019-06-23 19:11:41.269 25495-25495/sayhallo.cn.server E/LOG_: name:zhangsan----number:20000.0
    2019-06-23 19:11:41.270 25495-25495/sayhallo.cn.server E/LOG_: name:zhangsan----number:20000.0
    2019-06-23 19:11:46.363 25084-25099/sayhallo.cn.ilikeandroid E/LOG_: 查询数据
    2019-06-23 19:11:46.385 25495-25495/sayhallo.cn.server E/LOG_: name:zhangsan----number:20000.0
    2019-06-23 19:11:46.386 25495-25495/sayhallo.cn.server E/LOG_: name:zhangsan----number:20000.0
    2019-06-23 19:11:52.202 25084-25099/sayhallo.cn.ilikeandroid E/LOG_: 添加数据
    2019-06-23 19:11:54.213 25084-25099/sayhallo.cn.ilikeandroid E/LOG_: 查询数据
    2019-06-23 19:11:54.238 25495-25495/sayhallo.cn.server E/LOG_: name:zhangsan----number:20000.0
    2019-06-23 19:11:54.240 25495-25495/sayhallo.cn.server E/LOG_: name:zhangsan----number:20000.0
    2019-06-23 19:11:54.240 25495-25495/sayhallo.cn.server E/LOG_: name:zhangsan----number:10000.0
    2019-06-23 19:11:55.954 25084-25099/sayhallo.cn.ilikeandroid E/LOG_: 添加数据
    2019-06-23 19:11:57.383 25084-25099/sayhallo.cn.ilikeandroid E/LOG_: 查询数据
    2019-06-23 19:11:57.412 25495-25495/sayhallo.cn.server E/LOG_: name:zhangsan----number:20000.0
    2019-06-23 19:11:57.413 25495-25495/sayhallo.cn.server E/LOG_: name:zhangsan----number:20000.0
    2019-06-23 19:11:57.414 25495-25495/sayhallo.cn.server E/LOG_: name:zhangsan----number:10000.0
    2019-06-23 19:11:57.415 25495-25495/sayhallo.cn.server E/LOG_: name:zhangsan----number:10000.0
    

    相关文章

      网友评论

          本文标题:ContentProvider的总结

          本文链接:https://www.haomeiwen.com/subject/pchwqctx.html