Android 内容提供者、解释者和观察者学习笔记

本文介绍了如何使用Android的ContentProvider在两个应用间共享数据,并通过ContentResolver查询数据。同时,详细讲解了如何实现ContentObserver来观察ContentProvider中的数据变化,包括注册与注销观察者的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简单实现两个应用程序互相共享数据

A程序使用contentprovider 暴露出数据(内容提供者)

B程序使用ContentResolver根据url去查询A程序提供的数据(内容解释者),还可以使用ContentObserver去观察A'程序中数据的变化(内容观察者)


A程序(代码中取名为  TestContentProvider) 主要是ContentProvider,它是Android四大组件之一,使用时需要在Androidmanifest中注册一下。它和activity是同一个级别的,在manifest总的位置和activity是并列的, 以下是注册文件完整代码

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


    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="21" />


    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".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>
        
        <!-- android:authorities 先用包名试试  感觉可以修改成其他的 -->
        <provider 
            android:name="com.example.testcontentprovider.MyContentProvider"
            android:authorities="com.example.test"
            android:exported="true"/>
        <!-- 
        需要添加android:exported="true" 这个属性用于指示该服务是否能够被其他应用程序组件调用或跟它交互。
         -->
        <!-- 
        加上ndroid:multiprocess=true  就老是报错
         对于android:multiprocess=true的ContentProvider,
         意味着可以多实例,那么由调用者在自己的进程空间实例化一个ContentProvider对象,
         此时定义ContentProvider的App可能并没有启动
         
         对于android:multiprocess=false(默认值)的ContentProvider,
         由系统把定义该ContentProvider的App启动起来(一个独立的Process)并实例化ContentProvider,
         这种ContentProvider只有一个实例,运行在自己App的Process中。所有调用者共享该ContentProvider实例,
         调用者与ContentProvider实例位于两个不同的Process
         -->
    </application>


</manifest>

主要的参数就是android:authorities和android:exported,第一个参数是其他程序访问时使用的url的开头(下面有例子),第二个参数设置为true之后才可以被其他应用程序调用。还有第一个参数 android:name是.java的位置,这个不用过多说明,接下来去实现它

四大组件都差不多,都需要继承一下,所有它也需要继承一下ContentProvider,源码中给出了介绍android:authorities

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

public class MyContentProvider extends ContentProvider {
	
	/*
	 * URI,是uniform resource identifier,统一资源标识符,用来唯一的标识一个资源
	 * 
	 * riMatcher类用于匹配Uri,
	 * 首先第一步把你需要匹配Uri路径全部给注册上,
	 * 常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
	 */
	private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
	private static final int PERSONS = 1;  
    private static final int PERSON = 2;
    
	static {
		//匹配Uri路径全部给注册上,
		//如果match()方法匹配content://com.example.test/person路径,返回匹配码为PERSONS=1
		//com.example.test  这个值是在manifest里面设置的
		//android:authorities="com.example.test"
		MATCHER.addURI("com.example.test", "person", PERSONS);
		//#号为通配符 匹配content://com.example.test/person/xx
		MATCHER.addURI("com.example.test", "person/#", PERSON);
	}
	
	
	/**
	 * 自定义方法
	 * 这个方法不知道咋调用。。。貌似只能自己用,测试使用可以无视,,
	 * @param uri
	 */
	public void test(Uri uri) {
		switch (MATCHER.match(uri)) {
		case PERSONS:
			System.out.println(" test 返回persons的数据");
			break;
			
		case PERSON:
			System.out.println(" test 返回person的数据");
			break;
			
		default:
			break;
		}
	}
	
	@Override
	public int delete(Uri arg0, String arg1, String[] arg2) {
		// TODO Auto-generated method stub
		
		return 0;
	}

	@Override
	public String getType(Uri arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Uri insert(Uri arg0, ContentValues arg1) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean onCreate() {
		System.out.println("onCreate");
		
		return false;
	}

	@Override
	public Cursor query(Uri uri, String[] arg1, String arg2, String[] arg3,
			String arg4) {
		switch (MATCHER.match(uri)) {
		case PERSONS:
			System.out.println(" Cursor query 返回persons的数据");
			break;
		case PERSON:
			System.out.println(" Cursor query 返回person的数据");
			break;
			
		default:
			break;
		}
		return null;
	}

	@Override
	public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
		
		// 提示一下内容观察者  arg0 URI 对应的数据已经更新了 
		getContext().getContentResolver().notifyChange(arg0, null);
		
		switch (MATCHER.match(arg0)) {
		case PERSONS:
			System.out.println(" update persons的数据 " + arg1.getAsInteger("test"));
			break;
			
		case PERSON:
			System.out.println(" update person的数据");
			break;
		default:
			break;
		}
		return 0;
	}

}


// 提示一下内容观察者  arg0 URI 对应的数据已经更新了 
getContext().getContentResolver().notifyChange(arg0, null);

这段比较重要,不更新的话 后面的观察者就不知道数据发生了改变了。。。

在Activity中需要启动这个所谓的ContentProvider吗,不需要,他自己会动。。。


来看看另一个程序

主要是通过url来访问第一个程序中的数据 需要使用ContentResolver

值得注意的是的 ContentResolver的获取不是实例化对象,是使用的单例模式,resolver = context.getContentResolver();

然后它有一个query方法,去查询数据

resolver.query(uri, null, null, null, null);

