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的样例后面再加入。







本文转自mfrbuaa博客园博客,原文链接http://www.cnblogs.com/mfrbuaa/p/5120123.html,如需转载请自行联系原作者

相关文章
|
23天前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
2月前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
56 15
Android 系统缓存扫描与清理方法分析
|
18天前
|
运维 JavaScript jenkins
鸿蒙5.0版开发:分析CppCrash(进程崩溃)
在HarmonyOS 5.0中,CppCrash指C/C++运行时崩溃,常见原因包括空指针、数组越界等。系统提供基于posix信号机制的异常检测能力,生成详细日志辅助定位。本文详解CppCrash分析方法,涵盖异常检测、问题定位思路及案例分析。
43 4
|
18天前
|
运维 监控 JavaScript
鸿蒙next版开发:分析JS Crash(进程崩溃)
在HarmonyOS 5.0中,JS Crash指未处理的JavaScript异常导致应用意外退出。本文详细介绍如何分析JS Crash,包括异常捕获、日志分析和典型案例,帮助开发者定位问题、修复错误,提升应用稳定性。通过DevEco Studio收集日志,结合HiChecker工具,有效解决JS Crash问题。
38 4
|
23天前
|
存储 Unix Linux
进程间通信方式-----管道通信
【10月更文挑战第29天】管道通信是一种重要的进程间通信机制,它为进程间的数据传输和同步提供了一种简单有效的方法。通过合理地使用管道通信,可以实现不同进程之间的协作,提高系统的整体性能和效率。
|
23天前
|
消息中间件 存储 供应链
进程间通信方式-----消息队列通信
【10月更文挑战第29天】消息队列通信是一种强大而灵活的进程间通信机制,它通过异步通信、解耦和缓冲等特性,为分布式系统和多进程应用提供了高效的通信方式。在实际应用中,需要根据具体的需求和场景,合理地选择和使用消息队列,以充分发挥其优势,同时注意其可能带来的复杂性和性能开销等问题。
|
2月前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
45 1
|
2月前
|
存储 Linux Android开发
Android底层:通熟易懂分析binder:1.binder准备工作
本文详细介绍了Android Binder机制的准备工作,包括打开Binder驱动、内存映射(mmap)、启动Binder主线程等内容。通过分析系统调用和进程与驱动层的通信,解释了Binder如何实现进程间通信。文章还探讨了Binder主线程的启动流程及其在进程通信中的作用,最后总结了Binder准备工作的调用时机和重要性。
Android底层:通熟易懂分析binder:1.binder准备工作
|
2月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境的差异性分析
【10月更文挑战第8天】 本文旨在探讨Android和iOS两大移动操作系统在开发环境上的不同,包括开发语言、工具、平台特性等方面。通过对这些差异性的分析,帮助开发者更好地理解两大平台,以便在项目开发中做出更合适的技术选择。
|
2月前
|
存储 Python
Python中的多进程通信实践指南
Python中的多进程通信实践指南
23 0