PendingInent 与 AlarmManager

概要:本文通过对OPhone内置闹铃功能的简单介绍,来让开发者朋友们了解基于OPhone平台下客户/服务模式的编程模型,以及如何使用OPhone系统提供的闹铃唤醒功能。与此同时,本文还对PendingIntent做一些简单的介绍,并通过实例程序来演示如何通过PendingIntent将闹钟应用程序和系统闹铃服务联系起来。(作者:Yang Ai'in)

一、闹铃功能

        闹钟应用程序作为人们日常常用的基本应用程序之一,其重要性不言而喻。在OPhone系统中闹铃服务功能不仅仅对闹钟应用程序服务,最重要的是可以利用该闹铃服务功能提供的唤醒能力来做定时器。这样即便应用程序没有运行或者是没有启动的情况下,只要其注册过闹铃,那么该闹铃到时间后,OPhone系统可以自动将该应用程序启动,这就是所谓的闹铃“唤醒“功能。

        在OPhone系统中,底层系统提供了两种类型的时钟,软时钟与硬时钟,软时钟就是我们常说的Timer,硬时钟就是RTC。系统在正常运行的情况下,Timer工作提供时间服务和闹铃提醒,而在系统进入睡眠状态后,时间服务和闹铃提醒由RTC来负责。对于上层应用来说,我们并不需要关心是 timer还是RTC为我们提供服务,因为OPhone系统的Framework层把底层细节做了封装并统一提供API。这个API他的名字就叫 AlarmManager。在OPhone系统中有意思的是对应AlarmManage有一个AlarmManagerServie服务程序,该服务程序才是正真提供闹铃服务的,它主要维护应用程序注册下来的各类闹铃并适时的设置即将触发的闹铃给闹铃设备(在OPhone系统中,linux实现的设备名为”/dev/alarm”),并且一直监听闹铃设备,一旦有闹铃触发或者是闹铃事件发生,AlarmManagerServie服务程序就会遍历闹铃列表找到相应的注册闹铃并发出广播。该服务程序在系统启动时被系统服务程序system_service启动并初始化闹铃设备(/dev/alarm)。当然,在JAVA层的AlarmManagerService与Linux Alarm驱动程序接口之间还有一层封装,那就是JNI。

         AlarmManager将应用与服务分割开来后,使得应用程序开发者不用关心具体的服务,而是直接通过AlarmManager来使用这种服务。这也许就是客户/服务模式的好处吧。AlarmManager与AlarmManagerServie之间是通过Binder来通信的,他们之间是多对一的关系。在OPhone系统中,AlarmManage提供了3个接口5种类型的闹铃服务:

  •         3API调用接口:
  • void       cancel(PendingIntent operation)  
        // 取消已经注册的与参数匹配的闹铃  
        void      set(int type, long triggerAtTime, PendingIntent operation)  
        //注册一个新的闹铃  
        void      setRepeating(int type, long triggerAtTime, long interval, PendingIntent operation)  
        //注册一个重复类型的闹铃  
        void      setTimeZone(String timeZone)  
        //设置时区 

    • 5种闹铃类型:

    public static final int ELAPSED_REALTIME  
            //当系统进入睡眠状态时,这种类型的闹铃不会唤醒系统。直到系统下次被唤醒才传递它,该闹铃所用的时间是相对时间,是从系统启动后开始计时的,包括睡眠时间,可以通过调用SystemClock.elapsedRealtime()获得。系统值是3    (0x00000003)。  
     
            public static final int ELAPSED_REALTIME_WAKEUP  
            //能唤醒系统,用法同ELAPSED_REALTIME,系统值是2 (0x00000002) 。  
     
            public static final int RTC  
            //当系统进入睡眠状态时,这种类型的闹铃不会唤醒系统。直到系统下次被唤醒才传递它,该闹铃所用的时间是绝对时间,所用时间是UTC时间,可以通过调用 System.currentTimeMillis()获得。系统值是1 (0x00000001) 。   
      
            public static final int RTC_WAKEUP  
            //能唤醒系统,用法同RTC类型,系统值为 0 (0x00000000) 。  
     
            Public static final int POWER_OFF_WAKEUP  
            //能唤醒系统,它是一种关机闹铃,就是说设备在关机状态下也可以唤醒系统,所以我们把它称之为关机闹铃。使用方法同RTC类型,系统值为4(0x00000004)。

     

     

    二、闹钟设置与提醒

            我们首先通过一个直观的UI来感受一下OPhone系统内嵌的闹钟程序是如何响应用户设置和自动提醒的的:(如下4个图所示)

    (图一)(图二)

     

    (图三) (图四)

     

             上面4图直观的告诉了开发者如何使用OPhone内嵌的闹钟应用程序,但开发者可能更关心的是通过代码如何实现这些功能。比如说怎么设置一个闹铃,该设置哪种类型的闹铃,以及如何获得闹铃时间已到并提醒用户。依据这几个问题,我们在下面的章节中逐步来介绍。

     

    2.1 设置闹铃

            在第一小节中,我们已经提到过OPhone系统AlarmManagerService提供了两个设置闹铃的API,他们分别是:

    void      set(int type, long triggerAtTime, PendingIntent operation)  
    void      setRepeating(int type, long triggerAtTime, long interval, PendingIntent operation)  


            在OPhone 1.5版本中又增加了一个API。
            下面的程序演示了如何设置一个闹铃。

    /当设置的闹铃触发时,Ophone系统会广播一个Intent,当然我们需要在创建一个新的闹铃  
        //时注册一个闹铃事件接收对象AlarmReceiver ,该接收对象也可以通过在//AndroidManifest.xml中发布,也可以在代码中动态注册。  
     
    import android.app.AlarmManager;  
    import android.app.PendingIntent;  
    import android.content.Intent;  
    import android.os.SystemClock;  
    import android.os.Bundle;  
    import java.util.Calendar;  
     
    //创建一个PendingIntent  
    Intent intent = new Intent(ALARM_ALERT_ACTION);  
    intent.putExtra(ID, id);  
    intent.putExtra(TIME, atTimeInMillis);  
    PendingIntent sender = PendingIntent.getBroadcast(  
                    context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);  
    //获得AlarmMnager并注册一个新闹铃,  
    //一次性闹铃的设置  
     
    AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);  
    am.set(AlarmManager.POWER_OFF_WAKEUP, atTimeInMillis, sender);  
     
    //重复性闹铃的设置  
    // We want the alarm to go off 30 seconds from now.  
    long firstTime = SystemClock.elapsedRealtime();  
     firstTime += 15*1000;  
     
    // Schedule the alarm!  
    AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);  
    am.setRepeating(AlarmManager.POWER_OFF_WAKEUP,  
                                firstTime, 15*1000, sender); 




    //在AndroidManifest.xml中注册  
    <receiver android:name="AlarmReceiver">  
                <intent-filter>  
                   <action android:name="com.android.alarmclock.ALARM_ALERT" />  
                </intent-filter>  
     </receiver> 


    //在应用程序执行过程中注册  
     
    AlarmReceiver mAlarmReceiver;  
    IntentFilter filter = new IntentFilter();  
    filter.addAction(“com.android.alarmclock.ALARM_ALERT”);  
    context.registerReceiver(mAlarmReceiver, filter);  
     
    public class AlarmReceiver extends BroadcastReceiver {  
        @Override 
        public void onReceive(Context context, Intent intent) {  
           
       }  

     

     

    2.2 闹铃提醒

            在OPhone系统中,当应用程序所设置的闹铃时间到了后,OPhone系统中的AlarmManagerService就会从系统底层获取一个闹铃事件并从自己维护的队列中取出其匹配的闹铃,然后通过其应用注册的PendingIntent把该闹铃事件发送回给应用。

    PendingIntent.send();  

            当我们的应用收到该Intent后就会启动相应的Activity来提醒用户闹铃时间到。
            程序代码如下:

    package com.android.alarmclock;  
    import android.content.Context;  
    import android.content.Intent;  
    import android.content.BroadcastReceiver;  
    import android.os.Handler;  
    import android.os.PowerManager;  
    import android.os.SystemClock;  
     
    public class AlarmReceiver extends BroadcastReceiver {  
        @Override 
        public void onReceive(Context context, Intent intent) {  
            long now = System.currentTimeMillis();  
            int id = intent.getIntExtra(Alarms.ID, 0);  
            long setFor = intent.getLongExtra(Alarms.TIME, 0);  
            Intent fireAlarm = new Intent(context, AlarmAlert.class);  
            fireAlarm.putExtra(Alarms.ID, id);  
            fireAlarm.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
            //启动一个新的UI对象来提醒  
            context.startActivity(fireAlarm);  
       }  

     

     

     

    三、PendingIntent

            在前面的章节中,我们在注册闹铃﹑发送闹铃事件的时候,有过一个重要的参数PendingIntent。这个PendingIntent可以说是 Intent的进一步封装,他既包含了Intent的描述又是Intent行为的执行(这种定义也许不太严格),如果将Intent比作成一个订单的话,PendingIntent更像是一个下订单的人,因为它既要负责将订单发出去,也要负责订单发送后的处理,比如发送成功后要准备验收订单货物,发送失败后要重发还是取消订单等操作。开发者可以通过调用getActivity(Context, int, Intent, int), getBroadcast(Context, int, Intent, int), getService(Context, int, Intent, int)函数来得到一个PendingIntent实例。

    public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags)  

     

            通过该函数获得的PendingIntent将会扮演一个广播的功能,就像调用 Context.sendBroadcast()函数一样。当系统通过它要发送一个intent时要采用广播的形式,并且在该intent中会包含相应的 intent接收对象,当然这个对象我们可以在创建PendingIntent的时候指定,也可以通过ACTION 和CATEGORY等描述让OPhone系统自动找到该行为处理对象。

            实例代码如下:
     
    Intent intent = new Intent(AlarmController.this, OneShotAlarm.class);  
    PendingIntent sender = PendingIntent.getBroadcast(AlarmController.this,  
                        0, intent, 0); 

    Public static PendingIntent getActivity(Context, int, Intent, int)  


            通过该函数获得的PendingIntent可以直接启动新的activity, 就像调用 Context.startActivity(Intent)一样.不过值得注意的是要想这个新的Activity不再是当前进程存在的Activity 时。我们在intent中必须使用Intent.FLAG_ACTIVITY_NEW_TASK.
             实例代码如下:

     

    // The PendingIntent to launch our activity if the user selects this notification  
     PendingIntent contentIntent = PendingIntent.getActivity(this, 0,  
                                 new Intent(this, AlarmService.class), 0); 

    public static PendingIntent getService(Context context, int requestCode, Intent intent, int flags)   

            通过该函数获得的PengdingIntent可以直接启动新的Service,就像调用Context.startService()一样。
            实例代码如下:

    // Create an IntentSender that will launch our service, to be scheduled  
      // with the alarm manager.  
      mAlarmSender = PendingIntent.getService(AlarmService.this,  
                  0, new Intent(AlarmService.this, AlarmService_Service.class), 0); 

     

     

    四、PendingInent与 service

            在OPhone系统编程中,一个完整OPhone应用程序可以有4个需要创建的模块,他们分别是:
            Activity ,Broadcast intent Receiver,Service,Content Provider。Service作为一个OPhone应用程序组成部分,通常运行在系统后台,他与用户之间没有交互。像其他应用程序对象一样运行在所属进程的主线程中。那么这就意味着它有可能进入长时间的运行等待而导致应用得不到用户的相应。所以在开发者设计程序的时候就要考虑,如果一个Service 要做一些长时间的数据处理时(比如播放MP3,或者是网络下载),就需要把该工作切换到自己的线程空间来执行。
           实例代码如下:

    import android.app.Notification;  
    import android.app.NotificationManager;  
    import android.app.PendingIntent;  
    import android.app.Service;  
    import android.content.Intent;  
    import android.os.Binder;  
    import android.os.IBinder;  
    import android.os.Parcel;  
    import android.os.RemoteException;  
    import android.widget.Toast;  
     
    //定义一个 Service 对象  
    public class AlarmService_Service extends Service {  
        NotificationManager mNM;  
        public void onCreate() {  
             //创建一个线程来运行Runnable  
            Thread thr = new Thread(null, mTask, "AlarmService_Service");  
            thr.start();  
        }  
        public void onDestroy() {  
        }  
        Runnable mTask = new Runnable() {  
            public void run() {  
                // 通常我们就可以在这里设计长时间运行的功能,  
                long endTime = System.currentTimeMillis() + 15*1000;  
                while (System.currentTimeMillis() < endTime) {  
                    synchronized (mBinder) {  
                        try {  
                            mBinder.wait(endTime - System.currentTimeMillis());  
                        } catch (Exception e) {  
                        }  
                    }  
                }  
     
                // 停止Service  
                AlarmService_Service.this.stopSelf();  
            }  
        };  
     
        //在编写Service代码时,可以不实现onStart,onStop等函数,但一定要实现onBind函数  
        public IBinder onBind(Intent intent) {  
            return mBinder;  
        }  
         /*  通过该对象可以与客户端通信 
         */ 
        private final IBinder mBinder = new Binder() {  
            @Override 
            protected boolean onTransact(int code, Parcel data, Parcel reply,  
                    int flags) throws RemoteException {  
                return super.onTransact(code, data, reply, flags);  
            }  
        };  



    小结:本篇文章主要介绍了如何使用AlarmManager的定时唤醒功能,以及各种闹铃的含义与API使用实例,希望对读者朋友们在OPhone应用编程中,对AlarmManager的正确使用起到抛砖引玉的作用。同时我们还引入了一个重要的概念 PendingIntent,通过对PendingIntent参数的解析,相信读者朋友们对PendingIntent的使用有了一个基本的认识。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值