代码改变世界

Android ContentProvider 内容提供者

2014-02-20 17:05  kingshow  阅读(485)  评论(0编辑  收藏  举报

     ContentProvider 内容提供者,是Android四大组件之一。

一、简述

     可以理解为一个特殊的存储数据的类型,它提供了一套标准的接口来获取和操作数据。可以把数据封装到ContentProvider 中,从而是这些数据可以被其他的应用程序所共享。搭建起了所有应用程序之间数据交换的桥梁!
   1. 内容提供者可以将应用中的数据对外进行共享

 2.内容提供者将数据的访问方式统一,不必针对不同数据类型采取不同的访问策略

 3.内容提供者将数据封装,只暴露出我们希望提供给其他程序的数据

 4.内容提供者中数据更改可被监听

二、创建内容提供者

   定义类继承ContentProvider,根据需要重写内部方法

     在清单文件的<application>节点下进行配置,<provider>标签中需要指定name和authorities属性

    name为类名,包名从程序Package开始,以“.”开始

    authorities:是访问Provider时的路径,要唯一

       URI代表要操作的数据,由scheme、authorites、path三部分组成

    content://com.bruce.contentprovider.demo/student

    scheme:固定为content,代表访问内容提供者

    authorites:<provider>节点中的authorites属性

    path:程序定义的路径,可根据业务逻辑定义

--------------------------------------------------------------------------------------------------

 <provider
            android:name="com.bruce.contentprovider.demo.MyContentProvider"
            android:authorities="com.bruce.contentprovider.demo"
            ></provider>

--------------------------------------------------------------------------------------------------

getType方法中:

Ÿ   如果返回数据是单条数据:vnd.android.cursor.item

Ÿ   如果返回数据是多条数据:vnd.android.cursor.dir

三、完成CRUD方法

Ÿ   当程序调用CRUD方法时会传入Uri, 我们通过Uri判断调用者要操作的数据,可以使用工具类UriMatcher来判断Uri,addURI方法可以添加Uri,match方法可以匹配一个Uri判断其类型;根据业务逻辑操作数据。

  1.onCreate() :初始化该ContentProvider
      2.query(Uri,String[],String,Sring[],String) :通过Uri进行查询,返回Uri
      3.intert(Uri,ContentValues):将数据插入到Uri所指定的位置
      4.update(Uri,ContentValues,String,String []):更新uri指定位置的数据
      5.delete(Uri,String,String[]):删除uri指定位置的数据
      6.getType(Uri):返回数据的类型
四、建立contentprovider工程,添加单元测试(http://www.cnblogs.com/kingshow123/p/sqlitecreate.html 中有介绍)
 

注意:创建的内容提供者MyContentProvider,必须放在应用包(com.bruce.contentprovider),或者放在应用包的子包(com.bruce.contentprovider.demo)下。因为它是应用组件之一

五、创建DbOpenHelper类

package com.bruce.contentprovider.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class DbOpenHelper extends SQLiteOpenHelper {
    
    private static String name = "mycont.db";
    private static int version = 1;
    public DbOpenHelper(Context context) {
        super(context, name, null, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String sql = "create table student( id integer primary key autoincrement,name varchar(64),address varchar(64))";
        db.execSQL(sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
        // TODO Auto-generated method stub

    }

}
View Code

六、创建MyContentProvider类

package com.bruce.contentprovider.demo;

import com.bruce.contentprovider.db.DbOpenHelper;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

public class MyContentProvider extends ContentProvider {
    
    private DbOpenHelper helper;
    private final static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
    private final static int STUDENT = 0;
    private final static int STUDENTS = 1;
    static{
        matcher.addURI("com.bruce.contentprovider.demo", "student/#", STUDENT); //单条记录
        matcher.addURI("com.bruce.contentprovider.demo", "student", STUDENTS);  //多条记录
    }
    
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int count = -1;
        SQLiteDatabase database = helper.getWritableDatabase();
        switch (matcher.match(uri)) {
        case STUDENT:
            long id = ContentUris.parseId(uri);
            selection = (selection == null ? " id = " + id : " id = " + id + " and " + selection);
            count = database.delete("student", selection, selectionArgs);
            break;
        case STUDENTS:
            count = database.delete("student", selection, selectionArgs);
            break;
        default:
            throw new IllegalArgumentException("No match uri:" + uri);
        }
        return count;
    }

    @Override
    public String getType(Uri uri) {
        
        switch (matcher.match(uri)) {
        case STUDENT:
            return "vnd.android.cursor.item/student";
        case STUDENTS:
            return "vnd.android.cursor.dir/student";
        default:
            throw new IllegalArgumentException("No match uri:" + uri);
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Uri resultUri = null;
        switch (matcher.match(uri)) {
        case STUDENTS:
            SQLiteDatabase database = helper.getWritableDatabase();
            long id = database.insert("student", null,values);
            resultUri = ContentUris.withAppendedId(uri, id);
            break;

        default:
            throw new IllegalArgumentException("No match uri:" + uri);
        }
        
        return resultUri;
    }

    @Override
    public boolean onCreate() {
        helper = new DbOpenHelper(getContext());
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Cursor cursor = null;
        switch (matcher.match(uri)) {
        case STUDENT:
            long id = ContentUris.parseId(uri);
            selection = selection == null ? " id = " + id : " id = " + id + " and " + selection;
        case STUDENTS:
            SQLiteDatabase database = helper.getWritableDatabase();
            cursor = database.query("student", projection, selection, selectionArgs, null, null, sortOrder);
            break;
        default:
            throw new IllegalArgumentException("No match uri:" + uri);
        }
        return cursor;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        switch (matcher.match(uri)) {
        case STUDENT:
            long id = ContentUris.parseId(uri);
            selection = (selection == null ? " id = " + id : " id = " + id + " and " + selection);
        case STUDENTS:
            SQLiteDatabase database = helper.getWritableDatabase();
            return database.update("student", values, selection, selectionArgs); //directly return the number of rows affected
        default:
            throw new IllegalArgumentException("No match uri:" + uri);
        }
    }

}
View Code

七、创建MyTestProvider类

package com.bruce.contentprovider.test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.test.AndroidTestCase;
import android.util.Log;

public class MyTestProvider extends AndroidTestCase {
    
    private String TAG = "MyTestProvider";
    
    public void insertStudent(){
        ContentResolver resolver = getContext().getContentResolver();
        Uri uri = Uri.parse("content://com.bruce.contentprovider.demo/student");
        ContentValues values = new ContentValues();
        values.put("name", "姚明");
        values.put("address", "上海");
        Uri reUri = resolver.insert(uri, values);
        
        Cursor cursor = resolver.query(reUri, null, null, null, null);
        if(cursor.moveToNext()){
            int id = cursor.getInt(0);
            String name = cursor.getString(1);
            String address = cursor.getString(2);
            Log.i(TAG, "-->test insert id:" + id + ",name:" + name + ",address:" + address);
        }
        
    }
    
    public void deleteStudent(){
        ContentResolver resolver = getContext().getContentResolver();
        Uri uri = Uri.parse("content://com.bruce.contentprovider.demo/student/5");
        //String  selection = " name = ? ";
        //String[]  selectionArgs = {"姚明"};
        //int count = resolver.delete(uri, selection, selectionArgs);
        int count = resolver.delete(uri, null, null);
        Log.i(TAG, "-->test delete count=" + count);
        
    }
    
    public void updateStudent(){
        ContentResolver resolver = getContext().getContentResolver();
        Uri uri = Uri.parse("content://com.bruce.contentprovider.demo/student/1");
        ContentValues values = new ContentValues();
        values.put("name", "邹市明");
        values.put("address","遵义");
        //String selection = " name = ? ";
        //String[] selectionArgs = {"老毛"};
        //int count = resolver.update(uri, values, selection, selectionArgs);
        int count = resolver.update(uri, values, null, null);
        Log.i(TAG, "-->test update count = " + count);
    }
    
    public void queryById(){
        ContentResolver resolver = getContext().getContentResolver();
        Uri uri = Uri.parse("content://com.bruce.contentprovider.demo/student/1");
        Cursor cursor = resolver.query(uri, null, null, null, null);
        while(cursor.moveToNext()){
            int id = cursor.getInt(0);
            String name = cursor.getString(1);
            String address = cursor.getString(2);
            Log.i(TAG, "--> test queryById id = " + id + ",name = " + name + ",address= " + address);
        }
    }
    
    public void queryByName(){
        Map<String, String> map = new HashMap<String, String>();
        ContentResolver resolver = getContext().getContentResolver();
        Uri uri = Uri.parse("content://com.bruce.contentprovider.demo/student");
        String selection = " name = ? ";
        String[] selectionArgs = {"姚明"};
        Cursor cursor = resolver.query(uri, null, selection, selectionArgs, null);
        int count = cursor.getColumnCount();
        while(cursor.moveToNext()){
            for(int i=0; i<count; i++){
                String cols_name = cursor.getColumnName(i);
                String cols_value = cursor.getString(cursor.getColumnIndex(cols_name));
                map.put(cols_name, cols_value);
            }
        }
        Log.i(TAG, "-->test queryByName:" + map.toString());
    }
    
    public void queryListStudent(){
        List<Map<String, String>> list = new ArrayList<Map<String,String>>();
        ContentResolver resolver = getContext().getContentResolver();
        Uri uri = Uri.parse("content://com.bruce.contentprovider.demo/student");
        Cursor cursor = resolver.query(uri, null, null, null, null);
        int count = cursor.getColumnCount();
        while(cursor.moveToNext()){
            Map<String, String> map = new HashMap<String, String>();
            for(int i=0; i<count; i++){
                String cols_name = cursor.getColumnName(i);
                String cols_value = cursor.getString(cursor.getColumnIndex(cols_name));
                map.put(cols_name, cols_value);
            }
            list.add(map);
        }
        Log.i(TAG, "-->test queryListStudent:" + list.toString());
    }
    
    public void testGetType(){
        ContentResolver resolver = getContext().getContentResolver();
        Uri uri = Uri.parse("content://com.bruce.contentprovider.demo/student/1");
        Log.i(TAG, "-->testGetType plus id:" + resolver.getType(uri));
        
        uri = Uri.parse("content://com.bruce.contentprovider.demo/student");
        Log.i(TAG, "-->testGetType:" + resolver.getType(uri));
    }
}
View Code

八、监听内容提供者数据变化

Ÿ   在内容提供者中可以通知其他程序数据发生变化,通过Context的getContentResolver()方法获取ContentResolver,调用其notifyChange()方法发送数据修改通知;

Ÿ   在其他程序中可以通过ContentObserver监听数据变化

     通过Context的getContentResolver()方法获取ContentResolver

     调用其registerContentObserver()方法指定对某个Uri注册ContentObserver

     自定义ContentObserver,重写onChange()方法获取数据

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

1.需要被监听的对象(例如)下面放入代码

首先需要调用一下 getContext.getcontentResolver().notifyChange(uri,null);  放在删除添加 或者其他的方法后面

  

2.然后在Activity中继承ContentObserver类 自己实现oncreate方法在方法中写具体的业务代码 比如是

Private class MyContentObserver  extends contentObserver(){

    Public MyContentObserver(Handle handle){

    Super(handle)

   }

   Public void onChange(){// 具体的业务代码 

     //可以用toast类去实现一些显示的功能

  }

}

 

3. 在主类中注册uri

GetcontentResolver().registerContentObserver(“需要注册的uri”,ture,new MyContentObserver(new handle() ))   方法

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

发送修改通知:

public Uri insert(Uri uri, ContentValues values) {
        Uri resultUri = null;
        switch (matcher.match(uri)) {
        case STUDENTS:
            SQLiteDatabase database = helper.getWritableDatabase();
            long id = database.insert("student", null,values);
            
            // Notify registered observers that a row was updated. 注册 observer 
            // @param observer The observer that originated the change, may be null            
            // 产生改变的Observer. 此处为Provider带来的改变,传 null
            this.getContext().getContentResolver().notifyChange(uri, null);// 发送修改通知
            
            //resultUri = Uri.parse("content://com.bruce.contentprovider.demo/student" + id);
            resultUri = ContentUris.withAppendedId(uri, id);
            break;

        default:
            throw new IllegalArgumentException("No match uri:" + uri);
        }
        
        return resultUri;
    }
View Code

 

 

也可以在其他应用中定义一个ContentObserver,监听Uri所对应的内容提供者的变化

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //定义一个ContentObserver
        ContentObserver observer = new ContentObserver(new Handler()) {
        
             /**            
              * Returns true if this observer is interested in notifications for
              * changes made through the cursor the observer is registered with.            
              */           
            // 是否传递自己的改变
            @Override
            public boolean deliverSelfNotifications() {
                return super.deliverSelfNotifications();
            }

            // 当被监听的内容发生了改变时,调用该方法
            @Override
            public void onChange(boolean selfChange) {
                Log.i("MainActivity", "监听到了变化!!");
                //打印最后插入的信息
                ContentResolver resolver = getContentResolver();
                Uri uri = Uri.parse("content://com.bruce.contentprovider.demo/student");
                //select * from student order by id desc limit 1
                Cursor c = resolver.query(uri, null, null, null, " id desc limit 1");
                if(c.moveToNext()){
                    String string = "id="+ c.getInt(0) + ",name=" + c.getString(1) + ",address=" + c.getString(2);
                    Log.i("MainActivity", string);
                    Toast.makeText(MainActivity.this, string, 1).show();
                }
                //父类未做任何操作
                super.onChange(selfChange);
            }
            
        };
        Uri uri = Uri.parse("content://com.bruce.contentprovider.demo/student");
        getContentResolver().registerContentObserver(uri, true, observer);
    }
View Code

 九、监听内容提供者实例,三个应用来完成,注意:测试这个监听数据变化不能用单元测试来完成,因为但点击单元测试完成后,应用自动关闭了。

   内容提供者,就用contentprovider工程,另外在创建aprovidertest和bprovidertest工程。如图:

1.aprovidertest中的代码

activity_app.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".AppActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="test provider A" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:onClick="insert"
        android:text="Button" />

</RelativeLayout>
View Code

AppActivity.java

package com.bruce.eoe.provider;

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.util.Log;
import android.view.Menu;
import android.view.View;

public class AppActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_app);
    }
    
    public void insert(View v){
        ContentResolver resolver = this.getContentResolver();
        Uri uri = Uri.parse("content://com.bruce.contentprovider.demo/student");
        ContentValues values = new ContentValues();
        values.put("name", "发哥");
        values.put("address", "香港");
        resolver.insert(uri, values);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.app, menu);
        return true;
    }

}
View Code

