contentResolver and ContentProvider

本文详细介绍了Android平台上多种数据存储方式,包括SharedPreferences、文件存储、SQLite数据库存储及ContentProvider的使用方法。

Android 数据库的操作
android平台中可以用来存储数据的方式很多,平时常用的有sharedpreference、file存储、sqlite数据库存储以用网络存储几种基本的存储方法。
下面比较详细的介绍如何实现数据的存储和获取:
轻量级的SharedPreferences存储,它是Android提供用来存储一些简单的配置信息的一种机制,例如,一些默认欢迎语、登录的用户名和密码等。其以键值对的方式存储,使得我们可以很方便的读取和存入。这个界面有点类似J2EE平台中的简单的
登陆界面。例如手机平台中游戏或者应用程序登陆之前要输入个人的用户名和密码的数据,我们就可以用SharedPreferences来存储数据。
在我们的上海电信项目中的登录界面就用到sharedPreferences来保存数据。SharedPreferences是通过Context 类的 getSharedPreferences方法操作的。preferences储存在文件中,而文件名用来指向preferences。
getSharedPreferences(String,int)。在一个类里面实例化,就可以在其它地方共享这些数据。目前这个类不支持多进程使用。
下面的代码是怎么从prefecences中读取数据:

    • public static final String PREF_FILE_NAME = “PrefFile”;

    • SharedPreferences preferences = getSharedPreferences(PREF_FILE_NAME, MODE_PRIVATE); int storedPreference = preferences.getInt(”storedInt”, 0); // use the values

复制代码
其 中MODE_PRIVATE 是 preferences 的操作模式,这是默认的模式,代表只有创建这个 preferences 的程序才能访问这个preferences。其他两个模式是MODE_WORLD_READABL和 MODE_WORLD_WRITEABLE: 1、MODE_WORLD_READABLE 表示其他程序对这个SharedPreferences只有只读权限。

2、MODE_WORLD_WRITEABLE则是其他程序同时拥有读写权限。
Activity Preferences:
shared preferences 可以被其他程序的组件使用。但是如果你不需要和其他组件共享 preferences,而是希望一个在activities内设一个私有的 preferences。可以用 activity 类的 getPreferences() 方法来实现。getPreference 方法用 activity 类的名字作为参数调用 getSharedPreference()。
上海电信项目中就提供了一个可以供其它程序的组件使用的SharedPreferences,以下是怎么获得SharedPreferences对象的代码:
SharedPreferences sp = this.getPreferences(Context.MODE_PRIVATE);
用 SharedPreference.Editor 往 preference 文件中保存数据。Editor 是 SharedPreference 的内部接口。
API中提供了

 

    • abstract SharedPreferences.Editor edit () 通过这个方法返回一个SharedPrefenences.Editor实例化对象
    • abstract SharedPreferences.Editor putBoolean (String key, boolean value)----设置boolean值存储
    • abstract SharedPreferences.Editor putFloat (String key, float value)----设置浮点型数据存储
    • abstract SharedPreferences.Editor putInt (String key, int value)----设置整形数据存储
    • abstract SharedPreferences.Editor putLong (String key, long value)----设置长整型 abstract SharedPreferences.Editor putString (String key, String value)---设置字符串数据存储


我们通过以下代码来保存用户登录的数据:(在LoginActivity类中)

 

    • Editor editor = sp.edit();
    • editor.putBoolean("ischecked", checked);// value to store
    • editor.putString("username", checked ? username : "");
    • editor.putString("password", checked ? password : "");
    • editor.commit();


当然我们也可以用SharedPreference.Editor 来remove和clear来删除数据。
那我们用SharedPreference保存的在android平台的哪个位置呢?SharedPreference是以xml格式自动保存的,在 DDMS中的File Explorer中展开到/data/data/<package name>/<project name>下可以找到一个XXX.xml的文件,在xml文档中保存了设置进去的值。整体效率来看不是特别的高,对于常规的轻量级而言比 SQLite要好不少,如果真的存储量不大可以考虑自己定义文件格式。xml处理时Dalvik会通过自带底层的本地XML Parser解析,比如XMLpull方式,这样对于内存资源占用比较好。
文件存储和传统的java文件操作一样,在android Api中提供了openFileInput和openFileOutput方法来读取设备上的文件。但是在默认状态下,文件是不能在不同的程序之间共享 的,以上两个方法只支持读取该应用目录下的文件,读取非其自身目录下的文件将会抛出 FileNotFoundException异常。创建的文件存放在/data/data/<package name>/files目录下。关于文件流的操作是属于J2se的基本知识,这里就不再重复了,不了解的可以看看anroid的API。特别注意一 点使用openFileOutput方法创建的文件只能被其调用的应用使用,其他应用无法读取这个文件,如果需要在不同的应用中共享数据,可以使用 Content Provider实现,关于Content Provider我们将在稍后的内容中介绍。如果我们要用到的一些额外的资源文件,我们可以把这些文件放在应用程序的/raw/raw下,那么就可以在你 的应用中使用getResources获取资源后,以openRawResource方法(不带后缀的资源文件名)打开这个文件。

 

    • Resources myResources = getResources();
    • InputStream myFile = myResources.openRawResource(R.raw.myfilename);


