[Android]ContentProvider内容提供器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Gods_magic/article/details/84678225

本文源码:https://github.com/gitEkko/MyApplication.git

一、内容提供者是什么

内容提供者(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。目前,使用内容提供者是Android实现跨程序共享数据的标准方式。

 

原理: ContentProvider的底层是采用 Android中的Binder机制

二、ContentProvider的使用

 1.统一资源标识符-URI

 定义:Uniform Resource Identifier,即统一资源标识符

 作用:唯一标识ContentProvider以及其中的数据

  • 外界进程通过URI找到对应的ContentProvider,再进行数据操作。

 URI分为:

  1.  系统预置:系统内置的数据,如通讯录,短信,日程表等数据。
  2.  自定义:即自定义的数据库,提供给外界进程对自定义数据库进行操作。 

  需要将URI字符串解析成URI对象才可以使用。

Uri uri = Uri.parse("content://com.example.myinterview.myprovider/person");

 2.MIME数据类型

 作用:指定某个扩展名的文件用某种应用程序来打开。 

 ContentProvider根据URI来返回MIME数据类型:ContentProvider.geType(uri) ;

 有两种形式:

  •  单条记录:"vnd.android.cursor.item/vnd.com.example.myinterview.myprovider.person";
  •  多条记录:"vnd.android.cursor.dir/vnd.com.example.myinterview.myprovider.person";

三、ContentProvider类 

<-- 4个核心方法 -->
  public Uri insert(Uri uri, ContentValues values) 
  // 外部进程向 ContentProvider 中添加数据

  public int delete(Uri uri, String selection, String[] selectionArgs) 
  // 外部进程 删除 ContentProvider 中的数据

  public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
  // 外部进程更新 ContentProvider 中的数据

  public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,  String sortOrder)  
  //  外部应用 获取 ContentProvider 中的数据

// 注:
// 1. 上述4个方法由外部进程回调,并运行在ContentProvider进程的Binder线程池中(不是主线程)
// 2. 存在多线程并发访问,需要实现线程同步
// a. 若ContentProvider的数据存储方式是使用SQLite & 一个,则不需要,因为SQLite内部实现好了线程同步,若是多个SQLite则需要,因为SQL对象之间无法进行线程同步
// b. 若ContentProvider的数据存储方式是内存,则需要自己实现线程同步

<-- 2个其他方法 -->
public boolean onCreate() 
// ContentProvider创建后 或 打开系统后其它进程第一次访问该ContentProvider时 由系统进行调用
// 注:运行在ContentProvider进程的主线程,故不能做耗时操作

public String getType(Uri uri)
// 得到数据类型,即返回当前 Url 所代表数据的MIME类型

在ContentProvider类中要对数据库进行操作,所以要结合SQLiteOpenHelper。

ContentProvider类并不会直接与外部进程进行交互,而是通过ContentResolver类。

四、ContentResolver类

 通过URI来操作不同的ContentProvider。

  •  一般来说,一款应用要使用多个ContentProvider,若需要了解每个ContentProvider的不同实现从而再完成数据交互,操作成本高 & 难度大
  • 所以再ContentProvider类上加多了一个 ContentResolver类对所有的ContentProvider进行统一管理。

 ContentResolver 类提供了与ContentProvider类相同名字 & 作用的4个方法

// 向 ContentProvider 中添加数据
public Uri insert(Uri uri, ContentValues values)  

// 删除 ContentProvider 中的数据
public int delete(Uri uri, String selection, String[] selectionArgs)

// 更新 ContentProvider 中的数据
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)  

// 获取 ContentProvider 中的数据
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

 使用:

 Uri uri = Uri.parse("content://" + AUTOHORITY + "/person");
 ContentResolver resolver = getContentResolver();
 Cursor cursor = resolver.query(uri, null, null, null, null);

五、UriMatcher类

 作用:

  1. 在ContentProvider 中注册URI
  2. 根据 URI 匹配 ContentProvider 中对应的数据表 

 具体使用:

// 步骤1:初始化UriMatcher对象
    UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); 
    //常量UriMatcher.NO_MATCH  = 不匹配任何路径的返回码
    // 即初始化时不匹配任何东西

// 步骤2:在ContentProvider 中注册URI(addURI())
    int URI_CODE_a = 1;
    int URI_CODE_b = 2;
    matcher.addURI("cn.scu.myprovider", "user1", URI_CODE_a); 
    matcher.addURI("cn.scu.myprovider", "user2", URI_CODE_b); 
    // 若URI资源路径 = content://cn.scu.myprovider/user1 ,则返回注册码URI_CODE_a
    // 若URI资源路径 = content://cn.scu.myprovider/user2 ,则返回注册码URI_CODE_b

// 步骤3:根据URI 匹配 URI_CODE,从而匹配ContentProvider中相应的资源(match())

@Override   
    public String getType(Uri uri) {   
      Uri uri = Uri.parse(" content://cn.scu.myprovider/user1");   

      switch(matcher.match(uri)){   
     // 根据URI匹配的返回码是URI_CODE_a
     // 即matcher.match(uri) == URI_CODE_a
      case URI_CODE_a:   
        return tableNameUser1;   
        // 如果根据URI匹配的返回码是URI_CODE_a,则返回ContentProvider中的名为tableNameUser1的表
      case URI_CODE_b:   
        return tableNameUser2;
        // 如果根据URI匹配的返回码是URI_CODE_b,则返回ContentProvider中的名为tableNameUser2的表
    }   
}

六、ContentObserver类

定义:内容观察者

作用:观察 Uri 引起 ContentProvider 中的数据变化 & 通知外界(即访问该数据访问者)

  • ContentProvider 中的数据发生变化(增、删 & 改)时,就会触发该 ContentObserver

具体使用:

// 步骤1:注册内容观察者ContentObserver
    getContentResolver().registerContentObserver(uri);
    // 通过ContentResolver类进行注册,并指定需要观察的URI

// 步骤2:当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
    public class UserContentProvider extends ContentProvider { 
      public Uri insert(Uri uri, ContentValues values) { 
      db.insert("user", "userid", values); 
      getContext().getContentResolver().notifyChange(uri, null); 
      // 通知访问者
   } 
}

// 步骤3:解除观察者
 getContentResolver().unregisterContentObserver(uri);
    // 同样需要通过ContentResolver类进行解除

七、Demo实例

   通过访问自定义的ContentProvider 来操作数据库进行增删改查。并且利用ContentObserver监听数据库如果发生变化,就进行     更新UI操作。

 1.创建数据库类:

   SQLiteHelper.java:复用上篇代码:https://blog.csdn.net/Gods_magic/article/details/84706386

 2.自定义ContentProvider类

/**
 * 内容提供者
 * ContentProvider是不同应用程序之间进行数据交换的标准API,
 * ContentProvide以Uri的形式对外提供数据,允许其他应用访问和修改数据;
 * 其他应用使用ContentResolve根据Uri进行访问操作指定的数据。
 */
public class MyContentProvider extends ContentProvider {

    public static final String AUTOHORITY = "com.example.myinterview.myprovider";

    public static final int Person_DIR = 0;
    public static final int Person_ITEM = 1;
    public static final int User_DIR = 2;
    public static final int User_ITEM = 3;

    private Context mContext;
    private SQLiteHelper dbHelper = null;


    private static final UriMatcher mMatcher;

    static {
        mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        mMatcher.addURI(AUTOHORITY, "person", Person_DIR);
        mMatcher.addURI(AUTOHORITY, "person/#", Person_ITEM);
        mMatcher.addURI(AUTOHORITY, "user", User_DIR);
        mMatcher.addURI(AUTOHORITY, "user#", User_ITEM);
    }

    public MyContentProvider() {
    }

