Android四大组件之Content Provider


Content Provider是Android提供的第三方应用数据的访问方案。在Android中,对数据的保护是很严密的,除了放在SD卡中的数据,一个应用所持有的数据库、文件等内容,都是不允许其它程序直接访问的。Android当然不会真的把每个应用都做成一座孤岛,它为所有应用都准备了一扇窗,这就是Content Provider。应用向对外提供的数据,可以通过派生Content Provider类,封装成一枚Content Provider,每个Content Provider都用一个uri作为独立的标识,形如:content://com.xxxx。 所有东西看着像REST的样子,但实际上,它比REST更为灵活。和REST类似,uri也可以有两种类型,一种是带id的,另一种是列表的,但实现者不需要按照这个模式来做,给你id的uri你也可以返回列表类型的数据,只要调用者明白就无妨,不用苛求所谓的REST。 另外,Content Provider不和REST一样只有uri可用,它还可以接受Projection、Selection、OrderBy等参数,这样,就可以像数据库那样进行投影、选择和排序。查询到的结果,以Cursor的形式进行返回,调用者可以移动Cursor来访问各列的数据。 Content Provider屏蔽了内部数据的存储细节,向外提供了上述的统一接口模型,这样的抽象层次,大大简化了上层应用的编写,也对数据的整合提供了更方便的途径。Content Provider内部常用数据库来实现,Android提供了强大的Sqlite支持,但很多时候你也可以封装文件或其它混合的数据。
在Android中,ContentResolver是用来发起Content Provider的定位和访问的。但通常,Content Provider需要访问的可能是数据库等大数据源,效率上不足够快,会导致调用线程的拥塞。因此Android提供了一个AsyncQueryHandler,帮助进行异步访问Content Provider。 在各大组件中,Service和Content Provider都是需要持续访问的。Service如果是一个耗时的场景,往往会提供异步访问的接口,而Content Provider不论效率如何,都提供的是约定的同步访问接口。 下面提供两个实例------一个是ContentProvider所在的应用,一个是使用ContentProvider的应用,来说明如何使用ContentProvider。 我们先新建一个工程MyContentProvider,在这个工程中我们要实现ContentProvider,项目结构如下图所示。 Android四大组件之Content Provider_第1张图片
MyContentProvider目录结构
在MyUser.java中,MyUser这个类主要用来存放一些常量,比如这个ContentProvider的Uri以及使用的数据库表的列,如下所示:
public class MyUser {public static final String AUTHORITY = "com.veione.MyContentProvider";public static final class User implements BaseColumns {// 定义Uripublic static final Uri CONTENT_URI = Uri.parse("content://"+ AUTHORITY);// 定义数据表列public static final String USER_NAME = "USER_NAME";}}
接下来我们在MyContentProvider.java中实现这个Content Provider,在MyContentProvider中我们主要构造了一个继承于SQLiteOpenHelper的类DatabaseHelper,然后在这个SQLiteOpenHelper类中实现数据的增、删、改、查,如下所示:
/** * MyContentProvider继承ContentProvider类,实现其insert,update,delete, * getType,onCreate等方法 *  * @author veione *  */public class MyContentProvider extends ContentProvider {//定义一个SQLiteDatabase变量private SQLiteDatabase sqlDB;//定义一个DatabaseHelper变量private DatabaseHelper dbHelper;//数据库名private static final String DATABASE_NAME="Users.db";//数据库版本private static final int DATABASE_VERSION=1;//表名private static final String TABLE_NAME="User";/** * 定义一个内部类 * 这个内部类继承了SQLiteOpenHelper类,重写其方法 */public static class DatabaseHelper extends SQLiteOpenHelper{//构造方法public DatabaseHelper(Context context) {//父类构造方法super(context,DATABASE_NAME,null,DATABASE_VERSION);}//当第一次创建数据库的时候调用该方法,可以为数据库增加一些表和初始化一些数据@Overridepublic void onCreate(SQLiteDatabase db) {//在数据库里生成一张表db.execSQL("CREATE TABLE "+TABLE_NAME+" (_id INTEGER PRIMARY KEY AUTOINCREMENT,USER_NAME TEXT);");}//当更新数据库版本的时候,调用该方法。可以删除、修改表的一些信息@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME);onCreate(db);}}//这是一个回调函数,当生成所在类的对象时,这个方法被调用,创建一个数据库@Overridepublic boolean onCreate() {dbHelper=new DatabaseHelper(getContext());return (dbHelper==null)?false:true;}//查询@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {//新建数据库查询类SQLiteQueryBuilder qb=new SQLiteQueryBuilder();SQLiteDatabase db=dbHelper.getReadableDatabase();qb.setTables(TABLE_NAME);//取得查询结果的游标Cursor c=qb.query(db, projection,selection, null, null, null,sortOrder);c.setNotificationUri(getContext().getContentResolver(), uri);return c;}//取得类型@Overridepublic String getType(Uri uri) {return null;}//插入数据@Overridepublic Uri insert(Uri uri, ContentValues values) {sqlDB=dbHelper.getWritableDatabase();long rowId=sqlDB.insert(TABLE_NAME, "", values);if(rowId>0){Uri rowUri=ContentUris.appendId(MyUser.User.CONTENT_URI.buildUpon(), rowId).build();//通知更改getContext().getContentResolver().notifyChange(rowUri, null);return rowUri;}throw new SQLException("Failed to insert row into "+uri);}//删除数据@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {return 0;}//更新数据@Overridepublic int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {return 0;}}

另外我们新建一个MyContentProvider的界面,代码如下所示,在这个界面中简单地往数据库中插入两条记录Test和Guo作为实例数据,主要是验证客户端中是否能正确读取到这些数据。插入数据之后,我们还通过Toast的方式将数据库中的所有数据显示一遍,表明我们已经将数据正确存储到了数据库中。
  
public class MyContentActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 插入两条记录insertRecord("Test");insertRecord("Guo");// 显示记录displayRecord();}private void insertRecord(String userName) {ContentValues values=new ContentValues();values.put(MyUser.User.USER_NAME, userName);getContentResolver().insert(MyUser.User.CONTENT_URI, values);}private void displayRecord() {//构建一个字符串数组用于存放用户的记录String[] columns=new String[]{MyUser.User._ID,MyUser.User.USER_NAME};//设定ContentProvider的UriUri myUri=MyUser.User.CONTENT_URI;Cursor cur=getContentResolver().query(myUri, columns, null, null, null);if(cur.moveToFirst()){String id=null;String userName=null;do{id=cur.getString(cur.getColumnIndex(MyUser.User._ID));userName=cur.getString(cur.getColumnIndex(MyUser.User.USER_NAME));//显示数据表中的数据Toast.makeText(this, id+" "+userName, Toast.LENGTH_SHORT).show();}while(cur.moveToNext());}}}
这里我们没有用到任何的布局文件,因此我们直接来看看AndroidManifest.xml,如下所示。与其它AndroidManifest.xml不同点主要在于加了声明provider的部分,如代码所示。核心的两个属性分别是android:name和android:authorities。
                                                                                                    
运行之后效果如图所示,显示了添加的用户Test和Guo。
Android四大组件之Content Provider_第2张图片

 MyContentProvider运行效果图

  接下来我们新建另外一个程序MyContentClient,在这个程序中使用我们刚才建立的ContentProvider,对该数据表进行插入和查询操作。 如下所示,是MyContentClientActivity的源代码,代码中关键的地方在于要正确设置访问的Uri地址,如图所示。这个Uri是由"content://"加上我们在ContentProvider定义的authority组成。然后就是使用getContentResolver()函数得到数据表的所有数据,并保存到一个游标变量中。接着,通过移动这个游标我们可以得到数据表中的所有数据,并将它们保存到一个StringBuffer中,最后将它们输出到一个TextView控件中。 同时为了试验插入数据的功能,我们创建了一个EditText的文本输入框,在文本框中输入信息,并单击Button,可以将数据写入到数据库中。数据写入过程也要采用特有的格式:新建一个ContentResolver对象和一个ContentValue的对象,再往这个ContentValue对象中输入数据,最后通过这个ContentResolver对象将数据插入到指定的ContentProvider中。当然,这里没有做一个可以实时更新的界面,不过没关系,重新打开程序,就可以在TextView控件里看到刚才输入的数据。
package com.veione.mycontentprovider;import android.app.Activity;import android.content.ContentResolver;import android.content.ContentValues;import android.database.Cursor;import android.net.Uri;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;public class MyContentClientActivity extends Activity{public static final String AUTHORITY="com.veione.MyContentProvider";private Button insertBtn=null;//访问ContentProvider的UriUri CONTENT_URI=Uri.parse("content://"+AUTHORITY);@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextView show=(TextView)findViewById(R.id.show);StringBuffer sb=new StringBuffer("");//得到ContentProvider对应表的所有数据,以游标格式保存Cursor c=getContentResolver().query(CONTENT_URI, new String[]{"_id","USER_NAME"}, null, null, null);//循环输出ContentProvider的数据if(c.moveToFirst()){String _id=null;String userName=null;do{_id=c.getString(c.getColumnIndex("USER_NAME"));userName=c.getString(c.getColumnIndex("USER_NAME"));sb.append("_id="+_id+",userName="+userName+"\n");}while(c.moveToNext());}show.setText(sb);//根据Id得到控件对象insertBtn=(Button)findViewById(R.id.insertBtn);//给按钮添加监听器insertBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//得到EditText输入的数据String username=((EditText)findViewById(R.id.sername)).getText().toString();//生成一个ContentResolver对象ContentResolver cr=getContentResolver();//生成一个ContentValues对象ContentValues values=new ContentValues();values.put("USER_NAME", username);//插入数据cr.insert(CONTENT_URI, values);}});}}

activity_main.xml布局文件如下:
<?xml version="1.0" encoding="utf-8"?>            

最后运行就OK了,OMG,快1点了,睡觉!!!

更多相关文章

  1. Android 数据Parcel序列化过程源码分析
  2. Android——SQLite实现面向对象CRUD
  3. Android中Activity之间的数据传递和Intent使用
  4. 【Android 内存优化】使用 Memory Analyzer ( MAT ) 工具分析内
  5. 【Android 开发】: Android客户端与服务端之间使用GSON交互数据
  6. Android遍历SqlLite cursor对象:
  7. Android 反射获得控件对象

随机推荐

  1. Android向通讯录添加联系人的一般方法
  2. 关于使用adb命令安装apk到手机中的问题
  3. Android(安卓)Snake
  4. Android中日志信息的打印方式
  5. Android在TextView中设置图片显示问题
  6. Android—自动弹出软键盘
  7. android 线程通信Handler Bundle
  8. android tools 汇总
  9. 最全面的Android学习路线 思维导图附知识
  10. Android 开发中最常用的 GitHub 优秀的 A