Android中的跨进程通信方法实例及特点分析(二):ContentProvider

1.ContentProvider简单介绍

在Android中有些数据(如通讯录、音频、视频文件等)是要供非常多应用程序使用的。为了更好地对外提供数据,Android系统给我们提供了Content Provider使用,通过它能够訪问上面所说的数据。比如非常多音乐播放器中的扫描功能事实上就用到了Content Provider功能(当然,也有的播放器是自己去实现更底层的功能)。

这种优点是统一管理,比方添加了某个音频文件,底层就会将这种变化通知Content Provider。从而当应用程序訪问时就能够获得当前最新的数据。

当然,Android也同意我们定义自己的Content Provider。仅仅要继承它的基类,而且实现以下的方法就可以。

public boolean onCreate()
在创建ContentProvider时调用

public Cursor query(Uri, String[],
String, String[], String):用于查询指定Uri的ContentProvider,返回一个Cursor

public Uri insert(Uri, ContentValues):依据指定的Uri加入数据到ContentProvider中

public int update(Uri, ContentValues,
String, String[]):用于更新指定Uri的ContentProvider中的数据

public int delete(Uri, String,
String[]):依据Uri删除指定的数据

public String getType(Uri):用于返回指定的Uri中的数据的MIME类型

*假设操作的数据属于集合类型。那么MIME类型字符串应该以vnd.android.cursor.dir/开头。

比如:要得到全部p1记录的Uri为content://contacts/p1。那么返回的MIME类型字符串为"vnd.android.cursor.dir/p1"。

*假设要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头。

比如:要得到id为100的student记录的Uri为content://contacts/student/100,那么返回的MIME类型字符串应为"vnd.android.cursor.item/student"。

2.Uri简单介绍

一个标准的Uri为content://authority/path可分为下面三部分:

(1)content://:这个部分是ContentProvider规定的,就像http://代表Http这个协议一样。使用ContentProvider的协议是content://

(2)authorities:它在所在的Android系统必须是唯一的。由于系统就是通过它来决定操作或訪问哪个ContentProvider的。这与互联网上的网址必须唯一是一样的道理。

(3)path:资源路径。

显然,从上面的分析能够看出ContentProvider尽管也可实现跨进程通信。可是它适用的场景主要是与数据库相关。有时也可能是文本文件或XML等存储方式。

3.ContentResolver

假设仅仅是定义一个ContentProvider的话,没有不论什么意义,由于ContentProvider仅仅是内容提供者。它要被别的应用(进程)读取才有价值。与实现ContentProvder的方法相相应,使用ContentResolver相关的方法例如以下所看到的:

getContentResolver():Context类提供的。用于获取ContentResolver对象。

insert(Uri uri,ContentValues values):向Uri相应的ContentProvider中插入values相应的数据;

update(Uri uri,ContentValues values,String where,String[]selectionArgs):更新Uri相应的ContentProvider中where处的数据,当中selectionArgs是筛选參数。

query(Uri uri,String[]projection,String selection,String[]selectionArgs,String sortOrder):查询Uri相应的ContentProvider中where处的数据,当中selectionArgs是筛选參数。sortOrder是排序方式;

delete(Uri uri,String where,String[]selectionArgs):删除Uri相应的ContentProvider中where处的数据。当中selectionArgs是筛选參数。

4.UriMatcher

为了确定一个ContentProvider实际能处理的Uri,以及确定每一个方法中Uri參数所操作的数据。Android系统提供了UriMatcher工具类。它主要有例如以下两个方法:

(1)void addURI(String authority,String path,String code):该方法用于向UriMatcher对象注冊Uri。当中authority和path组合成一个Uri,而code则代表该Uri相应的标识码;

(2)int match(Uri uri):依据前面注冊的Uri来推断指定Uri相应的标识码。假设找不到匹配的标识码,该方法将会返回-1。

以下通过两个实例来解说ContentProvider的使用方法,第一个实例是自定义了一个ContentProvider而且在还有一个应用中读取它。第二个实例是读取当前手机中的联系人。

首先是第一个样例,项目结构例如以下图所看到的:

以下是各个类的代码,首先是常量的定义:

package com.android.student.utils;

import android.net.Uri;