    @Override
    public boolean onCreate() {
        mContext = getContext();
        dbHelper = new SQLiteHelper(mContext, 1);
        return true;
    }


    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        //根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
        String table = getTableName(uri);
        //向该表添加数据
        long newId = db.insert(table, null, values);
        Uri uriReturn = Uri.parse("content://" + AUTOHORITY + "/" + table + "/" + newId);

        //当该URI的ContentProvider数据发生变化时,通知外界,即访问该ContentProvider数据的访问者
        mContext.getContentResolver().notifyChange(uri, null);

        //返回一个用于表示该条新纪录的URI
        return uriReturn;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int deleteRows = 0;
        switch (mMatcher.match(uri)) {
            case Person_DIR:
                deleteRows = db.delete("person", selection, selectionArgs);
                break;
            case Person_ITEM:
                String personId = uri.getPathSegments().get(1);
                deleteRows = db.delete("person", "id = ?", new String[]{personId});
                break;
            case User_DIR:
                deleteRows = db.delete("user", selection, selectionArgs);
                break;
            case User_ITEM:
                String userId = uri.getPathSegments().get(1);
                deleteRows = db.delete("user", "id = ?", new String[]{userId});
                break;
        }

        //当该URI的ContentProvider数据发生变化时,通知外界,即访问该ContentProvider数据的访问者
        mContext.getContentResolver().notifyChange(uri, null);

        //返回被删除的行数
        return deleteRows;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int deleteRows = 0;
        switch (mMatcher.match(uri)) {
            case Person_DIR:
                deleteRows = db.update("person", values, selection, selectionArgs);
                break;
            case Person_ITEM:
                String personId = uri.getPathSegments().get(1);
                deleteRows = db.update("person", values, "id = ?", new String[]{personId});
                break;
            case User_DIR:
                deleteRows = db.update("user", values, selection, selectionArgs);
                break;
            case User_ITEM:
                String userId = uri.getPathSegments().get(1);
                deleteRows = db.update("user", values, "id = ?", new String[]{userId});
                break;
        }
        
        //当该URI的ContentProvider数据发生变化时,通知外界,即访问该ContentProvider数据的访问者
        mContext.getContentResolver().notifyChange(uri, null);

        return deleteRows;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {

        SQLiteDatabase db = dbHelper.getReadableDatabase();

        Cursor cursor = null;
        switch (mMatcher.match(uri)) {
            case Person_DIR:
                cursor = db.query("person", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case Person_ITEM:
                String personId = uri.getPathSegments().get(1);
                cursor = db.query("person", projection, "id = ?", new String[]{personId}, null, null, sortOrder);
                break;
            case User_DIR:
                cursor = db.query("user", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case User_ITEM:
                String userId = uri.getPathSegments().get(1);
                cursor = db.query("user", projection, "id = ?", new String[]{userId}, null, null, sortOrder);
                break;
        }
        return cursor;
    }

    @Override
    public String getType(Uri uri) {
        String mMIME = null;
        switch (mMatcher.match(uri)) {
            case Person_DIR:
                mMIME = "vnd.android.cursor.dir/vnd.com.example.myinterview.myprovider.person";
                break;
            case Person_ITEM:
                mMIME = "vnd.android.cursor.item/vnd.com.example.myinterview.myprovider.person";
                break;
            case User_DIR:
                mMIME = "vnd.android.cursor.dir/vnd.com.example.myinterview.myprovider.user";
                break;
            case User_ITEM:
                mMIME = "vnd.android.cursor.item/vnd.com.example.myinterview.myprovider.user";
                break;
        }
        return mMIME;
    }

    private String getTableName(Uri uri) {
        String tableName = null;
        switch (mMatcher.match(uri)) {
            case Person_DIR:
            case Person_ITEM:
                tableName = SQLiteHelper.PERSON_TABLE_NAME;
                break;
            case User_DIR:
            case User_ITEM:
                tableName = SQLiteHelper.USER_TABLE_NAME;
                break;
        }
        return tableName;
    }
}

  在AndroidManifest.xml中进行注册

<provider
    android:name=".myprovider.MyContentProvider"
    android:authorities="com.example.myinterview.myprovider"
    android:enabled="true"
    android:exported="true" />

  如果想要外部进程访问到此ContentProvider,必须设置 exported为 true

 3.自定义ContentObserver类

/**
 * 内容观察者   观察所监听Uri 引起ContentProvider中的数据变化
 */
public class MyContentObsever extends ContentObserver {

    private Context context;
    private Handler handler;


    public MyContentObsever(Context context, Handler handler) {
        super(handler);
        this.context = context;
        this.handler = handler;
    }

    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);

        Toast.makeText(context, "database has changed!", Toast.LENGTH_LONG).show();
		//发送handler消息,监听到数据库变化后,更新UI
        Message msg = new Message();
        msg.obj = "database has changed";
        handler.sendMessage(msg);
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        super.onChange(selfChange, uri);
		//如果需要用到uri可以使用此方法。
    }
}

 4.MainActivity

    操作数据库的insert、delete、update等activity以及ListAdapter等文件复用 https://blog.csdn.net/Gods_magic/article/details/84706386