除了前面介绍的读写文件外,Android还提供了诸如deleteFile、fileList 等方法来操作文件,不再赘述。
Sqlite数据库存储和ContentProvider对象介绍。
Android应用的另外一个方面:数据存储。用户可以将自己的数据存储到文件系统或者数据库当中,当然最经常的是,用户将自己的数据存储到SQLite 数据库当中。SQLite是Android所带的一个标准的数据库,它支持SQL语句,它是一个轻量级的嵌入式数据库。在我们上海电信和joyque电子 书项目中sqlite数据库存储和ContentProvider对象是联合在一起使用的。
下面我们来了解ContentProvider和ContentResolver以及ContentProvider和ContentResolver中都用到的URI.
1、ContentProvider对象。因为Android这个系统和其他的操作系统还不太一样,读者需要记住的是,数据在Android当中是私有 的,当然这些数据包括文件数据和数据库数据 以及一些其他类型的数据。那这个时候有读者就会提出问题,难道两个程序之间就没有办法对于数据进行交换?Android这么优秀的系统不会让这种情况发生 的。解决这个问题主要靠ContentProvider。一个Content Provider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。也就是说,一个程序可以通过实现一个Content Provider的抽象接口将自己的数据暴露出去。外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件 存储,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数 据,当然,中间也会涉及一些权限的问题。下边列举一些较常见的接口,这些接口如下所示。

 

    • query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
    • insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
    • update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
    • delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。

复制代码
我们可以参考上海电信项目中GamesProvider,joyque电子书项目的ReaderProvider都继承了ContentProvider对象,并且都实现了以上的接口。

 


2.什么是ContentResolver。外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据,在 Activity当中通过 getContentResolver()可以得到当前应用的ContentResolver实例。ContentResolver提供的接口和 ContentProvider中需要实现的接口对应,主要有以下几个。

  1. query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
  2. insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
  3. update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
  4. delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。

在上海电信项目中的SocketService类,我们可以看到service层是怎么更新,插入,删除数据的。

  1. ContentResolver conResolver;
  2. conResolver.insert(Games.GameData.CONTENT_URI, cv);
  3. conResolver.insert(Games.GameTypeData.CONTENT_URI, cv);
  4. conResolver.insert(Games.GameTopicData.CONTENT_URI, cv);
  5. /**
  6. * 删除数据
  7. */
  8. private void deleteData() {
  9. conResolver.delete(Games.GameTopicData.CONTENT_URI, null, null);
  10. conResolver.delete(Games.GameTypeData.CONTENT_URI, null, null);
  11. conResolver.delete(Games.GameData.CONTENT_URI, Games.GameData.STATE+ "!=" + Games.GameData.STATE_INSTALLED, null);
  12. }
  13. /**
  14. * 更新游戏表中的数据
  15. * @param gi
  16. * 游戏数据
  17. */
  18. private void updateGamesItemData(GamesItem gi) {
  19. ContentValues cv = gi.toContentValues();
  20. conResolver.update(Uri.withAppendedPath(Games.GameData.CONTENT_URI,
  21. String.valueOf(gi.get_id())), cv, null, null);
  22. }

 

3.ContentProvider和ContentResolver中用到的Uri。
在ContentProvider和ContentResolver当中用到了Uri的形式通常有两种,一种是指定全部数据,另一种是指定某个ID的数据。我们看下面的例子。
content://contacts/people/ 这个Uri指定的就是全部的联系人数据。
content://contacts/people/1 这个Uri指定的是ID为1的联系人的数据。在上边两个类中用到的Uri一般由3部分组成
第一部分是:"content://" 。
第二部分是要获得数据的一个字符串片段。最后就是ID(如果没有指定ID,那么表示返回全部)。
由于URI通常比较长,而且有时候容易出错,且难以理解。所以,在Android当中定义了一些辅助类,并且定义了一些常量来代替这些长字符串的使用,例 如下边的代码:Contacts.People.CONTENT_URI (联系人的URI)。像上面的Games.GameTopicData.CONTENT_URI、 Games.GameTypeData.CONTENT_URI、Games.GameData.CONTENT_URI 表示游戏分栏,游戏分类,游戏数据的URI。

