Android nrg格式,《Android Programming BNRG》笔记十四

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

本章引入了SQLite数据库,把原先使用Singleton记录在内存的数据,永久保存到了数据库上。使得app具备的数据保存的能力。

本章要点:SQLite数据的操作步骤

1. 创建数据库

首先派生SQLiteOpenHelper的子类,用于初始化数据库:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23public class extends SQLiteOpenHelper{

private static final int VERSION = 1;

private static final String DATABASE_NAME = "crimeBase.db";

public (Context context){

super(context, DATABASE_NAME, null, VERSION);

}

public void onCreate(SQLiteDatabase db){

db.execSQL("create table " + CrimeDbSchema.CrimeTable.NAME + "(" +

" _id integer primary key autoincrement, "+

CrimeDbSchema.CrimeTable.Cols.UUID + ", " +

CrimeDbSchema.CrimeTable.Cols.TITLE + ", " +

CrimeDbSchema.CrimeTable.Cols.DATE + ", " +

CrimeDbSchema.CrimeTable.Cols.SOLVED + ")");

}

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

}

}

在调用端数据库的代码如下:1

2

3

4

5// CrimeLab.java

private CrimeLab(Context context){

mContext = context.getApplicationContext();

mDatabase = new CrimeBaseHelper(mContext).getWritableDatabase();

}

它不用关心是否要创建、初始化数据库等问题,这些工作由CrimeBaseHelper::getWritableDatabase()完成:打开目录/data/data//databases/,如果其下不存在数据库文件crimeBase.db则新建

如果是首次创建数据库,则调用CrimeBaseHelper::onCreate(SQLiteDatabase)并保存VERSION

如果不是首次,则检查数据库的版本号,如果代码版本号高于数据库文件版本号,则调用onUpgrade(SQLiteDatabase, int, int)完成数据格式升级。

因此我们只需要在onCreate(SQLiteDatabase)中实现数据库创建的代码,在onUpgrade(SQLiteDatabase, int, int)中实现数据格式升级的代码即可。

封装数据库Schema

在上文中使用了数据库的表名、列名,这些数据数据库Schema的部分,需要自己封装,都是一些常量:1

2

3

4

5

6

7

8

9

10

11

12// CrimeDbSchema.java

public class CrimeDbSchema{

public static final class CrimeTable{

public static final String NAME = "crimes";

public static final class Cols{

public static final String UUID = "uuid";

public static final String TITLE = "title";

public static final String DATE = "date";

public static final String SOLVED = "solved";

}

}

}

2.添加新数据

用来操作数据库的CrimeBaseHelper实例保存在客户端代码CrimeLab::mDatabase中,对其增删改查也都是基于此实例完成的。使用CrimeBaseHapler::insert(...)添加新数据,代码如下:1

2

3

4

5

6

7

8

9

10

11

12

13

14// CrimeLab.java

public void addCrime(Crime c){

ContentValues values = getContentValues(c);

mDatabase.insert(CrimeDbSchema.CrimeTable.NAME, null, values);

}

...

private static ContentValues getContentValues(Crime crime){

ContentValues values = new ContentValues();

values.put(CrimeDbSchema.CrimeTable.Cols.UUID, crime.getId().toString());

values.put(CrimeDbSchema.CrimeTable.Cols.TITLE, crime.getTitle());

values.put(CrimeDbSchema.CrimeTable.Cols.DATE, crime.getDate().getTime());

values.put(CrimeDbSchema.CrimeTable.Cols.SOLVED, crime.isSolved() ? 1:0);

return values;

}

使用ContentValues

写数据库的时候需要过把数据封装成ContentValues实例,它类似HashMap或Bundle,内部保存了key-value对。在函数getContentValues(Crime)中,把业务层数据转成ContentValues实例。

调用insert(...)时,第1个参数是表名,第3个是要插入的值。第2个参数被称为nullColumnHack,如果insert的某个字段为空,SQLite的插入操作会失败,此时给nullColumnHack传入一个uuid就可以绕过这个问题。但是,如果数据库的设计要求某字段不能为空,还是应该按照数据库的设计意图填入内容,而不是绕过,因此建议不要使用这个tricky参数,总是传入null。

3.修改数据

和添加类似,修改也是在客户端操作CrimeLab::mDatabase对象,使用CrimeBaseHapler::update(...):1

2

3

4

5

6

7

8

9// CrimeLab.java

public void updateCrime(Crime crime){

String uuidString = crime.getId().toString();

ContentValues values = getContentValues(crime);

mDatabase.update(CrimeDbSchema.CrimeTable.NAME, values,

CrimeDbSchema.CrimeTable.Cols.UUID + "=?", // whereClause

new String[]{uuidString}); // whereArgs

}

和insert(...)的不同在于它要指定要修改哪一条数据,通过crimeId来指定。这里没有使用where语句,而是通过=?传入等号的右值参数,这样做的好处在于安全。如果使用where语句,形式如where ABC=“xxx”,等号右侧的xxx本应传入字符串,但是可以构造出特殊的字串来篡改整句SQL文,这被称为SQL注入攻击。使用=?可以把右值参数仅当作字符变量来处理,而不会变成SQL代码,从而避免了被注入的风险。

4.查询数据

直接上代码:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41// CrimeLab.java

private CrimeCursorWrapper queryCrimes(String whereClause, String[] whereArgs){

Cursor cursor = mDatabase.query(CrimeDbSchema.CrimeTable.NAME,

null, // columns - null selects all columns

whereClause,

whereArgs,

null, //groupBy

null, // having

null // orderBy

);

return new CrimeCursorWrapper(cursor);

}

public List getCrimes(){

List crimes = new ArrayList<>();

CrimeCursorWrapper cursor = queryCrimes(null, null);

try{

cursor.moveToFirst();

while(!cursor.isAfterLast()){

crimes.add(cursor.getCrime());

cursor.moveToNext();

}

}finally{

cursor.close();

}

return crimes;

}

public Crime getCrime(UUID id){

CrimeCursorWrapper cursor = queryCrimes(CrimeDbSchema.CrimeTable.Cols.UUID + "=?",

new String[] {id.toString()});

try{

if(cursor.getCount() == 0){

return null;

}

cursor.moveToFirst();

return cursor.getCrime();

}finally {

cursor.close();

}

}

其中whereClause和whereArgs参数和update(...)一致,需要关注最后一句CrimeCursorWrapper类。前文讲过,在写数据库的时候需要把数据封装成ContentValues实例,在读取数据的时候,SQLite则是以Cursor实例输出的,这属于底层机制层,CrimeCursorWrapper的作用就是隐藏掉数据库细节,转换成业务层数据:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20// CrimeCursorWrapper.java

public class CrimeCursorWrapper extends CursorWrapper{

public CrimeCursorWrapper(Cursor cursor){

super(cursor); // 内部保存了cursor

}

public Crime getCrime(){

// 从cursor提取每一个字段

String uuidString = getString(getColumnIndex(CrimeDbSchema.CrimeTable.Cols.UUID));

String title = getString(getColumnIndex(CrimeDbSchema.CrimeTable.Cols.TITLE));

long date = getLong(getColumnIndex(CrimeDbSchema.CrimeTable.Cols.DATE));

int isSolved = getInt(getColumnIndex(CrimeDbSchema.CrimeTable.Cols.SOLVED));

// 组织成crime实例

Crime crime = new Crime(UUID.fromString(uuidString));

crime.setTitle(title);

crime.setDate(new Date(date));

crime.setSolved(isSolved != 0);

return crime;

}

}

以上就是数据库的操作部分,本节的剩余部分就是引入数据库后对于业务层带来的变化了。

引入SQLite给业务层带来的变化

添加记录

点击“+”菜单,由CrimeListFragment::onOptionsItemSelected(...)响应:1

2

3

4

5

6

7

8

9

10

11

12

13// CrimeListFragment.java

public boolean onOptionsItemSelected(MenuItem item){

switch (item.getItemId()){

case R.id.new_crime:

Crime crime = new Crime();

CrimeLab.get(getActivity()).addCrime(crime);

Intent intent = CrimePagerActivity.newIntent(getActivity(), crime.getId());

startActivity(intent);

return true;

...

}

}

它调用到CrimeLab::addCrime(...)完成入库。

修改数据

当从CrimeFragment中退出回到CrimeListFragment时,需要完成数据的修改:1

2

3

4

5

6// CrimeFragment.java

public void onPause(){

super.onPause();

CrimeLab.get(getActivity()).updateCrime(mCrime);

}

它调用到CrimeLab::updateCrime(...)完成数据库的更新。

查询数据

当CrimeListFragment首次加载时,调用CrimeListFragment::onCreateView(...) > CrimeListFragment::updateUI() > CrimeLab::getCrimes() ;

当从CrimeFragment退回到CrimeListFragment时,调用CrimeListFragment::onResume() > CrimeListFragment::updateUI() > CrimeLab::getCrimes();

CrimeLab::getCrimes()。

从数据库中查询到数据,传递给CrimeAdapter,完成UI更新:1

2

3

4

5

6

7

8

9

10

11

12

13// CrimeListFragment.java

private void updateUI(){

CrimeLab crimeLab = CrimeLab.get(getActivity());

List crimes = crimeLab.getCrimes();

if(mAdapter == null) {

mAdapter = new CrimeAdapter(crimes);

mCrimeRecyclerView.setAdapter(mAdapter);

}else{

mAdapter.setCrimes(crimes);

mAdapter.notifyDataSetChanged();

}

updateSubtitle();

}

使用Android Device Monitor查看文件

Android Studio菜单 Tools > Android > Android Device Monitor,弹出Disable ADB Integration对话框,点击“No”:

0fc9a7ce381f2d5da3bb24a83178f023.png

在Android Device Monitor的File Explorer中就可以看到Android设备的文件了:

9f7a6a26dbc4ad62db22e3b4dc415a8d.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值