2021SC@SDUSC Zxing开源代码(十四)HistoryManager 代码分析


前言

在前面的博客中,我们已经分析了 Zxing 安卓端的几大主要功能。本次博客将重点分析历史记录功能的相关代码,同时学习 Android 数据库存储的相关知识


一、SQLiteOpenHelper

1. SQLite数据库介绍

  • SQLite是Android内置的一个小型、关系型、属于文本型的数据库。
    Android提供了对 SQLite数据库的完全支持,应用程序中的任何类都可以通过名称来访问任何的数据库,但是应用程序之外的就不能访问。

  • Android中,通过SQLiteOpenHelper类来实现对SQLite数据库的操作。

2. SQLiteOpenHelper类介绍

  • 定义:SQLiteOpenHelper是一个辅助类
  • 作用:管理数据库(创建、增、删、改、查) 以及版本的控制
  • 使用过程:通过创建子类继承SQLiteOpenHelper类,实现它的一些方法来对数据库进行操作
  • SQLiteOpenHelper类的数据库操作方法介绍如下:
方法作用
onCreate()创建数据库
onUpgrade()升级数据库
close()关闭所有打开的数据库对象
execSQL()可进行增删改操作, 不能进行查询操作
query()、rawQuery()查询数据库
insert()插入数据
delete()删除数据
getWritableDatabase()创建或打开可以读/写的数据库
getReadableDatabase()创建或打开可读的数据库

以上只是对 Android 数据库操作知识的基本介绍,具体使用将结合 Zxing 项目的代码来进行分析。


二、代码分析

在 Zxing 项目的主页面 CaptureActivity 中, 应用到历史记录的主要有以下几个地方:

1. 创建 HistoryManager 对象

在 CaptureActivity 的生命周期函数 onResume 中,就首先创建了一个 HistoryManager 对象,并且调用了 trimHistory 方法

    historyManager = new HistoryManager(this);
    historyManager.trimHistory();

HistoryManager 构造方法

  public HistoryManager(Activity activity) {
    this.activity = activity;
    // 根据 activity 获取程序设置
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
    // 是否 enable 历史记录
    enableHistory = prefs.getBoolean(PreferencesActivity.KEY_ENABLE_HISTORY, true);
  }

可知在 HistoryManager 的构造方法中,传入了 CaptureActivity 的实例对象,同时获取程序设置,来判断是否允许使用历史记录功能

DBHelper 类

trimHistory 方法中,就涉及到了 SQLite 数据库的处理。
而可以操作 SQLite 数据库的 SQLiteOpenHelper 是一个抽象类,因此需要首先自己定义一个类对其进行继承,Zxing 项目在 history 包中定义了 DBHelper 类来继承 SQLiteOpenHelper

DBHelper 构造方法

  //在SQLiteOpenHelper的子类当中,必须有该构造函数
  // 参数分别为:上下文对象、数据库名称、CursorFactory、数据库版本号
  DBHelper(Context context) {
    super(context, DB_NAME, null, DB_VERSION);
  }

onCreate 方法
onCreate 方法在数据库第一次创建时被调用,这里在创建数据库的同时,也创建了一个表。可知表中存储了一次扫码结果的条目编号、文字、编码格式、展现内容、时间戳和细节信息几个字段。

  @Override
  public void onCreate(SQLiteDatabase sqLiteDatabase) {
    // 执行SQL语句,创建一个表
    sqLiteDatabase.execSQL(
            "CREATE TABLE " + TABLE_NAME + " (" +
            ID_COL + " INTEGER PRIMARY KEY, " +
            TEXT_COL + " TEXT, " +
            FORMAT_COL + " TEXT, " +
            DISPLAY_COL + " TEXT, " +
            TIMESTAMP_COL + " INTEGER, " +
            DETAILS_COL + " TEXT);");
  }

onUpgrade 方法
当打开数据库时传入的版本号与当前的版本号不同时会调用该方法,可知这里首先删除了已有的数据,并且重新调用了 onCreate 方法来完成升级

  //系统发现现有数据库版本不同,即会调用onUpgrade()方法
  @Override
  public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
    sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
    onCreate(sqLiteDatabase);
  }

HistoryManager. trimHistory

  public void trimHistory() {
    // 创建 DBHelper 实例对象
    SQLiteOpenHelper helper = new DBHelper(activity);
    // 创建(打开)并获取数据库
    try (SQLiteDatabase db = helper.getWritableDatabase();
      // 查询数据库中的条目
     Cursor cursor = db.query(DBHelper.TABLE_NAME,
                                  ID_COL_PROJECTION,
                                  null, null, null, null,
                                  DBHelper.TIMESTAMP_COL + " DESC")) {
      // 移动到 Zxing 项目所允许存留的最大条目数处
      cursor.move(MAX_ITEMS);
      // 超过 MAX_ITEMS 部分的历史记录应当删除
      while (cursor.moveToNext()) {
        String id = cursor.getString(0);
        Log.i(TAG, "Deleting scan history ID " + id);
        db.delete(DBHelper.TABLE_NAME, DBHelper.ID_COL + '=' + id, null);
      }
    } catch (SQLException sqle) {
      Log.w(TAG, sqle);
    }
  }

可知在 HistoryManager 的 trimHistory 方法中,主要完成的任务就是清理数据库中过多的历史记录条目。

这里注意一下获取数据库对象的流程:

  • 首先创建一个 SQLiteOpenHelper 对象
  • 然后调用该对象的 getWritableDatabase 或 getReadableDatabase 方法来获得SQLiteDatabase 对象
  • 在 getWritableDatabase 和 getReadableDatabase 方法中,会判断指定的数据库是否存在,不存在则调用 SQLiteOpenHelper.onCreate 方法来创建数据库

2. 保存扫码结果条目

在前面的博客中,我们已经分析了整个扫码处理的流程,其中 CaptureActivity 的 handleDecode 方法,就对扫码得到的结果进行了各种处理,有代码:

historyManager.addHistoryItem(rawResult, resultHandler);

下面我们看一下 HistoryManager 的这个方法

HistoryManager.addHistoryItem

  public void addHistoryItem(Result result, ResultHandler handler) {
  	// 如果设置了不保留历史记录,那么直接返回
    if (!activity.getIntent().getBooleanExtra(Intents.Scan.SAVE_HISTORY, true) ||
        handler.areContentsSecure() || !enableHistory) {
      return;
    }
	// 获取程序设置,判断是否允许保留重复的扫码条目。如果不允许则调用 deletePrevious  方法,将重复的旧的条目删除
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
    if (!prefs.getBoolean(PreferencesActivity.KEY_REMEMBER_DUPLICATES, false)) {
      deletePrevious(result.getText());
    }
	// 创建插入的字段值
    ContentValues values = new ContentValues();
    values.put(DBHelper.TEXT_COL, result.getText());
    values.put(DBHelper.FORMAT_COL, result.getBarcodeFormat().toString());
    values.put(DBHelper.DISPLAY_COL, handler.getDisplayContents().toString());
    values.put(DBHelper.TIMESTAMP_COL, System.currentTimeMillis());
	// 创建 DBHelper 对象
    SQLiteOpenHelper helper = new DBHelper(activity);
    try (SQLiteDatabase db = helper.getWritableDatabase()) {
      // 将新条目加入数据库
      db.insert(DBHelper.TABLE_NAME, DBHelper.TIMESTAMP_COL, values);
    } catch (SQLException sqle) {
      Log.w(TAG, sqle);
    }
  }

这样就成功地保存了扫码结果到数据库

3. 点击“历史记录”按钮

这里类似前面分析过的 ShareActivity 代码部分的操作
在 CaptureAcitivity 的 onOptionsItemSelected 方法中,也对点击“历史记录”按钮进行了响应

      case R.id.menu_history:
        intent.setClassName(this, HistoryActivity.class.getName());
        startActivityForResult(intent, HISTORY_REQUEST_CODE);
        break;

其使用了意图跳转方法,也就是说在这里会跳转到 HistoryActivity 的界面,而在 HistoryActivity 中处理完毕后,会回调到 CaptureActivity 的 onActivityResult 方法来对结果进行处理
基本流程如下:
在这里插入图片描述
在第十篇博客中,我们介绍了 Android Adapter 体系,Adapter 完成对数据和界面的适配。一些不能直接赋值到界面上的数据类型,就需要靠适配器来展示到页面

  • HistoryActivity 页面中的条目就是通过继承了 ArrayAdapter 的 HistoryItemAdapter 来完成的适配显示
  • 在页面初始构造时,除了加载布局文件、设置适配器,还调用了 HistoryManager 的
    buildHistoryItems() 方法,来获取所有的历史记录
  • 最终在点击历史记录条目时,跳转回了 CaptureActivity 来处理

CaptureActivity.onActivityResult

  public void onActivityResult(int requestCode, int resultCode, Intent intent) {
    if (resultCode == RESULT_OK && requestCode == HISTORY_REQUEST_CODE && historyManager != null) {
      // 获取点击的历史记录条目号
      int itemNumber = intent.getIntExtra(Intents.History.ITEM_NUMBER, -1);
      if (itemNumber >= 0) {
        // 根据 HistoryManager 找到对应在数据库中的内容
        HistoryItem historyItem = historyManager.buildHistoryItem(itemNumber);
        // 显示返回结果(因为数据库中没有存储 Bitmap,这里无法显示二维码图片)
        decodeOrStoreSavedBitmap(null, historyItem.getResult());
      }
    }
  }

可知在方法中,根据返回点击的历史条目号,来查询数据库找到了对应的存储内容,而后调用 decodeOrStoreSavedBitmap 方法显示返回结果
而在 decodeOrStoreSavedBitmap 中有:

	Message message = Message.obtain(handler, R.id.decode_succeeded, savedResultToShow);
	handler.sendMessage(message);

这里向 CaptureActivityHandler 发送消息来处理,之后的流程与之前在博客(六)中分析的类似,在 CaptureActivityHandler.handleMessage 中处理消息,再调用 CaptureActivity 的 handleMessage 方法,在页面显示结果。
在这里插入图片描述
这里显示的是数据库中存储的内容,没有二维码图片


总结

通过本次代码分析,理清了在 Zxing 项目中历史记录的处理流程,同时学习了 Android 数据存储的相关知识

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值