Android源碼分析-Alarm機制與Binder的交互

轉載請註明出處:http://blog.csdn.net/singwhatiwanna/article/details/18448997java

前言

本次給你們分析的是Android中Alarm的機制以及它和Binder的交互,所用源碼爲最新的Android4.4。由於Alarm的功能都是經過Binder來完成的,因此,介紹Alarm以前必需要先介紹下它是如何調用Binder來完成定時功能的。因爲內容較多,本文會比較長,在文章結構安排上是這樣的:首先簡單介紹如何使用Alarm並給出其工做原理,接着分析Alarm和Timer以及Handler在完成定時任務上的差異,而後分析Alarm與Binder的交互,最後分析Alarm機制的源碼。android

什麼是Alarm

Alarm是android提供的用於完成鬧鐘式定時任務的類,系統經過AlarmManager來管理全部的Alarm,Alarm支持一次性定時任務和循環定時任務,它的使用方式很簡單,這裏很少作介紹,只給出一個簡單的示例:數據庫

AlarmManager alarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getApplicationContext(), TestActivity.class);
PendingIntent pendIntent = PendingIntent.getActivity(getApplicationContext(),
		0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//5秒後發送廣播,只發送一次
int triggerAtTime = SystemClock.elapsedRealtime() + 5 * 1000;
alarmMgr.set(AlarmManager.ELAPSED_REALTIME, triggerAtTime, pendIntent);

Alarm和Timer以及Handler在定時任務上的區別

相同點app

三者均可以完成定時任務,都支持一次性定時和循環定時(注:Handler能夠間接支持循環定時任務)ide

不一樣點oop

Handler和Timer在定時上是相似的,兩者在系統休眠的狀況下沒法正常工做,定時任務不會按時觸發。Alarm在系統休眠的狀況下能夠正常工做,而且還能夠決定是否喚醒系統,同時Alarm在自身不啓動的狀況下仍能正常收到定時任務提醒,可是當系統重啓或者應用被殺死的狀況下,Alarm定時任務會被取消。另外,從Android4.4開始,Alarm事件默認採用非精準方式,即定時任務可能會有小範圍的提早或延後,固然咱們能夠強制採用精準方式,而在此以前,Alarm事件都是精準方式。源碼分析

Alarm與Binder的交互

Alarm由AlarmManager來管理,從使用方式來看,AlarmManager很簡單,咱們只要獲得了AlarmManager的對象,就能夠調用set方法來設定定時任務了,而如何獲得AlarmManager對象呢?也很簡單,AlarmManager alarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);下面咱們去看看AlarmManager的set方法,固然AlarmManager還有setRepeating方法,可是兩者是相似的。爲了更好地理解下面的內容,須要你瞭解AIDL,若是你還不瞭解,請參看android跨進程通訊(IPC):使用AIDLfetch

code:AlarmManager#setui

public void set(int type, long triggerAtMillis, PendingIntent operation) {
	setImpl(type, triggerAtMillis, legacyExactLength(), 0, operation, null);
}

public void set(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
		PendingIntent operation, WorkSource workSource) {
	setImpl(type, triggerAtMillis, windowMillis, intervalMillis, operation, workSource);
}

private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
		PendingIntent operation, WorkSource workSource) {
	if (triggerAtMillis < 0) {
		/* NOTYET
		if (mAlwaysExact) {
			// Fatal error for KLP+ apps to use negative trigger times
			throw new IllegalArgumentException("Invalid alarm trigger time "
					+ triggerAtMillis);
		}
		*/
		triggerAtMillis = 0;
	}

	try {
		//定時任務實際上都有mService來完成,也就是說AlarmManager只是一個空殼
		//從下面的構造方法能夠看出,這個mService是IAlarmManager類型的,而IAlarmManager是一個接口
		//若是你們瞭解AIDL就應該知道IAlarmManager應該是一個AIDL接口
		mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation,
				workSource);
	} catch (RemoteException ex) {
	}
}

AlarmManager(IAlarmManager service, Context ctx) {
	mService = service;

	final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;
	mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KITKAT);
}

說明:我對代碼進行了註釋,從註釋能夠看出,如今咱們須要去找到這個mService,其實我已經幫你們找到了,它就是AlarmManagerService,看下它的類的聲明:this

