alarmmanager定时任务

在项目中,有这么一个功能点,app进程中,需要实现一个定时任务,只要设备处于开机状态,每隔半个小时,就需要定时向服务器上传一次位置信息,并且只要是有网络和获取到GPS信号,进程不能死,如果进程死掉了,需要自动重启。对该点进行细分梳理,包含如下几个小功能点:
     1.进程能够实现开机启动。
     2.进程需要一直存活,并且能够自动重启。
     3.需要定时(30分钟)一次,向server端上报信息。
      针对以上三个功能点,第1和2点,实现起来,都不难,唯独第三点,在实现时,一般情况,会考虑到多种方式实现。由于没有自己查阅相关资料,在实现定时上传功能时,分别使用了三种方案,导致多花了冤枉时间,吃力不讨好,最终的结果是,定时功能不准,时间跨度越长,误差越大。
方案一. 死循环中,使用sleep方法
       for(;;)
            Tread.sleep 30分钟
方案二. timer机制
      timerTask 30分钟一次
最终方案三.AlarmManager进行精准定时
     使用系统级AlarmManager,进行精准定时,与系统闹钟的类似功能点。
     方案一和方案二的定时方案不准,主要原因是sleep和timerTask的内部线程运行的时间。在thread运行时,CPU才开始计算时间,当线程挂起,CPU没有将时间片交给该线程,就没有计算时间。实际使用中,app在后台运行越长,误差就越大。
     由于之前没有使用过AlarmManager功能,对其各个API不熟悉,并且存在如下的一些疑问:
     question 1:进程死掉后,AlarmManager是否还有效果?是否需要使用自动重启的service来重启进程?
     question 2:设备reboot后,设置的AlarmManager是否还有效?
     question 3:重复set相同的 alarm,是否会影响AlarmManager的定时机制?
     在多番查阅资料后,发现网上的一片文章,写的还不错。 http://blog.csdn.net/vicluo/article/details/8484939 尤其是其讲述到的 关机 alarm 问题, 尤为重要。
   文章提到,在设备reboot后,为了确保AlarmManager的 setInexactRepeating还有效,必须在设备接收到ACTION_BOOT_COMPLETED事件时,重新进行set alarm。
    经过不断的尝试,在设备上进行验证,实现精准定时上传的方案也就出来的。
 1.注册广播,接收系统 ACTION_BOOT_COMPLETED事件时,进行set alarm,进行精准定时任务
 2.在 alarm中启动service,service中实现网络HTTP请求。
 3.首次启动后,要set alarm。
 实现一个简单的demo,运行效果如下:
                  
   点击“启动定时器”,进行设置arlam,点击“关闭定时器”按钮,取消定时任务。
   代码如下:

AndroidManifest.xml
<span style="font-family: 宋体; font-size: 15px;">    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <service android:name="com.coder80.timer.service.UploadPOIService" >
            <intent-filter>
                <action android:name="com.coder80.timer.service.UploadPOIService" />
            </intent-filter>
        </service>
    <receiver
            android:name="com.coder80.timer.receiver.BootBroadcastReceiver"
            android:exported="false" >
            <intent-filter android:priority="1000">
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
                <action android:name="android.intent.action.USER_PRESENT"/>
            </intent-filter>
        </receiver>
    </application></span>
<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">   类BootBroadcastReceiver,注册一个静态Broadcast,接收系统级的ACTION_BOOT_COMPLETED事件,用于开机set alarm。</span>
<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">
</span>
<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">    public void onReceive(Context context, Intent intent) {
        mContext = context;
        if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
            LogUtil.i("BootBroadcastReceiver", "BroadcastReceiver onReceive here,action = " + intent.getAction());
            Handler handler = new Handler(Looper.getMainLooper());
            //after reboot the device,about 2 minutes later,upload the POI info to server
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if(!ServiceUtil.isServiceRunning(mContext,Constants.POI_SERVICE)){
                        ServiceUtil.invokeTimerPOIService(mContext);
                    }
                }
            }, Constants.BROADCAST_ELAPSED_TIME_DELAY);
        }
    }