/**
 * 这里定义了与ContentProvider相关的字符串以及Student相应的数据表中的字段
 * @author Bettar
 *
 */
public class StudentWords {

	//注意Manifest文件里的authorities属性要跟这里保持一致。
	public static final String AUTHORITY="com.android.student.provider";

	public static final Uri STUDENT_WITH_ID_URI=Uri.parse("content://"+AUTHORITY+"/student");
	public static final Uri STUDENT_URI=Uri.parse("content://"+AUTHORITY+"/student");

	public static final String TABLE_NAME="student";
	public static final String ID="id";
	public static final String NAME="name";
	public static final String SCORE="score";
	public static final String ADDR="address";

}

然后是数据库帮助类:

import com.android.student.utils.StudentWords;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;

public class StudentDbHelper extends SQLiteOpenHelper{

	private Context context;
	public StudentDbHelper(Context context,String name,int version)
	{
		super(context,name,null,version);
		this.context=context;
	}

	@Override
	public void onCreate(SQLiteDatabase db)
	{
		String createSQL="create table "+StudentWords.TABLE_NAME+"("+StudentWords.ID
				+" integer primary key autoincrement,"
				+StudentWords.NAME+" varchar,"
				+StudentWords.SCORE+" integer,"
				+StudentWords.ADDR+" varchar)";
		db.execSQL(createSQL);
	}
	@Override
	public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)
	{
		//升级版本号时,可能要运行表结构的改动之类,此处临时不考虑升级问题,因而仅仅是用Toast提示
		Toast.makeText(context,
				"newVersion:"+newVersion+" will replace oldVersion:"+oldVersion,
				Toast.LENGTH_LONG).show();
	}

}

最后是ContentProvider类的定义:

package com.android.student.provider;

import com.android.student.database.StudentDbHelper;

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;
import android.provider.UserDictionary.Words;

import com.android.student.utils.StudentWords;

public class StudentProvider extends ContentProvider{

	private static final String TAG="StudentProvider";
	private static UriMatcher matcher=new UriMatcher(UriMatcher.NO_MATCH);
	private static final int STUDENT_WITH_ID=1;
    private static final int STUDENT=2;

    private StudentDbHelper dbHelper;

    static
    {
    	matcher.addURI(StudentWords.AUTHORITY,"student/#",STUDENT_WITH_ID);
    	//matcher.addURI(StudentWords.AUTHORITY, "student", SINGLE_STUDENT);
    	//注意当中的#为通配符
    	matcher.addURI(StudentWords.AUTHORITY, "student", STUDENT);
    }

    @Override
    public boolean onCreate()
    {
    	dbHelper=new StudentDbHelper(this.getContext(),"student.db3",1);
    	return true;
    }

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

    /**
     * 由单一的selection这一个筛选条件组合成包括id的复杂筛选条件
     * @param uri
     * @param selection
     * @return
     */
    private String getComplexSelection(Uri uri,String selection)
    {
    	long id=ContentUris.parseId(uri);
    	String complexSelection=StudentWords.ID+"="+id;
    	if(selection!=null&&!"".equals(selection))
    	{
    		complexSelection+=" and "+selection;
    	}
    	return complexSelection;
    }

	@Override
	public int delete(Uri uri,String selection,String[]selectionArgs) {

		SQLiteDatabase db=dbHelper.getReadableDatabase();
		int num=0;
		switch(matcher.match(uri))
		{
		case STUDENT_WITH_ID:
			String complexSelection=getComplexSelection(uri,selection);
			num=db.delete(StudentWords.TABLE_NAME, complexSelection, selectionArgs);
			break;
		case STUDENT:
			num=db.delete(StudentWords.TABLE_NAME,selection,selectionArgs);
			break;
		default:
			throw new IllegalArgumentException("Unknown Uri:"+uri);
		}
		//通知数据已经改变
		getContext().getContentResolver().notifyChange(uri, null);
		return num;
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		SQLiteDatabase db=dbHelper.getReadableDatabase();
		switch(matcher.match(uri))
		{

			case STUDENT_WITH_ID:
			case STUDENT:
				long rowId=db.insert(StudentWords.TABLE_NAME, StudentWords.ID,values);
				if(rowId>0)
				{
					Uri studentUri=ContentUris.withAppendedId(uri, rowId);
					//假设设置了观察者的话。要通知全部观察者
					getContext().getContentResolver().notifyChange(studentUri, null);
					return studentUri;
				}
				break;
			default:
				throw new IllegalArgumentException("Unknow Uri:"+uri);
		}
	    return null;
	}