class AlarmManagerService extends IAlarmManager.Stub

很顯然,AlarmManagerService的確實現了IAlarmManager接口,爲何是顯然呢?由於按照AIDL的規範,IAlarmManager.Stub是按照以下這種方式聲明的:

public static abstract class Stub extends Binder implements IAlarmManager {

	public static IAlarmManager asInterface(IBinder obj)
	...
}

可見這個Stub類就是一個普通的Binder,只不過它實現了IAlarmManager接口。它還有一個靜態方法asInterface,這個方法頗有用,經過它,咱們就能夠將IBinder對象轉換成IAlarmManager的實例,進而經過實例來調用其方法。什麼是Binder?這個還真很差說,可是咱們要知道Binder在Android系統中有大量的應用,大部分Manager都經過Binder來實現(包括AlarmManager),而Service和AIDL也是經過Binder來實現調用的。至於Binder和IBinder的關係,很簡單,就是Binder實現了IBinder接口。因爲AlarmManagerService繼承了IAlarmManager.Stub,因此AlarmManagerService也至關於實現了IAlarmManager接口,因此很顯然,AlarmManagerService就是AlarmManager中用於和其交互的mService。不過,尚未完,由於上面的結論不是我瞎猜的,是有代碼層面的依據的,下面我將帶領你們一塊兒去探索尋找mService的過程,經過這個過程,咱們會對Binder機制有更加深入的認識。

各類Manager和Binder服務的對應關係

首先Dalvik虛擬機會在SystemServer中建立一個叫作ServerThread的線程並調用它的initAndLoop方法,在initAndLoop方法中會建立主線程Looper和初始化各類Manager所對應的Binder服務,咱們所常見的Binder服務如WindowManagerService、AlarmManagerService、PowerManagerService等均在這裏建立並加入到ServiceManager中進行統一管理。而咱們經過getSystemService方式來獲得各類Manager的工做主要是在ContextImpl中完成的,不過LayoutInflater、WindowManager以及SearchManager除外。經過ContextImpl咱們能夠知道各類Manager和Binder服務的一一對應關係,好比AlarmManager對應AlarmManagerService、WindowManager對應WindowManagerService。

上面只是結論,爲了真正搞清楚各類Manager所對應的Binder服務,下面將要看一系列代碼,首先看SystemServer的代碼:

code:SystemServer

public class SystemServer {
    private static final String TAG = "SystemServer";

    public static final int FACTORY_TEST_OFF = 0;
    public static final int FACTORY_TEST_LOW_LEVEL = 1;
    public static final int FACTORY_TEST_HIGH_LEVEL = 2;

    static Timer timer;
    static final long SNAPSHOT_INTERVAL = 60 * 60 * 1000; // 1hr

    // The earliest supported time.  We pick one day into 1970, to
    // give any timezone code room without going into negative time.
    private static final long EARLIEST_SUPPORTED_TIME = 86400 * 1000;

    /**
     * Called to initialize native system services.
	 * 初始化本地系統服務,jni方法
     */
    private static native void nativeInit();

	//main方法,由底層調用
    public static void main(String[] args) {
        if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
            // If a device's clock is before 1970 (before 0), a lot of
            // APIs crash dealing with negative numbers, notably
            // java.io.File#setLastModified, so instead we fake it and
            // hope that time from cell towers or NTP fixes it
            // shortly.
            Slog.w(TAG, "System clock is before 1970; setting to 1970.");
            SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);
        }

        if (SamplingProfilerIntegration.isEnabled()) {
            SamplingProfilerIntegration.start();
            timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    SamplingProfilerIntegration.writeSnapshot("system_server", null);
                }
            }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);
        }

        // Mmmmmm... more memory!
        dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();

        // The system server has to run all of the time, so it needs to be
        // as efficient as possible with its memory usage.
        VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);

        Environment.setUserRequired(true);

        System.loadLibrary("android_servers");

        Slog.i(TAG, "Entered the Android system server!");

        // 初始化本地服務.
        nativeInit();

		//這裏是關鍵,ServerThread被建立,同時其initAndLoop被調用
        ServerThread thr = new ServerThread();
        thr.initAndLoop();
    }
}

接着看ServerThread的initAndLoop方法,該方法中,主線程Looper會被建立,各類Binder服務會被建立。該方法太長,我進行了截斷,只展出咱們所關心的代碼。

