Android《第一行代码》简要笔记 二
第六章、数据存储
IT行业方向众多,编程语言种类繁多,然而程序设计的核心则无外乎算法
与数据
,这两年概念火热的云计算、大数据、机器学习、人工智能等等,原理也就是基于以上两点。
Android中数据的存储大致分为五类:1、文件。2、SharedPreferences。3、Sqlite数据库。4、Content Provider。5、网络存储。
6.1、文件存储
文件存储,是基于Java IO技术的一个基本存储方式。Android中提供了openFileOutput()
方法用于存储文件到App自身data目录下:/data/data/<package_name>/files/
,在操作文件时,不用指定这个路径。
openFileOutput()有两个参数,一就是file文件名,二是操作模式。Android 4.2之前还有三种模式,现在只剩下MODE_PRIVATE
和MODE_APPEND
两种。
|
|
- 从文件读取数据
openFileInput()
方法,接收一个参数,就是文件名。
|
|
EditText:将光标移动到指定位置edit.setSelection(int position);
6.2、SharedPreferences
SharedPreferences利用键值对的形式保存轻量型的数据。有三种获取SP对象的方式:
Context中getSharedPreferences();
两个参数,一是sp的名称,存放与
/data/data/<package_name>/shared_perfs/
下;二是操作模式,先只有MODE_PRIVATE
可用,4.2版本之后废弃MODE_WORLD_WRITEABLE
和MODE_WORLD_READABLE
。6.0版本废弃MODE_MULTI_PROCESS
模式。Activity中getPreferences();
类似上面,只不过将文件名默认本包名。
PreferenceManager中getDefaultSharedPreferences();
静态方法,接收context,包名为文件名。
SharedPreferences使用方法,三步骤:
- 获取Editor对象
SharedPreferences.Editor
- editor.putString()等操作。
- editor.apply();或commit();
其中commit
会立即提交,而apply
会等合适时机操作。
从SP读取数据只需要使用SP的对象的getString()
之类的方法就可以。
6.3、SQLite数据库
SQLite是一个轻量型的数据库,内部一切字符串形式存储(好象是?)。Android中使用了sqlite并做了很好的封装。
SQLiteOpenHelper
是Android提供的操作数据库的帮助类。其包含getReadableDatabase()
和getWritableDatabase()
两个操作实例。
getReadableDatabase()
获得数据库的读写操作,当磁盘空间已满,则返回只读模式的对象。
getWritableDatabase()
获取数据库的读写操作,当磁盘空间已满,则会抛出异常。
SQLiteOpenHelper有两个构造函数,代码示例:
|
|
在其他调用MyDatabaseHelper
:
|
|
- 升级数据库
数据库创建在onCreate()
方法中,只创建一次,若是已经存在,哪怕改代码使得创建语句不同,依然不能创建新的。若是卸载删除,再新建,会使之前数据丢失。所以要使用onUpgrade()
方法升级数据库。
|
|
6.4、数据库CRUD
数据库的曾删改查Create
、Retrieve
查询、更新Update
、Delete
删除。SQL: Structured Query Language
insert
123456789101112131415...SQLiteDatabase db = helper.getWritableDatabase();//此处的helper是上面的MyDatabaseHelper对象ContentVaules values = new ContentValues();//insert the first datavalues.put("name","Head First Java");values.put("author","Jim Green");...db.insert("book",null,values);//插入数据,第一个参数是表名//清空values的数值,才能第二次使用values.clear();values.put("name","《第一行代码》");values.put("author","郭霖");...db.insert("book",null,values);...update
1234...//参数依次是表名、更新值、更新的列名、对应的数据,也就是选择出这个条件的条目db.update("book",values,"name = ? ",new String[]{"Head First Java"});...delete
1234...//此语句表示,删除页码大于500页的那本书db.delete("book","page > ? ",new String[]{"500"});...search
数据库最为常用的功能便是查询数据,SQLite提供了query方法,最短的也有7个参数:表名、指定列(默认全部)、约束条件、约束条件值、group by、filter、orderby。返回cursor对象。
查询所有数据
123456789101112...Cursor cursor = db.query("book",null,null,null,null,null,null);if(cursor.moveToFirst()){do{//遍历Cursor对象的数据String name = cursor.getString(cursor.getColumnIndex("name"));String author = cursor.getString(cursor.getColumnIndex("author"));...}while(cursor.moveToNext());cursor.close();}
Android同时也提供了原始SQL语句的操作方式:
123456db.exeSQL("insert into book(name,author,pages,price) values (?,?,?,?)",new String[]{"Head First Java","Jim Green","435","20.50"});db.exeSQL("update book set price = ? where name = ?" ,new String[]{"15.7","Head First Java"});//此方法用?站位,避免sql注入危险。db.exeSQL("delete from book where pages > ?",new String[]{"500"});//注意,查询使用的是rawQuerydb.rawQuery("select * from book",null);
6.5、LitePal开源框架
Android 开源的ORM(对象关系映射)模式的数据库框架比较多,GreenDAO、xUtils3、LitePal等。本书介绍的是LitePal。
使用步骤:
在AndroidStudio中配置依赖
12345dependencies{...compile 'org.litepal.android:core:1.4.1'...}在assets目录下创建
litepal.xml
123456<litepal><dbname value="BookStore" ></daname><version value="1"></version><list></list></litepal>在AnroidManifest.xml中配置Application
1234567...<applicationandroid:name="org.litpal.LitePalApplication"...>...</application>所谓的对象关系映射模式,就是将Java Bean对象,直接映射到数据库中的表结构。
- Java Bean:
|
|
- 此时在
litepal.xml
中的list
中配置需要映射的Java Bean类
|
|
在Java代码中创建
1234...//如此便创建了数据库LitePal.getDatabase();...
修改Book的成员属性信息,或者新增一个Cagegory
表,只需要在litepal.xml
中做修改版本号,以及添加对应类名到list中即可。重新运行上面的getDatabase();
|
|
一、向数据库操作数据才是真正的意义所在,所以Book需要继承DataSupport
才可以。
|
|
此时在Activity之中可以
|
|
二、更新
|
|
用条件语句:
|
|
//注意的是,updateAll不能用于设置默认值,比如整数型默认为0或0.0,引用型默认null,LitePal统一方法setToDefault()
|
|
三、删除数据
|
|
四、查询
|
|
第七章、Content Provider
Android中为了保护数据的安全共享,使用了ContentProvider这个组件。Android应用开发涉及权限较多,6.0以后加入了运行时权限,危险类的权限就需要在代码运行时做对应申请和处理。
权限组名 | 权限名称 |
---|---|
CALENDAR | READ_CALENDAR、WRITE_CALENDAR |
CAMERA | CAMERA |
CONTACTS | READ_CONTACTS、WRITE_CONTACTS、GET_ACCOUNTS |
LOCATION | ACCESS_FINE_LOCATION、ACCESS_COARSE_LOCATION |
MICROPHONE | RECORD_AUDIO |
PHONE | READ_PHONE_STATE、CALL_PHONE、READ_CALL_LOG、WRITE_CALL_LOG、ADD_VOICEMAIL、USE_SIP、PROCESS_OUTGING_CALLS |
SENSORS | BODY_SENSORS |
SMS | SEND_SMS、RECEIVE_SMS、READ_SMS、RECEIVE_WAP_PUSH、RECEIVE_MMS |
STORAGE | READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE |
以上均被视为危险权限,需要申请运行时权限处理。同一组权限,有一个申请了,其他的自动拥有。
1、运行时权限申请
|
|
2、Content Provider
可以将Content Provider理解为一个封装很好的SQLite数据操作,其接受的参数不是表名,而是一个uri
分为authority
和path
两部分。前面在加上协议头,完整的就是协议://authority/path
如:
content://com.example.app.provider/table1
使用Uri.parse
将如上uri解析为uri对象。读取Provider需要使用ContentResolver
|
|
接下来使用cursor遍历数据,添加数据都类似sqlite操作。
- 读取联系人示例
|
|
创建Provider对外共享数据
- 继承ContentProvider
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 > public class MyProvider extends ContentProvider {> public static final int TABLE1_DIR = 0;> public static final int TABLE1_ITEM = 1;> public static final int TABLE2_DIR = 2;> public static final int TABLE2_ITEM = 3;>> private static UriMatcher uriMatcher;>> static {> uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);> uriMatcher.addURI("com.example.app.provider","table1",TABLE1_DIR);> uriMatcher.addURI("com.example.app.provider","table1/#",TABLE1_ITEM);> uriMatcher.addURI("com.example.app.provider","table2",TABLE2_DIR);> uriMatcher.addURI("com.example.app.provider","table2/#",TABLE2_ITEM);> }> ...>> public boolean onCreate(){> return false;> }>> public Cursor query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder){> //根据不同的uri来查询不同> switch(uriMatcher.match(uri)){> case TABLE1_DIR:> //查询表1中所有数据> break;> case TABLE1_ITEM:> //查询表1中单条数据> break;> case TABLE2_DIR:>> break;> case TABLE2_ITEM:>> break;> default:> break;> }> return null;> }>> public Uri insert(Uri uri,ContentValues values){> return null;> }>> public int update(Uri uri,ContentValues values,String selection,String[] selectionArgs){> return 0;> }>> public int delete(Uri,String selection,String[] selectionArgs){> return 0;> }> //返回的是MIME类型>> public String getType(Uri uri){> switch(uriMatcher.match(uri)){> case TABLE1_DIR:> return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1"> break;> case TABLE1_ITEM:> return "vnd.android.cursor.item/vnd.com.example.app.provider.table1"> break;> case TABLE2_DIR:>> break;> case TABLE2_ITEM:>> break;> default:> break;> }> return null;> }> }>
>
匹配MIME类型可以用
*
和#
分别表示任意长度字符、任意长度的数字。MIME类型有3部分组成:
- vnd开头
- 如果是路径结尾,则加上
android.cursor.dir/
,若是id结尾,加上android.cursor.item/
- 最后接上
vnd.<authority>.<path>
示例:对应content://com.example.app.provider/table1
这个URI的MIME类型如下:
|
|
示例:对应content://com.example.app.provider/table1/1
这个URI的MIME类型如下:
|
|
注意看清,是item和dir的区别
==provider==在AndroidManifest中注册标签,除了android:name
外,还有个==android:authorities="com.example.app.provider"
==比较重要
|
|
- Git指令
|
|