	/**
	 * 注意当中的projection事实上是columns,即列名数组
	 */
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		SQLiteDatabase db=dbHelper.getReadableDatabase();
		switch(matcher.match(uri))
		{
		//这事实上是包括id信息的情况
		case STUDENT_WITH_ID:
			String complexSelection=getComplexSelection(uri,selection);
			return db.query(StudentWords.TABLE_NAME,projection,complexSelection,selectionArgs,null,null,sortOrder);
		//这是不带数字的情况。可是也未必就是好多个。一个也能够。
		case STUDENT:
			return db.query(StudentWords.TABLE_NAME
					,projection,selection,selectionArgs,null,null,sortOrder);
		default:
				throw new IllegalArgumentException("Unknow Uri"+uri);
		}

	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs)
	{
		SQLiteDatabase db=dbHelper.getWritableDatabase();
		int num=0;
		switch(matcher.match(uri))
		{
		case STUDENT_WITH_ID:
			String complexSelection=getComplexSelection(uri,selection);
			num=db.update(StudentWords.TABLE_NAME, values, complexSelection, selectionArgs);
			break;
		case STUDENT:
			num=db.update(StudentWords.TABLE_NAME, values, selection,selectionArgs);
			break;
			default:
				throw new IllegalArgumentException("Unknow Uri:"+uri);
		}

		getContext().getContentResolver().notifyChange(uri,null);
		return num;
	}

}

当然,要记得在Manifest文件里增加ContentProvider的声明:

 <provider android:name="com.android.student.provider.StudentProvider"
            android:authorities="com.android.student.provider"
            android:exported="true"/>

以下是对ContentResolver的样例,首先项目中的文件结构例如以下:

然后是layout文件 :

 <ScrollView
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      >
<LinearLayout
    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"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="下面为输入參数:"
        />
    <EditText
        android:id="@+id/nameET"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="Name"
        />
    <EditText
        android:id="@+id/scoreET"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="Score"
        />
    <EditText
        android:id="@+id/addrET"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="Address"
        />
    <Button
        android:id="@+id/insertButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Insert"
        />
      <TextView
        android:id="@+id/searchTv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="下面为查询參数:"
        />
      <EditText
          android:id="@+id/inputNameET"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:hint="Input name"
          />
      <Button
          android:id="@+id/searchButton"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:text="Search"
          />
       <TextView
        android:id="@+id/searchResultTv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="下面为Update參数"
        />
       <EditText
          android:id="@+id/inputIdET"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:hint="Input id"
          />
      <Button
          android:id="@+id/updateButton"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:text="Update"
          />
       <TextView
        android:id="@+id/searchResultTv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="下面为删除參数"
        />
       <EditText
          android:id="@+id/inputIdForDeleteET"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:hint="Input id"
          />
      <Button
          android:id="@+id/deleteButton"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:text="Delete"
          />

</LinearLayout>
 </ScrollView> 

项目中也有StudentWords这个类,由于与上面的StudentWords全然同样,故此处不再列出,MainActivity的代码例如以下:

package com.android.student.studentcontentresolver;

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

import com.android.student.utils.StudentWords;

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.view.Menu;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import android.view.View;
public class MainActivity extends Activity implements View.OnClickListener{

	ContentResolver contentResolver;

	private EditText nameET,scoreET,addressET;
	private Button insertButton;
	private EditText inputNameET;
	private Button searchButton;

	private EditText inputIdForUpdateET,inputIdForDeleteET;
	private Button updateButton,deleteButton;

