使用ContentProvider存储数据_qiqi_686的博客-程序员宝宝

技术标签: andriod  

http://www.codeceo.com/article/5-android-storage.html

数据在Android当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。
一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,
而且ContentProviders是以类似数据库中表的方式将数据暴露,也就是说ContentProvider就像一个“数据库”。
那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URI来表示外界需要访问的“数据库”。

查询记录:  

在Content Provider中使用的查询字符串有别于标准的SQL查询。
很多诸如select, add, delete, modify等操作我们都使用一种特殊的URI来进行,
这种URI由3个部分组成, 

“content://”, 代表数据的路径,和一个可选的标识数据的ID。

以下是一些示例URI:

content://media/internal/images 
这个URI将返回设备上存储的所有图片
content://contacts/people/ 
这个URI将返回设备上的所有联系人信息
content://contacts/people/45 
这个URI返回单个结果(联系人信息中ID为45的联系人记录)

尽管这种查询字符串格式很常见,但是它看起来还是有点令人迷惑。
为此,Android提供一系列的帮助类(在android.provider包下),里面包含了很多以类变量形式给出的查询字符串,
这种方式更容易让我们理解一点,参见下例:

MediaStore.Images.Media.INTERNAL_CONTENT_URI Contacts.People.CONTENT_URI

因此,如上面content://contacts/people/45这个URI就可以写成如下形式:

Uri person = ContentUris.withAppendedId(People.CONTENT_URI, 45);

然后执行数据查询:

 Cursor cur = managedQuery(person, null, null, null);

这个查询返回一个包含所有数据字段的游标,我们可以通过迭代这个游标来获取所有的数据:

package com.wissen.testApp; 
public class ContentProviderDemo extends Activity { 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
       displayRecords(); 
    } 

    private void displayRecords() { 
        //该数组中包含了所有要返回的字段 
     String columns[] = new String[] { People.NAME, People.NUMBER }; 
       Uri mContacts = People.CONTENT_URI; 
       Cursor cur = managedQuery( 
           mContacts, 
          columns,  // 要返回的数据字段 
       null,          // WHERE子句 
       null,         // WHERE 子句的参数 
       null         // Order-by子句 
     ); 
       if (cur.moveToFirst()) { 
           String name = null; 
           String phoneNo = null; 
           do { 
              // 获取字段的值 
         name = cur.getString(cur.getColumnIndex(People.NAME)); 
             phoneNo = cur.getString(cur.getColumnIndex(People.NUMBER)); 
             Toast.makeText(this, name + ” ” + phoneNo, Toast.LENGTH_LONG).show(); 
          } while (cur.moveToNext()); 
       } 
    } 
}


上例示范了一个如何依次读取联系人信息表中的指定数据列name和number。

修改记录:  

我们可以使用ContentResolver.update()方法来修改数据,我们来写一个修改数据的方法:

 

private void updateRecord(int recNo, String name) { 
         Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, recNo); 
         ContentValues values = new ContentValues(); 
         values.put(People.NAME, name); 
         getContentResolver().update(uri, values, null, null); 

    }

现在你可以调用上面的方法来更新指定记录: updateRecord(10, ”XYZ”); //更改第10条记录的name字段值为“XYZ”

添加记录:

要增加记录,我们可以调用ContentResolver.insert()方法,该方法接受一个要增加的记录的目标URI,以及一个包含了新记录值的Map对象,调用后的返回值是新记录的URI,包含记录号。

上面的例子中我们都是基于联系人信息簿这个标准的Content Provider,现在我们继续来创建一个insertRecord() 方法以对联系人信息簿中进行数据的添加:

 

private void insertRecords(String name, String phoneNo) { 
    ContentValues values = new ContentValues(); 
    values.put(People.NAME, name); 
    Uri uri = getContentResolver().insert(People.CONTENT_URI, values); 
    Log.d(”ANDROID”, uri.toString()); 
    Uri numberUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY); 
    values.clear(); 
    values.put(Contacts.Phones.TYPE, People.Phones.TYPE_MOBILE); 
    values.put(People.NUMBER, phoneNo); 
    getContentResolver().insert(numberUri, values); 

}

这样我们就可以调用insertRecords(name, phoneNo)的方式来向联系人信息簿中添加联系人姓名和电话号码。