这个url就是数据的位置,Uri uri = Uri.parse("content://com.example.test/person");这个url完全由第一个程序提供

开头固定为context://接下来就是第一个程序中的android:authorities,后面的参数也是有第一个程序中的ContentProvider给出的,可以理解为数据库表的名字


对于内容观察者的话,主要有几个步骤

首先自定义一个类继承ContentObserver实现里面的方法

然后使用ContentResolver注册监听

/*

* 注册数据变化监听

* 为指定的Uri注册一个ContentObserver派生类实例,当给定的Uri发生改变时,回调该实例对象去处理。
 * notifyForDescendents  为false 表示精确匹配,即只匹配该Uri;为true 表示可以同时匹配其派生的Uri
 */
resolver.registerContentObserver(uri, false, mObserver);


当然最后在生命周期结束的时候,记得要取消注册,要不然释放不掉会出问题的。。


@Override
protected void onDestroy() {

if(resolver != null) {
//取消内容观察者的监听
resolver.unregisterContentObserver(mObserver);
}

super.onDestroy();
}


主要源码如下

import java.util.Random;

import android.support.v7.app.ActionBarActivity;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.UriPermission;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends ActionBarActivity {
	
	
	public final static int ON_CHANGED = 0;
	
	public final static int DELIVER_SELF_NOTIFICATIONS = 1;

	Uri uri = Uri.parse("content://com.example.test/person");
	
	//内容解释者 获取数据
	ContentResolver resolver;
	
	//内容观察者 观察数据变化
	MyObserver mObserver;
	
	Button updateBt;
	
	//配合observer(内容观察者)的handler
	private Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			switch (msg.what) {
			case ON_CHANGED:
				System.out.println("ON_CHANGED");
				break;
			case DELIVER_SELF_NOTIFICATIONS:
				System.out.println("DELIVER_SELF_NOTIFICATIONS");
				break;
			default:
				break;
			}
		}
		
	};
	
	//自定义类容观察者
	private class MyObserver extends ContentObserver {

		Handler handler;
		
		public MyObserver(Handler handler) {
			super(handler);
			this.handler = handler;
		}

		@Override
		public boolean deliverSelfNotifications() {
			// TODO Auto-generated method stub
			//发条信息回去测试一下
			System.out.println("deliverSelfNotifications");
			handler.sendEmptyMessage(MainActivity.DELIVER_SELF_NOTIFICATIONS);
			return super.deliverSelfNotifications();
		}

		@Override
		public void onChange(boolean selfChange) {
			//发条信息回去测试一下
			System.out.println("onChange");
			handler.sendEmptyMessage(MainActivity.ON_CHANGED);
			super.onChange(selfChange);
		}

		@SuppressLint("NewApi")
		@Override
		public void onChange(boolean selfChange, Uri uri) {
			// TODO Auto-generated method stub
			System.out.println("onChange " + uri.toString());
			super.onChange(selfChange, uri);
		}
		
	}
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		//测试访问另一个应用程序中提供的数据
		testContentProvider();
		
		initView();
	}
	

	@SuppressLint("NewApi")
	private void testContentProvider() {
		/**
		 * 测试访问另一个应用程序中提供的数据
		 * 不能访问
		 * java.lang.RuntimeException:
		 *  Unable to start activity ComponentInfo{com.example.testand/com.example.testand.MainActivity}: 
		 *  java.lang.SecurityException:
		 *  Permission Denial: opening provider com.example.testcontentprovider.MyContentProvider from 
		 *  ProcessRecord{4141eca0 14823:com.example.testand/u0a10010}
		 *  (pid=14823, uid=10010) that is not exported from uid 10006
		 *  
		 */
		//不是使用new  使用getContentResolver()
		resolver = getContentResolver();
		
		resolver.query(uri, null, null, null, null);
		
		//内容观察者
		mObserver = new MyObserver(mHandler);
		/*
		 * 注册数据变化监听
		 * 
		 * 为指定的Uri注册一个ContentObserver派生类实例,当给定的Uri发生改变时,回调该实例对象去处理。
		 * notifyForDescendents  为false 表示精确匹配,即只匹配该Uri;为true 表示可以同时匹配其派生的Uri
		 */
		resolver.registerContentObserver(uri, false, mObserver);
		
	}
	
	/**
	 * 初始化update按钮
	 */
	private void initView() {	
		updateBt = (Button) findViewById(R.id.update_bt);
		updateBt.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View paramView) {
				//使用resolver修改数据
				changeData();
			}
		});
	}
	
	/**
	 * 修改数据 contentprovider中的数据
	 * 修改数据以触发前面定义内容观察者
	 * 注:这里是在修改 另一个程序的数据
	 * 如果有contentprovider有连接数据库的话直接修改稿数据库也可以达到同样的效果
	 */
	public void changeData() {
		if(resolver != null) {
			//更新数据之后 就会被上面注册的内容观察者发现
			ContentValues values = new ContentValues();
			try {
				Thread.sleep(500);
				values.put("test", new Random().nextInt());
				//修改这个uri对应的数据
				resolver.update(uri, values, null, null);
				
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	@Override
	protected void onDestroy() {
		
		if(resolver != null) {
			//取消内容观察者的监听
			resolver.unregisterContentObserver(mObserver);
		}
		
		super.onDestroy();
	}

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

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
}




两个程序源码 下载地址。。 http://download.youkuaiyun.com/download/asmots/8565937

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值