本篇讲述小米便签中的定时提醒功能。
  便签,可以理解为一件事情,一项任务,有个定时提醒的功能,还是蛮不错的~
  
  小米便签定时功能,是当编辑便签的时候,有个菜单项,选了之后,就弹出一个“日期对话框”,
选择了日期,就设置了定时功能。

下面讲解技术实现的整体思路(很多地方我也不懂,不懂的就搜索)
  
  AndroidManifest.xml配置
  
   

<receiver android:name=".ui.AlarmInitReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver> <receiver
android:name="net.micode.notes.ui.AlarmReceiver"
android:process=":remote" >
</receiver> <activity
android:name=".ui.AlarmAlertActivity"
android:label="@string/app_name"
android:launchMode="singleInstance"
android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" >
</activity>

定义了2个receiver和1个activity。
  
  Broadcast Receiver(广播接收器)是Android中的四大组件之一。
  
  AlarmInitReceiver的配置“android.intent.action.BOOT_COMPLETED”,表明
  Android手机开机后,会发送android.intent.action.BOOT_COMPLETED广播,监听这个广播就能监听开机。
  开机之后,就处理已有的便签,逐条比较日期,如果有到期的,就用AlarmManager提醒用户。
  
  场景1.刚刚提到的,开机的时候,需要处理已有的便签。
 

public class AlarmInitReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
long currentDate = System.currentTimeMillis();
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
new String[] { String.valueOf(currentDate) },
null); if (c != null) {
if (c.moveToFirst()) {
do {
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
Intent sender = new Intent(context, AlarmReceiver.class);
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
AlarmManager alermManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext());
}
c.close();
}
}
}

场景2:编辑便签的时候,需要设置日期。

NodeEditActivity的菜单事件
  
 

case R.id.menu_delete_remind:
mWorkingNote.setAlertDate(0, false);
break; WorkingNote
public void setAlertDate(long date, boolean set) {
if (date != mAlertDate) {
mAlertDate = date;
mNote.setNoteValue(NoteColumns.ALERTED_DATE,
String.valueOf(mAlertDate));
}
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onClockAlertChanged(date, set);
}
} 事件监听器,最终还是NodeEditActivity
public void onClockAlertChanged(long date, boolean set) {
/**
* User could set clock to an unsaved note, so before setting the alert
* clock, we should save the note first
*/
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
if (mWorkingNote.getNoteId() > 0) {
Intent intent = new Intent(this, AlarmReceiver.class);
intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
mWorkingNote.getNoteId()));
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
intent, 0);
AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE));
showAlertHeader();
if (!set) {
alarmManager.cancel(pendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent);
}
} else {
/**
* There is the condition that user has input nothing (the note is
* not worthy saving), we have no note id, remind the user that he
* should input something
*/
Log.e(TAG, "Clock alert setting error");
showToast(R.string.error_note_empty_for_clock);
}
}

如果用户设置了时间,就通过AlarmManager放一个监听事项。
  
  场景3:提醒广播的最终执行者
  AlarmInitReceiver是负责,系统启动的时候,处理notes。
  AlarmReceiver一直在等待“有note需要提醒用户”的广播。

public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, AlarmAlertActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
时间到期,提醒的活动是AlarmAlertActivity。
public class AlarmAlertActivity extends Activity implements OnClickListener,
OnDismissListener { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE); final Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); if (!isScreenOn()) {
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
} Intent intent = getIntent(); try {
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
mSnippet = DataUtils.getSnippetById(this.getContentResolver(),
mNoteId);
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet
.substring(0, SNIPPET_PREW_MAX_LEN)
+ getResources().getString(R.string.notelist_string_info)
: mSnippet;
} catch (IllegalArgumentException e) {
e.printStackTrace();
return;
} mPlayer = new MediaPlayer();
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId,
Notes.TYPE_NOTE)) {
showActionDialog();
playAlarmSound();
} else {
finish();
}
}
}

需要特别学习的Android机制和类库。
  a.AlarmManager
  AlarmManager,顾名思义,就是“提醒”,是Android中常用的一种系统级别的提示服务,在特定的时刻为我们广播一个指定的Intent。
  简单的说就是我们设定一个时间,然后在该时间到来时,AlarmManager为我们广播一个我们设定的Intent。
  通常我们使用PendingIntent,PendingIntent可以理解为Intent的封装包,简单的说就是在Intent上在加个指定的动作。
  在使用Intent的时候,我们还需要在执行startActivity、startService或sendBroadcast才能使Intent有用。
  而PendingIntent的话就是将这个动作包含在内了。
  b.MediaPlayer播放声音。
  c.RingtoneManager铃声。
  d.PowerManager和WindowManager。
  播放铃声的时候,首先判断PowerManager.isScreenOn判断屏幕是否"亮",如果没亮需要做一些处理。
  WindowManager允许app和窗口交互(The interface that apps use to talk to the window manager. )。
  应该就是在锁屏等情况,做一点特殊处理,还没有具体去细看。
  
  PowerManager,电源管理~
  
  写在最后:
  研究过程中,遇到很多不很懂的代码,网上搜索学习下,基本就知道怎么回事了。
  2011年,4年前就有过2个月时间的学习。
  现在工作好几年了,各种技术都研究或者了解过。
  现在,重新看Android应用开发,难度不是很大。
  对Android有了整体的了解,看了几本书,就可以自己研究写代码了。
  不懂的,网上找找资料,然后自己总结,时间长了,估计1~2年,就算是了熟练工了。
  预计,到2016年下半年,我的Android水平就很不错了。
  在武汉这种二线城市,如果让我搞Android开发,薪资在8k~15k左右吧~
  也许~
  
  
  参考资料
  Android开机广播android.intent.action.BOOT_COMPLETED
  http://my.oschina.net/onlytwo/blog/281892
  
  AndroidManifest.xml文件详解(receiver)
  http://blog.csdn.net/think_soft/article/details/7583047
  
  Android中Broadcast Receiver组件详解
  http://blog.csdn.net/zuolongsnail/article/details/6450156
  
  Android中的AlarmManager的使用
  http://blog.csdn.net/wangxingwu_314/article/details/8060312
  
  【Android笔记】MediaPlayer基本使用方式
  http://blog.csdn.net/ddna/article/details/5176233#reply
  
  Media开发之铃声设置(RingtoneManager)
  http://rocking-kaan-126-com.iteye.com/blog/1228215
  
  Android判断屏幕锁屏的方法总结
  http://www.2cto.com/kf/201404/296615.html
  
  android 之 PowerManager 与电源管理
  http://blog.csdn.net/xieqibao/article/details/6562256

小米开源便签Notes-源码研究(2)-定时提醒的便签的更多相关文章

  1. Android开源项目 Universal imageloader 源码研究之Lru算法

    https://github.com/nostra13/Android-Universal-Image-Loader universal imageloader 源码研究之Lru算法 LRU - Le ...

  2. Android开源项目 Universal imageloader 源码研究之项目框架

    Universal imageloader 的代码并不复杂 重点是缓存,线程池任务 下面都用UML图进行了绘制 基本使用流程就是 初始化配置,设置Options参数,最后Dispaly提交下载 pub ...

  3. springboot脚手架liugh-parent源码研究参考

    1. liugh-parent源码研究参考 1.1. 前言 这也是个开源的springboot脚手架项目,这里研究记录一些该框架写的比较好的代码段和功能 脚手架地址 1.2. 功能 1.2.1. 当前 ...

  4. OAuth2学习及DotNetOpenAuth部分源码研究

    OAuth2学习及DotNetOpenAuth部分源码研究 在上篇文章中我研究了OpenId及DotNetOpenAuth的相关应用,这一篇继续研究OAuth2. 一.什么是OAuth2 OAuth是 ...

  5. zepto源码研究 - zepto.js - 1

    简要:网上已经有很多人已经将zepto的源码研究得很细致了,但我还是想写下zepto源码系列,将别人的东西和自己的想法写下来以加深印象也是自娱自乐,文章中可能有许多错误,望有人不吝指出,烦请赐教. 首 ...

  6. dubbo源码研究(一)

    1. dubbo源码研究(一) 1.1. dubbo启动加载过程 我们知道,现在流行注解方式,用spring管理服务,dubbo最常用的就是@Reference和@Service了,那么我首先找到这两 ...

  7. 【JavaScript】$.extend使用心得及源码研究

    最近写多了js的面向对象编程,用$.extend写继承写得很顺手.但是在使用过程中发现有几个问题. 1.深拷贝 $.extend默认是浅拷贝,这意味着在继承复杂对象时,对象中内嵌的对象无法被拷贝到. ...

  8. underscore.js源码研究(8)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

  9. underscore.js源码研究(7)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

随机推荐

  1. libevent的使用(socket)

    这篇文章介绍下libevent在socket异步编程中的应用.在一些对性能要求较高的网络应用程序中,为了防止程序堵塞在socket I/O操作上造成程序性能的下降,须要使用异步编程,即程序准备好读写的 ...

  2. rtmutex赏析

    [摘要] rtmutex作为futex的底层实现,有两个比較重要的特性.一个是优先级继承,一个是死锁检測.本文对这两个特性的实现进行说明. 一.优先级继承 2007年火星探路者号的vxworks上发生 ...

  3. 编写SDR SDRAM页突发模式控制器的注意点-下篇

    本来是没打算写这些的,但是后面逐渐发现点问题,所以决定再写一个下篇来补充说明一下. 图一 细心的网友会发现上篇末尾的打印是有点问题的,因为我的数据产生器产生的是1-200,1-200,1-200,1- ...

  4. Webkit 的麻烦和解决

    * placeholder 在 focus 状态下内容为空时,依然显示文字.和 IE11,Firefox 均不一致: input:focus::-webkit-input-placeholder { ...

  5. SVN: repository browser 库浏览器

    SVN: repository browser  库浏览器 -----如果不想全部下载,可以通过repository browser  库浏览器 从库中选择要下载的文件夹内容下载(svn针对性下载)

  6. jqueryEasyUI form表单提交的一个困惑

    今天用到了jqueryEasyUI的form表单做一个增加操作的提交,想打开调试(用的是火狐)看看传的参数,但是怎么也看不到form表单提交的http请求?而且还会发送一个另外的请求! 在页面加载时, ...

  7. ES6中常用的简写方式

    1. var foo = 'bar'; var baz = {foo}; baz // {foo: "bar"} // 等同于 var baz = {foo: foo}; 2. f ...

  8. Decorator - 利用装饰器武装前端代码

    历史 以前做后端时,接触过一点Spring,也是第一次了解DI.IOC等概念,面向切面编程,对于面向对象编程还不怎么熟练的情况下,整个人慌的一批,它的日志记录.数据库配置等都非常方便,不回侵入到业务代 ...

  9. [NOIP2014提高组]联合权值

    题目:洛谷P1351.Vijos P1906.codevs3728.UOJ#16. 题目大意:有一个无向连通图,有n个点n-1条边,每个点有一个权值$W_i$,每条边长度为1.规定两个距离为2的点i和 ...

  10. db2部署

    下载地址: wget   ftp://public.dhe.ibm.com/software/hk/cobra/db2exc_970_LNX_x86_64.tar.gz 上传压缩包到/opt,再解压d ...