</span>
<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">
</span>
<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">
</span>
<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">
</span>
public class UploadPOIService extends Service implements Runnable{
    private String TAG = UploadPOIService.class.getSimpleName();
    @Override
    public void onCreate() {
        super.onCreate();
        uploadPOIInfo();
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "UploadPOIService onDestroy here.... ");
    }

    private void uploadPOIInfo() {
    	//simulation HTTP request to server 
    	new Thread(this).start();
    }
    
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			Log.i(TAG, "UploadPOIService beign to upload POI to server ");
			Thread.sleep(5*1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		stopSelf();
	}

}
public class ServiceUtil {

    public static boolean isServiceRunning(Context context, String className) {
        boolean isRunning = false;
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningServiceInfo> serviceInfos = activityManager.getRunningServices(Constants.RETRIVE_SERVICE_COUNT);

        if(null == serviceInfos || serviceInfos.size() < 1) {
            return false;
        }

        for(int i = 0; i < serviceInfos.size(); i++) {
            if(serviceInfos.get(i).service.getClassName().contains(className)) {
                isRunning = true;
                break;
            }
        }
        Log.i("ServiceUtil-AlarmManager", className + " isRunning =  " + isRunning);
        return isRunning;
    }

    public static void invokeTimerPOIService(Context context){
        Log.i("ServiceUtil-AlarmManager", "invokeTimerPOIService wac called.." );
        PendingIntent alarmSender = null;
        Intent startIntent = new Intent(context, UploadPOIService.class);
        startIntent.setAction(Constants.POI_SERVICE_ACTION);
        try {
            alarmSender = PendingIntent.getService(context, 0, startIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        } catch (Exception e) {
            Log.i("ServiceUtil-AlarmManager", "failed to start " + e.toString());
        }
        AlarmManager am = (AlarmManager) context.getSystemService(Activity.ALARM_SERVICE);
        am.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), Constants.ELAPSED_TIME, alarmSender);
    }

    public static void cancleAlarmManager(Context context){
        Log.i("ServiceUtil-AlarmManager", "cancleAlarmManager to start ");
        Intent intent = new Intent(context,UploadPOIService.class);
    	intent.setAction(Constants.POI_SERVICE_ACTION);
        PendingIntent pendingIntent=PendingIntent.getService(context, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);
        AlarmManager alarm=(AlarmManager)context.getSystemService(Activity.ALARM_SERVICE);
        alarm.cancel(pendingIntent);
    }
}



   MainActivity.java
[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public void onClick(View v) {  
  3.     // TODO Auto-generated method stub  
  4.     int id = v.getId();  
  5.     switch(id){  
  6.     case R.id.button1:  
  7.         ServiceUtil.invokeTimerPOIService(mContext);  
  8.         break;  
  9.     case R.id.button2:  
  10.         ServiceUtil.cancleAlarmManager(mContext);  
  11.         break;  
  12.     }  




  运行后,针对question 1和question 3进行测试。从打印的log分析问题。执行步奏:在控制台,使用adb kill命令杀死进程,观察先前设置的AlarmManager是否还有效果?log如下:

   进程PID“32267”和“1190”属于同一应用,从log发现,进程被kill后,自动重启进程,并且先前设置的alarm同样有效。此log信息,说明系统级的alarm,在进程kill后,会自动重启,并且重启后的定时任务,依旧有效。
   在设备reboot后,重新set alarm,alarm会执行。
   以上内容,仅仅从表像来分析AlarmManager的执行机制,如若要深入分析其原理,还需要研读 AlarmManager 的源码才行。然而,fucking the code,是一件很痛苦的事情。
   在测试代码时,发现某些设备上, AlarmManager 执行效果没有达到预期效果,10秒一次的定时任务,被搞成了5min+一次。例如雷布斯的小米3设备,源码设置的10秒一次,米3上,却是5分钟一次定时任务。后来查询资料,发现时由于Android碎片化的原因,各个厂家进行随意定制,考虑节能省电,一旦系统休眠,不会频繁唤醒系统造成的原因。相关链接如下:
    http://www.miui.com/thread-1241523-1-1.html
  上述功能点,不是很难,然而在真正开发过程中,做好这么一件小事,还是很费神的,也遭受过一些莫名其妙的坑。
  在如今的项目中,此项功能点使用得比较频繁,一般app都会偶push功能,push的精准定时任务,实现的方式,也大同小异。





参考:Android 4.0 Alarm机制浅析

        AlarmManager实现精准定时任务

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值