zoukankan      html  css  js  c++  java
  • 自定义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 xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <ListView
            android:id="@+id/lv"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
        </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()));
    	}
    }
    这里之所以把给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;
    	}
    }
    这些都很简单的显示部分的代码,我就不再详细解释。说完这些我们终于可以介绍今天的核心内容了,那就是ContentProvider。我们自定义的ContentProvider首先要继承ContentProvider,继承ContentProvider之后,我们会看到有多个方法需要我们实现,分别onCreate()、query、getType、insert、delete、update,主要是这几个方法,通过方法名字我们都能看出这几个方法的含义,就是执行增删改查操作,其中onCreate在应用启动的时候会调用,我们可以在里边做一些初始化的操作,但是不宜做一些耗时过长的操作,否则会导致应用启动时间变长,造成不好的用户体验。在其他的方法中我们分别执行相应的增删改查操作即可。说到这里,我们不得不介绍ContentProvider中另外一个非常重要的东西,那就是Uri。
    Uri是ContentResolver执行CRUD方法时的重要参数,我们可以从Uri中提取出我们要操作的数据对象,要操作哪一条数据等等信息,UriMatcher对象映射Uri的返回码,我们可以使用UriMatcher来方便的知道ContentResolver想要干什么。下面我们举例来说明一下:

    content://com.lenve.cphost.mycontentprovider/user/3
    一般情况下,我们见到的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;
    	}

    调用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:

    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;
    	}
    由于时间关系,有几个方法没有实现,不过原理都是一样的,不多说。

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

            <provider
                android:name="com.lenve.cphost.MyContentProvider"
                android:authorities="com.lenve.cphost.mycontentprovider"
                android:exported="true" />
    这里解释一下第三个参数,设置为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就说这么多。

    Demo下载http://download.csdn.net/detail/u012702547/9330445

  • 相关阅读:
    oracle中Blob和Clob类型的区别
    为什么要分库分表
    Enable file editing in Visual Studio's debug mode
    SQL Server Dead Lock Log
    Debug .NET Framework Source
    SQL Server text field里面有换行符的时候copy到excel数据会散乱
    诊断和修复Web测试记录器(Web Test Recorder)问题
    Can't load Microsoft.ReportViewer.ProcessingObjectModel.dll
    'telnet' is not recognized as an internal or external command
    Linq to XML
  • 原文地址:https://www.cnblogs.com/qitian1/p/6461716.html
Copyright © 2011-2022 走看看