自定義ContentProvider -電腦資料

電腦資料 時間:2019-01-01 我要投稿
【m.clearvueentertainment.com - 電腦資料】

    ContentProvider作為安卓的四大組件之一,在看開發(fā)中用到的頻率遠(yuǎn)不如其他三個,以至于我都把這個東西給忘了,最近由于工作原因,不得不重新拾起來總結(jié)一下,那么今天就來說說自定義ContentProvider吧,

自定義ContentProvider

。

    今天的案例是這樣的,我們有兩個App,一個叫做cpHost,作為內(nèi)容提供者;另外一個叫做cpTest,專門用來操作這個cpHost中的數(shù)據(jù)。我們的cpHost中有一個數(shù)據(jù)庫,該數(shù)據(jù)庫中有一個User表,我們通過內(nèi)容提供者將這個User表共享出去,供其他App調(diào)用。下面我們就來看看怎么實現(xiàn)這樣一個效果。

    既然用到數(shù)據(jù)庫存儲用戶表,那么毫無疑問我們需要一個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) {	}}
我們創(chuàng)建一個用戶表,然后在在MainActivity中將該表中的數(shù)據(jù)顯示出來,這樣方便我們后面看到操作效果,于是我們需要一個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設(shè)置Adapter的方法放在onResume()方法中執(zhí)行,主要是為了測試方便,沒有其他意思,那么我們在來看看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>
這些都很簡單的顯示部分的代碼,我就不再詳細(xì)解釋。說完這些我們終于可以介紹今天的核心內(nèi)容了,那就是ContentProvider。我們自定義的ContentProvider首先要繼承ContentProvider,繼承ContentProvider之后,我們會看到有多個方法需要我們實現(xiàn),分別onCreate()、query、getType、insert、delete、update,主要是這幾個方法,通過方法名字我們都能看出這幾個方法的含義,就是執(zhí)行增刪改查操作,其中onCreate在應(yīng)用啟動的時候會調(diào)用,我們可以在里邊做一些初始化的操作,但是不宜做一些耗時過長的操作,否則會導(dǎo)致應(yīng)用啟動時間變長,造成不好的用戶體驗。在其他的方法中我們分別執(zhí)行相應(yīng)的增刪改查操作即可。說到這里,我們不得不介紹ContentProvider中另外一個非常重要的東西,那就是Uri。

    Uri是ContentResolver執(zhí)行CRUD方法時的重要參數(shù),我們可以從Uri中提取出我們要操作的數(shù)據(jù)對象,要操作哪一條數(shù)據(jù)等等信息,UriMatcher對象映射Uri的返回碼,我們可以使用UriMatcher來方便的知道ContentResolver想要干什么。下面我們舉例來說明一下:

<strong>content://com.lenve.cphost.mycontentprovider/user/3</strong>
一般情況下,我們見到的Uri就是這樣的,我們可以將Uri分為三部分,第一部分是固定內(nèi)容,第二部分是authorities,也就是我們在清單文件中注冊ContentProvider時的authorities參數(shù),最后一部分表示數(shù)據(jù)源路徑,可有可無,這些根據(jù)自己的項目需求隨意定義即可,但是這里有一些約定俗成的規(guī)則,比如:

    1.user多數(shù)情況下表示我們要操作的表名,因為一個ContentProvider可能涉及到多個表,通過這里來進(jìn)行區(qū)分。

    2.如果user之后沒有參數(shù),默認(rèn)返回當(dāng)前表中所有數(shù)據(jù),或者是操作當(dāng)前表中所有數(shù)據(jù)

    3.user之后的3表示操作數(shù)據(jù)庫的條件,可以是id,也可以是其他字段。

    那么這么長一個字符串我們要怎么提取我們需要的數(shù)據(jù)呢?難道要用正則?其實不必,這里就用到我們前面說的UriMatcher,使用UriMatcher會自動對我們的Uri進(jìn)行匹配,但是,在匹配之前我們要先定義一下匹配規(guī)則,如下:

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);// 匹配任何數(shù)字		matcher.addURI(AUTHORITY, user/*, 3);// 匹配任何文本	}
我們一般習(xí)慣于在靜態(tài)模塊中初始化UriMatcher,我們可以向其中添加匹配規(guī)則,比如第6行,我們添加了匹配規(guī)則,如果如果Uri第三部分只用一個user,那么匹配結(jié)果為1,第7行,#表示任意數(shù)字,這句話表示如果Uri的第三部分是數(shù)字,那么匹配結(jié)果為2,第8行,*表示任意字符,user后還跟了第三個參數(shù),那么匹配結(jié)果為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, 刪除所有數(shù)據(jù)!);			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, 根據(jù)刪除一條數(shù)據(jù));			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>

    調(diào)用Uri中的matcher方法來進(jìn)行匹配,系統(tǒng)會根據(jù)我們在靜態(tài)模塊中的定義來返回相應(yīng)的匹配結(jié)果,根據(jù)不同的結(jié)果,執(zhí)行不同的操作,那么我們有什么方法可以快速提取出Uri中的參數(shù)呢?

    這里我們介紹兩個方法,

    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這個數(shù)字

    以上兩種方式基本已經(jīng)可以解決我們遇到的所有問題了,

電腦資料

自定義ContentProvider》(http://m.clearvueentertainment.com)。說了這么多,現(xiàn)在給大家看看一個完整的我的自定義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);// 匹配任何數(shù)字		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, 刪除所有數(shù)據(jù)!);			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, 根據(jù)刪除一條數(shù)據(jù));			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;		// 根據(jù)手動傳參id來更新		case 3:			result = db.update(DBHelper.USERTABLE, values, selection, selectionArgs);			break;		}		return result;	}</string>
由于時間關(guān)系,有幾個方法沒有實現(xiàn),不過原理都是一樣的,不多說。

    所有這些都做完之后,別忘了在清單文件中注冊ContentProvider,如下:

<provider android:authorities="com.lenve.cphost.mycontentprovider" android:exported="true" android:name="com.lenve.cphost.MyContentProvider"></provider>
這里解釋一下第三個參數(shù),設(shè)置為true表示允許其他App調(diào)用,設(shè)置為false表示不允許其他App調(diào)用。

    這里說完,我們再看看怎么在cpTest這個App中操作這些數(shù)據(jù):

    核心代碼如下:

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() {		// 根據(jù)id刪除		// cr.delete(Uri.parse(content://com.lenve.cphost.mycontentprovider/user/1),		// , new String[] {});		// 根據(jù)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);	}}

    好了,關(guān)于自定義ContentProvider就說這么多。

最新文章