其他
Android 时间设置的三个小彩蛋
https://juejin.cn/post/7212579242010132537
implements PreferenceControllerMixin, DatePickerDialog.OnDateSetListener {
//省略部分代码
private final DatePreferenceHost mHost;
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
//点击日期后处理
if (!TextUtils.equals(preference.getKey(), KEY_DATE)) {
return false;
}
//显示日期选择框
mHost.showDatePicker();
return true;
}
//省略部分代码
}
public class DateTimeSettings extends DashboardFragment implements
TimePreferenceController.TimePreferenceHost, DatePreferenceController.DatePreferenceHost {
//省略部分代码
@Override
public void showDatePicker() {
//显示日期选择对话框
showDialog(DatePreferenceController.DIALOG_DATEPICKER);
}
//省略部分代码
}
implements DialogCreatable, HelpResourceProvider, Indexable {
protected void showDialog(int dialogId) {
if (mDialogFragment != null) {
Log.e(TAG, "Old dialog fragment not null!");
}
//创建SettingsDialogFragment并进行show
mDialogFragment = SettingsDialogFragment.newInstance(this, dialogId);
mDialogFragment.show(getChildFragmentManager(), Integer.toString(dialogId));
}
}
private static final String KEY_DIALOG_ID = "key_dialog_id";
private static final String KEY_PARENT_FRAGMENT_ID = "key_parent_fragment_id";
private Fragment mParentFragment;
private DialogInterface.OnCancelListener mOnCancelListener;
private DialogInterface.OnDismissListener mOnDismissListener;
public static SettingsDialogFragment newInstance(DialogCreatable fragment, int dialogId) {
if (!(fragment instanceof Fragment)) {
throw new IllegalArgumentException("fragment argument must be an instance of "
+ Fragment.class.getName());
}
final SettingsDialogFragment settingsDialogFragment = new SettingsDialogFragment();
settingsDialogFragment.setParentFragment(fragment);
settingsDialogFragment.setDialogId(dialogId);
return settingsDialogFragment;
}
@Override
public int getMetricsCategory() {
if (mParentFragment == null) {
return Instrumentable.METRICS_CATEGORY_UNKNOWN;
}
final int metricsCategory =
((DialogCreatable) mParentFragment).getDialogMetricsCategory(mDialogId);
if (metricsCategory <= 0) {
throw new IllegalStateException("Dialog must provide a metrics category");
}
return metricsCategory;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mParentFragment != null) {
outState.putInt(KEY_DIALOG_ID, mDialogId);
outState.putInt(KEY_PARENT_FRAGMENT_ID, mParentFragment.getId());
}
}
@Override
public void onStart() {
super.onStart();
if (mParentFragment != null && mParentFragment instanceof SettingsPreferenceFragment) {
((SettingsPreferenceFragment) mParentFragment).onDialogShowing();
}
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mDialogId = savedInstanceState.getInt(KEY_DIALOG_ID, 0);
mParentFragment = getParentFragment();
int mParentFragmentId = savedInstanceState.getInt(KEY_PARENT_FRAGMENT_ID, -1);
if (mParentFragment == null) {
mParentFragment = getFragmentManager().findFragmentById(mParentFragmentId);
}
if (!(mParentFragment instanceof DialogCreatable)) {
throw new IllegalArgumentException(
(mParentFragment != null
? mParentFragment.getClass().getName()
: mParentFragmentId)
+ " must implement "
+ DialogCreatable.class.getName());
}
// This dialog fragment could be created from non-SettingsPreferenceFragment
if (mParentFragment instanceof SettingsPreferenceFragment) {
// restore mDialogFragment in mParentFragment
((SettingsPreferenceFragment) mParentFragment).mDialogFragment = this;
}
}
//通过DialogCreatable接口剥离了dialog的创建
return ((DialogCreatable) mParentFragment).onCreateDialog(mDialogId);
}
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
if (mOnCancelListener != null) {
mOnCancelListener.onCancel(dialog);
}
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
if (mOnDismissListener != null) {
mOnDismissListener.onDismiss(dialog);
}
}
public int getDialogId() {
return mDialogId;
}
@Override
public void onDetach() {
super.onDetach();
// This dialog fragment could be created from non-SettingsPreferenceFragment
if (mParentFragment instanceof SettingsPreferenceFragment) {
// in case the dialog is not explicitly removed by removeDialog()
if (((SettingsPreferenceFragment) mParentFragment).mDialogFragment == this) {
((SettingsPreferenceFragment) mParentFragment).mDialogFragment = null;
}
}
}
private void setParentFragment(DialogCreatable fragment) {
mParentFragment = (Fragment) fragment;
}
private void setDialogId(int dialogId) {
mDialogId = dialogId;
}
}
public class DateTimeSettings extends DashboardFragment implements
TimePreferenceController.TimePreferenceHost, DatePreferenceController.DatePreferenceHost {
//省略部分代码
@Override
public Dialog onCreateDialog(int id) {
//根据选项创建对应的dialog
switch (id) {
case DatePreferenceController.DIALOG_DATEPICKER:
return use(DatePreferenceController.class)
.buildDatePicker(getActivity());
case TimePreferenceController.DIALOG_TIMEPICKER:
return use(TimePreferenceController.class)
.buildTimePicker(getActivity());
default:
throw new IllegalArgumentException();
}
}
//省略部分代码
}
implements PreferenceControllerMixin, DatePickerDialog.OnDateSetListener {
//省略部分代码
public DatePickerDialog buildDatePicker(Activity activity) {
final Calendar calendar = Calendar.getInstance();
//创建DatePickerDialog
final DatePickerDialog d = new DatePickerDialog(
activity,
this,
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH));
// The system clock can't represent dates outside this range.
calendar.clear();
calendar.set(2007, Calendar.JANUARY, 1);
//设置最小时间为2007-01-01
d.getDatePicker().setMinDate(calendar.getTimeInMillis());
calendar.clear();
calendar.set(2037, Calendar.DECEMBER, 31);
//设置最大时间为2037-12-31
d.getDatePicker().setMaxDate(calendar.getTimeInMillis());
return d;
}
//省略部分代码
}
OnDateChangedListener {
private static final String YEAR = "year";
private static final String MONTH = "month";
private static final String DAY = "day";
@UnsupportedAppUsage
private final DatePicker mDatePicker;
private OnDateSetListener mDateSetListener;
//省略部分代码
private DatePickerDialog(@NonNull Context context, @StyleRes int themeResId,
@Nullable OnDateSetListener listener, @Nullable Calendar calendar, int year,
int monthOfYear, int dayOfMonth) {
super(context, resolveDialogTheme(context, themeResId));
final Context themeContext = getContext();
final LayoutInflater inflater = LayoutInflater.from(themeContext);
//初始化Dialog的View
final View view = inflater.inflate(R.layout.date_picker_dialog, null);
setView(view);
setButton(BUTTON_POSITIVE, themeContext.getString(R.string.ok), this);
setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this);
setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
if (calendar != null) {
year = calendar.get(Calendar.YEAR);
monthOfYear = calendar.get(Calendar.MONTH);
dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
}
mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
mDatePicker.init(year, monthOfYear, dayOfMonth, this);
mDatePicker.setValidationCallback(mValidationCallback);
mDateSetListener = listener;
}
//省略部分代码
/**
* Sets the listener to call when the user sets the date.
*
* @param listener the listener to call when the user sets the date
*/
public void setOnDateSetListener(@Nullable OnDateSetListener listener) {
mDateSetListener = listener;
}
@Override
public void onClick(@NonNull DialogInterface dialog, int which) {
switch (which) {
case BUTTON_POSITIVE:
if (mDateSetListener != null) {
// Clearing focus forces the dialog to commit any pending
// changes, e.g. typed text in a NumberPicker.
mDatePicker.clearFocus();
//设置完成回调
mDateSetListener.onDateSet(mDatePicker, mDatePicker.getYear(),
mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
}
break;
case BUTTON_NEGATIVE:
cancel();
break;
}
}
//省略部分代码
/**
* The listener used to indicate the user has finished selecting a date.
*/
public interface OnDateSetListener {
/**
* @param view the picker associated with the dialog
* @param year the selected year
* @param month the selected month (0-11 for compatibility with
* {@link Calendar#MONTH})
* @param dayOfMonth the selected day of the month (1-31, depending on
* month)
*/
void onDateSet(DatePicker view, int year, int month, int dayOfMonth);
}
}
implements PreferenceControllerMixin, DatePickerDialog.OnDateSetListener {
//省略部分代码
@Override
public void onDateSet(DatePicker view, int year, int month, int day) {
//设置日期
setDate(year, month, day);
//更新UI
mHost.updateTimeAndDateDisplay(mContext);
}
//省略部分代码
@VisibleForTesting
void setDate(int year, int month, int day) {
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month);
c.set(Calendar.DAY_OF_MONTH, day);
//设置日期与定义的最小日期取最大值,也就意味着设置的日期不能小于定义的最小日期
long when = Math.max(c.getTimeInMillis(), DatePreferenceHost.MIN_DATE);
if (when / 1000 < Integer.MAX_VALUE) {
//设置系统时间
((AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE)).setTime(when);
}
}
}
// Minimum time is Nov 5, 2007, 0:00.
long MIN_DATE = 1194220800000L;
void updateTimeAndDateDisplay(Context context);
}
//省略部分代码
/**
* Public-facing binder interface
*/
private final IBinder mService = new IAlarmManager.Stub() {
//省略部分代码
@Override
public boolean setTime(long millis) {
//先授权
getContext().enforceCallingOrSelfPermission(
"android.permission.SET_TIME",
"setTime");
//然后设置系统内核时间
return setTimeImpl(millis);
}
//省略部分代码
}
//省略部分代码
boolean setTimeImpl(long millis) {
if (!mInjector.isAlarmDriverPresent()) {
Slog.w(TAG, "Not setting time since no alarm driver is available.");
return false;
}
synchronized (mLock) {
final long currentTimeMillis = mInjector.getCurrentTimeMillis();
//设置系统内核时间
mInjector.setKernelTime(millis);
final TimeZone timeZone = TimeZone.getDefault();
final int currentTzOffset = timeZone.getOffset(currentTimeMillis);
final int newTzOffset = timeZone.getOffset(millis);
if (currentTzOffset != newTzOffset) {
Slog.i(TAG, "Timezone offset has changed, updating kernel timezone");
//设置系统内核时区
mInjector.setKernelTimezone(-(newTzOffset / 60000));
}
// The native implementation of setKernelTime can return -1 even when the kernel
// time was set correctly, so assume setting kernel time was successful and always
// return true.
return true;
}
}
//省略部分代码
@VisibleForTesting
static class Injector {
//省略部分代码
void setKernelTime(long millis) {
Log.d("jasonwan", "setKernelTime: "+millis);
if (mNativeData != 0) {
//在native层完成内核时间的设置
AlarmManagerService.setKernelTime(mNativeData, millis);
}
}
//省略部分代码
}
//native层完成
private static native int setKernelTime(long nativeData, long millis);
private static native int setKernelTimezone(long nativeData, int minuteswest);
//省略部分代码
}
//省略部分代码
@Override
public void onStart() {
mInjector.init();
synchronized (mLock) {
//省略部分代码
// We have to set current TimeZone info to kernel
// because kernel doesn't keep this after reboot
//设置时区,从SystemProperty中读取
setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
// Ensure that we're booting with a halfway sensible current time. Use the
// most recent of Build.TIME, the root file system's timestamp, and the
// value of the ro.build.date.utc system property (which is in seconds).
//设置时区
//先读取系统编译时间
long utc = 1000L * SystemProperties.getLong("ro.build.date.utc", -1L);
//再读取根目录最近的修改的时间
long lastModified = Environment.getRootDirectory().lastModified();
//然后读取系统构建时间,三个时间取最大值
final long systemBuildTime = Long.max(
utc,
Long.max(lastModified, Build.TIME));
//代码1
Log.d("jasonwan", "onStart: utc="+utc+", lastModified="+lastModified+", BuildTime="+Build.TIME+", currentTimeMillis="+mInjector.getCurrentTimeMillis());
//设置的时间小于最大值,则将最大值设置为系统内核的时间,注意,因为我们刚刚已经设置了内核时间,所以重启后通过System.currentTimeMillis()得到的时间戳为我们设置的时间,此判断意味着,系统编译时间、根目录最近修改时间、系统构建时间、设置的时间,这四者当中取最大值作为重启后的内核时间
if (mInjector.getCurrentTimeMillis() < systemBuildTime) {
//这里mInjector.getCurrentTimeMillis()其实就是System.currentTimeMillis()
Slog.i(TAG, "Current time only " + mInjector.getCurrentTimeMillis()
+ ", advancing to build time " + systemBuildTime);
mInjector.setKernelTime(systemBuildTime);
}
//省略部分代码
}
//省略部分代码
@VisibleForTesting
static class Injector {
//省略部分代码
void setKernelTimezone(int minutesWest) {
AlarmManagerService.setKernelTimezone(mNativeData, minutesWest);
}
void setKernelTime(long millis) {
//代码2
Log.d("jasonwan", "setKernelTime: "+millis);
if (mNativeData != 0) {
AlarmManagerService.setKernelTime(mNativeData, millis);
}
}
//省略部分代码
long getElapsedRealtime() {
return SystemClock.elapsedRealtime();
}
long getCurrentTimeMillis() {
return System.currentTimeMillis();
}
//省略部分代码
}
}
系统编译时间:1669271830000,格式化后为2022-11-24 14:37:10 根目录最近修改时间:1678865533000,格式化后为2023-03-15 15:32:13 构建时间:1669271830000,同系统编译时间 设置的时间:1664609754998,格式化后为2022-10-01 15:35:54
* Information about the current build, extracted from system properties.
*/
public class Build {
//省略部分代码
/** The time at which the build was produced, given in milliseconds since the UNIX epoch. */
public static final long TIME = getLong("ro.build.date.utc") * 1000;
//省略部分代码
}