当前位置: 首页 > 科技观察

Android闹钟设置解决方案

时间:2023-03-14 19:32:53 科技观察

Android闹钟设置不像IOS那么简单。做过Android闹钟设置的开发者都知道这个坑有多深。下面记录下我对安卓闹钟设置的解决方法。主要问题API19开始修改AlarmManager的机制。应用程序被杀死后,设置的闹钟将不会响起。在6.0以上进入Doze模式将使JobScheduler停止工作。重启手机设置后,闹钟失效。API19以上AlarmManager机制修改在API19之前,AlarmManager提供了三种设置闹钟的方法。由于业务需求闹钟只需要使用一次,set(inttype,longstartTime,PendingIntentpi);使用此方法。从API19开始,AlarmManager的机制是非精确传递,操作系统会切换闹钟以最小化唤醒和电池使用。由于之前的程序没有处理API19以上的闹钟设置,导致4.4以上手机的闹钟设置没有反应(不杀应用是没有闹钟的)。所以设置闹钟需要根据API版本单独处理。代码如下:recordTime,TimeUtils.NO_SECOND_FORMAT),sender);}else{am.set(AlarmManager.RTC_WAKEUP,TimeUtils.stringToLong(recordTime,TimeUtils.NO_SECOND_FORMAT),sender);}这样可以保证闹钟在应用程序启动时闹钟没有被杀死。应用被杀时的处理应用被杀后,设置的闹钟失效。这里使用daemon进程和graykeep-alive来保证后台闹钟服务不会被kill掉。当应用程序和闹钟服务被杀死后,守护进程和灰度保活会重启闹钟服务,重新设置闹钟。关于daemon进程的处理,这里使用了一个开源的daemon进程库。Android-AppDaemon在闹钟服务的onCreat中加入Android-AppDaemon的开源守护进程。代码如下:@OverridepublicvoidonCreate(){super.onCreate();Daemon.run(DaemonService.this,DaemonService.class,Daemon.INTERVAL_ONE_MINUTE);startTimeTask();grayGuard();}以进一步保证存活闹钟服务,添加Keepitgrayed(利用系统漏洞启动前台Service)。代码如下:privatevoidgrayGuard(){if(Build.VERSION.SDK_INT<18){//API<18,该方法可以有效隐藏Notification上的图标startForeground(GRAY_SERVICE_ID,newNotification());}else{IntentinnerIntent=newIntent(this,DaemonInnerService.class);startService(innerIntent);startForeground(GRAY_SERVICE_ID,newNotification());}//发送唤醒广播提示挂起的UI进程重启AlarmManageralarmManager=(AlarmManager)getSystemService(Context.ALARM_SERVICE);IntentalarmIntent=newIntent();alarmIntent.setAction(WakeReceiver.GRAY_WAKE_ACTION);PendingIntentoperation=PendingIntent.getBroadcast(这,WAKE_REQUEST_CODE,alarmIntent,PendingIntent.FLAG_UPDATE_CURRENT);if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KindMankAT){WalarmAlarmManager.RTC_WAKEUP,System.currentTimeMillis(),ALARM_INTERVAL,operation);}else{alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),ALARM_INTERVAL,operation);}}/***对于有API的平台>=18灰色keep-a上面使用的live方法*/publicstaticclassDaemonInnerServiceextendsService{@OverridepublicvoidonCreate(){Log.i(LOG_TAG,"InnerService->onCreate");super.onCreate();}@OverridepublicintonStartCommand(Intentintent,intflags,intstartId){Log.i(LOG_TAG,"InnerService->onStartCommand");startForeground(GRAY_SERVICE_ID,newNotification());//stopForeground(true);stopSelf();returnsuper.onStartCommand(intent,flags,startId);}@OverridepublicIBinderonBind(Intentintent){thrownewUnsupportedOperationException("Notyeimplemented");}@OverridepublicvoidonDestroy(日志。i(LOG_TAG,"InnerService->onDestroy");super.onDestroy();}}以上操作可以尽可能的提高闹钟服务的存活率,闹钟服务会被彻底杀死。为了解决5.0以上的问题,这里介绍一下5.0以上的新特性JobScheduler,对于5.0以上的JobScheduler,可以先阅读这篇关于5.0中新的JobSchedulerAPI的文章,这里使用5.0以上的JobScheduler创建一个定时任务,定时检查是否闹钟服务存在,重启闹钟服务(如果不存在)。(这里我设置的闹钟服务每分钟检测一次)进入应用时,检查当前系统是否在5.0以上,如果是,则启动JobScheduler服务。代码如下:JobSchedulerService.class.getName()));builder.setPeriodic(60*1000);//每60秒运行一次builder.setRequiresCharging(true);builder.setPersisted(true);//设置是否重新执行任务生成器重启设备后.setRequiresDeviceIdle(true);if(mJobScheduler.schedule(builder.build())<=0){//Ifsomethinggoeswrong}}builder.setPersisted(true);method是设备重启后是否重新执行任务,这里测试是可以重启任务的。以上操作进一步保证了闹钟服务被杀掉后重启。不过6.0以上引入了Doze模式。6.0以上的手机进入该模式后,JobScheduler会停止工作。6.0以上Doze模式的处理为了让JobScheduler能够在6.0以上的Doze模式下工作,这里针对6.0以上的Doze模式做了特殊处理——忽略电池优化。在Manifest.xml中添加权限。设置闹钟时判断系统是否为6.0以上,如果是则判断是否忽略电池优化。判断是否忽略电池优化代码如下:name.isIgnoringimBatteryOptimizations(Optimizations)){returntrue;}else{returnfalse;}}如果不忽略电池优化,会弹出提醒对话框,提示用户忽略电池优化操作。代码如下:StringpackageName=activity.getPackageName();PowerManagerpm=(PowerManager)activity.getSystemService(Context.POWER_SERVICE);if(!pm.isIgnoringBatteryOptimizations(packageName)){//intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);intent.setMIGAction(Settings.TIIGSACTION_Z_REQ);intent.setData(Uri.parse("package:"+packageName));activity.startActivityForResult(intent,REQUEST_IGNORE_BATTERY_CODE);}}catch(Exceptione){e.printStackTrace();}}}重写onActivityResult中的方法捕获用户选择的界面。例如代码如下:@OverrideprotectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata){if(resultCode==RESULT_OK){if(requestCode==BatteryUtils.REQUEST_IGNORE_BATTERY_CODE){//TODOsomething}}elseif(resultCode==RESULT_CANCELED){if(requestCode==BatteryUtils.REQUEST_IGNORE_BATTERY_CODE){ToastUtils.show(getActivity(),"请开启忽略电池优化~");}}}当应用被杀死,但闹钟服务没有被杀死时,设置这时候又是闹钟。这意味着设置的闹钟没有放在闹钟服务中。所以在这种情况下,设置的闹钟将失效。为了解决这种情况,使用AIDL(闹钟服务需要在另一个进程进行进程间通信)调用闹钟服务的resetalarmclock方法来重置闹钟。在应用的onCreat()方法中启动闹钟服务,然后绑定闹钟服务。privatevoidinitAlarmService(){startService(newIntent(this,DaemonService.class));//启动闹钟服务if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP){//JobScheduler...}//绑定闹钟服务Intentintent=newIntent(this,DaemonService.class);intent.setAction("android.intent.action.DaemonService");bindService(intent,mConnection,Context.BIND_AUTO_CREATE);}在onDestroy()方法中,调用闹钟服务的重置闹钟方法。代码如下:@OverrideprotectedvoidonDestroy(){super.onDestroy();try{//判断是否有闹钟,没有则关闭闹钟服务Stringalarm=localPreferencesHelper.getString(LocalPreferencesHelper.ALARM_CLOCK);if(daemonService!=-1&&mIRemoteService!=null){//android.os.Process.killProcess(daemonService);mIRemoteService.resetAlarm();}if(!alarm.equals("[]")){if(daemonService!=-1){startService(newIntent(this,DaemonService.class));}}else{if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP){mJobScheduler.cancel(JOB_ID);}}unbindService(mConnection);//解除绑定服务。}catch(Exceptione){}}这里说明一下,服务启动绑定时,unbindService不会停止服务。您可以查看这篇文章了解详情。这里***以上并不代表所有安卓手机的闹钟都可以使用,这里只是为了保证大部分手机尽可能的好。