code:ServerThread#initAndLoop

public void initAndLoop() {
	EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN,
		SystemClock.uptimeMillis());
	//主線程Looper被建立
	Looper.prepareMainLooper();

	android.os.Process.setThreadPriority(
			android.os.Process.THREAD_PRIORITY_FOREGROUND);

	BinderInternal.disableBackgroundScheduling(true);
	android.os.Process.setCanSelfBackground(false);
	...此處省略
	//下面是各類Binder服務,從名字咱們應該可以大體看出它們所對應的Manager
	Installer installer = null;
	AccountManagerService accountManager = null;
	ContentService contentService = null;
	LightsService lights = null;
	PowerManagerService power = null;
	DisplayManagerService display = null;
	BatteryService battery = null;
	VibratorService vibrator = null;
	AlarmManagerService alarm = null;
	MountService mountService = null;
	NetworkManagementService networkManagement = null;
	NetworkStatsService networkStats = null;
	NetworkPolicyManagerService networkPolicy = null;
	ConnectivityService connectivity = null;
	WifiP2pService wifiP2p = null;
	WifiService wifi = null;
	NsdService serviceDiscovery= null;
	IPackageManager pm = null;
	Context context = null;
	WindowManagerService wm = null;
	BluetoothManagerService bluetooth = null;
	DockObserver dock = null;
	UsbService usb = null;
	SerialService serial = null;
	TwilightService twilight = null;
	UiModeManagerService uiMode = null;
	RecognitionManagerService recognition = null;
	NetworkTimeUpdateService networkTimeUpdater = null;
	CommonTimeManagementService commonTimeMgmtService = null;
	InputManagerService inputManager = null;
	TelephonyRegistry telephonyRegistry = null;
	ConsumerIrService consumerIr = null;
	...此處省略
	Slog.i(TAG, "Alarm Manager");
	//這裏AlarmManager對應的Binder服務被建立
	alarm = new AlarmManagerService(context);
	//將AlarmManagerService加入ServiceManager中統一管理
	ServiceManager.addService(Context.ALARM_SERVICE, alarm);

	Slog.i(TAG, "Init Watchdog");
	Watchdog.getInstance().init(context, battery, power, alarm,
			ActivityManagerService.self());
	Watchdog.getInstance().addThread(wmHandler, "WindowManager thread");

	Slog.i(TAG, "Input Manager");
	inputManager = new InputManagerService(context, wmHandler);

	Slog.i(TAG, "Window Manager");
	//這裏WindowManager所對應的Binder服務被建立
	wm = WindowManagerService.main(context, power, display, inputManager,
			wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
			!firstBoot, onlyCore);
	//將WindowManagerService加入ServiceManager中統一管理
	ServiceManager.addService(Context.WINDOW_SERVICE, wm);
	ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

	ActivityManagerService.self().setWindowManager(wm);
	...此處省略
}

說明:針對上述代碼,我要說明一下,首先其建立的各類Binder服務其實並非真正的服務,說它們是Binder比較恰當,由於它們的確繼承自Binder而不是Service;另外一點就是ServiceManager其實也僅僅是個殼子,真正的工做是經過其Binder服務ServiceManagerNative來完成的,ServiceManager提供的工廠方法addService和getService均在ServiceManagerNative中經過代理來實現。

到此爲止,咱們已經知道各類Binder服務的建立過程,下面咱們要看一下Manager是如何和其Binder服務關聯上的,再回到getSystemService方法。首先咱們要知道Activity的繼承關係,以下圖所示:


 再看以下代碼,觀察下它們中的getSystemService方法是如何實現的

code:各類getSystemService方法

//#Context
public abstract Object getSystemService(String name);

//#ContextWrapper
@Override
public Object getSystemService(String name) {
	return mBase.getSystemService(name);
}

//#ContextThemeWrapper	
@Override 
public Object getSystemService(String name) {
	if (LAYOUT_INFLATER_SERVICE.equals(name)) {
		if (mInflater == null) {
			mInflater = LayoutInflater.from(mBase).cloneInContext(this);
		}
		return mInflater;
	}
	return mBase.getSystemService(name);
}

