自定义ContentProvider -电脑资料

电脑资料 时间:2019-01-01 我要投稿
【www.unjs.com - 电脑资料】

    ContentProvider作为安卓的四大组件之一,在看开发中用到的频率远不如其他三个,以至于我都把这个东西给忘了,最近由于工作原因,不得不重新拾起来总结一下,那么今天就来说说自定义ContentProvider吧,

自定义ContentProvider

    今天的案例是这样的,我们有两个App,一个叫做cpHost,作为内容提供者;另外一个叫做cpTest,专门用来操作这个cpHost中的数据。我们的cpHost中有一个数据库,该数据库中有一个User表,我们通过内容提供者将这个User表共享出去,供其他App调用。下面我们就来看看怎么实现这样一个效果。

    既然用到数据库存储用户表,那么毫无疑问我们需要一个DBHelper,如下:

public class DBHelper extends SQLiteOpenHelper {	public static final String USERTABLE = user_table;	public DBHelper(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  + USERTABLE				+ (_id INTEGER PRIMARY KEY AUTOINCREMENT,USERNAME TEXT,NICKNAME TEXT,GENDER TEXT,AGE TEXT););	}	@Override	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {	}}
我们创建一个用户表,然后在在MainActivity中将该表中的数据显示出来,这样方便我们后面看到操作效果,于是我们需要一个listview,看看MainActivity的布局文件:

<relativelayout android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"><li></listview></relativelayout>

    MainActivity.java:

public class MainActivity extends Activity {	private ListView lv;	private DBHelper helper;	@Override	protected void onCreate(Bundle savedInstanceState) {		super.onCreate(savedInstanceState);		setContentView(R.layout.activity_main);		lv = (ListView) this.findViewById(R.id.lv);	}	public List<user>getData() {		helper = new DBHelper(this, lenve.db, null, 1);		SQLiteDatabase db = helper.getWritableDatabase();		List<user>list = new ArrayList<user>();		Cursor c = db.rawQuery(SELECT * FROM  + DBHelper.USERTABLE, null);		User u = null;		while (c.moveToNext()) {			String username = c.getString(c.getColumnIndex(USERNAME));			String nickname = c.getString(c.getColumnIndex(NICKNAME));			String gender = c.getString(c.getColumnIndex(GENDER));			String age = c.getString(c.getColumnIndex(AGE));			u = new User(username, nickname, gender, age);			list.add(u);		}		c.close();		return list;	}	@Override	protected void onResume() {		super.onResume();		lv.setAdapter(new MyAdapter(this, getData()));	}}</user></user></user>
这里之所以把给ListView设置Adapter的方法放在onResume()方法中执行,主要是为了测试方便,没有其他意思,那么我们在来看看UserBean:

public class User {	private String username;	private String nickname;	private String gender;	private String age;	public String getUsername() {		return username;	}	public void setUsername(String username) {		this.username = username;	}	public String getNickname() {		return nickname;	}	public void setNickname(String nickname) {		this.nickname = nickname;	}	public String getGender() {		return gender;	}	public void setGender(String gender) {		this.gender = gender;	}	public String getAge() {		return age;	}	public void setAge(String age) {		this.age = age;	}	public User(String username, String nickname, String gender, String age) {		this.username = username;		this.nickname = nickname;		this.gender = gender;		this.age = age;	}	public User() {	}}
还有一个MyAdapter:

public class MyAdapter extends BaseAdapter {	private Context context;	private List<user>list;	public MyAdapter(Context context, List<user>list) {		this.context = context;		this.list = list;	}	@Override	public int getCount() {		return list.size();	}	@Override	public Object getItem(int position) {		return list.get(position);	}	@Override	public long getItemId(int position) {		return position;	}	@Override	public View getView(int position, View convertView, ViewGroup parent) {		ViewHolder holder = null;		if (convertView == null) {			convertView = LayoutInflater.from(context).inflate(R.layout.lv_item, null);			holder = new ViewHolder();			holder.ageTv = (TextView) convertView.findViewById(R.id.age_tv);			holder.nickNameTv = (TextView) convertView.findViewById(R.id.nickname_tv);			holder.userNameTv = (TextView) convertView.findViewById(R.id.username_tv);			holder.genderTv = (TextView) convertView.findViewById(R.id.gender_tv);			convertView.setTag(holder);		} else {			holder = (ViewHolder) convertView.getTag();		}		User u = list.get(position);		holder.ageTv.setText(u.getAge());		holder.genderTv.setText(u.getGender());		holder.nickNameTv.setText(u.getNickname());		holder.userNameTv.setText(u.getUsername());		return convertView;	}	class ViewHolder {		TextView userNameTv, nickNameTv, genderTv, ageTv;	}}</user></user>
这些都很简单的显示部分的代码,我就不再详细解释。说完这些我们终于可以介绍今天的核心内容了,那就是ContentProvider。我们自定义的ContentProvider首先要继承ContentProvider,继承ContentProvider之后,我们会看到有多个方法需要我们实现,分别onCreate()、query、getType、insert、delete、update,主要是这几个方法,通过方法名字我们都能看出这几个方法的含义,就是执行增删改查操作,其中onCreate在应用启动的时候会调用,我们可以在里边做一些初始化的操作,但是不宜做一些耗时过长的操作,否则会导致应用启动时间变长,造成不好的用户体验。在其他的方法中我们分别执行相应的增删改查操作即可。说到这里,我们不得不介绍ContentProvider中另外一个非常重要的东西,那就是Uri。

    Uri是ContentResolver执行CRUD方法时的重要参数,我们可以从Uri中提取出我们要操作的数据对象,要操作哪一条数据等等信息,UriMatcher对象映射Uri的返回码,我们可以使用UriMatcher来方便的知道ContentResolver想要干什么。下面我们举例来说明一下:

<strong>content://com.lenve.cphost.mycontentprovider/user/3</strong>
一般情况下,我们见到的Uri就是这样的,我们可以将Uri分为三部分,第一部分是固定内容,第二部分是authorities,也就是我们在清单文件中注册ContentProvider时的authorities参数,最后一部分表示数据源路径,可有可无,这些根据自己的项目需求随意定义即可,但是这里有一些约定俗成的规则,比如:

    1.user多数情况下表示我们要操作的表名,因为一个ContentProvider可能涉及到多个表,通过这里来进行区分。

    2.如果user之后没有参数,默认返回当前表中所有数据,或者是操作当前表中所有数据

    3.user之后的3表示操作数据库的条件,可以是id,也可以是其他字段。

    那么这么长一个字符串我们要怎么提取我们需要的数据呢?难道要用正则?其实不必,这里就用到我们前面说的UriMatcher,使用UriMatcher会自动对我们的Uri进行匹配,但是,在匹配之前我们要先定义一下匹配规则,如下:

private static final String AUTHORITY = com.lenve.cphost.mycontentprovider;	static {		matcher = new UriMatcher(UriMatcher.NO_MATCH);		matcher.addURI(AUTHORITY, user, 1);// 配置表		matcher.addURI(AUTHORITY, user/#, 2);// 匹配任何数字		matcher.addURI(AUTHORITY, user/*, 3);// 匹配任何文本	}
我们一般习惯于在静态模块中初始化UriMatcher,我们可以向其中添加匹配规则,比如第6行,我们添加了匹配规则,如果如果Uri第三部分只用一个user,那么匹配结果为1,第7行,#表示任意数字,这句话表示如果Uri的第三部分是数字,那么匹配结果为2,第8行,*表示任意字符,user后还跟了第三个参数,那么匹配结果为3,我们以delete方法为例:

@Override	public int delete(Uri uri, String selection, String[] selectionArgs) {		int code = matcher.match(uri);		int result = 0;		switch (code) {		case UriMatcher.NO_MATCH:			break;		case 1:			// 删除所有			result = db.delete(DBHelper.USERTABLE, null, null);			Log.d(qf, 删除所有数据!);			break;		case 2:			// content://com.lenve.cphost.mycontentprovider/user/10			// 按条件删除,id			result = db.delete(DBHelper.USERTABLE, _id=?, new String[] { ContentUris.parseId(uri) +  });			Log.d(qf, 根据删除一条数据);			break;		case 3:			// content://com.lenve.cphost.mycontentprovider/user/zhangsan			// uri.getPathSegments()拿到一个List<string>,里边的值分别是0-->user、1-->zhangsan			result = db.delete(DBHelper.USERTABLE, USERNAME=?, new String[] { uri.getPathSegments().get(1) });			break;		default:			break;		}		return result;	}</string>

    调用Uri中的matcher方法来进行匹配,系统会根据我们在静态模块中的定义来返回相应的匹配结果,根据不同的结果,执行不同的操作,那么我们有什么方法可以快速提取出Uri中的参数呢?

    这里我们介绍两个方法,

    1.比如我们的Uri是这样的:

content://com.lenve.cphost.mycontentprovider/user/zhangsan
那么我们通过uri.getPathSegments()方法可以拿到一个List集合,该集合中放了两个字符串,第一个是user,第二个是zhangsan

    2.比如我们的Uri是这样的:

content://com.lenve.cphost.mycontentprovider/user/10
那么我们可以通过ContentUris.parseId(uri)方法获得10这个数字

    以上两种方式基本已经可以解决我们遇到的所有问题了,

电脑资料

自定义ContentProvider》(https://www.unjs.com)。说了这么多,现在给大家看看一个完整的我的自定义ContentProvider:

public class MyContentProvider extends ContentProvider {	private SQLiteOpenHelper helper;	private SQLiteDatabase db;	private static UriMatcher matcher;	private static final String AUTHORITY = com.lenve.cphost.mycontentprovider;	static {		matcher = new UriMatcher(UriMatcher.NO_MATCH);		matcher.addURI(AUTHORITY, user, 1);// 配置表		matcher.addURI(AUTHORITY, user/#, 2);// 匹配任何数字		matcher.addURI(AUTHORITY, user/*, 3);// 匹配任何文本	}	@Override	public boolean onCreate() {		helper = new DBHelper(getContext(), lenve.db, null, 1);		db = helper.getWritableDatabase();		Log.d(qf, MyContentProvider--->onCreate());		return true;	}	@Override	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {		return null;	}	@Override	public String getType(Uri uri) {		return null;	}	@Override	public Uri insert(Uri uri, ContentValues values) {		db.insert(DBHelper.USERTABLE, null, values);		return null;	}	@Override	public int delete(Uri uri, String selection, String[] selectionArgs) {		int code = matcher.match(uri);		int result = 0;		switch (code) {		case UriMatcher.NO_MATCH:			break;		case 1:			// 删除所有			result = db.delete(DBHelper.USERTABLE, null, null);			Log.d(qf, 删除所有数据!);			break;		case 2:			// content://com.lenve.cphost.mycontentprovider/user/10			// 按条件删除,id			result = db.delete(DBHelper.USERTABLE, _id=?, new String[] { ContentUris.parseId(uri) +  });			Log.d(qf, 根据删除一条数据);			break;		case 3:			// content://com.lenve.cphost.mycontentprovider/user/zhangsan			// uri.getPathSegments()拿到一个List<string>,里边的值分别是0-->user、1-->zhangsan			result = db.delete(DBHelper.USERTABLE, USERNAME=?, new String[] { uri.getPathSegments().get(1) });			break;		default:			break;		}		return result;	}	@Override	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {		int code = matcher.match(uri);		int result = 0;		switch (code) {		case 1:			result = db.update(DBHelper.USERTABLE, values, selection, selectionArgs);			break;		case 2:			result = db.update(DBHelper.USERTABLE, values, _id= + ContentUris.parseId(uri) +  AND  + selection,					selectionArgs);			break;		// 根据手动传参id来更新		case 3:			result = db.update(DBHelper.USERTABLE, values, selection, selectionArgs);			break;		}		return result;	}</string>
由于时间关系,有几个方法没有实现,不过原理都是一样的,不多说。

    所有这些都做完之后,别忘了在清单文件中注册ContentProvider,如下:

<provider android:authorities="com.lenve.cphost.mycontentprovider" android:exported="true" android:name="com.lenve.cphost.MyContentProvider"></provider>
这里解释一下第三个参数,设置为true表示允许其他App调用,设置为false表示不允许其他App调用。

    这里说完,我们再看看怎么在cpTest这个App中操作这些数据:

    核心代码如下:

public class MainActivity extends Activity {	private ContentResolver cr;	private ContentValues values;	@Override	protected void onCreate(Bundle savedInstanceState) {		super.onCreate(savedInstanceState);		setContentView(R.layout.activity_main);		cr = getContentResolver();		values = new ContentValues();	}	public void onClickBtn(View v) {		switch (v.getId()) {		case R.id.add_btn:			addData();			break;		case R.id.delete_btn:			deleteData();			break;		case R.id.update_btn:			updateData();			break;		case R.id.search_btn:			break;		default:			break;		}	}	private void updateData() {		// values.put(USERNAME, lisi);		// cr.update(Uri.parse(content://com.lenve.cphost.mycontentprovider/user),		// values, _id=?,		// new String[] { 4 +  });		values.put(USERNAME, wangwu);		cr.update(Uri.parse(content://com.lenve.cphost.mycontentprovider/user/3), values, USERNAME=?,				new String[] { lisi });	}	private void deleteData() {		// 根据id删除		// cr.delete(Uri.parse(content://com.lenve.cphost.mycontentprovider/user/1),		// , new String[] {});		// 根据username删除		cr.delete(Uri.parse(content://com.lenve.cphost.mycontentprovider/user/zhangsan), , new String[] {});	}	private void addData() {		values.put(USERNAME, zhangsan);		values.put(NICKNAME, 张三);		values.put(AGE, 18);		values.put(GENDER, 男);		cr.insert(Uri.parse(content://com.lenve.cphost.mycontentprovider), values);	}}

    好了,关于自定义ContentProvider就说这么多。

最新文章