	private void initView()
	{
		nameET=(EditText)findViewById(R.id.nameET);
		scoreET=(EditText)findViewById(R.id.scoreET);
		addressET=(EditText)findViewById(R.id.addrET);
		insertButton=(Button)findViewById(R.id.insertButton);
		inputNameET=(EditText)findViewById(R.id.inputNameET);
		searchButton=(Button)findViewById(R.id.searchButton);

		inputIdForUpdateET=(EditText)findViewById(R.id.inputIdET);
		inputIdForDeleteET=(EditText)findViewById(R.id.inputIdForDeleteET);

		updateButton=(Button)findViewById(R.id.updateButton);
		deleteButton=(Button)findViewById(R.id.deleteButton);

		insertButton.setOnClickListener(this);
		searchButton.setOnClickListener(this);
		updateButton.setOnClickListener(this);
		deleteButton.setOnClickListener(this);
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		contentResolver=getContentResolver();
		initView();
	}

	@Override
	public void onClick(View view)
	{
		switch(view.getId())
		{
		case R.id.insertButton:
			insert();
			break;
		case R.id.searchButton:
			query();
			break;
		case R.id.updateButton:
			update();
			break;
		case R.id.deleteButton:
			delete();
			break;
			default:
				break;
		}
	}

	private void insert()
	{
		String name=nameET.getText().toString();
		String score=scoreET.getText().toString();
		String addr=addressET.getText().toString();
		ContentValues values=new ContentValues();
		values.put(StudentWords.NAME, name);
		values.put(StudentWords.SCORE, new Integer(score));
		values.put(StudentWords.ADDR, addr);

		//contentResolver.insert(StudentWords.SINGLE_STUDENT_URI, values);
		//一个是多个的特例,所以此处用MANY_STUDENTS_URI就可以。

contentResolver.insert(StudentWords.STUDENT_URI, values);

		Toast.makeText(getBaseContext(), "加入学生信息成功", Toast.LENGTH_SHORT).show();		

	}