//#Activity
@Override
public Object getSystemService(String name) {
	if (getBaseContext() == null) {
		throw new IllegalStateException(
				"System services not available to Activities before onCreate()");
	}

	if (WINDOW_SERVICE.equals(name)) {
		return mWindowManager;
	} else if (SEARCH_SERVICE.equals(name)) {
		ensureSearchManager();
		return mSearchManager;
	}
	return super.getSystemService(name);
}

說明:經過上述代碼能夠看出LayoutInflater、WindowManager以及SearchManager的處理比較特殊,直接在方法中返回對象,剩下的全部Manager將經過mBase.getSystemService(name)返回,如今問題轉移到mBase上面,mBase是什麼呢?我已經查清楚了,Activity的mBase就是ContextImpl對象,何以見得?請看下面分析

ContextImpl:Activity的mBase

不知道你們對我寫的另一篇源碼分析是否有印象:Android源碼分析-Activity的啓動過程,在這篇文章中我指出:Activity的最終啓動過程由ActivityThread中的performLaunchActivity方法來完成,在performLaunchActivity中,Activity的mBase將被賦值爲ContextImpl對象,下面經過代碼來講明:

code:mBase的賦值過程

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
	...
	if (activity != null) {
		//這裏的appContext就是ContextImpl對象
		Context appContext = createBaseContextForActivity(r, activity);
		
		CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
		Configuration config = new Configuration(mCompatConfiguration);
		if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
				+ r.activityInfo.name + " with config " + config);
		//經過Activity的attach方法將ContextImpl對象賦值給mBase
		activity.attach(appContext, this, getInstrumentation(), r.token,
				r.ident, app, r.intent, r.activityInfo, title, r.parent,
				r.embeddedID, r.lastNonConfigurationInstances, config);
		...
	}
	...
}

private Context createBaseContextForActivity(ActivityClientRecord r,
		final Activity activity) {
	//很顯然,此方法返回的就是ContextImpl對象
	ContextImpl appContext = new ContextImpl();
	appContext.init(r.packageInfo, r.token, this);
	appContext.setOuterContext(activity);
	Context baseContext = appContext;
	...
	return baseContext;
}

final void attach(Context context, ActivityThread aThread,
		Instrumentation instr, IBinder token, int ident,
		Application application, Intent intent, ActivityInfo info,
		CharSequence title, Activity parent, String id,
		NonConfigurationInstances lastNonConfigurationInstances,
		Configuration config) {
	//將context賦值給mBase,這裏的context就是performLaunchActivity中的appContext,即ContextImpl對象
	attachBaseContext(context);

	mFragments.attachActivity(this, mContainer, null);
	
	mWindow = PolicyManager.makeNewWindow(this);
	mWindow.setCallback(this);
	...
}

@Override protected void attachBaseContext(Context newBase) {
	super.attachBaseContext(newBase);
	//這裏很顯然,對mBase進行賦值
	mBase = newBase;
}

說明:看了上面的代碼,咱們已經知道,mBase的確是ContextImpl對象。上面我提到:除了LayoutInflater、WindowManager以及SearchManager,剩下的全部Manager將經過mBase.getSystemService(name)返回,那麼如今,咱們去看下ContextImpl中的getSystemService方法。

code:ContextImpl#getSystemService

class ContextImpl extends Context {
	...
	@Override
    public Object getSystemService(String name) {
		//首先從SYSTEM_SERVICE_MAP根據服務名獲得一個fetcher對象
		//其中SYSTEM_SERVICE_MAP是一個HashMap,而後再經過fetcher去取service
        ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
        return fetcher == null ? null : fetcher.getService(this);
    }
	...
}

說明:看了ContextImpl的getSystemService方法,發現失望了,尚未找到真正的實現,看來還要去看這個fetcher是怎麼回事,下面請看代碼:

code:服務註冊過程和fetcher

//一個哈希表,用來根據服務名存儲對應服務的ServiceFetcher(能夠理解爲經過ServiceFetcher能夠獲得服務)
private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP =
		new HashMap<String, ServiceFetcher>();

