文章目录
前言
在前面的博客中,我们已经分析完了 Zxing 项目的扫码、编码、分享、历史记录等主要功能所涉及的代码。本次博客将分析剩余的一些辅助功能
一、活动计时器 InactivityTimer
在 Zxing 项目中,因为涉及到了调用摄像头等操作,如果程序一直处于运行状态,则是对手机电量的一种耗费。因此设置了机制,如果一段时间不进行操作,会关闭程序,其主要实现逻辑就在类 InactivityTimer
中
活动计时器 InactitityTimer 的逻辑与主活动 CaptureActivity 的生命周期函数息息相关,如下图:
InactivityTimer 构造方法
在 CaptureActivity 启动后,就创建了 InactivityTimer 的实例对象,并将此时的 CaptureActivity 对象作为参数传入,首先看一下 InactivityTimer 的构造方法
InactivityTimer(Activity activity) {
this.activity = activity;
powerStatusReceiver = new PowerStatusReceiver();
registered = false;
onActivity();
}
这里涉及到了一个 InactivityTimer 的内部类 PowerStatusReceiver
,继承了 BroadcastReceiver 类
BroadcastReceiver,也就是广播接收者,用来接收来自系统和应用的广播。其可以接收到系统开机完成的广播、系统电量不足的广播、系统收到短信的广播等等。在接收到广播后程序就可以进行相应的处理操作
当收到注册的广播时,onReceive方法就会被调用,其中参数context是上下文,Intent 就是广播携带的数据
private final class PowerStatusReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 如果系统发出电池变化广播
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
// 判断是否在充电(0表示正在充电)
boolean onBatteryNow = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) <= 0;
if (onBatteryNow) { // 未在充电,则开启监听任务
InactivityTimer.this.onActivity();
} else { // 如果在充电,则取消任务
InactivityTimer.this.cancel();
}
}
}
}
InactivityTimer 的作用就是为了防止程序一直处于运行状态,耗费电量,因此这里就通过获取系统广播,来判断此时设备是否处于充电状态。如果设备此时在充电,那么就不必担心电量的耗费,可以直接取消 InactivityTimer ,否则的话就需要开启 InactivityTimer 的计时监听任务。
在 InactivityTimer 的构造方法中,在创建了一个电量状态接收者类 PowerStatusReceiver 后(此时尚未对其进行注册),调用了 onActivity 方法,下面看一下这个方法的代码
InactivityTimer.onActivity
onActivity 方法中,创建了一个 InactivityTimer 的内部类 InactivityAsyncTask
的实例对象
InactivityAsyncTask 继承了 AsyncTask 类,我们在博客(九)中对其有过介绍,AsyncTask 是由 Android 封装的一个轻量级异步类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程
而 doInBackground 就是该类的一个核心方法,用以处理子线程的后台任务
private final class InactivityAsyncTask extends AsyncTask<Object,Object,Object> {
@Override
protected Object doInBackground(Object... objects) {
try {
// 线程陷入睡眠
Thread.sleep(INACTIVITY_DELAY_MS);
Log.i(TAG, "Finishing activity due to inactivity");
// 因为一直没有进行操作结束活动(注意这里的 activity 是传递进来的 CaptureActivity 实例对象)
activity.finish();
} catch (InterruptedException e) {
// continue without killing
}
return null;
}
}
由此便可以得出,在 InactivityTimer 中,专门有一个子线程来一直执行着后台任务,在休眠指定时间后,如果一直没有对程序进行操作,则直接结束主活动。
下面看一下 onActivity 的代码:
synchronized void onActivity() {
// 终止现存的 inactivityTask
cancel();
// 创建一个新的 inactivityTask 对象
inactivityTask = new InactivityAsyncTask();
try {
// 使 inactivityTask 开始执行任务
inactivityTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} catch (RejectedExecutionException ree) {
Log.w(TAG, "Couldn't schedule inactivity task; ignoring");
}
}
这里需要注意的是,在 onActivity 的一开始调用了 cancel() 方法,首先终止了现存的 inactivityTask,而后再对其创建新的实例对象,也就是说,每次调用 onActivity 方法,都会重新开启计时任务,重新进行计时
InactivityTimer 的其他方法
- onResume:我们注意到,在前面 InactivityTimer 的构造方法中,只是创建了广播接收者对象,并没有对其进行注册。那么 powerStatusReceiver 的注册接收就放到了 onResume 方法:
activity.registerReceiver(powerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED))
。在注册完广播接收器后,调用 onActivity 函数,开启计时任务 - onPause:若主活动 CaptureActivity 被其他 Activity 覆盖或者锁屏,就会执行CaptureActivity.onPause,其中调用了 InactivityTimer.onPause,其主要任务就是停止当前的计时任务,并且通过
activity.unregisterReceiver(powerStatusReceiver)
,取消注册广播接收器 - shutdown:调用 cancel 方法,终止后台计时任务
二、程序设置页面 PreferencesActivity
在 Android 中,有一个专门用来实现程序设置界面及参数存储的 Activity——PreferencesActivity,官方建议 PreferenceActivity 与 PreferenceFragment 结合使用,其中 PreferenceActivity 负责加载选项配置列表的布局文件,PreferenceFragment 负责加载选项配置的布局文件
Fragment是一种可以嵌入在活动中的UI片段,能够让程序更加合理和充分地利用大屏幕的空间,可以将其看成一个小型Activity,又称作Activity片段。
Fragment不能够单独使用,需要嵌套在Activity中使用,其生命周期也受到宿主Activity的生命周期的影响
Zxing 项目的程序设置页面如下:
PreferenceActivity
在 PreferenceActivity 的代码中,只重写了生命周期函数 onCreate
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
getFragmentManager().beginTransaction().replace(android.R.id.content, new PreferencesFragment()).commit();
}
这里直接创建了一个 PreferencesFragment 对象,并将其覆盖在了 PreferenceActivity 之上
PreferencesFragment
public final class PreferencesFragment
extends PreferenceFragment
implements SharedPreferences.OnSharedPreferenceChangeListener
首先注意到,PreferencesFragment 继承了 PreferenceFragment 类,同时实现了 SharedPreferences.OnSharedPreferenceChangeListener 接口,以监听设置参数的改变
SharedPreferences 是一个轻量级的存储类,采用键值对的方式存储,适用于保存软件配置参数。利用 SharedPreferences 可以实现应用内数据的共享,因此在 PreferencesFragment 中完成某项设置后,其余的类就可以直接通过 PreferenceManager.getDefaultSharedPreferences(context)
来获取保存的设置
PreferencesFragment.onCreate
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// 加载 xml布局文件
addPreferencesFromResource(R.xml.preferences);
// 创建 PreferenceScreen 对象
PreferenceScreen preferences = getPreferenceScreen();
//注册SharedPreference设置监听器
preferences.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
...
在 onCreate 方法中,首先做了三件事:
- 加载 xml 布局文件
- 创建一个 PreferenceScreen 的对象作为 PreferencesFragment 基本布局的根容器
- 为 SharedPreference 的改变设置监听器
而后的代码是对设置勾选框(CheckBoxPreference)的一些处理:
// 搜索 SharedPreference,根据key找到value(CheckBoxPreference对象),放到checkBoxPrefs数组中
checkBoxPrefs = findDecodePrefs(preferences,
PreferencesActivity.KEY_DECODE_1D_PRODUCT,
PreferencesActivity.KEY_DECODE_1D_INDUSTRIAL,
PreferencesActivity.KEY_DECODE_QR,
PreferencesActivity.KEY_DECODE_DATA_MATRIX,
PreferencesActivity.KEY_DECODE_AZTEC,
PreferencesActivity.KEY_DECODE_PDF417);
// 更新勾选设置
disableLastCheckedPref();
}
CheckBoxPreference 也是 Android 中,与设置相关的一个类。其复选框选中为 true,取消选中为 false ,值会以boolean的形式储存在SharedPreferences中
PreferencesFragment.disableLastCheckedPref
private void disableLastCheckedPref() {
Collection<CheckBoxPreference> checked = new ArrayList<>(checkBoxPrefs.length);
// 遍历 CheckBoxPreference 设置数组中的每一项,判断是否勾选
for (CheckBoxPreference pref : checkBoxPrefs) {
// 如果勾选上了,就加到 checked 数组中
if (pref.isChecked()) {
checked.add(pref);
}
}
// 如果没有被勾选中的设置时,disable为true
boolean disable = checked.size() <= 1;
for (CheckBoxPreference pref : checkBoxPrefs) {
// 使 checked 数组中的设置生效
pref.setEnabled(!(disable && checked.contains(pref)));
}
}
可见在该方法中,就是获取了所有勾选中的设置,并且使其生效
在 PreferencesFragment 中重写的 onSharedPreferenceChanged 方法,就是直接调用了该函数,来更新用户选择的设置
总结
通过此次代码分析,对 Zxing 项目中的辅助功能有了更深的了解。同时学习了 Android 中 Preference 的相关处理操作