Android5.1AlarmManager Service 심층 분석(Android4.4 보완)
먼저 AlarmManager에 새로 추가된 커넥터를 살펴보겠습니다.
public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, operation, null, info);
}
이 인터페이스는 주로 자신이 새로운 대상인 Alarm Clock Info를 설정하는 것을 응용한다. 이것은 다중 사용자를 대상으로 하는 것이다. 모든 사용자는 Alarm Manager 서비스에서 자신의 다음 Alarm Clock Info를 얻을 수 있고 triggertime,pending Intent를 얻을 수 있다.
public AlarmClockInfo getNextAlarmClock() {
return getNextAlarmClock(UserHandle.myUserId());
}
AlarmManager입니다. AlarmManager Service의 5.1 변경 사항을 분석해 보겠습니다.
먼저 Alarm의 설정, 즉 Alarm Manager 서비스의 주요 라인을 분석한 결과, 주요 변화가 가장 큰 것은reschedule Kernel AlarmsLocked 함수이다.
void rescheduleKernelAlarmsLocked() {
// Schedule the next upcoming wakeup alarm. If there is a deliverable batch
// prior to that which contains no wakeups, we schedule that as well.
long nextNonWakeup = 0;
if (mAlarmBatches.size() > 0) {
final Batch firstWakeup = findFirstWakeupBatchLocked();
final Batch firstBatch = mAlarmBatches.get(0);
// always update the kernel alarms, as a backstop against missed wakeups
if (firstWakeup != null) {
mNextWakeup = firstWakeup.start;
setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
}
if (firstBatch != firstWakeup) {// ,
nextNonWakeup = firstBatch.start;
}
}
if (mPendingNonWakeupAlarms.size() > 0) {// alarm
if (nextNonWakeup == 0 || mNextNonWakeupDeliveryTime < nextNonWakeup) {
nextNonWakeup = mNextNonWakeupDeliveryTime;
}
}
// always update the kernel alarm, as a backstop against missed wakeups
if (nextNonWakeup != 0) {
mNextNonWakeup = nextNonWakeup;
setLocked(ELAPSED_REALTIME, nextNonWakeup);
}
}
다음은 주로 AlarmThread의run 함수를 분석했는데 주로 alarm을 보내는 데 사용되었고 주요 내용을 직접 골랐다.
boolean hasWakeup = triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
if (!hasWakeup && checkAllowNonWakeupDelayLocked(nowELAPSED)) {// wakeup alarm, ( )
// if there are no wakeup alarms and the screen is off, we can
// delay what we have so far until the future.
if (mPendingNonWakeupAlarms.size() == 0) {
mStartCurrentDelayTime = nowELAPSED;
mNextNonWakeupDeliveryTime = nowELAPSED// alarm
+ ((currentNonWakeupFuzzLocked(nowELAPSED)*3)/2);
}
mPendingNonWakeupAlarms.addAll(triggerList);
mNumDelayedAlarms += triggerList.size();
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
} else {// alarm
// now deliver the alarm intents; if there are pending non-wakeup
// alarms, we need to merge them in to the list. note we don't
// just deliver them first because we generally want non-wakeup
// alarms delivered after wakeup alarms.
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
if (mPendingNonWakeupAlarms.size() > 0) {// alarm,
calculateDeliveryPriorities(mPendingNonWakeupAlarms);
triggerList.addAll(mPendingNonWakeupAlarms);
Collections.sort(triggerList, mAlarmDispatchComparator);
final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime;
mTotalDelayTime += thisDelayTime;
if (mMaxDelayTime < thisDelayTime) {
mMaxDelayTime = thisDelayTime;
}
mPendingNonWakeupAlarms.clear();
}
deliverAlarmsLocked(triggerList, nowELAPSED);// ,
}
다음은 checkAllowNonWakeupDelayLocked라는 함수를 상세하게 분석해 보겠습니다.
4
long currentNonWakeupFuzzLocked(long nowELAPSED) {
long timeSinceOn = nowELAPSED - mNonInteractiveStartTime;
if (timeSinceOn < 5*60*1000) {
// If the screen has been off for 5 minutes, only delay by at most two minutes.
return 2*60*1000;
} else if (timeSinceOn < 30*60*1000) {
// If the screen has been off for 30 minutes, only delay by at most 15 minutes.
return 15*60*1000;
} else {
// Otherwise, we will delay by at most an hour.
return 60*60*1000;
}
}
boolean checkAllowNonWakeupDelayLocked(long nowELAPSED) {
if (mInteractive) {//
return false;
}
if (mLastAlarmDeliveryTime <= 0) {
return false;
}
if (mPendingNonWakeupAlarms.size() > 0 && mNextNonWakeupDeliveryTime > nowELAPSED) {// alarm ,
// This is just a little paranoia, if somehow we have pending non-wakeup alarms
// and the next delivery time is in the past, then just deliver them all. This
// avoids bugs where we get stuck in a loop trying to poll for alarms.
return false;
}
long timeSinceLast = nowELAPSED - mLastAlarmDeliveryTime;//
return timeSinceLast <= currentNonWakeupFuzzLocked(nowELAPSED);// 。
}
다음에 분석하면 시스템이 clockInfo 블록을 업데이트한다. * Recomputes the next alarm clock for all users.
*/
private void updateNextAlarmClockLocked() {
if (!mNextAlarmClockMayChange) {
return;
}
mNextAlarmClockMayChange = false;
SparseArray<AlarmManager.AlarmClockInfo> nextForUser = mTmpSparseAlarmClockArray;
nextForUser.clear();
final int N = mAlarmBatches.size();
for (int i = 0; i < N; i++) {
ArrayList<Alarm> alarms = mAlarmBatches.get(i).alarms;
final int M = alarms.size();
for (int j = 0; j < M; j++) {
Alarm a = alarms.get(j);
if (a.alarmClock != null) {
final int userId = a.userId;
// Alarms and batches are sorted by time, no need to compare times here.
if (nextForUser.get(userId) == null) {
nextForUser.put(userId, a.alarmClock);//uid、alarmClock
}
}
}
}
// Update mNextAlarmForUser with new values.
final int NN = nextForUser.size();
for (int i = 0; i < NN; i++) {
AlarmManager.AlarmClockInfo newAlarm = nextForUser.valueAt(i);
int userId = nextForUser.keyAt(i);
AlarmManager.AlarmClockInfo currentAlarm = mNextAlarmClockForUser.get(userId);
if (!newAlarm.equals(currentAlarm)) {// mNextAlarmClockForUser uid alarmclockinfo ,
updateNextAlarmInfoForUserLocked(userId, newAlarm);
}
}
// Remove users without any alarm clocks scheduled.
final int NNN = mNextAlarmClockForUser.size();
for (int i = NNN - 1; i >= 0; i--) {
int userId = mNextAlarmClockForUser.keyAt(i);
if (nextForUser.get(userId) == null) {// userId, AlarmClockInfo ,
updateNextAlarmInfoForUserLocked(userId, null);
}
}
}
private void updateNextAlarmInfoForUserLocked(int userId,
AlarmManager.AlarmClockInfo alarmClock) {
if (alarmClock != null) {
mNextAlarmClockForUser.put(userId, alarmClock);
} else {
mNextAlarmClockForUser.remove(userId);
}
mPendingSendNextAlarmClockChangedForUser.put(userId, true);// AlarmClockInfo userId
mHandler.removeMessages(AlarmHandler.SEND_NEXT_ALARM_CLOCK_CHANGED);
mHandler.sendEmptyMessage(AlarmHandler.SEND_NEXT_ALARM_CLOCK_CHANGED);
}
AlarmHandler를 보냅니다.SEND_NEXT_ALARM_CLOCK_CHANGED 메시지는 다음 함수를 호출합니다.
private void sendNextAlarmClockChanged() {
SparseArray<AlarmManager.AlarmClockInfo> pendingUsers = mHandlerSparseAlarmClockArray;
pendingUsers.clear();
synchronized (mLock) {
final int N = mPendingSendNextAlarmClockChangedForUser.size();
for (int i = 0; i < N; i++) {
int userId = mPendingSendNextAlarmClockChangedForUser.keyAt(i);
pendingUsers.append(userId, mNextAlarmClockForUser.get(userId));
}
mPendingSendNextAlarmClockChangedForUser.clear();
}
final int N = pendingUsers.size();
for (int i = 0; i < N; i++) {
int userId = pendingUsers.keyAt(i);
AlarmManager.AlarmClockInfo alarmClock = pendingUsers.valueAt(i);
Settings.System.putStringForUser(getContext().getContentResolver(),
Settings.System.NEXT_ALARM_FORMATTED,
formatNextAlarm(getContext(), alarmClock, userId),
userId);
getContext().sendBroadcastAsUser(NEXT_ALARM_CLOCK_CHANGED_INTENT,
new UserHandle(userId));// , AlarmClockInfo
}
}
모니터 화면을 켜고 끄는 브로드캐스트 receiver가 추가되었습니다.
class InteractiveStateReceiver extends BroadcastReceiver {
public InteractiveStateReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
getContext().registerReceiver(this, filter);
}
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
interactiveStateChangedLocked(Intent.ACTION_SCREEN_ON.equals(intent.getAction()));
}
}
}
화면 변경 사항을 받으면 interactiveStateChangedLocked 함수가 호출됩니다.
void interactiveStateChangedLocked(boolean interactive) {
if (mInteractive != interactive) {
mInteractive = interactive;
final long nowELAPSED = SystemClock.elapsedRealtime();
if (interactive) {// , alarm
if (mPendingNonWakeupAlarms.size() > 0) {
final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime;
mTotalDelayTime += thisDelayTime;
if (mMaxDelayTime < thisDelayTime) {
mMaxDelayTime = thisDelayTime;
}
deliverAlarmsLocked(mPendingNonWakeupAlarms, nowELAPSED);
mPendingNonWakeupAlarms.clear();
}
if (mNonInteractiveStartTime > 0) {
long dur = nowELAPSED - mNonInteractiveStartTime;
if (dur > mNonInteractiveTime) {
mNonInteractiveTime = dur;
}
}
} else {// , mNonInteractiveStartTime
mNonInteractiveStartTime = nowELAPSED;
}
}
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.