2.bprovidertest中的代码

MainActivity.java

package com.bruce.bprovidertest;

import android.app.Activity;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        Uri uri = Uri.parse("content://com.bruce.contentprovider.demo/student");
        ContentResolver resolver = this.getContentResolver();
        resolver.registerContentObserver(uri, true, new MyContentObserver(new Handler()));
    }
    
    private class MyContentObserver extends ContentObserver{

        public MyContentObserver(Handler handler) {
            super(handler);
        }
        
         // 得到数据的变化通知,该方法只能粗略知道数据的改变,并不能判断是哪个业务操作进行的改变   
        @Override  
        public void onChange(boolean selfChange)  
        {  
            // select * from person order by id desc limit 1 // 取得最近插入的值(序号大——>小并取第一个)   
            Uri uri = Uri.parse("content://com.bruce.contentprovider.demo/student");  
            ContentResolver resolver = MainActivity.this.getContentResolver();  
            Cursor cursor = resolver.query(uri, null, null, null, "id desc limit 1");  
            if(cursor.moveToFirst())  
            {  
                String string = "id=" + cursor.getInt(0) + ",name=" + cursor.getString(1) + ",address=" + cursor.getString(2);  
                Log.i("MyTest", string);  
            }  
        }  

    }

    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}
View Code

 

首先运行contentprovider工程,在运行bprovidertest工程,然后再运行aprovidertest工程,点击Button按钮,就可以向contentprovider工程中插入数据,然后bprovidertest中可以监听到数据变化。

 

测试时,或许会提示:java.lang.SecurityException: Permission Denial: opening provider  异常,在AndroidManifest.xml文件中的provider节点中加入 android:exproted = "true"  即可解决。

 <provider
            android:name="com.bruce.contentprovider.demo.MyContentProvider"
            android:authorities="com.bruce.contentprovider.demo"
            android:exported="true"
            ></provider>