ContentObserver的使用完整详细示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package cn.testcontentprovider;
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;
/**
 * Demo描述:
 * 自定义ContentProvider的实现
 * ContentProvider主要用于在不同的应用程序之间共享数据,这也是官方推荐的方式.
 *
 * 注意事项:
 * 1 在AndroidManifest.xml中注册ContentProvider时的属性
 *   android:exported=true表示允许其他应用访问.
 * 2 注意*和#这两个符号在Uri中的作用
 *   其中*表示匹配任意长度的字符
 *   其中#表示匹配任意长度的数据
 *   所以:
 *   一个能匹配所有表的Uri可以写成:
 *   一个能匹配person表中任意一行的Uri可以写成:
 *  
 */
public class ContentProviderTest extends ContentProvider {
    private SQLiteDatabaseOpenHelper mSQLiteDatabaseOpenHelper;
    private final static String  AUTHORITY=cn.bs.testcontentprovider;
    private  static UriMatcher mUriMatcher;
    private static final int PERSON_DIR = 0;
    private static final int PERSON = 1;
     
    /**
     * 利用静态代码块初始化UriMatcher
     * 在UriMatcher中包含了多个Uri,每个Uri代表一种操作
     * 当调用UriMatcher.match(Uri uri)方法时就会返回该uri对应的code;
     * 比如此处的PERSONS和PERSON
     */
    static {
        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        // 该URI表示返回所有的person,其中PERSONS为该特定Uri的标识码
        mUriMatcher.addURI(AUTHORITY, person, PERSON_DIR);
        // 该URI表示返回某一个person,其中PERSON为该特定Uri的标识码
        mUriMatcher.addURI(AUTHORITY, person/#, PERSON);
    }
     
     
    /**
     * 在自定义ContentProvider中必须覆写getType(Uri uri)方法.
     * 该方法用于获取Uri对象所对应的MIME类型.
     *
     * 一个Uri对应的MIME字符串遵守以下三点:
     * 1  必须以vnd开头
     * 2  如果该Uri对应的数据可能包含多条记录,那么返回字符串应该以vnd.android.cursor.dir/开头
     * 3  如果该Uri对应的数据只包含一条记录,那么返回字符串应该以vnd.android.cursor.item/开头
     */
    @Override
    public String getType(Uri uri) {
        switch (mUriMatcher.match(uri)) {
        case PERSON_DIR:
            return vnd.android.cursor.dir/+AUTHORITY+.persons;
        case PERSON:
            return vnd.android.cursor.item/+AUTHORITY+.person;
        default:
            throw new IllegalArgumentException(unknown uri+uri.toString());
        }
    }  
 
     
    @Override
    public boolean onCreate() {
        mSQLiteDatabaseOpenHelper=new SQLiteDatabaseOpenHelper(getContext());
        return true;
    }
     
 
    /**
     * 插入操作:
     * 插入操作只有一种可能:向一张表中插入
     * 返回结果为新增记录对应的Uri
     * 方法db.insert()返回结果为新增记录对应的主键值
     */
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
        switch (mUriMatcher.match(uri)) {
        case PERSON_DIR:
            long newId = db.insert(person, name,phone,salary, values);
            //向外界通知该ContentProvider里的数据发生了变化 ,以便ContentObserver作出相应
            getContext().getContentResolver().notifyChange(uri, null); 
            return ContentUris.withAppendedId(uri, newId);
        default:
            throw new IllegalArgumentException(unknown uri + uri.toString());
        }
    }
     
    /**
     * 更新操作:
     * 更新操作有两种可能:更新一张表或者更新某条数据
     * 在更新某条数据时原理类似于查询某条数据,见下.
     */
    @Override
    public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {
        SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
        int updatedNum = 0;
        switch (mUriMatcher.match(uri)) {
        // 更新表
        case PERSON_DIR:
            updatedNum = db.update(person, values, selection, selectionArgs);
            break;
        // 按照id更新某条数据
        case PERSON:
            long id = ContentUris.parseId(uri);
            String where = personid= + id;
            if (selection != null && !.equals(selection.trim())) {
                where = selection +  and  + where;
            }
            updatedNum = db.update(person, values, where, selectionArgs);
            break;
        default:
            throw new IllegalArgumentException(unknown uri + uri.toString());
        }
        //向外界通知该ContentProvider里的数据发生了变化 ,以便ContentObserver作出相应
        getContext().getContentResolver().notifyChange(uri, null); 
        return updatedNum;
    }
     
