本文系原创博客,文中不妥烦请指出,如需转载摘要请注明出处!

ContentProvider的学习与运用

Alpha Dog

2016-04-13  10:27:06

  首先,项目的地址:https://github.com/DarkPointK/MyContentProvider.git

  网上对于Android的Content Provider框架以及SQLite这款轻量级的嵌入式数据库的介绍有很多,我不再复述,在此我将着重于如何实现对数据库的操作。

  工作之余开始系统性的自主学习,在阅读了网络上很多大牛的各类技术文章教程后,想试着写一篇属于自己的博客,以记录一些心得成果还可给其他正在学习的朋友一点帮助,而对于文中存在的不妥之处还烦请指出。

  这是一个布局很简单的例子,主要内容在于后台对一个SQLite表的增删改查一系列操作。

  • “增”:进入应用时“删”“改”按钮不可用,在ET中输入想要添加的字符串后点击“增”弹出新增数据的URI(关于URI的介绍可以通过这片不错的文章了解:http://www.cnblogs.com/linjiqin/archive/2011/05/28/2061396.html);
  • “查”:会根据ET中输入的文字在表中搜索并将搜索到的行的_id显示在TV上,此时“删”“改”按钮变成可用;
  • “删”:被点击后也将使这两按钮不可用,并删除TV中显示的ID的行,在下次点击“查”并查得数据时按钮又被启用;
  • “改”:当查询到数据并在TV中显示ID时可以在ET中输入新的字符串,点击按钮即可改变相应ID行的数据为此字符串并会刷新搜索队列。

  而数据库的表在被创建后会存在于/data/data/package_name/databases文件夹中,如有需要在windows环境下查看表可以利用AS的Device Monitor导出:

  我们建立一个自己的项目在AS中,在这个项目中我们至多需要3个类,分别为:继承自SQLiteOpenHelper的类用于直接对数据库操作,继承自ContentProvider的类会被用于决定以何种方式操作数据库,然后就是我们的Activity。

  1.1继承自SQLiteOpenHelper的StringDataBase类:

  在继承SQLiteOpenHelper后我们需要重写它的onCreate()方法和onUpgrade()方法并且实现构造方法。当StringDataBase被初始化时,其构造方法将提交数据库名及其版本号给父类的方法得到处理。首次打开数据库onCreate()方法会被调用将会处理SQL_CREATE字串中的SQL语句,创建数据库得到一个“string”表。当版本号得到提升将调用onUpgrade()方法。

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; /**
* Created by Alpha Dog on 2016/4/8.
*/
public class StringDataBase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "StringDatabase";
private static final String TABLE_NAME = "string";
private static final String KEY_NAME = "str"; private static final String SQL_CREATE =
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + KEY_NAME + " TEXT)";
private static final String SQL_DROP = "DROP TABLE IS EXISTS" + TABLE_NAME; public StringDataBase(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);
} }

  重写完这两个方法后,开始着手完善将会被StringProvider类用到的所有对数据库增删改查操作的方法。

  在这一阶段,你可能会发现这里对数据库的操作都是通过以getWritableDataBase()方法获得一个可以操作数据库的SQLiteDatabase实例,且有读写权(此外,有getReadableDatabase()方法可获得只读权实例)。在获得SQLiteDatabase的实例后,调用相应的方法并将返回的结果作为返回值,当在StringProvider类中得到这些返回值时,可以进行下一步的处理。

public long addString(ContentValues values) throws SQLException {

        long id = getWritableDatabase().insert(TABLE_NAME, "", values);
if (id <= 0) {
throw new SQLException("Failed to add String");
} return id;
} public int deleteString(String id) {
if (id == null) {
return getWritableDatabase().delete(TABLE_NAME, null, null);
} else {
return getWritableDatabase().delete(TABLE_NAME, "_id=?", new String[]{id});
} } public int updateString( ContentValues values,String id) {
return getWritableDatabase().update(TABLE_NAME,values,"_id=?", new String[]{id});
} public Cursor getString(String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder sqb = new SQLiteQueryBuilder();
sqb.setTables(TABLE_NAME); if (sortOrder == null || sortOrder == "") {
sortOrder = "_id";
} return sqb.query(getReadableDatabase(), projection, selection, selectionArgs, null, null, sortOrder);
}

  对于SQLiteDatabase及SQLiteQueryBuilder在这里调用到的那些增删改查方法的参数列表有必要在这里细说:

    • getWritableDatabase().insert (String table, String nullColumnHack, ContentValues values)  第一个参数为要操作的表名;第三个参数类似于需要插入的Map,键为列名,值为值;第二个参数不为空时,当values的值为空将会在指定列安全的插入一个null值;插入失败返回-1。
    • getWritableDatabase().delete (String table, String whereClause, String[] whereArgs);  第二个参数是String的语句用于作为SQL的where语句部分,如果null将删除所有行;第三个参数是在whereClause中含有?号时将其取代并绑定;删除成功返回受影响的行数目,否则返回0。
    • getWritableDatabase().update (String table, ContentValues values, String whereClause, String[] whereArgs); 参考以上。update方法将会选择表,查询行,更新列,返回受影响的行数目; 
    • SQLiteQueryBuilder.query (SQLiteDatabase db, String[] projectionIn, String selection, String[] selectionArgs, String groupBy, String having, String sortOrder);  有了以上的解释,根据query的参数名及这个SDK的API文档链接了解它们的作用  http://www.android-doc.com/reference/android/database/sqlite/SQLiteQueryBuilder.html#query%28android.database.sqlite.SQLiteDatabase,%20java.lang.String[],%20java.lang.String,%20java.lang.String[],%20java.lang.String,%20java.lang.String,%20java.lang.String%29

  1.2继承自ContentProvider的StringProvider类:

  ContentProvider需要重写的方法,这些方法将在获取ContentProvider的实例后可以被利用URI调用:

  由于需要URI连接的原因,我们需要在AndroidManifest.xml中注册这个Provider:

<provider
android:authorities="com.alphadog.mycontentprovider"
android:name="com.alphadog.mycontentprovider.StringProvider"/>
</application>

  其中authorities中的字段将作为URI中关键的一部分,用于请求Provider权限:  content://com.alphadog.mycontentprovider/

  name中的参数指定的是这个Provider具体提供服务的类。

  好了,注册完Provider之后,现在来看看这个StringProvider里究竟要写些什么:

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; /**
* Created by Alpha Dog on 2016/4/8.
*/
public class StringProvider extends ContentProvider { private static final String PROVIDER_NAME = "com.alphadog.mycontentprovider";
private static final Uri CONTENT_URI = Uri.parse("content://" + PROVIDER_NAME + "/string");
private static final int STR = 1;
private static final int STRS = 2;
private StringDataBase sdb = null; private static final UriMatcher um = getUriMatcher(); private static UriMatcher getUriMatcher() {
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, "string/#", STR);
uriMatcher.addURI(PROVIDER_NAME, "string", STRS);
return uriMatcher;
} //初始化StringDatabase
@Override
public boolean onCreate() {
Context context = getContext();
sdb = new StringDataBase(context);
return true;
} @Override
public String getType(Uri uri) {
switch (um.match(uri)) {
case STR:
return "vnd.android.cursor.item/vnd.com.alphadog.mycontentprovider.string";
case STRS:
return "vnd.android.cursor.dir/vnd.com.alphadog.mycontentprovider.string";
} return "";
} //增
@Override
public Uri insert(Uri uri, ContentValues values) { try {
long id = sdb.addString(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) { return sdb.deleteString(selection);
} //改
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return sdb.updateString( values,selection);
} //查 @Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return sdb.getString(projection,selection,selectionArgs,sortOrder);
}
}

  这里的代码为便于理解也非常简洁,onCreate()里显示对StringDataBase进行了初始化,这将导致连接或创建数据库,而其他的增删改查方法也是调用这个初始化过sdb里的方法对数据库进行相应的操作。其中,getType()方法可以被外部调用,它会分析传入的URI字串并返回一个MIME字串,关于MIME的具体意义可以参考这篇博客:http://blog.csdn.net/harvic880925/article/details/44620851

  2.1 Activity中的调用

  至此,一个自定义的Provide就算大功告成了,剩下的便是对这个Provider的使用方法了,这是个令人激动的阶段,对此可以参考下我写这个Activity,然后根据你的需求进行优化改动。

import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast; import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick; import static android.widget.Toast.LENGTH_SHORT; public class MainActivity extends AppCompatActivity { private static final String PROVIDER_NAME = "com.alphadog.mycontentprovider";
private static final Uri CONTENT_URI = Uri.parse("content://" + PROVIDER_NAME + "/string"); private Cursor c;
private String s;
private boolean fresh = true;
private Uri seleUri;
private int id; @InjectView(R.id.tv)
TextView tv;
@InjectView(R.id.et)
EditText et;
@InjectView(R.id.add)
Button add;
@InjectView(R.id.del)
Button del;
@InjectView(R.id.update)
Button update;
@InjectView(R.id.query)
Button query; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this); update.setEnabled(false);
del.setEnabled(false); et.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
} @Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
fresh = true;
} @Override
public void afterTextChanged(Editable s) {
}
});
} @OnClick({R.id.add, R.id.del, R.id.update, R.id.query})
public void onClick(View view) {
switch (view.getId()) {
case R.id.add:
add();
break;
case R.id.del:
del();
break;
case R.id.update:
update();
break;
case R.id.query:
query();
break;
}
} private void add() {
ContentValues cv = new ContentValues();
cv.put("str", et.getText().toString());
Uri uri = getContentResolver().insert(CONTENT_URI, cv);
if (uri != null) {
Toast.makeText(getBaseContext(), uri.toString(), LENGTH_SHORT).show();
}
} private void del() { // 条件只传id,因为在StringDataBase中有完整处理where语句
int uri = getContentResolver().delete(CONTENT_URI, "" + id, null);
Toast.makeText(getBaseContext(), "删除成功" + id, LENGTH_SHORT).show(); update.setEnabled(false);
del.setEnabled(false);
fresh = true;
} private void update() {
ContentValues cv = new ContentValues(); if (!c.getString(1).equals(et.getText().toString())) {
cv.put("str", et.getText().toString());
int uri = getContentResolver().update(CONTENT_URI, cv, "" + id, null);
Log.i("修改id为" + id + "的值为", " " + et.getText().toString());
Toast.makeText(getBaseContext(), "修改成功" + id+"为"+et.getText().toString(), LENGTH_SHORT).show();
fresh = true;
} else {
Toast.makeText(this, "没做任何更改!", LENGTH_SHORT).show();
fresh = false;
}
} private void query() {
s = et.getText().toString();
if (fresh&& !s.equals("") ) {
s = "str = '" + et.getText().toString() + "'";
Log.i("正在搜索的s为", s); Cursor cursor = getContentResolver().query(CONTENT_URI, null, s, null, null); if (cursor == null || cursor.getCount() <= 0) {
Toast.makeText(this, "然而什么都没搜到", LENGTH_SHORT).show();
return;
}
c = cursor;
}
display();
} private void display() {
if (c != null && c.moveToNext()) {
update.setEnabled(true);
del.setEnabled(true);
Log.i("c", c.getString(0) + " " + c.getString(1));
tv.setText(c.getString(0));
id = c.getInt(0);
Toast.makeText(this, "正在查找:"+c.getString(1)+" = "+id, LENGTH_SHORT).show(); fresh = false;
} else {
tv.setText("");
update.setEnabled(false);
del.setEnabled(false);
Toast.makeText(this, "搜索结束,队列将刷新并从头开始", LENGTH_SHORT).show();
fresh = true;
}
} }

  这一块的内容主要是描述“增删改查”操作时逻辑上的判断,本文的开头已经具体描述。每个操作都是先得到一个ContentResolver实例然后以URI及必要参数传入调用方法。需要注意的是在query()方法查询到数据后返回的是cursor类型,取值的方法具体参考:http://www.cnblogs.com/TerryBlog/archive/2010/07/05/1771459.html

一、Android四大框架之ContentProvider的学习与运用,实现SQLite的增删改查。的更多相关文章

  1. Android SQLite 数据库 增删改查操作

    Android SQLite 数据库 增删改查操作 转载▼ 一.使用嵌入式关系型SQLite数据库存储数据 在Android平台上,集成了一个嵌入式关系型数据库--SQLite,SQLite3支持NU ...

  2. ssm学习(四)--完整的增删改查demo

    上一篇文章简要介绍了将sping mvc加入整个框架,算是完成了ssm的集成.本节继续前面的内容,结合spring mvc做一个简单的增删改查demo. 1.首先,重写一下GeckoList.jsp页 ...

  3. ASP.NET学习笔记(3)——用户增删改查(三层)

    说明(2017-10-6 11:21:58): 1. 十一放假在家也没写几行代码,本来还想着利用假期把asp.net看完,结果天天喝酒睡觉,回去的票也没买到,惨.. 2. 断断续续的把用户信息的页面写 ...

  4. .NET ORM框架HiSql实战-第二章-使用Hisql实现菜单管理(增删改查)

    一.引言 上一篇.NET ORM框架HiSql实战-第一章-集成HiSql 已经完成了Hisql的引入,本节就把 项目中的菜单管理改成hisql的方式实现. 菜单管理界面如图: 二.修改增删改查相关代 ...

  5. Android学习笔记_9_SQLiteOpenHelper对象之数据库增删改查以及事务回滚操作

    一.SQLite数据库: 在Android平台上,集成了一个嵌入式关系型数据库—SQLite,SQLite3支持 NULL.INTEGER.REAL(浮点数字).TEXT(字符串文本)和BLOB(二进 ...

  6. Android SQLite数据库增删改查操作

    一.使用嵌入式关系型SQLite数据库存储数据 在Android平台上,集成了一个嵌入式关系型数据库——SQLite,SQLite3支持NULL.INTEGER.REAL(浮点数字). TEXT(字符 ...

  7. mongo学习笔记(一):增删改查

    安装:我是按这篇来弄的 一.Insert 1.db.person.insert({"name":"jack","age":20}) 2.va ...

  8. Mybatis框架基于映射文件和配置文件的方式,实现增删改查,可以打印日志信息

    首先在lib下导入: 与打印日志信息有关的架包 log4j-1.2.16.jar mybatis架包:mybatis-3.1.1.jar 连接数据库的架包:mysql-connector-java-5 ...

  9. Mybatis框架基于注解的方式,实对数据现增删改查

    编写Mybatis代码,与spring不一样,不需要导入插件,只需导入架包即可: 在lib下 导入mybatis架包:mybatis-3.1.1.jarmysql驱动架包:mysql-connecto ...

随机推荐

  1. 兼容ie7以上的 placeholder属性

    最近项目踩过的坑,不考虑ie的可以拐弯绕路走了. css3的新属性 占位符 placeholder用着多舒服 . 偏偏万恶的ie不支持,网上有几种方法是用焦点事件代替的,不过会失去原有的特性.一旦获取 ...

  2. java代码中init method和destroy method的三种使用方式

    在java的实际开发过程中,我们可能常常需要使用到init method和destroy method,比如初始化一个对象(bean)后立即初始化(加载)一些数据,在销毁一个对象之前进行垃圾回收等等. ...

  3. MTBF

    MTBF,即平均故障间隔时间,英文全称是"Mean Time Between Failure".是衡量一个产品(尤其是电器产品)的可靠性指标.单位为"小时".它 ...

  4. zTree实现多选树

    zTree实现多选树 1.实现源码 <!DOCTYPE html> <html> <head> <title>多选树</title> < ...

  5. Ajax前后台交互函数

    function AJAX(obj){ //做网络请求的时候参数以对象的形式传递进来 //我们规定obj里面包含属性:url, //请求方式type, //date前端给后端传递的参数 //回调函数s ...

  6. 最新数组方法(包括es6)

    整理目前所用过的数组方法,学习了新增的es6方法. 1 arr.push() 从后面添加元素,返回值为添加完后的数组的长度 let arr = [1,2,3,4,5] console.log(arr. ...

  7. class-感知机Perception

    1 感知机模型1.1 模型定义2 感知机学习策略2.1 数据的线性可分性2.2 学习策略3 学习算法3.1 算法原始形式3.2 收敛性3 学习算法的对偶形式 1 感知机模型 感知机perceptron ...

  8. Java开发速度神器Lombok,Eclipse端安装使用教程

    一.Lombok简介 Lombok是一个代码生成器,可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法. 使用 ...

  9. jQuery拓展简易快速实现触摸效果

    1.js代码 //触摸开始事件,改变元素的样式 function touchstart(e) { $(this).removeClass("touchend").addClass( ...

  10. SpringBoot 文件上传临时文件路径问题

    年后放假回来,一向运行OK的项目突然图片上传不了了,后台报错日志如下: java.io.IOException: The temporary upload location [/tmp/tomcat. ...