忽然飞来一个新的需求,APP的日程安排需要添加一个闹钟提醒,我的天,这是要逆天啊,刚开始做的时候,没想那么多,因为了解的少吗,再加上不知道QQ和微信等已经被厂商加入了白名单,本来想也就是几天的工作量,没想到结果一做就是一星期,感觉脑细胞已经死光,我还能不能知道1+1=3啊

在15年使用过一个软件,灵犀语音助手(我不是在打广告,他也不给我钱),他的功能是可以语音定时,告诉他定时时间和需要做的任务,他帮你定时,到时候提醒你,但是做android开发的,都知道android系统的机制,内存不足的时候,会杀死一部分进程,导致灵犀还没有提醒你,他的进程就被杀死了。一个星期以前再次下载了,他还是那样,功能也没有修改,到时增加了其他的功能,没有具体看,反正跟我没关系,我只在乎闹钟。

需求:

1.APP关闭了,到点了,闹钟必须响铃,日程安排必须提醒

2.APP更新之后,闹钟还能继续运行,日程安排还可以提醒。

3.新的日程安排有服务器使用jputh(极光推送)进行推送,android进行闹钟设置。

4.开机之后不用用户启动APP,手机自启动APP(坑爹的厂商,不说了,反正我做不出来了,说多了都是泪)

5.还有什么啊,我也不知道了,反正就是跟系统闹钟的日程安排一样一样的,就是这个功能。

好了,接下来交给你们了,你们自己想怎么做吧,我颠儿了,继续装逼去

*********************************************我是分割线********************************************

装完逼真刺激,所以说,人啊要会装逼,不会装逼,你可以学嘛,啥,你学都学不会,那你活着多无聊啊!!!玩笑话,进入正题。

相比接触过开机,闹钟,服务,广播的一些人(毕竟有些APP基本上不需要这些来实现功能),已经知道android的一些知识

1.android3.0以后,Google对系统做了修改,尽可能的防止了流氓软件的存在,为什么呢?自己看下吧,点击进去,了解下就可以了。然而这只是Google的办法,再说了,咱们的APP开发出来,肯定是运行一遍了,那么他的逻辑就不存在了啊,那为什么还是存在问题呢,这就要归功于各大万恶的开发商了(华为的还好吧,至少有个开机自启动,而我只是为三星的平板做的一款APP,三星怎么还没有滚出中国啊,禁韩令完蛋了吗(三星粉丝请无视)),原因是各大厂商已经吧开机广播屏蔽掉了,最坑爹的是,今天像接受其他的广播,然而,锁屏广播和接触锁屏广播全部需要动态加载,监听网络变化的广播竟然也给我屏蔽,心中千万只草泥马奔腾而过

2.服务的问题,相信你们思考的时候已经想过了,有什么服务可以保证一直在后台运行,并且不会被系统杀死。或许你们会说,我可以响铃的时候接收到的广播,在广播里边重启服务啊,还有的人会说,简单,我直接进行双进程守护就可以啊,但是你了解过吗,在android5.0的时候Google又来了一次对流氓软件的惩戒,直接在系统杀死APP主进程之后,子进程直接被杀死。这又是咋回事呢?(没找到原文地址,这个里边排版有问题,凑合着看吧,了解下,明白咋回事就可以了)。

3.至于其他的一些问题了,都是小问题,就是上边两部分,只要解决了,就没有问题了,可是本菜能力一般,水平有限,只能解决第二个问题,第一个问题一直不知道咋解决,有知道的大神,指点一下,有好的建议的,大家探讨下。

*******************************************我是分割线**********************************************

言尽于此,开胃菜已经上完了,主菜马上上,来来来,帅哥美女让个道,上主菜了!!!

1.针对5.0的杀进程论,Google也没有直接一棒子打死,5.0新出的JobScheduler已经完美解决了问题,具体怎么使用,他的机制是什么,大家百度看下吧,或者,亦或者,至于怎么保活的呢,看下这段代码

JobScheduler jobScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);JobInfo jobInfo = new JobInfo.Builder(1, new ComponentName(getPackageName(), MyJobService.class.getName())).setPeriodic(2000).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build();.setPeriodic(2000).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build();

其中主要的红色部分的代码,以毫秒计算,重启时间是多少秒(反正我是这么理解的)。运行流程,从onCreate()到onDestory()全部走一遍,当触发事件的时候,走的方法是OnStartJob()。你只需要知道这些就可以使用了,记得要使用异步哦!

private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);//闹钟开启int index = 0;calendar = Calendar.getInstance();calendar.setTimeInMillis(System.currentTimeMillis());index = new ShareUtils(getApplicationContext()).getClockIndex();pendingIntent = PendingIntent.getBroadcast(MyJobService.this, index +=1, intent1, PendingIntent.FLAG_UPDATE_CURRENT);//获取系统时间Date date = new Date(System.currentTimeMillis());long time1 = date.getTime();if (time1 <= time) {int time2 = (int) (time - time1);calendar.add(Calendar.SECOND, (time2 / 1000));manager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);BaseApplication.time = 0;}new ShareUtils(getApplicationContext()).setClockIndex(index);}};

因为设置的多个闹钟,所以需要使用SharedPreferences来进行保存,什么你说可以用静态变量,好吧,你开心就好。

接下来,该处理什么了,走神了,唉,老了。

接下来就是接收推送的消息了

 ToastUtil.showShort(context, "有新的日程安排");long date = jsonObject.getLong("remindTime");BaseApplication.time = date;pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);wakeLock = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP| PowerManager.SCREEN_DIM_WAKE_LOCK, "WakeAndLock");wakeLock.acquire();int index = new ShareUtils(context).getClockIndex();bean = new ClockTimeBean(index += 1, date + "");LogUtil.i("abc123", date + "");ClockDAO clockDAO = new ClockDAOImpl(context);clockDAO.save(bean);android.util.Log.i("cxq", "screenOn");

什么,你说wakelock是干什么的,好吧,这是用来点亮屏幕的,在锁屏的时候,service处于停止状态,需要唤醒屏幕,让jobservice再次跑起来,奔跑吧,jobservice。不然,闹钟是设置不成功的。至于用法,不会还要我帮你百度吧,行吧,小弟认怂了。你还有啥不懂的?你别跟我说clockDao,这是数据库,封装好的,没办法跟你解释了,自己看我demo里边的代码吧。

你不会还要我帮你写BroadCastReceiver吧,给你,给你,都给你

public class ScheduleReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);// 默认通知 API16及之后可用PendingIntent pendingIntent3 = PendingIntent.getService(context, 0, new Intent(context, MyJobService.class), 0);// 通过Notification.Builder来创建通知,注意API Level (API16之后才支持)Notification notify3 = new Notification.Builder(context).setSmallIcon(R.mipmap.icon_bigdata).setTicker("提醒: " + "您有新短消息,请注意查收!").setContentTitle("日程安排").setContentText("您有新的日程安排").setContentIntent(pendingIntent3).setNumber(1)// 需要注意build()是在API level16及之后增加的,API11可以使用getNotificatin()来替代.build();// FLAG_AUTO_CANCEL表明当通知被用户点击时,通知将被清除。notify3.flags |= Notification.FLAG_AUTO_CANCEL;// 步骤4:通过通知管理器来发起通知。如果id不同,则每click,在status哪里增加一个提示manager.notify(1, notify3);//启动Service播放音乐context.startService(new Intent(context, MusicService.class));context.startService(new Intent(context, ScreenService.class));//再次开启ScheduleService这个服务,从而可以
//        Intent i = new Intent(context, MyJobService.class);
//        context.startService(i);}

音乐播放service

public class MusicService extends Service {//为日志工具设置标签private static String TAG = "MusicService";//定义音乐播放器变量
//    private MediaPlayer mPlayer;
//    //    private Vibrator vibrator;
//    private AlertDialog.Builder builder;
//    private AlertDialog dialog;
//    private boolean isPlaying = false;@Overridepublic void onCreate() {super.onCreate();}@Overridepublic void onStart(Intent intent, int startId) {
//        mPlayer.start();super.onStart(intent, startId);}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {//创建vibrator对象Log.e(TAG, "MusicSerice onCreate()");
//        vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);// 第一个参数为等待指定时间后开始震动,震动时间为第二个参数。后边的参数依次为等待震动和震动的时间
//        long[] pattern = {100, 400, 100, 400};
//        //-1为不重复,0为一直震动vibrator.vibrate(pattern, 0);
//        mPlayer = new MediaPlayer();
//        try {
//            //默认的闹钟铃声
//            Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
//            mPlayer.setDataSource(this, uri);
//            final AudioManager audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
//            if (audioManager.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION) != 0) {
//                //收到通知时的声音
//                mPlayer.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
//                mPlayer.setLooping(true);
//                mPlayer.prepare();
//                mPlayer.start();
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        //创建提示窗口
//        builder = new AlertDialog.Builder(this);
//        builder.setTitle("提示");
//        builder.setMessage("您有新的日程安排该完成了");
//        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
//            @Override
//            public void onClick(DialogInterface dialog, int which) {
//                isPlaying = true;vibrator.cancel();
//                mPlayer.stop();
//                dialog.dismiss();
//            }
//        });
//        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
//            @Override
//            public void onClick(DialogInterface dialog, int which) {
//                isPlaying = true;vibrator.cancel();
//                mPlayer.stop();
//                dialog.dismiss();
//            }
//        });
//        dialog = builder.create();
//        //在dialog  show方法之前添加如下代码,表示该dialog是一个系统的dialog**
//        dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
//        dialog.show();
//        Message msg = new Message();
//        msg.what = 1;
//        handler.sendMessage(msg);Intent intent1 = new Intent(MusicService.this, MusicActivity.class);intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(intent1);return super.onStartCommand(intent, flags, startId);}
//
//    private Handler handler = new Handler() {
//        @Override
//        public void handleMessage(Message msg) {
//            super.handleMessage(msg);
//            if (!isPlaying) {
//                new Thread(new Runnable() {
//                    @Override
//                    public void run() {
//                        try {
//                            Thread.sleep(120000);vibrator.cancel();
//                            mPlayer.stop();
//                            isPlaying = false;
//                        } catch (InterruptedException e) {
//                            e.printStackTrace();
//                        }
//                    }
//                }).start();
//            }
//        }
//    };@Overridepublic void onDestroy() {Log.e(TAG, "MusicSerice onDestroy()");
//        isPlaying = true;vibrator.cancel();
//        mPlayer.stop();
//        dialog.dismiss();super.onDestroy();}//其他对象通过bindService 方法通知该Service时该方法被调用@Overridepublic IBinder onBind(Intent intent) {Log.e(TAG, "MusicSerice onBind()");
//        mPlayer.start();return null;}//其它对象通过unbindService方法通知该Service时该方法被调用@Overridepublic boolean onUnbind(Intent intent) {Log.e(TAG, "MusicSerice onUnbind()");
//        mPlayer.stop();return super.onUnbind(intent);}

唤醒屏幕service

public class ScreenService extends Service {//声明键盘管理器KeyguardManager mKeyguardManager = null;//声明键盘锁private KeyguardManager.KeyguardLock mKeyguardLock = null;//声明电源管理器private PowerManager pm;private PowerManager.WakeLock wakeLock;@Overridepublic IBinder onBind(Intent arg0) {return null;}@Overridepublic void onCreate() {//获取电源的服务pm = (PowerManager) getSystemService(Context.POWER_SERVICE);//获取系统服务mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);super.onCreate();}@Overridepublic void onStart(Intent intent, int startId) {//点亮亮屏wakeLock = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");wakeLock.acquire();Log.i("Log : ", "------>mKeyguardLock");//初始化键盘锁,可以锁定或解开键盘锁mKeyguardLock = mKeyguardManager.newKeyguardLock("");//禁用显示键盘锁定mKeyguardLock.disableKeyguard();}@Overridepublic void onDestroy() {wakeLock.release();super.onDestroy();}

不知道为什么,我在手机上测试,音乐播放service是可以使用的,dialog显示也没有问题,我明明已经设置了dialog为系统级的dialog,而权限我也添加了,

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> 

但是为什么三星平板还会报错,直接崩了,错误信息是内存泄漏,不明白为什么,也没有找到有原因。只能写一个透明的activity,来实现功能了。你们可以试下,毕竟这样的效果比较好的,代码也没有那么多。

public class MusicActivity extends Activity {//定义音乐播放器变量private MediaPlayer mPlayer;private Vibrator vibrator;private AlertDialog.Builder builder;private AlertDialog dialog;private boolean isPlaying = false;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);initView();}private void initView(){requestWindowFeature(Window.FEATURE_NO_TITLE);vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);// 第一个参数为等待指定时间后开始震动,震动时间为第二个参数。后边的参数依次为等待震动和震动的时间long[] pattern = {100, 400, 100, 400};//-1为不重复,0为一直震动vibrator.vibrate(pattern, 0);mPlayer = new MediaPlayer();try {//默认的闹钟铃声Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);mPlayer.setDataSource(this, uri);final AudioManager audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);if (audioManager.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION) != 0) {//收到通知时的声音mPlayer.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);mPlayer.setLooping(true);mPlayer.prepare();mPlayer.start();}} catch (IOException e) {e.printStackTrace();}//创建提示窗口builder = new AlertDialog.Builder(this);builder.setCancelable(false);builder.setTitle("提示");builder.setMessage("您有新的日程安排该完成了");builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {setClose();}});builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {setClose();}});dialog = builder.create();dialog.show();Message msg = new Message();msg.what = 1;handler.sendMessage(msg);}@Overrideprotected void onStart() {super.onStart();mPlayer.start();}private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (!isPlaying) {new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(120000);vibrator.cancel();mPlayer.stop();isPlaying = false;} catch (InterruptedException e) {e.printStackTrace();}}}).start();}}};@Overridepublic void onDestroy() {setClose();super.onDestroy();}private void setClose(){isPlaying = true;vibrator.cancel();mPlayer.stop();finish();dialog.dismiss();}

至于其他的代码呢,我就不贴出来了,下边我会写项目链接,自己下载看下吧。

基本上一半功能实现了,剩下的就是,更新APP之后该怎么实现闹钟设置了。代码很简单,逻辑也很简单,利用数据库,吧数据存到数据库中,第一次进入jobService的时候,取出数据库的数据,设置闹钟,就是这么简单。

private Handler handler1 = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);intent1 = new Intent(MyJobService.this, ScheduleReceiver.class);manager = (AlarmManager) getSystemService(ALARM_SERVICE);holder = new ArrayList<>();calendar = Calendar.getInstance();ClockDAO clockDAO = new ClockDAOImpl(getApplicationContext());holder = clockDAO.findAll();if (BaseApplication.isClock) {for (int i = 0; i < holder.size(); i++) {if (i == holder.size() - 1) {BaseApplication.isClock = false;}time = Long.parseLong(holder.get(i).getDate());if (time != 0) {pendingIntent = PendingIntent.getBroadcast(MyJobService.this, holder.get(i).getId(), intent1, PendingIntent.FLAG_UPDATE_CURRENT);//获取系统时间Date date = new Date(System.currentTimeMillis());long time1 = date.getTime();calendar.setTimeInMillis(System.currentTimeMillis());if (time1 <= time) {LogUtil.i("datas", calendar.getTimeInMillis() + "  ============系统时间");int time2 = (int) (time - time1);calendar.add(Calendar.SECOND, (time2 / 1000));manager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);LogUtil.i("datas", calendar.getTimeInMillis() + "  ============变更时间");BaseApplication.time = 0;LogUtil.i("datas", holder.get(i).getId() + "    " + holder.get(i).getDate());}}}}}};

********************************************我又出来了*****************************************

好了,系统闹钟就这样了,如果手机自带开机自启动,直接设置同意你的APP,然后,系统闹钟的日程安排就完成了,如果不带开机自启动的功能,那么你可以监听联网状态广播,如果还是不行,那没办法了,花钱吧,找厂商把你的APP加入白名单,为什么QQ和微信可以不被系统杀死,因为人家花钱了,人民币APP啊。反正我是没有想到其他好的办法,如果你有好的办法,请告诉我,如果你有什么好的观点,咱们可以探讨下。谢谢!!

仿系统闹钟,实现更新安装之后还可以响铃,开机启动功能需要手机自带开机自启动相关推荐

  1. w7运行里的计算机怎么设置,W7系统怎么设置开机启动项

    我们都知道电脑刚装完w7系统,开机速度很快,但使用一段时间后会发现开机启动越来越慢.导致开机启动越来越慢的原因有很多,比如:电脑使用久了,不知不觉启动的项目就多了,电脑系统垃圾也多了.那w7系统开机启 ...

  2. 双系统 ubuntu 和win7 修改开机启动项

    问题:系统原本为win7,为了体验下Linux,安装了双系统,多了一个Ubuntu,但是日常使用还是win7系统,所以需要修改系统启动项,不然默认启动项是Ubuntu,每次开机后都要选择还挺麻烦的. ...

  3. Parallels Desktop 安装Win 10提示“安全启动功能防止操作系统启动”该怎么解决?

    Parallels Desktop 安装 Windows10 提示"安全启动功能防止操作系统启动"该怎么解决?未来小编就来帮大家解决这个难题!一起来看以下教程吧- pd17虚拟机 ...

  4. CentOS 7使用dnf安装Memcached以及启动、停止、开机启动等设置

    1.安装Memcached dnf install memcached 根据提示完成安装 2.启动Memcached 输入以下命令: service memcached start 输出以下内容: R ...

  5. CentOS 6.3安装Nginx开启目录浏览、下载功能

    本次实验实现目的: 安装Nginx,Nginx开启目录浏览.下载功能,开机默认启动:咐件自带开机启动脚本.重启脚本: 1.关闭SELINUX 查看获取SELinux的状态: [root@localho ...

  6. linux电脑自启动,caddyserver在linux系统下设置开机启动

    caddyserver在linux系统下的开机启动设置方法 方法一: vi /etc/rc.local 在exit 0前添加如下代码cd /root/caddy ./caddy & 保存后重启 ...

  7. win10系统更新服务器太慢了,Win10系统更新安装慢如何解决

     随着win10系统的发布和不断更新,许多用户都想要体验一下,可是最近小编想要安装win10系统的时候,却发现win10更新安装慢,这让人很困扰,那么要怎么解决win10系统更新安装慢这个问题呢,接 ...

  8. [原创内容] 秒变老司机--系统更新安装和集成批处理解决方案[Win7SP1x64简体中文官方镜像专用]1.0...

    秒变老司机--系统更新安装和集成批处理解决方案 A Batch Solution For Windows Updates of Windows7 SP1 x64 Simplified Chinese ...

  9. 仿苹果手机闹钟_高仿iOS系统闹钟 UserNotifications

    Alarm_Clock_UserNotifications 仿ios系统闹钟 添加闹钟效果图 收到通知效果图 更新日志 2018.09.12 由于iOS系统限制了注册本地推送的数量,最大的注册量为64 ...

最新文章

  1. HTTP协议基础解读
  2. Angular CLI的安装和使用
  3. 是否非要用interface关键字来实现接口?
  4. 使用Maven,Jetty和Tomcat在嵌入式容器中运行Java Web应用程序
  5. 【渝粤教育】电大中专建设工程法规1作业 题库
  6. 第11篇:Flowable-BPMN部署常见问题没有对ACT_RE_PROCDEF表进行插入操作
  7. SPARK全栈 全流程 大数据实战 之 技术选型篇
  8. jdialog 数据量大加载出现白板_王者荣耀:队友真的有人机?白板熟练进排位,资料面都是假的...
  9. php自动下载 linux,Linux下软件、PHP和Perl的模块等都实现了自动安装
  10. .net core判断当前访问源是PC端还是移动端
  11. python入门神器下载_Python编程神器 v3.7.2 最新免费版
  12. h3c交换机-初级命令
  13. u检验中的查u界值表_统计学中,知道u值,如何确定P值,是通过查表吗
  14. c语言大作业酒店管理系统,管理信息系统大作业《酒店管理系统》.doc
  15. 机电传动控制第二周作业
  16. 现实迷途 第二十章 峰回路转
  17. iOS客户端如何测试推送
  18. SHT30温湿度模块使用
  19. 力扣刷题记录-回溯算法相关题目
  20. 蚂蚁金服Java后端(一面)面试题

热门文章

  1. python14 Prompting and Passing
  2. 力扣题解: 55. 跳跃游戏
  3. Embodied Question Answering导读
  4. C++强制类型转化:reinterpret_cast
  5. java石头剪刀布界面_利用Java模拟石头剪刀布游戏
  6. C1认证学习十一(常用网络命令以及端口)
  7. 创建一个子进程,子进程向无名管道中写入数据,父进程打印输出。
  8. 查询rssi指令_h3c无线控制器常用命令(wx)
  9. java8 协程_Java8 异步编程—CompletableFuture
  10. C语言 用switch语句算工资