删除记录:

Content Provider中的getContextResolver.delete()方法可以用来删除记录。

下面的记录用来删除设备上所有的联系人信息:

 

private void deleteRecords() {  

 Uri uri = People.CONTENT_URI;  

 getContentResolver().delete(uri, nullnull);  

    }

你也可以指定WHERE条件语句来删除特定的记录:

getContentResolver().delete(uri, “NAME=” + “‘XYZ XYZ’”, null);

这将会删除name为‘XYZ XYZ’的记录。

创建Content Provider:  

至此我们已经知道如何使用Content Provider了,现在让我们来看下如何自己创建一个Content Provider。

要创建我们自己的Content Provider的话,我们需要遵循以下几步:

1. 创建一个继承了ContentProvider父类的类

2. 定义一个名为CONTENT_URI,并且是public static final的Uri类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称,

如: public static final Uri CONTENT_URI = Uri.parse( “content://com.google.android.MyContentProvider”);

3. 创建你的数据存储系统。大多数Content Provider使用Android文件系统或SQLite数据库来保持数据,但是你也可以以任何你想要的方式来存储。

4. 定义你要返回给客户端的数据列名。如果你正在使用Android数据库,则数据列的使用方式就和你以往所熟悉的其他数据库一样。但是,你必须为其定义一个叫_id的列,它用来表示每条记录的唯一性。

5. 如果你要存储字节型数据,比如位图文件等,那保存该数据的数据列其实是一个表示实际保存文件的URI字符串,客户端通过它来读取对应的文件数据,处理这种数据类型的Content Provider需要实现一个名为_data的字段,_data字段列出了该文件在Android文件系统上的精确路径。这个字段不仅是供客户端使用,而且也可以供ContentResolver使用。客户端可以调用ContentResolver.openOutputStream()方法来处理该URI指向的文件资源,如果是ContentResolver本身的话,由于其持有的权限比客户端要高,所以它能直接访问该数据文件。

6. 声明public static String型的变量,用于指定要从游标处返回的数据列。

7. 查询返回一个Cursor类型的对象。所有执行写操作的方法如insert(), update() 以及delete()都将被监听。我们可以通过使用ContentResover().notifyChange()方法来通知监听器关于数据更新的信息。

8. 在AndroidMenifest.xml中使用标签来设置Content Provider。

9. 如果你要处理的数据类型是一种比较新的类型,你就必须先定义一个新的MIME类型,以供ContentProvider.geType(url)来返回。

MIME类型有两种形式:

一种是为指定的单个记录的,还有一种是为多条记录的。这里给出一种常用的格式: vnd.android.cursor.item/vnd.yourcompanyname.contenttype (单个记录的MIME类型) 比如, 一个请求列车信息的URI如content://com.example.transportationprovider/trains/122 可能就会返回typevnd.android.cursor.item/vnd.example.rail这样一个MIME类型。

vnd.android.cursor.dir/vnd.yourcompanyname.contenttype (多个记录的MIME类型) 比如, 一个请求所有列车信息的URI如content://com.example.transportationprovider/trains 可能就会返回vnd.android.cursor.dir/vnd.example.rail这样一个MIME 类型。

下列代码将创建一个Content Provider,它仅仅是存储用户名称并显示所有的用户名称(使用 SQLLite数据库存储这些数据):

 

package com.wissen.testApp; 
public class MyUsers { 
    public static final String AUTHORITY  = “com.wissen.MyContentProvider”; 

    // BaseColumn类中已经包含了 _id字段 
   public static final class User implements BaseColumns { 
        public static final Uri CONTENT_URI  = Uri.parse(”content://com.wissen.MyContentProvider”); 
        // 表数据列 
     public static final String  USER_NAME  = “USER_NAME”; 
    } 
}

上面的类中定义了Content Provider的CONTENT_URI,以及数据列。下面我们将定义基于上面的类来定义实际的Content Provider类:

 

package com.wissen.testApp.android; 
public class MyContentProvider extends ContentProvider { 
    private SQLiteDatabase     sqlDB; 
    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”; 
    private static final String TAG = “MyContentProvider”; 

    private static class DatabaseHelper extends SQLiteOpenHelper { 
        DatabaseHelper(Context context) { 
            super(context, DATABASE_NAME, null, DATABASE_VERSION); 
        } 

        @Override 
        public void onCreate(SQLiteDatabase db) { 
            //创建用于存储数据的表 
        db.execSQL(”Create table ” + TABLE_NAME + “( _id INTEGER PRIMARY KEY AUTOINCREMENT, USER_NAME TEXT);”); 
        } 

        @Override 
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
            db.execSQL(”DROP TABLE IF EXISTS ” + TABLE_NAME); 
            onCreate(db); 
        } 
    } 

    @Override 
    public int delete(Uri uri, String s, String[] as) { 
        return 0; 
    } 

    @Override 
    public String getType(Uri uri) { 
        return null; 
    } 

    @Override 
    public Uri insert(Uri uri, ContentValues contentvalues) { 
        sqlDB = dbHelper.getWritableDatabase(); 
        long rowId = sqlDB.insert(TABLE_NAME, “”, contentvalues); 
        if (rowId > 0) { 
            Uri rowUri = ContentUris.appendId(MyUsers.User.CONTENT_URI.buildUpon(), rowId).build(); 
            getContext().getContentResolver().notifyChange(rowUri, null); 
            return rowUri; 
        } 
        throw new SQLException(”Failed to insert row into ” + uri); 
    } 

    @Override 
    public boolean onCreate() { 
        dbHelper = new DatabaseHelper(getContext()); 
        return (dbHelper == null) ? false : true; 
    } 

    @Override 
    public 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, nullnullnull, sortOrder); 
        c.setNotificationUri(getContext().getContentResolver(), uri); 
        return c; 
    } 

    @Override 
    public int update(Uri uri, ContentValues contentvalues, String s, String[] as) { 
        return 0; 
    } 
}