    /**
     * 删除操作:
     * 删除操作有两种可能:删除一张表或者删除某条数据
     * 在删除某条数据时原理类似于查询某条数据,见下.
     */
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
        int deletedNum = 0;
        switch (mUriMatcher.match(uri)) {
        // 删除表
        case PERSON_DIR:
            deletedNum = db.delete(person, selection, selectionArgs);
            break;
        // 按照id删除某条数据
        case PERSON:
            long id = ContentUris.parseId(uri);
            String where = personid= + id;
            if (selection != null && !.equals(selection.trim())) {
                where = selection +  and  + where;
            }
            deletedNum = db.delete(person, where, selectionArgs);
            break;
        default:
            throw new IllegalArgumentException(unknown uri + uri.toString());
        }
        //向外界通知该ContentProvider里的数据发生了变化 ,以便ContentObserver作出相应
        getContext().getContentResolver().notifyChange(uri, null); 
        return deletedNum;
    }
 
    /**
     * 查询操作:
     * 查询操作有两种可能:查询一张表或者查询某条数据
     *
     * 注意事项:
     * 在查询某条数据时要注意--因为此处是按照personid来查询
     * 某条数据,但是同时可能还有其他限制.例如:
     * 要求personid为2且name为xiaoming1
     * 所以在查询时分为两步:
     * 第一步:
     * 解析出personid放入where查询条件
     * 第二步:
     * 判断是否有其他限制(如name),若有则将其组拼到where查询条件.
     *
     * 详细代码见下.
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = mSQLiteDatabaseOpenHelper.getWritableDatabase();
        Cursor cursor =null;
        switch (mUriMatcher.match(uri)) {
        // 查询表
        case PERSON_DIR:
            cursor = db.query(person, projection, selection, selectionArgs,null, null, sortOrder);
            break;
        // 按照id查询某条数据
        case PERSON:
            // 第一步:
            long id = ContentUris.parseId(uri);
            String where = personid= + id;
            // 第二步:
            if (selection != null && !.equals(selection.trim())) {
                where = selection +  and  + where;
            }
            cursor = db.query(person, projection, where, selectionArgs, null, null, sortOrder);
            break;
        default:
            throw new IllegalArgumentException(unknown uri + uri.toString());
        }
        return cursor;
    }
     
 
}


SQLiteDatabaseOpenHelper如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package cn.testcontentprovider;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class SQLiteDatabaseOpenHelper extends SQLiteOpenHelper {
    public SQLiteDatabaseOpenHelper(Context context) {
        super(context, contentprovidertest.db, null, 1);
    }
 
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(create table person(personid integer primary key autoincrement,name varchar(20),phone varchar(12),salary  Integer(12)));    
    }
 
    //当数据库版本号发生变化时调用该方法
    @Override
    public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
        //db.execSQL(ALTER TABLE person ADD phone varchar(12) NULL);
        //db.execSQL(ALTER TABLE person ADD salary  Integer NULL);
    }
 
}


MainActivity如下:

1
2
3
4
5
6
7
8
9
10
11
package cn.testcontentprovider;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
 
}


AndroidManifest.xml如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!--?xml version=1.0 encoding=utf-8?-->
<manifest android:versioncode="1" android:versionname="1.0" package="cn.testcontentprovider" xmlns:android="http://schemas.android.com/apk/res/android">
 
    <uses-sdk android:minsdkversion="8" android:targetsdkversion="8">
 
    <uses-permission android:name="android.permission.INTERNET">
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS">
     
     
         
            <intent-filter>
                 
 
                <category android:name="android.intent.category.LAUNCHER">
            </category></action></intent-filter>
        </activity>
         
         <provider android:authorities="cn.bs.testcontentprovider" android:exported="true" android:name="cn.testcontentprovider.ContentProviderTest">
    </provider></application>
 
</uses-permission></uses-permission></uses-permission></uses-permission></uses-sdk></manifest>


main.xml如下:

1
<relativelayout android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"><button android:layout_centerinparent="true" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="该应用包含一个自定义的ContentProvider" android:textsize="15sp"></button></relativelayout>



以下代码为工程TestBaidu

MainActivity如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
package cn.testbaidu;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.ContentObserver;
import android.database.Cursor;
/**
 * Demo描述:
 * 应用A(TestBaidu)调用另外一个应用(TestContentProvider)中的自定义ContentProvider,即:
 * 1 自定义ContentProvider的使用
 * 2 其它应用调用该ContentProvider
 * 3 ContentObserver的使用
 *
 * 备注说明:
 * 1 该例子在以前版本的基础上整理了代码
 * 2 该例子在以前版本的基础上融合了ContentObserver的使用
 *   利用ContentObserver随时监听ContentProvider的数据变化.
 *   为实现该功能需要在自定义的ContentProvider的insert(),update(),delete()
 *   方法中调用getContext().getContentResolver().notifyChange(uri, null);
 *   向外界通知该ContentProvider里的数据发生了变化 ,以便ContentObserver作出相应 
 *
 * 测试方法:
 * 1 依次测试ContentProvider的增查删改(注意该顺序)!!
 * 2 其它应用查询该ContentProvider的数据
 *
 */
