Android ContentProvider
应用场景:
在Android官方指出的Android的数据存储方式总共有五种,分别是:Shared Preferences、网络存储、文件存储、外储存储、SQLite。但是我们知道一般这些存储都只是在单独的一个应用程序之中达到一个数据的共享,而且这些知识在前面我都有介绍,有时候我们需要操作其他应用程序的一些数据,例如我们需要操作系统里的媒体库、通讯录等,这时我们就可能通过ContentProvider来满足我们的需求了
ContentProvider概述:
ContentProvider向我们提供了我们在应用程序之前共享数据的一种机制,而我们知道每一个应用程序都是运行在不同的应用程序的,数据和文件在不同应用程序之间达到数据的共享不是没有可能,而是显得比较复杂,而正好Android中的ContentProvider则达到了这一需求,比如有时候我们需要操作手机里的联系人,手机里的多媒体等一些信息,我们都可以用到这个ContentProvider来达到我们所需。
如何理解ContentProvider
上面说了一大堆ContentProvider的概述,可能大家还是不太特别理解ContentProvider到底是干什么的,那么我们以一个网站来形象的描述这个ContentProvider吧,可以这么理解为ContentProvider就是一个网站,它向我们去访问网站这里的数据达到了一种可能,它就是一个向外提供数据的接口。那么既然它是向外提供数据,我们有时候也需要去修改数据,这时我们就可以用到另外一个类来实现这个对数据的修改ContentResolver类,这个类就可以通过URI来操作数据。至于这些类的作用及描述在下面就会一一的介绍到。
如何实现ContentProvider
理解了ContentProvider类,那么我们怎么去实现ContentProvider呢?怎么样让外部程序去访问或者修改我们的数据呢?这样的一个操作其实是非常简单的,我们只需要下面的两步就可以实现ContentProvider
1、 编写一个实现ContentProvider的子类,这个子类必须要实现一些必须实现的方法,在ContentProvider类里面,有一系列针对于数据的增、删、改、查等方法
2、 ContentProvider也是Android中的四大组件,因此也必须在AndroidMainfest.xml中完成对ContentProvider的注册。注册方式为:
与ContentProvider相关操作的类介绍
从Uri谈起
什么是Uri?
Uri是指通用资源标志符
A:前缀表明数据受控于一个内容提供者。它从不修改,也就是schema
B:是指在AndroidMainfest.xml中我们注册的provider中的android:authorities属性所对应的
C:具体操作于哪个条目
D:具体指定到哪个条目下的哪条记录
再看它的类结构和常用方法:
Uri
在这个里它是没有构造方法的,它通常通过下面的这个方法来返回一个Uri对象
方法名称 |
描述 |
public static Uri parse (String uriString) |
通过一个传入的字符串来构造一个Uri对象 |
熟悉完Uri类再看与之相关的另外两个类
UriMatcher类:
因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris 。掌握它们的使用,会便于我们的开发工作。
先看下它比较常用的几个方法:
方法名称 |
描述 |
public void addURI (String authority, String path, int code) |
往UriMatcher类里添加一个拼凑的Uri,在此我们可以理解为UriMatcher为一个Uri的容器,为个容器里面包含着我们即将可能要操作的Uri,它用于我们业务逻辑的处理,特别是第三个参数code,如果通过下面的match()方法匹配成功就返回这个code值 |
public int match (Uri uri) |
与传入的Uri匹配,它会首先与找我们之前通过addURI方法添加进来的Uri匹配,如果匹配成功就返回之前我们设置的code值,否则返回一个UriMatcher.NO_MATCH常量值为-1 |
熟悉完上面的方法,那么我们再来看它如何使用:
UriMatcher类用于匹配Uri,它的用法如下:
UriMatcher类的用法
首先第一步把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content:// com.jiahui.provider.myprovider/person路径,返回匹配码为1
sMatcher.addURI(“com.jiahui.provider.myprovider”, “person”, 1);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配content:// com.jiahui.provider.myprovider /person/230路径,返回匹配码为2
sMatcher.addURI(“com.jiahui.provider.myprovider”, “person/#”, 2);//#号为通配符
switch (sMatcher.match(Uri.parse("content:// com.jiahui.provider.myprovider /person/10" ) )) {
case 1
break;
case 2
break;
default://不匹配
break;
}
注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://cn.itcast.provider.personprovider/person路径,返回的匹配码为1
再看另外一个工具类
ContentUris:
它用于在Uri后面追加一个ID或者解析出传入的Uri所带上的ID值,常用的两个方法如下:
方法名称 |
描述 |
public static Uri withAppendedId (Uri contentUri, long id) |
用于为路径加上ID部分 |
public static long parseId (Uri contentUri) |
从路径中获取ID部分 |
熟悉完上面所提及的相关的类,接下来我们再看这个ContentProvider核心类
ContentProvider
常用方法
方法名称 |
描述 |
public abstract boolean onCreate () |
在ContentProvider创建后被调用。 |
public abstract Uri insert (Uri uri, ContentValues values) |
根据Uri插入values对就的数据 |
public abstract int delete (Uri uri, String selection, String[] selectionArgs) |
根据Uri删除selection指定的条件所匹配的全部记录 |
public abstract int update (Uri uri, ContentValues values, String selection, String[] selectionArgs) |
根据Uri修改selection指定的条件所匹配的全部记录 |
public abstract Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) |
根据Uri查询出selection指定的条件所匹配的全部记录,并且可以指定查询哪些列(projection),以什么方式(sortOrder)排序 |
public abstract String getType (Uri uri) |
返回当前Uri所数据的MIME类型,如果该Uri对应的数据可能包括多条记录,那么MIME类型字符串就是以vnd.android.cursor.dir/开头,如果Uri对应的数据只包含一条记录,那么MIME类型字符串就是以vnd.android.cursor.item/开头 |
既然我们知道了ContentProvider类是向外提供数据的一种机制,那么在之前我们也说过要想来操作这个对外提供的数据,我们就用到了另外一个类:
ContentResolver
在这个类里面也定义了一系列的增、删、改、查方法,与其ContentProvider定义的方法基本上相同,在此不再复核。读者可以自己查阅相关文档。
可能大家在这里还是有点理不清这些类的一些关系,特别是ContentResolver与ContentProvider与Uri类的关系,那么我上张图吧,或许对大家有所帮助:
好了熟悉完上面所述的这么多类那么我们就在实践中见证真理吧:
实例:
实现效果:
代码实现:
先开发我们自己的ContentProvider:
千万别忘记了要在AndroidMainfest.xml文件中注册这个组件哦:
然后在一个主Activity编写一些实现代码:
package com.jiahui.provider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
import com.jiahui.model.Person;
public class ContentProviderDemoActivity extends Activity {
private Button btnadd, btnqueryall;
private EditText edtname, edtage;
private ListView lvall;
private List<Person> persons;
private SimpleAdapter simpleAdapter;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
List<Map<String, Object>> data = (List<Map<String, Object>>) msg.obj;
System.out.println(data.size());
simpleAdapter = new SimpleAdapter(
ContentProviderDemoActivity.this, data, R.layout.list_item,
new String[] { "id", "name", "age" }, new int[] {
R.id.tvId, R.id.tvname, R.id.tvage });
lvall.setAdapter(simpleAdapter);
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
persons = new ArrayList<Person>();
btnqueryall = (Button) this.findViewById(R.id.btnqueryall);
btnadd = (Button) this.findViewById(R.id.btnadd);
edtname = (EditText) this.findViewById(R.id.edtname);
edtage = (EditText) this.findViewById(R.id.edtage);
lvall = (ListView) this.findViewById(R.id.lvall);
btnadd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ContentResolver contentResolver = ContentProviderDemoActivity.this
.getContentResolver();
Uri url = Uri
.parse("content://com.jiahui.provider.myprovider/person");
ContentValues values = new ContentValues();
values.put("name", edtname.getText().toString());
values.put("age", edtage.getText().toString());
Uri result = contentResolver.insert(url, values);
System.out.println(result.toString());
if (ContentUris.parseId(result)>0) {
Toast.makeText(ContentProviderDemoActivity.this, "添加成功", Toast.LENGTH_LONG).show();
//添加成功后再启动线程查询
MyThread thread = new MyThread(ContentProviderDemoActivity.this);
thread.start();
}
}
});
//查询所有
btnqueryall.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyThread thread = new MyThread(ContentProviderDemoActivity.this);
thread.start();
}
});
lvall.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// Toast.makeText(ContentProviderDemoActivity.this, position,
// Toast.LENGTH_LONG).show();
System.out.println("position:" + position);
Person person = persons.get(position);
Bundle bundle = new Bundle();
bundle.putInt("id", person.getId());
bundle.putString("name", person.getName());
bundle.putInt("age", person.getAge());
Intent intent = new Intent(ContentProviderDemoActivity.this,
ItemActivity.class);
intent.putExtra("item", bundle);
startActivityForResult(intent, 1);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode==2) {
MyThread thread = new MyThread(ContentProviderDemoActivity.this);
thread.start();
}
}
class MyThread extends Thread {
Context context;
public MyThread(Context context) {
//一定要清空。否则会 有问题,每执行一次都会把之前的全部的item加进去
persons.clear();
lvall.setAdapter(null);
this.context = context;
}
@Override
public void run() {
Uri url = Uri
.parse("content://com.jiahui.provider.myprovider/person");
Cursor cursor = context.getContentResolver().query(url,
new String[] { "_id", "name", "age" }, null, null, "_id");
while (cursor.moveToNext()) {
// System.out.println("_id:"
// + cursor.getInt(cursor.getColumnIndex("_id")));
// System.out.println("name:"
// + cursor.getString(cursor.getColumnIndex("name")));
// System.out.println("age:"
// + cursor.getInt(cursor.getColumnIndex("age")));
Person person = new Person();
person.setId(cursor.getInt(cursor.getColumnIndex("_id")));
person.setName(cursor.getString(cursor.getColumnIndex("name")));
person.setAge(cursor.getInt(cursor.getColumnIndex("age")));
persons.add(person);
}
cursor.close();
List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
Map<String, Object> map=null;
for (int i = 0; i < persons.size(); i++) {
map = new HashMap<String, Object>();
map.put("id", persons.get(i).getId());
map.put("name", persons.get(i).getName());
map.put("age", persons.get(i).getAge());
data.add(map);
}
if (data.size()>=persons.size()) {
}
Message msg = handler.obtainMessage();
msg.obj = data;
handler.sendMessage(msg);
}
}
}
ItemActivity代码:
在手机开发中SQLite的使用demo
简单的bean类
package com.easyway.android.sql; /** * 普通JavaBean * @author longgangbai * */ public class CityBean { public static final String ID = "_id"; public static final String CITY = "city"; public static final String CODE = "code"; private String id; private String city; private String code; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } }
界面展现类:
package com.easyway.android.sql; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; /** * * * 本例实现SQLite数据库增加、删除、修改、模糊查询操作。这里不是最好的实现方法, * 如想研究SQL如何封装,请详细查看SQLiteDatebase类. * 查看SQL语句:String sql = SQLiteQueryBuilder.buildQueryString(); * * * @author longgangbai */ public class AndroidSQL extends Activity { private static String DB_NAME = "mycity.db"; private static int DB_VERSION = 1; private static int POSTION; private ListView listview; private Cursor cursor; private SQLiteDatabase db; private SQLiteHelper dbHelper; private ListAdapter listAdapter; private EditText etCity; private EditText etCode; private Button bt_add; private Button bt_modify; private Button bt_query; private List<CityBean> cityList = new ArrayList<CityBean>(); /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); etCity = (EditText) findViewById(R.id.etCity); etCode = (EditText) findViewById(R.id.etCode); bt_add = (Button) findViewById(R.id.bt_add); bt_modify = (Button) findViewById(R.id.bt_modify); bt_query = (Button) findViewById(R.id.bt_query); try{ /* 初始化并创建数据库 */ dbHelper = new SQLiteHelper(this, DB_NAME, null, DB_VERSION); /* 创建表 */ db = dbHelper.getWritableDatabase(); //调用SQLiteHelper.OnCreate() /* 查询表,得到cursor对象 */ cursor = db.query(SQLiteHelper.TB_NAME, null, null, null, null, null, CityBean.CODE + " DESC"); cursor.moveToFirst(); while(!cursor.isAfterLast() && (cursor.getString(1) != null)){ CityBean city = new CityBean(); city.setId(cursor.getString(0)); city.setCity(cursor.getString(1)); city.setCode(cursor.getString(2)); cityList.add(city); cursor.moveToNext(); } }catch(IllegalArgumentException e){ //当用SimpleCursorAdapter装载数据时,表ID列必须是_id,否则报错column '_id' does not exist e.printStackTrace(); //当版本变更时会调用SQLiteHelper.onUpgrade()方法重建表 注:表以前数据将丢失 ++ DB_VERSION; dbHelper.onUpgrade(db, --DB_VERSION, DB_VERSION); // dbHelper.updateColumn(db, SQLiteHelper.ID, "_"+SQLiteHelper.ID, "integer"); } listview = (ListView)findViewById(R.id.listView); listAdapter = new ListAdapter(); listview.setAdapter(listAdapter); listview.setOnItemClickListener(new ListView.OnItemClickListener(){ @Override public void onItemClick(AdapterView<?> parent, View view, int postion, long arg3) { setSelectedValues(postion); } }); /* 插入表数据并ListView显示更新 */ bt_add.setOnClickListener(new Button.OnClickListener(){ @Override public void onClick(View arg0) { if(etCity.getText().length() > 1 && etCode.getText().length() >1){ ContentValues values = new ContentValues(); values.put(CityBean.CITY, etCity.getText().toString().trim()); values.put(CityBean.CODE, etCode.getText().toString().trim()); //插入数据 用ContentValues对象也即HashMap操作,并返回ID号 Long cityID = db.insert(SQLiteHelper.TB_NAME, CityBean.ID, values); CityBean city = new CityBean(); city.setId(""+cityID); city.setCity(etCity.getText().toString().trim()); city.setCode(etCode.getText().toString().trim()); cityList.add(city); listview.setAdapter(new ListAdapter()); resetForm(); } } }); /* 查询表,模糊条件查询 */ bt_query.setOnClickListener(new Button.OnClickListener(){ @Override public void onClick(View view) { cityList.removeAll(cityList); String sql = null; String sqlCity = etCity.getText().length() > 0 ? CityBean.CITY + " like '%" + etCity.getText().toString().trim() + "%'" : ""; String sqlCode = etCode.getText().length() > 0 ? CityBean.CITY + " like '%" + etCity.getText().toString().trim() + "%'" : ""; if( (!"".equals(sqlCity)) && (!"".equals(sqlCode)) ){ sql = sqlCity + " and" + sqlCode; }else if(!"".equals(sqlCity)){ sql = sqlCity; }else if(!"".equals(sqlCode)){ sql = sqlCode; } cursor = db.query(true, SQLiteHelper.TB_NAME, new String[]{CityBean.ID, CityBean.CITY, CityBean.CODE}, sql, null, null, null, null, null); cursor.moveToFirst(); while(!cursor.isAfterLast() && (cursor.getString(1) != null)){ CityBean city = new CityBean(); city.setId(cursor.getString(0)); city.setCity(cursor.getString(1)); city.setCode(cursor.getString(2)); cityList.add(city); cursor.moveToNext(); } listview.setAdapter(new ListAdapter()); resetForm(); } }); /* 修改表数据 */ bt_modify.setOnClickListener(new Button.OnClickListener(){ @Override public void onClick(View arg0) { ContentValues values = new ContentValues(); values.put(CityBean.CITY, etCity.getText().toString().trim()); values.put(CityBean.CODE, etCode.getText().toString().trim()); db.update(SQLiteHelper.TB_NAME, values, CityBean.ID + "=" + cityList.get(POSTION).getId(), null); cityList.get(POSTION).setCity(etCity.getText().toString().trim()); cityList.get(POSTION).setCode(etCode.getText().toString().trim()); listview.setAdapter(new ListAdapter()); resetForm(); } }); } /* 设置选中ListView的值 */ public void setSelectedValues(int postion){ POSTION = postion; etCity.setText(cityList.get(postion).getCity()); etCode.setText(cityList.get(postion).getCode()); } /* 重值form */ public void resetForm(){ etCity.setText(""); etCode.setText(""); } @Override protected void onDestroy() { db.delete(SQLiteHelper.TB_NAME, null, null); super.onDestroy(); } private class ListAdapter extends BaseAdapter{ public ListAdapter(){ super(); } @Override public int getCount() { return cityList.size(); } @Override public Object getItem(int postion) { return postion; } @Override public long getItemId(int postion) { return postion; } @Override public View getView(final int postion, View view, ViewGroup parent) { view = getLayoutInflater().inflate(R.layout.listview, null); TextView tv = (TextView) view.findViewById(R.id.tvCity); tv.setText("" + cityList.get(postion).getCity()); TextView bu = (TextView) view.findViewById(R.id.btRemove); bu.setText(R.string.delete); bu.setId(Integer.parseInt(cityList.get(postion).getId())); /* 删除表数据 */ bu.setOnClickListener(new Button.OnClickListener(){ @Override public void onClick(View view) { try{ db.delete(SQLiteHelper.TB_NAME, CityBean.ID + "=" + view.getId(), null); cityList.remove(postion); listview.setAdapter(new ListAdapter()); }catch(Exception e){ e.printStackTrace(); } } }); return view; } } }
SQLite实现类:
package com.easyway.android.sql; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteDatabase.CursorFactory; /** * 实现对表的创建、更新、变更列名操作 * * 在Android 中针对少量数据的操作在SQLite操作实现相关功能功能 * ,但是必须继承SQLiteOpenHelper,实现相关的功能。 * * * @author longgangbai * */ public class SQLiteHelper extends SQLiteOpenHelper { public static final String TB_NAME = "citys"; public SQLiteHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } /** * 创建新表 */ @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE IF NOT EXISTS " + TB_NAME + "(" + CityBean.ID + " integer primary key," + CityBean.CITY + " varchar," + CityBean.CODE + " integer"+ ")"); } /** * 当检测与前一次创建数据库版本不一样时,先删除表再创建新表 */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + TB_NAME); onCreate(db); } /** * 变更列名 * @param db * @param oldColumn * @param newColumn * @param typeColumn */ public void updateColumn(SQLiteDatabase db, String oldColumn, String newColumn, String typeColumn){ try{ db.execSQL("ALTER TABLE " + TB_NAME + " CHANGE " + oldColumn + " "+ newColumn + " " + typeColumn ); }catch(Exception e){ e.printStackTrace(); } } }