您的位置:首页 > 移动开发 > Android开发

Android基础学习__第3天__SQLite与数据处理

2013-10-22 20:14 405 查看

1.SQLite

在Android平台上,集成了一个嵌入式关系型数据库—SQLite,SQLite3支持 NULL、INTEGER、REAL(浮点数字)、TEXT(字符串文本)和BLOB(二进制对象)数据类型,虽然它支持的类型只有五种,但实际上sqlite3也接受varchar(n)、char(n)、decimal(p,s) 等数据类型,只不过在运算或保存时会转成对应的五种数据类型。

SQLite最大的特点是你可以把各种类型的数据保存到任何字段中,而不用关心字段声明的数据类型是什么。例如:可以在Integer类型的字段中存放字符串,或者在布尔型字段中存放浮点数,或者在字符型字段中存放日期型值。 但有一种情况例外:定义为INTEGER PRIMARY KEY的字段只能存储64位整数, 当向这种字段保存除整数以外的数据时,将会产生错误。 另外, SQLite 在解析CREATE TABLE
语句时,会忽略 CREATE TABLE 语句中跟在字段名后面的数据类型信息,如下面语句会忽略 name字段的类型信息:

CREATE TABLE person (personid integer primary key autoincrement, name varchar(20))

SQLite可以解析大部分标准SQL语句,如:

查询语句:select * from 表名 where 条件子句 group by 分组字句 having ... order by 排序子句

如:select * from person

select * from person order by id desc

select name from person group by name having count(*)>1

分页SQL与mysql类似,下面SQL语句获取5条记录,跳过前面3条记录

select * from Account limit 5 offset 3 或者 select * from Account limit 3,5

插入语句:insert into 表名(字段列表) values(值列表)。如: insert into person(name, age) values(‘传智’,3)

更新语句:update 表名 set 字段名=值 where 条件子句。如:update person set name=‘传智‘ where id=10

删除语句:delete from 表名 where 条件子句。如:delete from person where id=10

SQLiteOpenHelper抽象类:数库帮助类

要使用数据库,就要先创建出数据库,第二步就要创建表结构。使用SQLiteOperHelper类创建数据库,示例如下:

public class PersonDBOpenHelper extends SQLiteOpenHelper {

public PersonDBOpenHelper(Context context) {
//context 上下文
//name 数据库的名称
//factory 数据库查询结果的游标工厂,使用null会使用系统默认
//version 数据库的版本 >=1
super(context, "person.db", null, 3);
}

/**
* 数据库第一次被创建的时候调用的方法
* @param db 被创建的数据库
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table person (id integer primary key autoincrement , name varchar(20), number varchar(20))");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

}

}


注意:实例化PersonDBOpenHelper类是不会创建数据库的,要使用这个类的对象去调用getWritableDatabase()或者getReadableDatabase()方法之后,数据库才会创建。

数据库路径:data/data/包名/databases/数据库名称

使用SQLiteOpenHelper及系统Api对数据库的增删改查

public class PersonDao2 {
private PersonDBOpenHelper helper;

public PersonDao2(Context context) {
helper = new PersonDBOpenHelper(context);
}

//添加
public boolean add(String name, String phone) {
SQLiteDatabase db = helper.getWritableDatabase();
// db.execSQL("insert into person (name,phone) values (?,?)", new Object[]{name,phone});
ContentValues values = new ContentValues();
values.put("name", name);
values.put("phone", phone);

long id = db.insert("person", null, values);
db.close();
return id != -1;
}

//查找
public boolean find(String name) {
SQLiteDatabase db = helper.getReadableDatabase();

//Cursor cursor = db.rawQuery("select * from peron where name =?", new String[]{name});

Cursor cursor = db.query("person", null, "name=?",
new String[] { name }, null, null, null);

boolean result = cursor.moveToFirst();
cursor.close();
db.close();
return result;
}

/**
* 获取全部的person信息
* @return
*/
public List<Person> findAll(){
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.query("person", null, null, null, null, null, null);
List<Person> persons = new ArrayList<Person>();
while(cursor.moveToNext()){
int id = cursor.getInt(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
String phone = cursor.getString(cursor.getColumnIndex("phone"));
Person person = new Person(name, phone, id);
persons.add(person);
}
cursor.close();
db.close();
return persons;
}

//更新
public boolean update(String name, String newphone) {
SQLiteDatabase db = helper.getReadableDatabase();
// db.execSQL("update person set phone=? where name=?", new Object[]{newphone,name});
ContentValues values = new ContentValues();
values.put("phone", newphone);
int result = db.update("person", values, "name=?",
new String[] { name });

db.close();
return result>0;
}

//删除
public boolean delete(String name) {
SQLiteDatabase  db = helper.getWritableDatabase();
//	db.execSQL("delete from person where name=?", new Object[]{ name});
int result = db.delete("person", "name=?", new String[]{name});
db.close();
return result>0;
}
}

使用系统Api的好处就是不用记住sql操作语句也能对数据库进行操作,而且使用系统Api可以更清楚的看到对数据库的各种操作的结果。

数据库的事务

使用SQLiteDatabase的beginTransaction()方法可以开启一个事务,程序执行到endTransaction() 方法时会检查事务的标志是否为成功,如果程序执行到endTransaction()之前调用了setTransactionSuccessful() 方法设置事务的标志为成功则提交事务,如果没有调用setTransactionSuccessful() 方法则回滚事务。使用例子如下(银行转账):

public void testTransection(){
PersonSQLiteOperHelper helper = new PersonSQLiteOpenHelper(getContext());
SQLiteDatabase db = helper.getWritableDatabase();
db.beginTransaction();
try{
db.execSQL("update person set account=account-1000 where name =?",new Object[]{"zhangsan"});
db.execSQL("update person set account=account+1000 where name =?",new Object[]{"wangwu"});
//标记数据库事务执行成功
db.setTransactionSuccessful();////调用此方法会在执行到endTransaction() 时提交当前事务,如果不调用此方法会回滚事务
}catch(Exception e){

}finally{
//由事务的标志决定是提交事务,还是回滚事务
db.endTransection();
db.close();
}
}

上面两条SQL语句在同一个事务中执行,如果其他一条没有执行成功或者没有执行,整个事务都不会执行,所以不会出现银行转账的时候出现A给B转钱,A扣了钱,B却没收到钱的尴尬。

2.ListView入门

通过ListView将Person数据库的数据呈现出来。

步骤:1.调用listview的setAdapter()的方法。

2.里面传入一个adatper的适配器,覆写getCount()方法,覆写getView(int postion ,View convertView , ViewGroup parent)方法

关键代码:

public class MainActivity extends Activity {

pirvate List<Person> persons;
private ListView lv;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PersonDao dao = new PersonDao(this);
persons = dao.findAll();
lv = (ListView) findViewById(R.id.lv);

lv.setAdapter(new MyAdapter());

}

//默认实现类 simplexxx defaultxxx basexxx
private class MyAdapter extends BaseAdapter{

private static final String TAG = "MyAdapter";

//返回条目数量
public int getCount(){
return persons.size;//条目个数 == 集合的size
}

public Object getItem(int postion){
return null;
}

public long getItemId(int postion){
return 0;
}

public View getView(int position ,View convertView , ViewGroup parent){
Log.i(TAG,"返回view对象,位置:"+postion);
TextView tv = new TextView();
tv.setTextSize(20);
tv.setTextColor(Color.BLACK);
//得到某个位置对应的person对象
Person person = persons.get(postion);
tv.setText(person.toString());
return tv;
}
}
}

3.数据适配器

通过adapter格式化数据显示,原理就是将要显示数据的格式用布局文件设定好,然后通过adapter与ListView关联进行显示,下面将示例将person对象信息显示出来

布局代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dip"
android:gravity="center_vertical"
android:orientation="horizontal" >

<TextView
android:id="@+id/tv_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="id"
android:layout_marginLeft="20dip"
android:textColor="#ff0000"
android:textSize="18sp" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dip"
android:orientation="vertical" >

<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="名字" />
<TextView
android:id="@+id/tv_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="电话" />
</LinearLayout>

</LinearLayout>

重写View getView(int postion ,View convertView , ViewGroup parent)方法

public View getView(int position ,View convertView , ViewGroup parent){
//得到某个位置的person对象
Person person = person.get(position);
View view = View.inflate(MainActivity.this,R.layout.list_item,null);
//一定要在view对象里面寻找孩子的id
TextView tv_id = (TextView) view.findViewById(R.id.tv_id);
tv_id.setText("id:"+person.getId());

TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
tv_id.setText("id:"+person.getName());

TextView tv_number = (TextView) view.findViewById(R.id.tv_number);
tv_id.setText("id:"+person.getNumber());

return view;

}


ArrayAdapter

用于显示比较简单的文本