public class MainActivity extends Activity {
    private Button mAddButton;
    private Button mDeleteButton;
    private Button mUpdateButton;
    private Button mQueryButton;
    private Button mTypeButton;
    private long lastTime=0;
    private ContentResolver mContentResolver;
    private ContentObserverSubClass mContentObserverSubClass;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        init();
        initContentObserver();
    }
 
    private void init() {
        mContentResolver=this.getContentResolver();
         
        mAddButton=(Button) findViewById(R.id.addButton);
        mAddButton.setOnClickListener(new ClickListenerImpl());
         
        mDeleteButton=(Button) findViewById(R.id.deleteButton);
        mDeleteButton.setOnClickListener(new ClickListenerImpl());
         
        mUpdateButton=(Button) findViewById(R.id.updateButton);
        mUpdateButton.setOnClickListener(new ClickListenerImpl());
         
        mQueryButton=(Button) findViewById(R.id.queryButton);
        mQueryButton.setOnClickListener(new ClickListenerImpl());
         
        mTypeButton=(Button) findViewById(R.id.typeButton);
        mTypeButton.setOnClickListener(new ClickListenerImpl());
         
    }
     
     
 
    // 注册一个针对ContentProvider的ContentObserver用来观察内容提供者的数据变化
    private void initContentObserver() {
        Uri uri = Uri.parse(content://cn.bs.testcontentprovider/person);
        mContentObserverSubClass=new ContentObserverSubClass(new Handler());
        this.getContentResolver().registerContentObserver(uri, true,mContentObserverSubClass);
    }
     
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mContentObserverSubClass!=null) {
            this.getContentResolver().unregisterContentObserver(mContentObserverSubClass);
        }
    }
 
     
     
     
    // 自定义一个内容观察者ContentObserver
    private class ContentObserverSubClass extends ContentObserver {
 
        public ContentObserverSubClass(Handler handler) {
            super(handler);
        }
 
        //采用时间戳避免多次调用onChange( )
        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            System.out.println(ContentObserver onChange() selfChange=+ selfChange);
            if (System.currentTimeMillis()-lastTime>2000) {
                ContentResolver resolver = getContentResolver();
                Uri uri = Uri.parse(content://cn.bs.testcontentprovider/person);
                // 获取最新的一条数据
                Cursor cursor = resolver.query(uri, null, null, null,personid desc limit 1);
                while (cursor.moveToNext()) {
                    int personid = cursor.getInt(cursor.getColumnIndex(personid));
                    System.out.println(内容提供者中的数据发生变化,现数据中第一条数据的personid=+ personid);
                }
                cursor.close();
                lastTime=System.currentTimeMillis();
            }else{
                System.out.println(时间间隔过短,忽略此次更新);
            }
             
             
        }
         
        @Override
        public boolean deliverSelfNotifications() {
            return true;
        }
         
    }
     
     
     
     
 
    private class ClickListenerImpl implements OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.addButton:
                Person person = null;
                for (int i = 0; i < 5; i++) {
                    person = new Person(xiaoming + i, 9527 + i, (8888 + i));
                    testInsert(person);
                }
                break;
            case R.id.deleteButton:
                testDelete(1);
                break;
            case R.id.updateButton:
                testUpdate(3);
                break;
            case R.id.queryButton:
                // 查询表
                // queryFromContentProvider(-1);
 
                // 查询personid=2的数据
                testQuery(2);
                break;
            case R.id.typeButton:
                testType();
                break;
            default:
                break;
            }
 
        }
 
    }
    private void testInsert(Person person) {
        ContentValues contentValues=new ContentValues();
        contentValues.put(name, person.getName());
        contentValues.put(phone, person.getPhone());
        contentValues.put(salary,person.getSalary());
        Uri insertUri=Uri.parse(content://cn.bs.testcontentprovider/person);
        Uri returnUri=mContentResolver.insert(insertUri, contentValues);
        System.out.println(新增数据:returnUri=+returnUri);
    }
     
    private void testDelete(int index){
        Uri uri=Uri.parse(content://cn.bs.testcontentprovider/person/+String.valueOf(index));
        mContentResolver.delete(uri, null, null);
    }
     
    private void testUpdate(int index){
        Uri uri=Uri.parse(content://cn.bs.testcontentprovider/person/+String.valueOf(index));
        ContentValues values=new ContentValues();
        values.put(name, hanmeimei);
        values.put(phone, 1234);
        values.put(salary, 333);
        mContentResolver.update(uri, values, null, null);
    }
 
    private void testQuery(int index) {
        Uri uri=null;
        if (index<=0) {
            //查询表
            uri=Uri.parse(content://cn.bs.testcontentprovider/person);
        } else {
             //按照id查询某条数据
            uri=Uri.parse(content://cn.bs.testcontentprovider/person/+String.valueOf(index));
        }
         
        //对应上面的:查询表
        //Cursor cursor= mContentResolver.query(uri, null, null, null, null);
         
        //对应上面的:查询personid=2的数据
        //注意:因为name是varchar字段的,所以应该写作name='xiaoming1'
        //     若写成name=xiaoming1查询时会报错
        Cursor cursor= mContentResolver.query(uri, null, name='xiaoming1', null, null);
         
        while(cursor.moveToNext()){
            int personid=cursor.getInt(cursor.getColumnIndex(personid));
            String name=cursor.getString(cursor.getColumnIndex(name));
            String phone=cursor.getString(cursor.getColumnIndex(phone));
            int salary=cursor.getInt(cursor.getColumnIndex(salary));
            System.out.println(查询得到:personid= + personid+,name=+name+,phone=+phone+,salary=+salary);
        }
        cursor.close();
    }
     
    private void testType(){
        Uri dirUri=Uri.parse(content://cn.bs.testcontentprovider/person);
        String dirType=mContentResolver.getType(dirUri);
        System.out.println(dirType:+dirType);
         
        Uri itemUri=Uri.parse(content://cn.bs.testcontentprovider/person/3);
        String itemType=mContentResolver.getType(itemUri);
        System.out.println(itemType:+itemType);
    }
 
}



Person如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package cn.testbaidu;
 
public class Person {
    private Integer id;
    private String name;
    private String phone;
    private Integer salary;
    public Person(String name, String phone,Integer salary) {
        this.name = name;
        this.phone = phone;
        this.salary=salary;
    }
    public Person(Integer id, String name, String phone,Integer salary) {
        this.id = id;
        this.name = name;
        this.phone = phone;
        this.salary=salary;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    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 Integer getSalary() {
        return salary;
    }
    public void setSalary(Integer salary) {
        this.salary = salary;
    }
    @Override
    public String toString() {
        return Person [id= + id + , name= + name + , phone= + phone+ , salary= + salary + ];
    }
     
     
     
}