目录
正文
zram writeback开关
将settings的值存储到属性中,然后再执行zram writeback的schedule,正常情况下属性值是1和0这两种,如果没有值的话,则表示没有打开。
frameworks/base/core/java/android/provider/Settings.java
public static final String ZRAM_ENABLED =
"zram_enabled";
frameworks/base/services/core/java/com/android/server/StorageManagerService.java
/* Read during boot to decide whether to enable zram when available */
private static final String ZRAM_ENABLED_PROPERTY =
"persist.sys.zram_enabled";
// storage service起来之后,首先执行的动作
private void handleSystemReady() {
// 监听Settings的ZRAM_ENABLED,如果发生变动,则触发refreshZramSettings()
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ZRAM_ENABLED),
false /*notifyForDescendants*/,
new ContentObserver(null /* current thread */) {
@Override
public void onChange(boolean selfChange) {
refreshZramSettings();
}
});
refreshZramSettings();
// persist.sys.zram_enabled 判断是否为非0,并且config_zramWriteback设置为true
// 则进行writeback的job的调度
String zramPropValue = SystemProperties.get(ZRAM_ENABLED_PROPERTY);
if (!zramPropValue.equals("0")
&& mContext.getResources().getBoolean(
com.android.internal.R.bool.config_zramWriteback)) {
ZramWriteback.scheduleZramWriteback(mContext);
}
}
private void refreshZramSettings() {
String propertyValue = SystemProperties.get(ZRAM_ENABLED_PROPERTY);
if ("".equals(propertyValue)) {
return; // System doesn't have zram toggling support
}
String desiredPropertyValue =
Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ZRAM_ENABLED,
1) != 0
? "1" : "0";
// 如果属性值跟settings里面的值不相等,则更新属性里面的值
if (!desiredPropertyValue.equals(propertyValue)) {
SystemProperties.set(ZRAM_ENABLED_PROPERTY, desiredPropertyValue);
// 如果settings里面的值是1,则进行调度。
if (desiredPropertyValue.equals("1")
&& mContext.getResources().getBoolean(
com.android.internal.R.bool.config_zramWriteback)) {
ZramWriteback.scheduleZramWriteback(mContext);
}
}
}
JobScheduler的API介绍
public class JobSchedulerService extends JobService{
@Overrid
public boolean onStartJob(JobParameters params){
return false;
}
@Override
public boolean onStopJob(JobParameters params){
return false;
}
}
onStartJob
当系统要触发执行我们的Job的时候,会调用onStartJob方法。这个方法会返回一个布尔型的值。
当返回false的时候,系统会认为onStartJob这个方法返回的时候,我们要做的工作已经做完了,这不是一个耗时的工作。
当返回true的时候,系统会认为我们要执行一个耗时的工作,在onStartJob这个方法返回的时候,我们的工作仍然在异步执行。当我们的工作执行完成的时候,我们必须手动调用jobFinished(JobParameters params, boolean needRescheduled)
务必要注意,如果onStartJob返回了true,在异步工作执行完成之后,我们必须手动调用jobFinished方法,如果不调用jobFinished,系统会一直认为我们在执行当前Job,那么系统就不会再入队其他的Job去执行,也就是说JobScheduler的执行队列就会被阻塞了
onStopJob
当系统收到一个cancel job的请求时,并且这个job仍然在执行,系统就会调用onStopJob方法。
也就是说在系统收到取消请求时,并不会一定会调用onStopJob方法,只有onStartJob返回true的时候,才会调用onStopJob,否则不调用。
但不论是否调用onStopJob方法,系统受到取消请求时,都会取消该job。
JobSchedulerService在cancel一个Job的大体思路是:
1. 将Job从PendingJobs中移除,这个PendingJob包含了达到触发条件但还没有执行的Job
2. 如果在cancel的时候该Job正在被执行,则最终会调用到我们App的onStopJob方法。如果已经执行完了,则不会调用onStopJob方法。
需要注意的是,JobService是运行在我们应用的主线程,这意味着我们需要开启新线程或者使用Handler或者AsyncTask来处理耗时的工作。
这部分主要参考Android JobScheduler的使用和原理 - 简书 (jianshu.com)
ZramWriteback的实现
首先是从StorageServiceManager那边调用过来的,会触发两次的schedule行为,第一次是markPageAsIdle(),第二次则writeback的行为,这次writeback要求是20分钟之后,3小时以内一定会执行一次writeback的操作,这两个具体逻辑实现都在onStartJob里面。并且执行完第一次writeback之后,将会触发下一次的schedule的操作,是要求24小时之后,并且需要设备进入idle 71分钟。
frameworks/base/services/core/java/com/android/server/ZramWriteback.java
private static final ComponentName sZramWriteback =
new ComponentName("android", ZramWriteback.class.getName());
private static final int MARK_IDLE_JOB_ID = 811;
private static final int WRITEBACK_IDLE_JOB_ID = 812;
private static final String MARK_IDLE_DELAY_PROP = "ro.zram.mark_idle_delay_mins";
private static final String FIRST_WB_DELAY_PROP = "ro.zram.first_wb_delay_mins";
private static final String PERIODIC_WB_DELAY_PROP = "ro.zram.periodic_wb_delay_hours";
private static final String FORCE_WRITEBACK_PROP = "zram.force_writeback";
/**
* 从StorageServiceManager调用过来的,
* Schedule the zram writeback job to trigger a writeback when idle
*/
public static void scheduleZramWriteback(Context context) {
int markIdleDelay = SystemProperties.getInt(MARK_IDLE_DELAY_PROP, 20);
int firstWbDelay = SystemProperties.getInt(FIRST_WB_DELAY_PROP, 180);
boolean forceWb = SystemProperties.getBoolean(FORCE_WRITEBACK_PROP, false);
JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
// 触发一个job,至少20分钟之后执行,在3小时的时候准时执行,忽略其他条件,将文件设置为idle状态
js.schedule(new JobInfo.Builder(MARK_IDLE_JOB_ID, sZramWriteback)
.setMinimumLatency(TimeUnit.MINUTES.toMillis(markIdleDelay))
.setOverrideDeadline(TimeUnit.MINUTES.toMillis(markIdleDelay))
.build());
// 第一次的schedule,是在3小时之后,并且要求设备进入idle状态,即设备处于屏幕关闭或dreaming状态(类似window的休眠动画状态)71分钟后。
js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback)
.setMinimumLatency(TimeUnit.MINUTES.toMillis(firstWbDelay))
.setRequiresDeviceIdle(!forceWb)
.build());
}
接下来看看onStartJob的行为:
@Override
public boolean onStartJob(JobParameters params) {
if (!isWritebackEnabled()) {
jobFinished(params, false);
return false;
}
if (params.getJobId() == MARK_IDLE_JOB_ID) {
markPagesAsIdle();
jobFinished(params, false);
return false;
} else {
new Thread("ZramWriteback_WritebackIdlePages") {
@Override
public void run() {
markAndFlushPages();
// 出发下一次的调度行为
schedNextWriteback(ZramWriteback.this);
// 表示这个job已经完成了,无需进行重新调度
jobFinished(params, false);
}
}.start();
}
return true;
}
private static final String IDLE_SYS = "/sys/block/zram%d/idle";
private static int sZramDeviceId = 0;
private static final String IDLE_SYS_ALL_PAGES = "all";
// 往/sys/block/zram0/idle 写入 all
private void markPagesAsIdle() {
String idlePath = String.format(IDLE_SYS, sZramDeviceId);
try {
FileUtils.stringToFile(new File(idlePath), IDLE_SYS_ALL_PAGES);
} catch (IOException e) {
Slog.e(TAG, "Failed to write to " + idlePath);
}
}
private static final String WB_SYS = "/sys/block/zram%d/writeback";
private static final String WB_SYS_IDLE_PAGES = "idle";
// 往 /sys/block/zram0/writeback 写入 idle
private void flushIdlePages() {
String wbPath = String.format(WB_SYS, sZramDeviceId);
try {
FileUtils.stringToFile(new File(wbPath), WB_SYS_IDLE_PAGES);
} catch (IOException e) {
Slog.e(TAG, "Failed to write to " + wbPath);
}
if (DEBUG) Slog.d(TAG, "Finished writeback back idle pages");
}
private void markAndFlushPages() {
// 往 /sys/block/zram0/writeback 写入 idle
flushIdlePages();
// 往/sys/block/zram0/idle 写入 all
markPagesAsIdle();
}
}
private static final String PERIODIC_WB_DELAY_PROP = "ro.zram.periodic_wb_delay_hours";
private static void schedNextWriteback(Context context) {
int nextWbDelay = SystemProperties.getInt(PERIODIC_WB_DELAY_PROP, 24);
boolean forceWb = SystemProperties.getBoolean(FORCE_WRITEBACK_PROP, false);
JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
// 延时24小时之后,并且设备进入idle 71分钟会触发writeback的行为
js.schedule(new JobInfo.Builder(WRITEBACK_IDLE_JOB_ID, sZramWriteback)
.setMinimumLatency(TimeUnit.HOURS.toMillis(nextWbDelay))
.setRequiresDeviceIdle(!forceWb)
.build());
}