### ### ContentProvider进行数据库操作的方法 在Android中,ContentProvider提供了一种标准化的方式,用于在不同应用程序之间共享数据。通过ContentProvider,可以将数据库操作封装为统一的接口,其他应用可以通过URI访问这些数据,类似于访问数据库中的表[^1]。 #### 创建ContentProvider 首先,需要定义一个继承自`ContentProvider`的类,并实现其方法。该类需要定义URI、数据类型(MIME类型)、并实现增删改查操作。URI的格式通常是`content://authority/path`,其中`authority`是ContentProvider的唯一标识,`path`表示具体的数据路径[^3]。 ```java import android.content.ContentProvider; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; public class PersonDBProvider extends ContentProvider { private static final String AUTHORITY = "cn.itcast.contentprovider.personprovider"; private static final String BASE_PATH = "persons"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH); private static final int PERSONS = 1; private static final int PERSON_ID = 2; private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { uriMatcher.addURI(AUTHORITY, BASE_PATH, PERSONS); uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", PERSON_ID); } private SQLiteDatabase database; @Override public boolean onCreate() { DatabaseHelper dbHelper = new DatabaseHelper(getContext()); database = dbHelper.getWritableDatabase(); return database != null; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Cursor cursor; switch (uriMatcher.match(uri)) { case PERSON_ID: String id = uri.getLastPathSegment(); String where = DatabaseHelper.COLUMN_ID + "=" + id; if (selection != null && !selection.isEmpty()) { where += " AND " + selection; } cursor = database.query(DatabaseHelper.TABLE_USERS, projection, where, selectionArgs, null, null, sortOrder); break; default: cursor = database.query(DatabaseHelper.TABLE_USERS, projection, selection, selectionArgs, null, null, sortOrder); break; } if (getContext() != null) { cursor.setNotificationUri(getContext().getContentResolver(), uri); } return cursor; } @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case PERSONS: return "vnd.android.cursor.dir/vnd.cn.itcast.person"; case PERSON_ID: return "vnd.android.cursor.item/vnd.cn.itcast.person"; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } } @Override public Uri insert(Uri uri, ContentValues values) { long id = database.insert(DatabaseHelper.TABLE_USERS, null, values); return Uri.parse(BASE_PATH + "/" + id); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count; switch (uriMatcher.match(uri)) { case PERSON_ID: String id = uri.getLastPathSegment(); String where = DatabaseHelper.COLUMN_ID + "=" + id; if (selection != null && !selection.isEmpty()) { where += " AND " + selection; } count = database.delete(DatabaseHelper.TABLE_USERS, where, selectionArgs); break; default: count = database.delete(DatabaseHelper.TABLE_USERS, selection, selectionArgs); break; } return count; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int count; switch (uriMatcher.match(uri)) { case PERSON_ID: String id = uri.getLastPathSegment(); String where = DatabaseHelper.COLUMN_ID + "=" + id; if (selection != null && !selection.isEmpty()) { where += " AND " + selection; } count = database.update(DatabaseHelper.TABLE_USERS, values, where, selectionArgs); break; default: count = database.update(DatabaseHelper.TABLE_USERS, values, selection, selectionArgs); break; } return count; } } ``` #### 注册ContentProvider 在`AndroidManifest.xml`中注册该ContentProvider,并指定其`authority`属性[^4]: ```xml <provider android:name="cn.itcast.contentprovider.PersonDBProvider" android:authorities="cn.itcast.contentprovider.personprovider" android:exported="true" /> ``` #### 使用ContentProvider进行数据库操作 外部应用可以通过`ContentResolver`访问ContentProvider提供的数据。以下是一个示例代码,展示如何通过ContentResolver进行增删改查操作: ```java import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; public class ContentProviderActivity extends AppCompatActivity { private static final Uri CONTENT_URI = Uri.parse("content://cn.itcast.contentprovider.personprovider/persons"); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_content_provider); ContentResolver contentResolver = getContentResolver(); // 插入数据 ContentValues values = new ContentValues(); values.put("name", "John Doe"); values.put("email", "john@example.com"); contentResolver.insert(CONTENT_URI, values); // 查询数据 Cursor cursor = contentResolver.query(CONTENT_URI, null, null, null, null); if (cursor != null && cursor.moveToFirst()) { do { String name = cursor.getString(cursor.getColumnIndex("name")); String email = cursor.getString(cursor.getColumnIndex("email")); System.out.println("Name: " + name + ", Email: " + email); } while (cursor.moveToNext()); } // 更新数据 ContentValues updateValues = new ContentValues(); updateValues.put("name", "Jane Doe"); contentResolver.update(CONTENT_URI, updateValues, "name = ?", new String[]{"John Doe"}); // 删除数据 contentResolver.delete(CONTENT_URI, "name = ?", new String[]{"Jane Doe"}); } } ``` 通过上述方式,可以利用ContentProvider实现跨应用的数据库操作,并且提供统一的访问接口[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值