//註冊服務,將服務的fetcher存到哈希表中
private static void registerService(String serviceName, ServiceFetcher fetcher) {
	if (!(fetcher instanceof StaticServiceFetcher)) {
		fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
	}
	SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
}
//靜態代碼塊,註冊各類服務
//也就是說,ContextImpl這個類被加載的時候就會把以下的各類服務的fetcher加入到哈希表中
//這樣咱們經過getSystemService就能夠獲得一個服務的fetcher,再經過fetcher去獲得服務的對象
static {
	registerService(ACCESSIBILITY_SERVICE, new ServiceFetcher() {
			public Object getService(ContextImpl ctx) {
				return AccessibilityManager.getInstance(ctx);
			}});

	registerService(CAPTIONING_SERVICE, new ServiceFetcher() {
			public Object getService(ContextImpl ctx) {
				return new CaptioningManager(ctx);
			}});

	registerService(ACCOUNT_SERVICE, new ServiceFetcher() {
			public Object createService(ContextImpl ctx) {
				IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);
				IAccountManager service = IAccountManager.Stub.asInterface(b);
				return new AccountManager(ctx, service);
			}});

	registerService(ACTIVITY_SERVICE, new ServiceFetcher() {
			public Object createService(ContextImpl ctx) {
				return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
			}});

	//這裏是Alarm服務的註冊
	registerService(ALARM_SERVICE, new ServiceFetcher() {
			public Object createService(ContextImpl ctx) {
				/**還記得ALARM_SERVICE嗎?
				 * alarm = new AlarmManagerService(context);
				 * 將AlarmManagerService加入ServiceManager中統一管理
				 * ServiceManager.addService(Context.ALARM_SERVICE, alarm);
				 */
				//經過ServiceManager的getService獲得Alarm服務,很顯然,下面的b就是AlarmManagerService對象
				IBinder b = ServiceManager.getService(ALARM_SERVICE);
				//還記得AlarmManager中的mService嗎?就是這裏的service,很顯然它是一個Binder服務
				//分析到這裏,事實已經得出:AlarmManager所對應的Binder服務就是AlarmManagerService
				IAlarmManager service = IAlarmManager.Stub.asInterface(b);
				return new AlarmManager(service, ctx);
			}});

	registerService(AUDIO_SERVICE, new ServiceFetcher() {
			public Object createService(ContextImpl ctx) {
				return new AudioManager(ctx);
			}});
	...省略:下面還有許多服務
}
說明:經過上述代碼的分析,相信你們已經很明確Manager是如何和Binder服務一一對應的,而後Manager的各類功能將會交由Binder服務來完成。儘管我只詳細分析了AlarmManager和AlarmManagerService的對應過程,可是其它Manager的對應過程是幾乎徹底同樣的。好了,到了這裏,咱們已經把Manager和Binder服務的對應過程進行了深刻地分析,下面開始咱們的最後一個主題:Alarm機制的源碼分析。

Alarm機制分析

經過上面的一系列分析,咱們知道AlarmManager的全部功能都是經過AlarmManagerService來完成的,在分析源碼以前,我先來描述下Alarm的工做原理:從Android4.4開始,Alarm默認爲非精準模式,除非顯示指定採用精準模式。在非精準模式下,Alarm是批量提醒的,每一個alarm根據其觸發時間和最大觸發時間的不一樣會被加入到不一樣的batch中,同一個batch的不一樣alarm是同時發生的,這樣就沒法實現精準鬧鐘,官方的解釋是批量處理能夠減小設備被喚醒次數以及節約電量,不過針對精準鬧鐘,官方預留的方法是setExact和setWindow,兩者都是經過將時間窗口定義爲0來實現精準鬧鐘的,由於時間窗口爲0,意味着觸發時間和最大觸發時間是同樣的,由於典型的狀況下:最大觸發時間= 觸發時間 + 時間窗口。同時全部的batch是按開始時間升序排列的,在一個batch內部,不一樣的鬧鐘也是按觸發時間升序排列的,因此鬧鐘的喚醒順序是按照batch的排序依次觸發的,而同一個batch中的alarm是同時觸發的,能夠用下面這個示意圖來描述:

 

上圖是示意圖,系統中能夠有多個batch,每一個batch中能夠有多個alarm。下面咱們分析一下AlarmManagerService中的代碼。其入口方法爲set,set又調用了setImplLocked,因此咱們直接看setImplLocked。

code:AlarmManagerService#setImplLocked

