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


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)方法,发送一个数据库改变的消息。我们也可以在实际开发中,将自己的数据库改变的消息通过些原理发送出去。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值