ContentProvider使用完整示范(二)——ContentObserver

ContentProvider使用完整示例(二)——ContentObserver

//以下为TestBaidu

MainActivity如下:

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 且在应用A(TestBaidu)使用ContentObserver观察ContentProvider的数据变化
 *
 */
public class MainActivity extends Activity {
	private Button mAddButton;
	private Button mUpdateButton;
	private Button mTypeButton;
    private ContentResolver mContentResolver;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		initViews();
		initContentObserver();
	}

	private void initViews() {
		mContentResolver=this.getContentResolver();
		
		mAddButton=(Button) findViewById(R.id.addButton);
		mAddButton.setOnClickListener(new ClickListenerImpl());
		
		mUpdateButton=(Button) findViewById(R.id.updateButton);
		mUpdateButton.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");  
	     this.getContentResolver().registerContentObserver(uri, true,new ContentObserverSubClass(new Handler()));  
	}

	private class ClickListenerImpl implements OnClickListener{
		@Override
		public void onClick(View v) {
			switch (v.getId()) {
			case R.id.addButton:
				Person person=null;
				person=new Person("xiaoming", "9527",8888);
				testInsert(person);
				break;
			case R.id.updateButton:
				testUpdate(1);
					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");
        mContentResolver.insert(insertUri, contentValues);
        System.out.println("-----> MainActivity中执行Insert操作");
	}
	

	
	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);
		System.out.println("-----> MainActivity中执行Update操作");
	}

	
	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);
	}
	
	
	//自定义一个内容观察者
	private class ContentObserverSubClass extends ContentObserver {
        long lastTime=0;

		public ContentObserverSubClass(Handler handler) {
			super(handler);
		}

		@Override
		public void onChange(boolean selfChange) {
			super.onChange(selfChange);
            //该方式的目的:
			//防止onChange()的多次调用
			//但是注意问题:
			//关于onChange()的多次调用看似解决了,但是今天又测试到一个新的问题--->
			//1 手机界面停留在TestBaidu,然后一直点击Back键盘直至Home画面
			//2 此时再从应用列表进入TestBaidu应用进行操作,依然会出现该问题.
			//该问题困惑不解.有待进一步分析.测试环境--->摩托罗拉,OS:2.3.6
            if (System.currentTimeMillis()-lastTime>1000) {
            	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();
			}
            
		}
	}

}

Person.java如下:

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 + "]";
	}
	
	
	
}

main.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" >

    <Button
        android:id="@+id/addButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="100dip"
        android:text="增加"
        android:textSize="20sp" />

    <Button
        android:id="@+id/updateButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="30dip"
         android:layout_below="@id/addButton"
        android:text="修改"
        android:textSize="20sp" />

    
      <Button
        android:id="@+id/typeButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="30dip"
         android:layout_below="@id/updateButton"
        android:text="类型"
        android:textSize="20sp" />

</RelativeLayout>

//以下为TestContentProvider

MainActivity如下:

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);
	}

}

ContentProviderTest如下:

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;
/**
 * 注意事项:
 * 1 在AndroidManifest.xml中注册ContentProvider时的属性
 *   android:exported="true"表示允许其他应用访问.
 *   这样TestBaidu这个应用才可以访问该处的ContentProvider
 * 2 注意getContext().getContentResolver().notifyChange(uri, null);
 *   的使用.否则ContentObserver无响应.
 */
public class ContentProviderTest extends ContentProvider {
	private DBOpenHelper dbOpenHelper;
	private  UriMatcher URI_MATCHER;
	private static final int PERSONS = 0;
	private static final int PERSON = 1;
	 
	
	@Override
	public boolean onCreate() {
		initUriMatcher();
		dbOpenHelper=new DBOpenHelper(getContext());
		return true;
	}
	//初始化UriMatcher
	private void initUriMatcher(){
		URI_MATCHER=new UriMatcher(UriMatcher.NO_MATCH);
		//表示返回所有的person,其中PERSONS为该特定Uri的标识码
		URI_MATCHER.addURI("cn.bs.testcontentprovider","person", PERSONS);
		//表示返回某一个person,其中PERSON为该特定Uri的标识码
		URI_MATCHER.addURI("cn.bs.testcontentprovider","person/#", PERSON);
	}

	/**
	 * 插入操作:
	 * 插入操作只有一种可能:向一张表中插入
	 * 返回结果为新增记录对应的Uri
	 * 方法db.insert()返回结果为新增记录对应的主键值
	 */
	@Override
	public Uri insert(Uri uri, ContentValues values) {
		SQLiteDatabase db=dbOpenHelper.getWritableDatabase();
		switch (URI_MATCHER.match(uri)) {
		case PERSONS:
           long rowid=db.insert("person", "name,phone,salary", values);
           //向外界通知该ContentProvider里的数据发生了变化
           getContext().getContentResolver().notifyChange(uri, null);
           return ContentUris.withAppendedId(uri, rowid);
		default:
			throw new IllegalArgumentException("unknown uri"+uri.toString());
		}
	}
	
	/**
	 * 更新操作:
	 * 更新操作有两种可能:更新一张表或者更新某条数据
	 * 在更新某条数据时原理类似于查询某条数据,见下.
	 */
	@Override
	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
		SQLiteDatabase db=dbOpenHelper.getWritableDatabase();
		int updataNum=0;
		switch (URI_MATCHER.match(uri)) {
		//更新表
		case PERSONS:
			updataNum=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;
		   }
		    updataNum=db.update("person", values, where, selectionArgs);
		break;
		default:
			throw new IllegalArgumentException("unknown uri"+uri.toString());
		}
		//向外界通知该ContentProvider里的数据发生了变化
        getContext().getContentResolver().notifyChange(uri, null);
		return updataNum;
	}
	
	//在此不实现该方法
	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		return 0;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
		SQLiteDatabase db=dbOpenHelper.getWritableDatabase();
		Cursor cursor;
		switch (URI_MATCHER.match(uri)) {
		//查询表
		case PERSONS:
			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;
	}
	
	/**
	 * 返回当前Uri所代表的数据的MIME类型.
	 * 如果该Uri对应的数据可能包含多条记录,那么返回
	 * 字符串应该以"vnd.android.cursor.dir/"开头
	 * 如果该Uri对应的数据只包含一条记录,那么返回
	 * 字符串应该以"vnd.android.cursor.item/"开头
	 */
	@Override
	public String getType(Uri uri) {
		switch (URI_MATCHER.match(uri)) {
		case PERSONS:
			return "vnd.android.cursor.dir/persons";
		case PERSON:
			return "vnd.android.cursor.item/person";
		default:
			throw new IllegalArgumentException("unknown uri"+uri.toString());
		}
	}		

}

DBOpenHelper如下:
package cn.testcontentprovider;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DBOpenHelper extends SQLiteOpenHelper {
	public DBOpenHelper(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) {
	}

}

main.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"
    >

     <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="该应用包含一个自定义的ContentProvider"
        android:textSize="15sp"
        android:layout_centerInParent="true"
    />

</RelativeLayout>

AndroidManifest.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.testcontentprovider"
    android:versionCode="1"
    android:versionName="1.0" >

    <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" />
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="cn.testcontentprovider.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
         <provider 
            android:name="cn.testcontentprovider.ContentProviderTest"
            android:authorities="cn.bs.testcontentprovider"
            android:exported="true"
         />
    </application>

</manifest>