public class MainActivity extends Activity {

pirvate static String[] names = {"功能1","功能2","功能3","功能4","功能5"}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

ListView lv = (ListView) findViewById(R.id.lv);

//设置listview的数据适配器
lv.setAdapter(new ArrayAdapter<String>(this, R.layout.item, R.id.tv, names));
}
}

xml文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >

<TextView
android:id="@+id/tv"
android:layout_width="match_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="文本" />

</LinearLayout>


SimpleAdapter

可以显示比较复杂的列表,包括每行显示图片、文字等,但不能对列表进行后期加工(在java代码中加工),也是只是单纯的负责显示(当然可以设计复杂点的布局来显示复杂列表),例如,每行显示不同背景等。

public class MainActivity extends Activity {
private ListView lv;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.lv);
// 定义一个list集合 集合里面存放的是 每一个条目 item item数据是一个map的类型
List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();

Map<String, Object> map1 = new HashMap<String, Object>();
map1.put("icon", R.drawable.ic_menu_login);
map1.put("name", "我是第一个功能");

Map<String, Object> map2 = new HashMap<String, Object>();
map2.put("icon", R.drawable.ic_menu_manage);
map2.put("name", "我是第二个功能");

Map<String, Object> map3 = new HashMap<String, Object>();
map3.put("icon", R.drawable.ic_menu_myplaces);
map3.put("name", "我是第三个功能");

Map<String, Object> map4 = new HashMap<String, Object>();
map4.put("icon", R.drawable.ic_menu_notifications);
map4.put("name", "我是第四个功能");

Map<String, Object> map5 = new HashMap<String, Object>();
map5.put("icon", R.drawable.ic_menu_paste);
map5.put("name", "我是第五个功能");

data.add(map1);
data.add(map2);
data.add(map3);
data.add(map4);
data.add(map5);

lv.setAdapter(new SimpleAdapter(this, data, R.layout.layout_item,
new String[] { "icon", "name" }, new int[]{R.id.iv,R.id.tv}));

}
}

xml文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >

<ImageView
android:id="@+id/iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>

显示效果:



4.ContendProvider(内容提供者)

ContentProvider 在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider 对你应用中的数据进行添删改查。使用ContentProvider对外共享数据的好处是统一了数据的访问方式。

对外共享数据处理步骤:

第1步:需要继承ContentProvider类并重写相关方法

第2步:在AndroidManifest.xml使用<provider>对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider , ContentProvider 采用了authorities(主机名/域名)对它进行唯一标识,你可以把ContentProvider看作是一个网站(想想,网站也是提供数据者),authorities 就是他的域名:

<manifest .... >

<application android:icon="@drawable/icon" android:label="@string/app_name">

<provider android:name=".PersonContentProvider" android:authorities="cn.itcast.providers.personprovider"/>

</application>

</manifest>

内容提供者的写法:(将自己的应用的数据提供一个访问方式让别的应该也能访问)

public class PersonDBProvider extends ContentProvider {

//定义一个uri的匹配器,用于匹配uri 如果路径不满足条件返回-1
private static UriMatcher mather = new UriMatcher(UriMatcher.NO_MATCH);//如果发现路径不正确 不匹配 返回-1
private static final int INSERT = 1;
private static final int DELETE = 2;
private static final int UPDATE = 3;
private static final int QUERY = 4;
private PersonDBOpenHelper helper;

//定义uri的匹配规则
static{
//添加一组匹配规则
mather.addURI("com.itheima.db.personprovider", "insert", INSERT);
mather.addURI("com.itheima.db.personprovider", "delete", DELETE);
mather.addURI("com.itheima.db.personprovider", "update", UPDATE);
mather.addURI("com.itheima.db.personprovider", "query", QUERY);
}

// content://com.itheima.db.personprovider/insert 添加
// content://com.itheima.db.personprovider/delete 删除
// content://com.itheima.db.personprovider/update 更新
// content://com.itheima.db.personprovider/query 查找

/**
* 当内容提供者被创建的时候调用适合数据的初始化
*/
@Override
public boolean onCreate() {
helper = new PersonDBOpenHelper(getContext());
return false;
}
//查询数据库
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
int result = mather.match(uri);
if(result==QUERY){
//返回查询的结果集
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.query("person", projection, selection, selectionArgs, null, null, sortOrder);
return cursor;
}else if(result == ID){
int postion = uri.getPath().lastIndexOf("/");
String newid = uri.getPath().substring(postion+1);
System.out.println(uri.getPath());
SQLiteDatabase db = helper.getWritableDatabase();
Cursor cursor = db.query("person", projection, "id=?", new String[]{newid}, null, null, sortOrder);
return cursor;
}
else{
throw new IllegalArgumentException("路径不匹配,不能执行查询操作");
}
}