	private void query()
	{
		String name=inputNameET.getText().toString();
		//Cursor cursor=contentResolver.query(uri, projection, selection, selectionArgs, sortOrder)
		Cursor cursor=contentResolver.query(StudentWords.STUDENT_URI, null, "name like ?

or address like ?", new String[]{"%"+name+"%","%"+name+"%"}, null);
		Toast.makeText(getBaseContext(), getResult(cursor), Toast.LENGTH_LONG).show();

	}

	private void update()
	{
		//Uri updateUri=StudentWords.SINGLE_STUDENT_URI
		//更新id值为id的记录
		Integer id=new Integer(inputIdForUpdateET.getText().toString());
		Uri updateUri=ContentUris.withAppendedId(StudentWords.STUDENT_WITH_ID_URI,id);
		ContentValues values=new ContentValues();
		values.put(StudentWords.NAME,"VictorWang");
		contentResolver.update(updateUri, values, null, null);
	}

	private void delete()
	{
		//删除id值为id的记录
		Integer id=new Integer(inputIdForDeleteET.getText().toString());
		Uri deleteUri=ContentUris.withAppendedId(StudentWords.STUDENT_WITH_ID_URI, id);
		contentResolver.delete(deleteUri, null, null);
	}

	private List<String>convertCursor2List(Cursor cursor)
	{
	    List<String>result=new ArrayList<String>();
		while(cursor.moveToNext())
		{
			result.add(cursor.getString(1)+"  ");
			result.add(cursor.getString(2)+"  ");
			result.add(cursor.getString(3)+"  ");
		}
		return result;
	}

	private String getResult(Cursor cursor)
	{
		StringBuilder sb=new StringBuilder();
		while(cursor.moveToNext())
		{
		     sb.append(cursor.getString(1)+"  ");
		     sb.append(cursor.getString(2)+"  ");
		     sb.append(cursor.getString(3)+"\n");
		}
		return sb.toString();
	}

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

}

执行结果例如以下:

今天先写到这里,联系人和ContentObserver的样例后面再加入。

时间: 2024-10-17 15:15:53

Android中的跨进程通信方法实例及特点分析(二):ContentProvider的相关文章

Android中的跨进程通信方法实例及特点分析(一):AIDL Service

转载请注明出处:http://blog.csdn.net/bettarwang/article/details/40947481 最近有一个需求就是往程序中加入大数据的采集点,但是因为我们的Android程序包含两个进程,所以涉及到跨进程通信的问题.现将Android中的跨进程通信方式总结如下. Android中有4种跨进程通信方式,分别是利用AIDL Service.ContentProvider.Broadcast.Activity实现. 1.利用AIDL Service实现跨进程通信 这是

Android中AIDL实现进程通信(附源码下载)

AIDL概述 之前的博客<Android中通过Messenger与Service实现进程间双向通信>演示了如何通过Messenger实现与Service进行跨进程通信,即IPC.但用Messenger实现的IPC存在一点不足:Service内部维护着一个Messenger,Messenger内部又维护着一个Hanlder,当多个client向该Service发送Message时,这些Message需要依次进入Hanlder的消息队列中,Hanlder只能处理完一个Message之后,再从消息队

Android随笔之——跨进程通信(一) Activity篇

在Android应用开发中,我们会碰到跨进程通信的情况,例如:你用QQ通讯录打电话的时候会调用系统的拨号应用.某些新闻客户端可以将新闻分享到QQ.微信等应用,这些都是跨进程通信的情况.简而言之,就是一个应用调用了另一个应用,并传递了一定的数据.在Android中有Activity.Content Provider.BroadcastReceiver.Service四大组件,巧的是Android进程间通信也离不开这四大组件(也有说是AIDL机制,但是现在还分不清Service和AIDL的区别(/

Android中的跨进程调用技术AIDL

什么是AIDL Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信. 为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现. 与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口. Android的四大组件中的三个(Activity.Broadcast

android自动化测试解决跨进程通信问题

大概用这些吧: IPC  Handler    Messager   Bundle  service(binder)   messageconnection ,thead.getXXX.getId 注意message的replayTo方法和oxxxMessage方法可以发送和获得message,message中getData()可以获得bundle对象, bundle可以封装参数key-value,如果是复杂的object对象参数,那么可以考虑intent的父接口重写几个方法实现

Android基础——Messenger在跨进程通信中的使用

Messenger在跨进程通信中的使用 事先说明: 本人也是个初学者,所以本文是从初学者的角度入手,如果有不妥的地方请留言教导我,谢谢. 本篇文章主要针对讲解Messenger的使用和Messenger在应用层上的原理解析和Messenger在服务端的回复. 什么是Messenger? Messenger可以翻译为信使,Messenger是一种轻量级的IPC方案,通过它可以在不同进程中传递Message对象,在Message中放入我们需要传递的数据,就可以实现数据的进程间传递了. 步骤一:Mes

Android基础笔记(十二)- 使用AIDL来进行跨进程通信

绑定服务调用服务里方法的过程 音乐盒小案例 利用服务注册特殊广播接收者 使用AIDL来进行跨进程通信 绑定服务调用服务里方法的过程 整个Activty绑定Service并调用其中方法的过程可以体现为下面的一张图,其中的核心是通过借助中间人IBinder来达到调用Service中方法的目的.. 接下来在明确一下调用过程的代码步骤: ①首先服务里有一个方法需要被调用 ②定义一个中间人对象(继承Bidner类的内部类MyBinder) ③在onBind方法中把我们自己定义的中间人返回MyBinder

【朝花夕拾】一篇文章搞懂Android跨进程通信

前言 只要是面试中高级工程师岗位,Android跨进程通信就是最受面试官青睐的知识点.Android系统的运行由大量相互独立的进程相互协助来完成的,所以Android进程间通信问题,是做好Android开发高级工程师必须要跨过的一道坎.如果您还对这方面的知识还做不到如数家珍,那就和我一起来攻克它吧! 本文主要包含了如下内容: 其行文脉络大致如下,希望能加深读者对这方面内容的记忆:(1)Android基于Linux系统,所以先说系统进程相关知识和Linux IPC.(2)总结Android的IPC

android 远程Service以及AIDL的跨进程通信

在Android中,Service是运行在主线程中的,如果在Service中处理一些耗时的操作,就会导致程序出现ANR. 但如果将本地的Service转换成一个远程的Service,就不会出现这样的问题了. 转换成远程Service非常简单,只需要在注册Service的时候将他的android:process的属性制定成 :remote就可以了. 重新运行项目,你会发现,不会出现ANR了. 为什么将MyService转换成远程Service后就不会导致程序ANR了呢?这是由于,使用了远程Serv