private void setImplLocked(int type, long when, long whenElapsed, long maxWhen, long interval,
		PendingIntent operation, boolean isStandalone, boolean doValidate,
		WorkSource workSource) {
	/**建立一個alarm,其中各參數的含義以下:
	 * type 鬧鐘類型 ELAPSED_REALTIME、RTC、RTC_WAKEUP等
	 * when 觸發時間 UTC類型,絕對時間,經過System.currentTimeMillis()獲得
	 * whenElapsed 相對觸發時間,自開機算起,含休眠,經過SystemClock.elapsedRealtime()獲得
	 * maxWhen 最大觸發時間
	 * interval 觸發間隔,針對循環鬧鐘有效
	 * operation 鬧鐘觸發時的行爲,PendingIntent類型
	 */
	Alarm a = new Alarm(type, when, whenElapsed, maxWhen, interval, operation, workSource);
	//根據PendingIntent刪除以前已有的同一個鬧鐘
	removeLocked(operation);

	boolean reschedule;
	//嘗試將alarm加入到合適的batch中,若是alarm是獨立的或者沒法找到合適的batch去容納此alarm,返回-1
	int whichBatch = (isStandalone) ? -1 : attemptCoalesceLocked(whenElapsed, maxWhen);
	if (whichBatch < 0) {
		//沒有合適的batch去容納alarm,則新建一個batch
		Batch batch = new Batch(a);
		batch.standalone = isStandalone;
		//將batch加入mAlarmBatches中,並對mAlarmBatches進行排序:按開始時間升序排列
		reschedule = addBatchLocked(mAlarmBatches, batch);
	} else {
		//若是找到合適了batch去容納此alarm,則將其加入到batch中
		Batch batch = mAlarmBatches.get(whichBatch);
		//若是當前alarm的加入引發了batch開始時間和結束時間的改變,則reschedule爲true
		reschedule = batch.add(a);
		if (reschedule) {
			//因爲batch的起始時間發生了改變,因此須要從列表中刪除此batch並從新加入、從新對batch列表進行排序
			mAlarmBatches.remove(whichBatch);
			addBatchLocked(mAlarmBatches, batch);
		}
	}

	if (DEBUG_VALIDATE) {
		if (doValidate && !validateConsistencyLocked()) {
			Slog.v(TAG, "Tipping-point operation: type=" + type + " when=" + when
					+ " when(hex)=" + Long.toHexString(when)
					+ " whenElapsed=" + whenElapsed + " maxWhen=" + maxWhen
					+ " interval=" + interval + " op=" + operation
					+ " standalone=" + isStandalone);
			rebatchAllAlarmsLocked(false);
			reschedule = true;
		}
	}

	if (reschedule) {
		rescheduleKernelAlarmsLocked();
	}
}

說明:經過上述代碼能夠看出,當咱們建立一個alarm的時候,僅僅是將這個alarm加入到某個batch中,系統中有一個batch列表,專門用於存儲全部的alarm。但是僅僅把alarm加入到batch中還不行,系統還必須提供一個相似於Looper的東西一直去遍歷這個列表,一旦它發現有些alarm的時間已經到達就要把它取出來去執行。事實上,AlarmManagerService中的確有一個相似於Looper的東西去幹這個事情,只不過它是個線程,叫作AlarmThread。下面看它的代碼:

code:AlarmManagerService#AlarmThread

private class AlarmThread extends Thread
{
	public AlarmThread()
	{
		super("AlarmManager");
	}
	
	public void run()
	{
		//當前時間觸發的alarm列表
		ArrayList<Alarm> triggerList = new ArrayList<Alarm>();

		while (true)
		{
			//jni方法,顧名思義,阻塞式方法,當有alarm的時候會被喚醒
			int result = waitForAlarm(mDescriptor);

			triggerList.clear();

			if ((result & TIME_CHANGED_MASK) != 0) {
				if (DEBUG_BATCH) {
					Slog.v(TAG, "Time changed notification from kernel; rebatching");
				}
				remove(mTimeTickSender);
				//將全部的alarm從新排序
				rebatchAllAlarms();
				mClockReceiver.scheduleTimeTickEvent();
				Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
				intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
						| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
				mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
			}
			
			synchronized (mLock) {
				final long nowRTC = System.currentTimeMillis();
				final long nowELAPSED = SystemClock.elapsedRealtime();
				if (localLOGV) Slog.v(
					TAG, "Checking for alarms... rtc=" + nowRTC
					+ ", elapsed=" + nowELAPSED);

				if (WAKEUP_STATS) {
					if ((result & IS_WAKEUP_MASK) != 0) {
						long newEarliest = nowRTC - RECENT_WAKEUP_PERIOD;
						int n = 0;
						for (WakeupEvent event : mRecentWakeups) {
							if (event.when > newEarliest) break;
							n++; // number of now-stale entries at the list head
						}
						for (int i = 0; i < n; i++) {
							mRecentWakeups.remove();
						}

						recordWakeupAlarms(mAlarmBatches, nowELAPSED, nowRTC);
					}
				}
				//這個方法會把batch列表中的第一個batch取出來而後加到觸發列表中
				//固然,前提是此batch的開始時間不大於當前時間
				//同時,若是是循環鬧鐘,則會對下次任務進行再次定時
				triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
				rescheduleKernelAlarmsLocked();

				// 遍歷觸發列表,發送PendingIntent
				for (int i=0; i<triggerList.size(); i++) {
					Alarm alarm = triggerList.get(i);
					try {
						if (localLOGV) Slog.v(TAG, "sending alarm " + alarm);
						//這裏PendingIntent會被send,結果就是咱們的定時任務被執行了
						alarm.operation.send(mContext, 0,
								mBackgroundIntent.putExtra(
										Intent.EXTRA_ALARM_COUNT, alarm.count),
								mResultReceiver, mHandler);
						
						// we have an active broadcast so stay awake.
						if (mBroadcastRefCount == 0) {
							setWakelockWorkSource(alarm.operation, alarm.workSource);
							mWakeLock.acquire();
						}
						final InFlight inflight = new InFlight(AlarmManagerService.this,
								alarm.operation, alarm.workSource);
						mInFlight.add(inflight);
						mBroadcastRefCount++;

						final BroadcastStats bs = inflight.mBroadcastStats;
						bs.count++;
						if (bs.nesting == 0) {
							bs.nesting = 1;
							bs.startTime = nowELAPSED;
						} else {
							bs.nesting++;
						}
						final FilterStats fs = inflight.mFilterStats;
						fs.count++;
						if (fs.nesting == 0) {
							fs.nesting = 1;
							fs.startTime = nowELAPSED;
						} else {
							fs.nesting++;
						}
						if (alarm.type == ELAPSED_REALTIME_WAKEUP
								|| alarm.type == RTC_WAKEUP) {
							bs.numWakeup++;
							fs.numWakeup++;
							//針對能喚醒設備的鬧鐘,這裏會作一些喚醒設備的事情
							ActivityManagerNative.noteWakeupAlarm(
									alarm.operation);
						}
					} catch (PendingIntent.CanceledException e) {
						if (alarm.repeatInterval > 0) {
							// This IntentSender is no longer valid, but this
							// is a repeating alarm, so toss the hoser.
							remove(alarm.operation);
						}
					} catch (RuntimeException e) {
						Slog.w(TAG, "Failure sending alarm.", e);
					}
				}
			}
		}
	}
}
說明:上述代碼中,AlarmThread會一直循環的跑着,一旦有新的alarm觸發,它就會取出一個batch而後逐個發送PendingIntent,具體alarm的觸發是由底層來完成的,我無法再繼續分析下去。還有就是Alarm中有一些細節,我沒有進行很具體的分析,實際上很簡單,你們一看就懂。到此爲止,Alarm機制的主要流程也分析完了。

總結

本文沒有詳細介紹如何使用Alarm,由於很簡單,看一下官方文檔或者網上搜一下,處處都是。關於Alarm,有一點須要強調一下:當手機重啓或者應用被殺死的時候,Alarm會被刪除,所以,若是想經過Alarm來完成長久定時任務是不可靠的,若是非要完成長久定時任務,能夠這樣:將應用的全部Alarm信息存到數據庫中,每次應用啓動的時候都從新註冊Alarm並更新Alarm的觸發時間,經過這種方式就不存在Alarm丟失的狀況了。本文很長,耗時8個小時才完成的,感謝你們閱讀本文,但願本文能給你們帶來一點幫助。

相關文章
相關標籤/搜索