public class CPMainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final int INSERT_REQUESTCODE = 1;
    private static final int INSERT_RESULTCODE = 11;
    private static final int DELETE_REQUESTCODE = 2;
    private static final int DELETE_RESULTCODE = 22;
    private static final int UPDATE_REQUESTCODE = 3;
    private static final int UPDATE_RESULTCODE = 33;

    private ListAdapter listAdapter;
    public static final String AUTOHORITY = "com.example.myinterview.myprovider";

    MyContentObsever myContentObsever;
    TextView text;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cpmain);

        Button insertPersonBtn = (Button) findViewById(R.id.person_insert);
        Button deletePersonBtn = (Button) findViewById(R.id.person_delete);
        Button updatePersonBtn = (Button) findViewById(R.id.person_update);
        Button queryPersonBtn = (Button) findViewById(R.id.person_query);
        text = (TextView) findViewById(R.id.change_text);
        insertPersonBtn.setOnClickListener(this);
        deletePersonBtn.setOnClickListener(this);
        updatePersonBtn.setOnClickListener(this);
        queryPersonBtn.setOnClickListener(this);


        listAdapter = new ListAdapter(new ArrayList<Person>(), this);
        ListView listView = (ListView) findViewById(R.id.list_data2);
        listView.setAdapter(listAdapter);

        //注册ContentObsever 传入Context 和 Handler
        Uri uri = Uri.parse("content://com.example.myinterview.myprovider/person");
        myContentObsever = new MyContentObsever(this, handler);
        getContentResolver().registerContentObserver(uri, true, myContentObsever);

    }

    //主线程 Handler 更新UI
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            text.setText(msg.obj.toString());
        }
    };

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        //取消注册 ContentObserver
        getContentResolver().unregisterContentObserver(myContentObsever);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.person_insert:
                //case R.id.user_insert:
                insert();
                break;
            case R.id.person_delete:
                // case R.id.user_delete:
                delete();
                break;
            case R.id.person_update:
                //case R.id.user_update:
                update();
                break;
            case R.id.person_query:
                //case R.id.user_query:
                query();
                break;
            default:
                break;
        }
    }

    private void insert() {
        Intent intent = new Intent(this, InsertDialog.class);
        startActivityForResult(intent, INSERT_REQUESTCODE);
    }

    private void delete() {
        Intent intent = new Intent(this, DeleteDialog.class);
        startActivityForResult(intent, DELETE_REQUESTCODE);
    }

    private void update() {
        Intent intent = new Intent(this, UpdateDialog.class);
        startActivityForResult(intent, UPDATE_REQUESTCODE);
    }

    private void query() {
        Uri uri = Uri.parse("content://" + AUTOHORITY + "/person");
        ContentResolver resolver = getContentResolver();
        Cursor cursor = resolver.query(uri, null, null, null, null);
        List<Person> list = new ArrayList<>();
        if (cursor != null && cursor.getCount() != 0) {
            cursor.moveToFirst();
            do {
                int id = cursor.getInt(cursor.getColumnIndex("id"));
                String name = cursor.getString(cursor.getColumnIndex("name"));
                int age = cursor.getInt(cursor.getColumnIndex("age"));
                String sex = cursor.getString(cursor.getColumnIndex("sex"));

                Person person = new Person(id, name, age, sex);
                list.add(person);
            } while (cursor.moveToNext());
            cursor.close();
        }

        loadData(list);
    }

    private void loadData(List<Person> list) {
        listAdapter.setList(list);
        listAdapter.notifyDataSetChanged();
    }

    private Cursor queryPersonById(Uri uri) {
        ContentResolver resolver = getContentResolver();
        return resolver.query(uri, null, null, null, null);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case INSERT_REQUESTCODE:
                if (resultCode == INSERT_RESULTCODE) {
                    if (data != null) {
                        ContentResolver resolver = getContentResolver();
                        Person person = (Person) data.getSerializableExtra("person");
                        Uri uri = Uri.parse("content://" + AUTOHORITY + "/person");
                        ContentValues values = new ContentValues();
                        values.put("name", person.getName());
                        values.put("age", person.getAge());
                        values.put("sex", person.getSex());
                        resolver.insert(uri, values);

                        Toast.makeText(this, "Insert Success!", Toast.LENGTH_SHORT).show();
                        query();
                    } else {
                        Toast.makeText(this, "Insert Cancel!", Toast.LENGTH_SHORT).show();
                    }
                }
                break;
            case DELETE_REQUESTCODE:
                if (resultCode == DELETE_RESULTCODE) {
                    if (data != null) {
                        int id = data.getIntExtra("id", -1);
                        if (id != -1) {
                            ContentResolver resolver = getContentResolver();
                            Uri uri = Uri.parse("content://" + AUTOHORITY + "/person/" + id);
                            if (queryPersonById(uri).getCount() != 0) {
                                resolver.delete(uri, null, null);
                                Toast.makeText(this, "Delete Success!", Toast.LENGTH_SHORT).show();
                                query();
                            } else {
                                Toast.makeText(this, "Delete Fail! id " + id + " not exist", Toast.LENGTH_SHORT).show();
                            }
                        } else {
                            Toast.makeText(this, "Delete Fail!", Toast.LENGTH_SHORT).show();

                        }
                    } else {
                        Toast.makeText(this, "Delete Cancel!", Toast.LENGTH_SHORT).show();
                    }
                }
                break;
            case UPDATE_REQUESTCODE:
                if (resultCode == UPDATE_RESULTCODE) {
                    if (data != null) {
                        Person person = (Person) data.getSerializableExtra("person");
                        int id = person.getId();
                        ContentResolver resolver = getContentResolver();
                        Uri uri = Uri.parse("content://" + AUTOHORITY + "/person/" + id);
                        if (queryPersonById(uri).getCount() != 0) {
                            ContentValues values = new ContentValues();
                            values.put("name", person.getName());
                            values.put("age", person.getAge());
                            values.put("sex", person.getSex());
                            resolver.update(uri, values, null, null);
                            Toast.makeText(this, "Update Success!", Toast.LENGTH_SHORT).show();
                            query();
                        } else {
                            Toast.makeText(this, "Update Fail! id " + id + " not exist", Toast.LENGTH_SHORT).show();
                        }
                    } else {
                        Toast.makeText(this, "Update Cancel!", Toast.LENGTH_SHORT).show();
                    }
                }
                break;
            default:
                break;
        }

    }
}

大部分理论知识参考: https://blog.csdn.net/carson_ho/article/details/76101093

猜你喜欢

转载自blog.csdn.net/Gods_magic/article/details/84678225