`
从此醉
  • 浏览: 1089312 次
  • 性别: Icon_minigender_1
  • 来自: US
社区版块
存档分类
最新评论

Android源码分析-Alarm机制与Binder的交互

 
阅读更多

转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/18448997

前言

本次给大家分析的是Android中Alarm的机制以及它和Binder的交互,所用源码为最新的Android4.4。因为Alarm的功能都是通过Binder来完成的,所以,介绍Alarm之前必须要先介绍下它是如何调用Binder来完成定时功能的。由于内容较多,本文会比较长,在文章结构安排上是这样的:首先简单介绍如何使用Alarm并给出其工作原理,接着分析Alarm和Timer以及Handler在完成定时任务上的差别,然后分析Alarm与Binder的交互,最后分析Alarm机制的源码。

什么是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在定时任务上的区别

相同点

三者都可以完成定时任务,都支持一次性定时和循环定时(注:Handler可以间接支持循环定时任务)

不同点

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):使用AIDL

code:AlarmManager#set

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,看下它的类的声明:

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个小时才完成的,感谢大家阅读本文,希望本文能给大家带来一点帮助。

分享到:
评论

相关推荐

    Android的IPC机制-Binder

    ### Android的IPC机制-Binder #### 进程间通信(IPC)的概念 进程间通信(Inter-Process Communication,简称IPC)是指操作系统中的不同进程之间交换数据的方法。由于现代操作系统为了安全考虑,通常会隔离各进程...

    Android学习笔记--Binder

    总结来说,Binder机制是Android系统中实现进程间通信的关键,它使得Android能够有效地管理多个应用程序和服务之间的交互,保证了系统的稳定性和效率。理解和掌握Binder的工作原理对于深入学习Android开发至关重要。...

    《android-binder-arch》pdf

    由于Android系统的沙盒机制,每个应用程序都有自己的用户ID和权限范围,Binder在通信过程中会进行权限检查,确保安全的数据交互。 总结来说,《Android Binder架构解析》这本书全面覆盖了Binder的相关知识点,包括...

    Android进程间通信-Binder机制详解

    Android 进程间通信-Binder 机制详解 Android 系统中,每一个应用程序都是由一些 Activity 和 Service 组成的,一般 Service 运行在独立的进程中,而不同的 Activity 有可能运行在同一个进程中,也可能运行在不同的...

    Android底层源码分析_Binder

    ### Android底层源码分析_Binder #### 总体概述 Binder是Android系统中实现进程间通信(IPC)的核心机制之一。其设计模式基于客户端-服务器(Client-Server)架构,其中提供服务的一方称为Server进程,请求服务的...

    AndroidBinder机制总结[归纳].pdf

    在Android系统中,Binder机制是实现进程间通信(IPC)的核心工具,尤其在跨应用程序组件交互时至关重要。本文将深入探讨Android Binder机制及其在组件化思想中的应用。 1. Android组件化思想 Android应用的组件化...

    Android进程间通信-Binder机制

    在Android的应用程序框架层中,Binder机制被广泛应用于各个服务的管理和交互中。这部分涉及到大量的Java接口定义和实现。 **示例代码片段:** ```java public interface IMyService extends IInterface { void ...

    Android Binder机制

    Android Binder机制是Android系统的核心组件之一,它负责进程间通信(IPC,Inter-Process Communication),使得不同应用程序或者同一系统中的不同组件能够有效地交互。在Android系统中,由于每个应用程序运行在自己...

    android通信机制binder实例

    在Android系统中,Binder是实现进程间通信(IPC,Inter-Process Communication)的关键技术,它是一种轻量级的通信机制,使得应用程序之间可以共享服务和数据。本篇将通过一个学习实例来深入理解Android Binder的...

    mDemo-Binder机制学习

    - 在Android系统中,Service通过Binder与客户端进行交互,实现了服务端和客户端的逻辑解耦。 2. **Binder工作原理** - Binder通信涉及三个主要角色:Client、Server和ServiceManager。Client发起请求,Server处理...

    android技术内幕:系统卷-第三章Binder

    Binder作为Android系统中一种核心的进程间通信(IPC)机制,不仅在效率上表现出色,还具备许多独特的优点,使其成为Android开发者的首选。 ##### 为什么选择Binder? 1. **高效性**:与其他IPC机制相比,Binder...

    Android进程间通信机制(Binder)介绍

    Binder机制作为Android中进程间通信的重要组成部分,不仅提供了灵活的服务发现与调用方式,还具备高性能和低延迟的特点。通过对Binder的深入理解,开发者可以更好地利用这一机制来提高应用程序的性能和用户体验。在...

    Android Binder机制完全解析

    Android Binder机制完全解析 在Android系统中,Binder是实现进程间通信(IPC,Inter-Process Communication)的关键技术,它是Android系统服务和应用程序之间交互的主要方式。深入理解Binder机制对于Android应用...

    Android通信篇--深入剖析Binder原理

    Binder是一种进程间通信机制,基于开源的OpenBinder实现;...5.众多插件化框架的设计原理等等这些问题的背后都与Binder有莫大的关系,要弄懂上面这些问题理解Bidner通信机制是必须的。我们知道Android应用程序是由Ac

    Android binder-轻松了解Binder.rar|Android binder-轻松了解Binder.rar

    在Android系统架构中,服务、应用以及其他组件之间的交互往往离不开Binder。本资料详细解析了Binder的工作原理,旨在帮助读者深入理解这一概念。 Binder机制在Android中的角色: 1. **进程间通信**:Binder是...

    Android代码-recyclerview-binder

    recyclerview-binder Android Library for RecyclerView to manage order of items and multiple view types. Features Insert any items to wherever you want without calculating their position. Insert any...

    Android aidl Binder框架浅析

    在Android系统中,服务间的通信是通过Binder机制实现的,它是Android系统中进程间通信(IPC)的核心组件。Binder框架允许不同进程间的对象互相调用方法,使得数据和操作可以在不同的应用程序或系统组件之间共享。本...

Global site tag (gtag.js) - Google Analytics