@Override
public String getType(Uri uri) {
int result = mather.match(uri);
if(result==OK){//口令正确
return "vnd.android.cursor.dir/person";
}else if(result == ID){
return "vnd.android.cursor.item/person";
}
return null;
}

@Override
public Uri insert(Uri uri, ContentValues values) {
int result = mather.match(uri);
if(result==INSERT){
SQLiteDatabase db = helper.getWritableDatabase();
db.insert("person", null, values);
db.close();
}else{
throw new IllegalArgumentException("路径不匹配,不能执行插入操作");
}
return null;
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int result = mather.match(uri);
if(result==DELETE){
SQLiteDatabase db = helper.getWritableDatabase();
db.delete("person", selection, selectionArgs);
db.close();
}else{
throw new IllegalArgumentException("路径不匹配,不能执行删除操作");
}
return 0;
}

@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int result = mather.match(uri);
if(result==UPDATE){//口令正确
SQLiteDatabase db = helper.getWritableDatabase();
db.update("person", values, selection, selectionArgs);
db.close();
}else{
throw new IllegalArgumentException("路径不匹配,不能执行更新操作");
}
return 0;
}
}

写内容提供者的时候,cursor所对应的数据库是不用关闭的

通过内容提供者读取其他程序数据:

public void click(View view){
//得到手机中间人
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://com.itheima.db.personprovider/query");
Cursor cursor = resolver.query(uri,null,null,null,null);
while(cursor.moveToNext()){
String name = cursor.getString(cursor.getColumnIndex("name"));
String id = cursor.getString(cursor.getColumnIndex("id"));
System.out.println("name="+name+" : "+"id="+id);
}
cursor.close();
}

短信的备份

首先查看源代码,查看packages/providers/TelephoneProvider文件夹中的AndroidManifest.xml的清单文件,再查看源码文件,可以得到短信的内容提供者的Uri

public void readSms(View view) {
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms/"); // 全部短信的uri
Cursor cursor = resolver.query(uri, null, null, null, null);
while (cursor.moveToNext()) {
String address = cursor.getString(cursor.getColumnIndex("address"));
long date = cursor.getLong(cursor.getColumnIndex("date"));
String body = cursor.getString(cursor.getColumnIndex("body"));
String type = cursor.getString(cursor.getColumnIndex("type"));
System.out.println("地址:" + address + "  时间" + new Date(date)
+ " 内容:" + body);
if ("1".equals(type)) {
System.out.println("接收到的短信");
} else {
System.out.println("发送的短信");
}
System.out.println("------------------");
}
cursor.close();
}

记得添加android.permission.WRITE_SMS权限

向系统短信应用插入一条短信记录

public void click(View view){
new Thread(){
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}

//插入一条短信
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms/");
ContentValues values = new ContentValues();
values.put("address", "95533");
values.put("date", System.currentTimeMillis());
values.put("type", 1);
values.put("body", "尊敬的用户,你的尾号为558的建行卡,收到汇款人民币888888元,活期余额人民币99999999.52元");
resolver.insert(uri, values);
};
}.start();
}

5.ContentObserver(内容观察者)

观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理

要想实现一个内容观察者,必须在ContentResolver的实例中注册一个ContentObserver:

resovler.registContentObserver(Uri uri,notifyForDescendents,observer),参数解释如下:

uri 需要观察的Uri(需要在UriMatcher里注册,否则该Uri也没有意义了)

notifyForDescendents 这是一个布尔类型的数值,表示是否模糊匹配该uri

observer ContentObserver的派生类实例

使用内容观察者监听短信变化

public class MainActivety extends Activity{

@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreadte(savedInstanceState);
setContentView(R.layout.activety_main);

ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms/");

resolver.registerContentObserver(uri,true,new MyObserver(new Handler()));
}

private class MyObserver extends ContentObserver{
public MyObserver(Handler handler){
super(handler);
}

//当内容观察者观察到是数据库的内容变化了,调用这个方法
//观察到消息邮箱里面有一条数据库内容变化的通知
@Override
public void onChange(boolean selfChange){

super.onChange(selfchange);
Toast.makeText(MainActivity.this,"数据库的内容发生改变了",1).show();
}
}
}

实现原理:当内容发生改变的时候,底层会调用ContentResolver的notifyChange(uri,observer)方法,发送一个数据库改变的消息。我们也可以在实际开发中,将自己的数据库改变的消息通过些原理发送出去。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: