如何在Android中自定义 Content Provider
2016-03-22 09:46
405 查看
http://codecloud.net/android-content-provider-5474.html
Android 提供的了多种不同的方法来供应用之间相互通信。Android app可以与其它app共享数据,这样其它app便可以使用数据数据来构建自己的业务。举个例子,你有时可能需要查询手机上的联系人的信息。在Android中推荐的方式是通过content providers来使用共享数据。Content provider是某些内容的拥有者,它提供定义好的API接口来供read, insert, update 和delete数据。Content provider在内部可以使用任何存储空间,比如本地文件、本地数据库或者远程服务器。在这篇文章中,我们将学习如何创建自己的 content provider 并从其它app来访问数据。
创建 Content Provider 类
我们从创建一个Content provider类开始,这类用来管理图片元数据。我们使用本地的 SQLite 数据库来存储数据,当然你可以将数据存在其它地方。
首先创建一个继承了 ContentProvider 的类ImagesProvider,并重写继承来的方法。
在上面的代码中,我们创建了ImagesProvider类,并实重写了一些需要用到的方法。
我们将从实现getType方法入手,getType方法将数据的MIME类型作为字符串返回。
返回的MIME类型应该是这种格式:vnd../vnd..,对于单行应该是 android.cursor.item,对于多行而言应该是android.cursor.dir ,而应该是全局唯一的(使用包名)。对于相应的URI应该是唯一的。现在让我们更新一下getType方法的实现:
实现 onCreate 方法和查询功能
为了存储内容,content provider需要一个 database helper类,创建 ImageDataBase类如下:
上面这个类继承了 SQLiteOpenHelper并在数据库ImagesDatabase.db中创建了一个命名为imagestore的表,设定的列属性有:’id’, IMAGETITLE, ‘IMAGEURL’ 和’IMAGEDESC’。函数getImages使用SQLiteQueryBuilder类查询数据库并返回结果cursor。现在我们来实现content provider中的onCreate and query函数。
在 onCreate 函数中,我们创建了一个ImageDatabase 类的对象,并给它传递了一个context。存储这个变量,以至于我们可以用它来对content provider做各种操作。在query 函数中,我们检查查询是否具有给定的id,如果有,我们获取id,如果没有,将其记为null,并传递这些参数给 ImageDatabase:getImages 去查询image数据,并返回结果集cursor。
在 content provider 中 Inserting, Deleting, Updating data
实现好了查询函数后,接下来实现content provider中的其它函数。我们需要在ImageDatabase中添加支持实现insert, delete and update操作的函数,代码如下:
所有的新函数访问数据库并调用相应的insert, delete and update函数,并传递相应的id、content values参数。现在更新ImagesProvider 类如下:
在上面的代码中,如果URI是为获取单个image,我们就获取id值,否则,我们保持id为null。然后将值传递给ImageDataBase中相应的函数。
在 AndroidManifest 中声明 content provider
我们必须得在AndroidManifest.xml文件中用标签provide声明content provider。
在标签中android:name属性应该是Content Provider类,android:authorities 应该指定使用content的URI。
使用 content provider
一旦上面的工作都完成了,我们便可以测试content provider了。因此,我们可以创建一个test app并在Main Activity测试。
这个activity涉及到了两个layout文件,一个是main content的view,一个是list view。实现如下:
activitymain.xml
listlayout.xml
在上面的代码中,onCreate 函数使用CursorLoader 函数从content provider中获取所有contents,同时传递CONTENT_URI给它。
为了插入值,getContentResolver().insert 方法被调用,同时传递了content URI和ContentValues供插入。运行效果如下:
Android 中的Content provider提供了干净系统级的方式供apps来共享数据和使用其它apps的数据。它标准化了如何在多个apps中共享数据。Android中提供了很多内建的content providers和API,这使得非常容易自定义content providers。
Android 提供的了多种不同的方法来供应用之间相互通信。Android app可以与其它app共享数据,这样其它app便可以使用数据数据来构建自己的业务。举个例子,你有时可能需要查询手机上的联系人的信息。在Android中推荐的方式是通过content providers来使用共享数据。Content provider是某些内容的拥有者,它提供定义好的API接口来供read, insert, update 和delete数据。Content provider在内部可以使用任何存储空间,比如本地文件、本地数据库或者远程服务器。在这篇文章中,我们将学习如何创建自己的 content provider 并从其它app来访问数据。
创建 Content Provider 类
我们从创建一个Content provider类开始,这类用来管理图片元数据。我们使用本地的 SQLite 数据库来存储数据,当然你可以将数据存在其它地方。
首先创建一个继承了 ContentProvider 的类ImagesProvider,并重写继承来的方法。
import android.content.ContentProvider; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; public class ImagesProvider extends ContentProvider { @Override public String getType(Uri uri) { return ""; } @Override public boolean onCreate() { return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; } @Override public Uri insert(Uri uri, ContentValues values) { return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } }
在上面的代码中,我们创建了ImagesProvider类,并实重写了一些需要用到的方法。
我们将从实现getType方法入手,getType方法将数据的MIME类型作为字符串返回。
返回的MIME类型应该是这种格式:vnd../vnd..,对于单行应该是 android.cursor.item,对于多行而言应该是android.cursor.dir ,而应该是全局唯一的(使用包名)。对于相应的URI应该是唯一的。现在让我们更新一下getType方法的实现:
import android.content.ContentProvider; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.net.Uri; public class ImagesProvider extends ContentProvider { private static final String PROVIDER_NAME = "androidcontentproviderdemo.androidcontentprovider.images"; private static final Uri CONTENT_URI = Uri.parse("content://" + PROVIDER_NAME + "/images"); private static final int IMAGES = 1; private static final int IMAGE_ID = 2; private static final UriMatcher uriMatcher = getUriMatcher(); private static UriMatcher getUriMatcher() { UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(PROVIDER_NAME, "images", IMAGES); uriMatcher.addURI(PROVIDER_NAME, "images/#", IMAGE_ID); return uriMatcher; } @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case IMAGES: return "vnd.android.cursor.dir/vnd.com.androidcontentproviderdemo.androidcontentprovider.provider.images"; case IMAGE_ID: return "vnd.android.cursor.item/vnd.com.androidcontentproviderdemo.androidcontentprovider.provider.images"; } return ""; } @Override public boolean onCreate() { return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; } @Override public Uri insert(Uri uri, ContentValues values) { return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } }
实现 onCreate 方法和查询功能
为了存储内容,content provider需要一个 database helper类,创建 ImageDataBase类如下:
import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; public class ImageDataBase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "ImagesDatabase.db"; private static final String TABLE_NAME = "imagestore"; private static final String SQL_CREATE = "CREATE TABLE " + TABLE_NAME + " (_id INTEGER PRIMARY KEY, IMAGETITLE TEXT , IMAGEURL TEXT , IMAGEDESC TEXT )"; private static final String SQL_DROP = "DROP TABLE IS EXISTS " + TABLE_NAME ; ImageDataBase(Context context) { super(context, DATABASE_NAME, null, 1); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(SQL_DROP); onCreate(db); } public Cursor getImages(String id, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder(); sqliteQueryBuilder.setTables(TABLE_NAME); if(id != null) { sqliteQueryBuilder.appendWhere("_id" + " = " + id); } if(sortOrder == null || sortOrder == "") { sortOrder = "IMAGETITLE"; } Cursor cursor = sqliteQueryBuilder.query(getReadableDatabase(), projection, selection, selectionArgs, null, null, sortOrder); return cursor; } }
上面这个类继承了 SQLiteOpenHelper并在数据库ImagesDatabase.db中创建了一个命名为imagestore的表,设定的列属性有:’id’, IMAGETITLE, ‘IMAGEURL’ 和’IMAGEDESC’。函数getImages使用SQLiteQueryBuilder类查询数据库并返回结果cursor。现在我们来实现content provider中的onCreate and query函数。
import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.net.Uri; public class ImagesProvider extends ContentProvider { private static final String PROVIDER_NAME = "androidcontentproviderdemo.androidcontentprovider.images"; private static final Uri CONTENT_URI = Uri.parse("content://" + PROVIDER_NAME + "/images"); private static final int IMAGES = 1; private static final int IMAGE_ID = 2; private static final UriMatcher uriMatcher = getUriMatcher(); private static UriMatcher getUriMatcher() { UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(PROVIDER_NAME, "images", IMAGES); uriMatcher.addURI(PROVIDER_NAME, "images/#", IMAGE_ID); return uriMatcher; } private ImageDatabase imageDataBase = null; @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case IMAGES: return "vnd.android.cursor.dir/vnd.com.androidcontentproviderdemo.androidcontentprovider.provider.images"; case IMAGE_ID: return "vnd.android.cursor.item/vnd.com.androidcontentproviderdemo.androidcontentprovider.provider.images"; } return ""; } @Override public boolean onCreate() { Context context = getContext(); imageDataBase = new ImageDataBase(context); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { String id = null; if(uriMatcher.match(uri) == IMAGE_ID) { //Query is for one single image. Get the ID from the URI. id = uri.getPathSegments().get(1); } return imageDataBase.getImages(id, projection, selection, selectionArgs, sortOrder); } @Override public Uri insert(Uri uri, ContentValues values) { return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } }
在 onCreate 函数中,我们创建了一个ImageDatabase 类的对象,并给它传递了一个context。存储这个变量,以至于我们可以用它来对content provider做各种操作。在query 函数中,我们检查查询是否具有给定的id,如果有,我们获取id,如果没有,将其记为null,并传递这些参数给 ImageDatabase:getImages 去查询image数据,并返回结果集cursor。
在 content provider 中 Inserting, Deleting, Updating data
实现好了查询函数后,接下来实现content provider中的其它函数。我们需要在ImageDatabase中添加支持实现insert, delete and update操作的函数,代码如下:
import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import java.sql.SQLException; public class ImageDataBase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "ImagesDatabase.db"; private static final String TABLE_NAME = "imagestore"; private static final String SQL_CREATE = "CREATE TABLE " + TABLE_NAME + " (_id INTEGER PRIMARY KEY, IMAGETITLE TEXT , IMAGEURL TEXT , IMAGEDESC TEXT )"; private static final String SQL_DROP = "DROP TABLE IS EXISTS " + TABLE_NAME ; ImageDataBase(Context context) { super(context, DATABASE_NAME, null, 1); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(SQL_DROP); onCreate(db); } public Cursor getImages(String id, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder(); sqliteQueryBuilder.setTables(TABLE_NAME); if(id != null) { sqliteQueryBuilder.appendWhere("_id" + " = " + id); } if(sortOrder == null || sortOrder == "") { sortOrder = "IMAGETITLE"; } Cursor cursor = sqliteQueryBuilder.query(getReadableDatabase(), projection, selection, selectionArgs, null, null, sortOrder); return cursor; } public long addNewImage(ContentValues values) throws SQLException { long id = getWritableDatabase().insert(TABLE_NAME, "", values); if(id <=0 ) { throw new SQLException("Failed to add an image"); } return id; } public int deleteImages(String id) { if(id == null) { return getWritableDatabase().delete(TABLE_NAME, null , null); } else { return getWritableDatabase().delete(TABLE_NAME, "_id=?", new String[]{id}); } } public int updateImages(String id, ContentValues values) { if(id == null) { return getWritableDatabase().update(TABLE_NAME, values, null, null); } else { return getWritableDatabase().update(TABLE_NAME, values, "_id=?", new String[]{id}); } } }
所有的新函数访问数据库并调用相应的insert, delete and update函数,并传递相应的id、content values参数。现在更新ImagesProvider 类如下:
import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.net.Uri; import android.util.Log; public class ImagesProvider extends ContentProvider { private static final String PROVIDER_NAME = "androidcontentproviderdemo.androidcontentprovider.images"; private static final Uri CONTENT_URI = Uri.parse("content://" + PROVIDER_NAME + "/images"); private static final int IMAGES = 1; private static final int IMAGE_ID = 2; private static final UriMatcher uriMatcher = getUriMatcher(); private static UriMatcher getUriMatcher() { UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(PROVIDER_NAME, "images", IMAGES); uriMatcher.addURI(PROVIDER_NAME, "images/#", IMAGE_ID); return uriMatcher; } private ImageDatabase imageDataBase = null; @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case IMAGES: return "vnd.android.cursor.dir/vnd.com.androidcontentproviderdemo.androidcontentprovider.provider.images"; case IMAGE_ID: return "vnd.android.cursor.item/vnd.com.androidcontentproviderdemo.androidcontentprovider.provider.images"; } return ""; } @Override public boolean onCreate() { Context context = getContext(); imageDataBase = new ImageDatabase(context); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { String id = null; if(uriMatcher.match(uri) == IMAGE_ID) { //Query is for one single image. Get the ID from the URI. id = uri.getPathSegments().get(1); } return imageDataBase.getImages(id, projection, selection, selectionArgs, sortOrder); } @Override public Uri insert(Uri uri, ContentValues values) { try { long id = imageDataBase.addNewImage(values); Uri returnUri = ContentUris.withAppendedId(CONTENT_URI, id); return returnUri; } catch(Exception e) { return null; } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { String id = null; if(uriMatcher.match(uri) == IMAGE_ID) { //Delete is for one single image. Get the ID from the URI. id = uri.getPathSegments().get(1); } return imageDataBase.deleteImages(id); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { String id = null; if(uriMatcher.match(uri) == IMAGE_ID) { //Update is for one single image. Get the ID from the URI. id = uri.getPathSegments().get(1); } return imageDataBase.updateImages(id, values); } }
在上面的代码中,如果URI是为获取单个image,我们就获取id值,否则,我们保持id为null。然后将值传递给ImageDataBase中相应的函数。
在 AndroidManifest 中声明 content provider
我们必须得在AndroidManifest.xml文件中用标签provide声明content provider。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.androidcontentproviderdemo.androidcontentprovider"> <application android:allowBackup="true" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:theme="@style/AppTheme"> <provider android:name="com.androidcontentproviderdemo.androidcontentprovider.ImagesProvider" android:authorities="androidcontentproviderdemo.androidcontentprovider.images"> </provider> </application> </manifest>
在标签中android:name属性应该是Content Provider类,android:authorities 应该指定使用content的URI。
使用 content provider
一旦上面的工作都完成了,我们便可以测试content provider了。因此,我们可以创建一个test app并在Main Activity测试。
import android.app.Activity; import android.content.ContentValues; import android.content.CursorLoader; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.EditText; import android.widget.ListView; import android.widget.SimpleCursorAdapter; import android.widget.Toast; public class MainActivity extends Activity { private static final String PROVIDER_NAME = "androidcontentproviderdemo.androidcontentprovider.images"; private static final Uri CONTENT_URI = Uri.parse("content://" + PROVIDER_NAME + "/images"); private ListView listView; private SimpleCursorAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = (ListView) findViewById(R.id.lstViewImages); adapter = new SimpleCursorAdapter(getBaseContext(), R.layout.list_layout, null, new String[] { "IMAGETITLE", "IMAGEURL", "IMAGEDESC"}, new int[] { R.id.imgTitle , R.id.imgUrl, R.id.imgDesc }, 0); listView.setAdapter(adapter); refreshValuesFromContentProvider(); } private void refreshValuesFromContentProvider() { CursorLoader cursorLoader = new CursorLoader(getBaseContext(), CONTENT_URI, null, null, null, null); Cursor c = cursorLoader.loadInBackground(); adapter.swapCursor(c); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } public void onClickAddImage(View view) { ContentValues contentValues = new ContentValues(); contentValues.put("IMAGETITLE", ((EditText) findViewById(R.id.edtTxtImageTitle)).getText().toString()); contentValues.put("IMAGEURL" , ((EditText)findViewById(R.id.edtImageUrl)).getText().toString()); contentValues.put("IMAGEDESC", ((EditText) findViewById(R.id.edtImageDesc)).getText().toString()); Uri uri = getContentResolver().insert(CONTENT_URI, contentValues); Toast.makeText(getBaseContext(), uri.toString(), Toast.LENGTH_LONG).show(); refreshValuesFromContentProvider(); } }
这个activity涉及到了两个layout文件,一个是main content的view,一个是list view。实现如下:
activitymain.xml
<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" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".ContentProviderUsageActivity"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerHorizontal="true"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="Image Title" android:id="@+id/txtViewImageTitle" android:layout_gravity="center_horizontal" /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/edtTxtImageTitle" android:layout_gravity="center_horizontal" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="Image URL" android:id="@+id/txtViewImageUrl" android:layout_gravity="center_horizontal" /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/edtImageUrl" android:layout_gravity="center_horizontal" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="Image description" android:id="@+id/txtViewImageDesc" android:layout_gravity="center_horizontal" /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/edtImageDesc" android:layout_gravity="center_horizontal" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Add Image" android:id="@+id/btnAddImage" android:layout_gravity="center_horizontal" android:onClick="onClickAddImage" /> <ListView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/lstViewImages" android:layout_gravity="center_horizontal" /> </LinearLayout> </RelativeLayout>
listlayout.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/imgTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/imgUrl" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/imgDesc" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
在上面的代码中,onCreate 函数使用CursorLoader 函数从content provider中获取所有contents,同时传递CONTENT_URI给它。
为了插入值,getContentResolver().insert 方法被调用,同时传递了content URI和ContentValues供插入。运行效果如下:
Android 中的Content provider提供了干净系统级的方式供apps来共享数据和使用其它apps的数据。它标准化了如何在多个apps中共享数据。Android中提供了很多内建的content providers和API,这使得非常容易自定义content providers。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories