一、Android四大框架之ContentProvider的学习与运用,实现SQLite的增删改查。
本文系原创博客,文中不妥烦请指出,如需转载摘要请注明出处!
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的增删改查。的更多相关文章
- Android SQLite 数据库 增删改查操作
Android SQLite 数据库 增删改查操作 转载▼ 一.使用嵌入式关系型SQLite数据库存储数据 在Android平台上,集成了一个嵌入式关系型数据库--SQLite,SQLite3支持NU ...
- ssm学习(四)--完整的增删改查demo
上一篇文章简要介绍了将sping mvc加入整个框架,算是完成了ssm的集成.本节继续前面的内容,结合spring mvc做一个简单的增删改查demo. 1.首先,重写一下GeckoList.jsp页 ...
- ASP.NET学习笔记(3)——用户增删改查(三层)
说明(2017-10-6 11:21:58): 1. 十一放假在家也没写几行代码,本来还想着利用假期把asp.net看完,结果天天喝酒睡觉,回去的票也没买到,惨.. 2. 断断续续的把用户信息的页面写 ...
- .NET ORM框架HiSql实战-第二章-使用Hisql实现菜单管理(增删改查)
一.引言 上一篇.NET ORM框架HiSql实战-第一章-集成HiSql 已经完成了Hisql的引入,本节就把 项目中的菜单管理改成hisql的方式实现. 菜单管理界面如图: 二.修改增删改查相关代 ...
- Android学习笔记_9_SQLiteOpenHelper对象之数据库增删改查以及事务回滚操作
一.SQLite数据库: 在Android平台上,集成了一个嵌入式关系型数据库—SQLite,SQLite3支持 NULL.INTEGER.REAL(浮点数字).TEXT(字符串文本)和BLOB(二进 ...
- Android SQLite数据库增删改查操作
一.使用嵌入式关系型SQLite数据库存储数据 在Android平台上,集成了一个嵌入式关系型数据库——SQLite,SQLite3支持NULL.INTEGER.REAL(浮点数字). TEXT(字符 ...
- mongo学习笔记(一):增删改查
安装:我是按这篇来弄的 一.Insert 1.db.person.insert({"name":"jack","age":20}) 2.va ...
- Mybatis框架基于映射文件和配置文件的方式,实现增删改查,可以打印日志信息
首先在lib下导入: 与打印日志信息有关的架包 log4j-1.2.16.jar mybatis架包:mybatis-3.1.1.jar 连接数据库的架包:mysql-connector-java-5 ...
- Mybatis框架基于注解的方式,实对数据现增删改查
编写Mybatis代码,与spring不一样,不需要导入插件,只需导入架包即可: 在lib下 导入mybatis架包:mybatis-3.1.1.jarmysql驱动架包:mysql-connecto ...
随机推荐
- MSQL的基准测试
Mysql基准测试 基准测试 直接.简单.易于比较,用于评估服务器的处理能力 压力测试 对真实的月数据进行测试,获得真是系统所能承受的压力 基准测试的目的 1.建立MySQL服务器的性能基准线 2.模 ...
- qwe框架- CNN 实现
CNN实现 概述 我在qwe中有两种,第一种是按照Ng课程中的写法,多层循环嵌套得到每次的"小方格",然后WX+b,这样的做法是最简单,直观.但是效率极其慢.基本跑个10张以内图片 ...
- VS2010 如何添加H文件目录和LIB目录
第一次使用VS2010,也是初学者开始编写VC++,程序首先学习编写DLL文件,编译完自己的DLL文件后,要在其它项目中使用,开始遇到很多错,但是在网上搜索了好久后,终于解决了问题. H文件目录: 依 ...
- JXL组件生成报表报错(二)
JXL组件生成报表 1.具体报错如下: usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonam ...
- Caused by: org.h2.jdbc.JdbcSQLException: Table "T_STUDENT_INFO" not found; SQL statement
1.错误描述 org.hibernate.exception.SQLGrammarException: error executing work at org.hibernate.exception. ...
- 如何注册Filter
AX文件的一个对外接口DllRegisterServer,由外部调用,比如注册AX的时候:regsvr32 xxx.ax 通常情况下,我们的filter可能注册在"Direct ...
- hdu5937 Equation
题解其实网上有 突然有点感想 为什么可以用搜索或状压,因为方案数很有限,它要求每种方案不同就意味着搜索的次数也一定,所以现在就应该坚定往这方面想,找部分方案的贪心.这和上一题一样,都是先暴力,后面处理 ...
- html中的div span和frameset框架标签
Div和span 1. div独占一层,由div九不允许有别的. 2. span标签不是独自占用一行,span一般用来设置字体. 框架标签: 什么是框架标签,就是把一个页面分成很多 ...
- 【转】MySQL乐观锁在分布式场景下的实践
背景 在电商购物的场景下,当我们点击购物时,后端服务就会对相应的商品进行减库存操作.在单实例部署的情况,我们可以简单地使用JVM提供的锁机制对减库存操作进行加锁,防止多个用户同时点击购买后导致的库存不 ...
- [JZOJ5511] 送你一个DAG
题目描述: 给出一个 \(n\) 个点 \(m\) 条边的 \(DAG\) 和参数 \(k\). 定义一条经过 \(l\) 条边的路径的权值为 \(l^k\). 对于 \(i = 1-n\), 求出所 ...