一个名为MyContentProvider的Content Provider创建完成了,它用于从Sqlite数据库中添加和读取记录。

Content Provider的入口需要在AndroidManifest.xml中配置:

之后,让我们来使用这个定义好的Content Provider:

 

package com.wissen.testApp; 
public class MyContentDemo extends Activity { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        insertRecord(”MyUser”); 
        displayRecords(); 
    } 

    private void insertRecord(String userName) { 
        ContentValues values = new ContentValues(); 
        values.put(MyUsers.User.USER_NAME, userName); 
        getContentResolver().insert(MyUsers.User.CONTENT_URI, values); 
    } 

    private void displayRecords() { 
        String columns[] = new String[] { MyUsers.User._ID, MyUsers.User.USER_NAME }; 
        Uri myUri = MyUsers.User.CONTENT_URI; 
        Cursor cur = managedQuery(myUri, columns,nullnullnull ); 
        if (cur.moveToFirst()) { 
            String id = null; 
            String userName = null; 
            do { 
                id = cur.getString(cur.getColumnIndex(MyUsers.User._ID)); 
                userName = cur.getString(cur.getColumnIndex(MyUsers.User.USER_NAME)); 
                Toast.makeText(this, id + ” ” + userName, Toast.LENGTH_LONG).show(); 
           } while (cur.moveToNext()); 
       } 
    } 
}

上面的类将先向数据库中添加一条用户数据,然后显示数据库中所有的用户数据。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zzq123686/article/details/52440561

智能推荐

fname matlab,求大神帮我解释一下matlab最后几行是什么意思_蒋寻的博客-程序员宝宝

该楼层疑似违规已被系统折叠隐藏此楼查看此楼clear;if nargin<1;action='initialized';end;[fname,pname]=uigetfile('*.wav','Open Wave File');file=[pname,fname];[x,fs,bits]=wavread(file); % 读入声音文件(*.wav)sound(x,fs,bits...

SSM企业版:多数据源+事务_戰士的博客-程序员宝宝

前言之前博客中《SSM+nginx+tomcat+maven+mysql+redis环境搭建及工程全套配置,实现前后端动静分离》只是初级版的SSM体系建设,在这篇文章之前,可参照:https://blog.csdn.net/qq_36632174/article/details/102461255来了解前博客内容。本篇对上一篇做一个升级,配置多数据源,加入事务、定时任务、流程组件activ...

ArcGIS 9.3下载 ArcGIS 9.2下载(包含ArcGIS Desktop、ArcGIS Engine、ArcGIS Server、ArcSDE、workstation)..._weixin_30564901的博客-程序员宝宝

ArcGIS_Server_Java_Editioned2k://|file|ArcGIS_Server_Java_Edition.rar|1125815962|26CF7DF54987FD597754A67F0ADDF23E|h=SSF6PY4G6S74ZPDH5RPHLVPQ6VDKCZ6H|/ ArcGIS_Server_DotNet_Editioned2k://|file|A...

vlc 运行时配置参数读写的实现分析_王二の黄金时代的博客-程序员宝宝_vlc参数设置

版本 v3.0.16 源码http://get.videolan.org/vlc/3.0.16/vlc-3.0.16.tar.xz每一完整的软件系统都会有一个参数配置模块,用来实现运行时不同的参数输入,有些软件系统会做得比较简单,直接通过方法的输入,作为参数,有些系统做得比较通用复杂,比如vlc,也是比较完整独立的通用的配置。vlc 使用c语言编写,实质是一个多媒体播放器, 有OB面向对象的设计,对一个结构体的创建,可以类比为创建一个对象,vlc都通过使用malloc等函数,动态申请内存来存...

cv2.getRotationMatrix2D()和cv2.warpAffine()_shelleyHLX的博客-程序员宝宝

cv2.getRotationMatrix2D()图像的旋转矩阵一般为: 但是单纯的这个矩阵是在原点处进行变换的,为了能够在任意位置进行旋转变换,opencv采用了另一种方式: 为了构造这个矩阵,opencv提供了一个函数:cv2.getRotatio...

基础算法之排序(1)--冒泡排序_NTSK13的博客-程序员宝宝

/********************************************************************************************************** * Function : test * Create Date : 2014/03/23 * Autho

随便推点

MSP430的内部函数_weixin_30929295的博客-程序员宝宝

内部函数:编译器提供的一些针对目标CPU的特殊函数,以及经过汇编高度优化的常用函数。MSP430的ICC430编译器提供的内部函数如:__delay_cycles(long int cycles)靠CPU空操作延迟cycles个时钟周期。程序中不需要包含任何头文件,可以直接使用内部函数常用精确延时函数如下:#define CPU_F ((double)8000000)#de...

VLC的视频编音频等码格式的缩写_Li Xiaolin的博客-程序员宝宝

在vlc的vlc_fourcc.h文件中使用示例:libvlc_video_set_format(player, "RV32", width, height, width * 4);列出如下/* Video codec */#define VLC_CODEC_MPGV VLC_FOURCC('m','p','g','v')#define VLC_C

[plugin:vite:import-analysis] Failed to parse source for import analysis because the content contain_我的腿毛很可爱的博客-程序员宝宝

[plugin:vite:import-analysis] Failed to parse source for import analysis because the content contains invalid JS syntax. Install @vitejs/plugin-vue to handle .vue files.问题原因问题截图解决方案问题原因vue3+vite的项目,vite从1.xx升级到2.xx问题截图解决方案1、安装"@vitejs/plugin-vue": “^

谷歌开发者大会,3000字记录,好看好吃好玩,还有独家面试..._菜鸟学Python的博客-程序员宝宝

谷歌是一个大部分码农心里都是非常有份量的互联网巨头公司,算算我已经是连续2年参加谷歌开发者大会了。第一次参加也是非常的偶然的机会,然后一发不可收拾,赶着高铁请假2天参加的...

完整好用Dynasim.Dymola.v7.0动态系统仿真建模软件,广泛用在汽车,航空,航天等领域_software2017的博客-程序员宝宝

Zoho.ManageEngine.EventLog.Analyzer.v6.0.x64-SHOCK\Zoho.ManageEngine.MSPCenter.Plus.v7.2-SHOCK\soft\67\Agilent.SystemVue.v2009.05.rarCAM-TOOL V4.0.rarCoWare.Signal.Processing.Designer.v2009.

MLOps极致细节:0. 背景介绍_破浪会有时的博客-程序员宝宝

此博客主要介绍什么是MLOps,为什么用MLOps,以及MLOps与DevOps,破布,敏捷等产品开发流程的区别。

推荐文章

热门文章

相关标签