在嵌入式开发中,时常会遇到延时函数的调用,比如在协议的制定时需考虑到各个节点之间的数据交互冲突情况。对于51系列单片机由于每条指令执行所耗费的时间可知,因此精确延时较为容易实现。在ARM7系列的处理器中由于指令的执行采用流水线技术,加上应用层程序的编写方面编译器所解析的机器周期未知,因此难以实现精确延时。一般应用中往往使用定时器解决此类问题。ARM7处理器中除定时器模块之外,还有周期性间隔定时器模块。周期性间隔定时器一般为操作系统的调度程序提供时间间隔中断,PIT的设计提供了程序时间管理上的精确度。图示为周期性间隔定时器的方框图。
PIT有一个可编程的溢出计数器,它具有溢出后自动复位的特性。PIT基于两个计数器:一个20位的CPIV计数器及一个12位的PICNT计数器。两个计数器的时钟都是主时钟的1/16。通过PIT_MR模式寄存器的PITIEN位设置中断使能后,CPIV计数器从0开始计数,一直到模式寄存器PIT_MR的PIV域所定义的溢出数值为止。CPIV计数器溢出后立即复位为0,同时PICNT寄存器累加,状态寄存器PIT_SR的PITS被置位从而触发中断。
通过读取周期性间隔数值寄存器PIT_PIVR获得CPIV和PICNT的数值后,溢出计数器(PICNT)复位,PITS清零,从而确认中断。继而使得周期性间隔定时器中断模式继续进行。周期性间隔定时器的使能与关闭通过寄存器PIT_MR的PITEN位实现。注意,只有在CPIV为0时对PITEN的操作才有效。
以下为使用周期性间隔定时器模块产生定时中断函数。
void ClockHandler(void) __irq { Display_Received++; *AT91C_AIC_EOICR = *AT91C_PITC_PIVR; //Acknowledge Interrupt void PIT_Init(void) { *AT91C_PITC_PIMR = AT91C_PITC_PITIEN | AT91C_PITC_PITEN | 784741; //PIT使能及定时周期配置 AT91F_AIC_ConfigureIt(AT91C_BASE_AIC,AT91C_ID_SYS,1,AT91C_AIC_SRCTYPE_INT_POSITIVE_EDGE,(void (*)())ClockHandler);//中断触发方式及函数入口地址 AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_SYS); //Enable Interrupt }
1、Service的种类
按运行地点分类:
类别 区别 优点 缺点 应用 本地服务(Local) 该服务依附在主进程上, 服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。 主进程被Kill后,服务便会终止。 非常常见的应用如:HTC的音乐播放服务,天天动听音乐播放服务。 远程服务(Remote) 该服务是独立的进程, 服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。 该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。 一些提供系统服务的Service,这种Service是常驻的。其实remote服务还是很少见的,并且一般都是系统服务。
按运行类型分类:
类别 区别 应用 前台服务 会在通知一栏显示 ONGOING 的 Notification, 当服务被终止的时候,通知一栏的 Notification 也会消失,这样对于用户有一定的通知作用。常见的如音乐播放服务。 后台服务 默认的服务即为后台服务,即不会在通知一栏显示 ONGOING 的 Notification。 当服务被终止的时候,用户是看不到效果的。某些不需要运行或终止提示的服务,如天气更新,日期同步,邮件同步等。有同学可能会问,后台服务我们可以自己创建 ONGOING 的 Notification 这样就成为前台服务吗?答案是否定的,前台服务是在做了上述工作之后需要调用 startForeground ( android 2.0 及其以后版本 )或 setForeground (android 2.0 以前的版本)使服务成为 前台服务。这样做的好处在于,当服务被外部强制终止掉的时候,ONGOING 的 Notification 任然会移除掉。
按使用方式分类:
类别 区别 startService 启动的服务 主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService bindService 启动的服务 该方法启动的服务要进行通信。停止服务使用unbindService startService 同时也 bindService 启动的服务 停止服务应同时使用stepService与unbindService以上面三种方式启动的服务其生命周期也有区别,将在随后给出。
2、Service 与 Thread 的区别
很多时候,你可能会问,为什么要用 Service,而不用 Thread 呢,因为用 Thread 是很方便的,比起 Service 也方便多了,下面我详细的来解释一下。
1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。
2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。因此请不要把 Service 理解成线程,它跟线程半毛钱的关系都没有!
既然这样,那么我们为什么要用 Service 呢?其实这跟 android 的系统机制有关,我们先拿 Thread 来说。Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。
举个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。
因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。
3、Service的生命周期
onCreate onStart onDestroy onBind
1). 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。
2). 被绑定的服务的生命周期:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。
一、自定义组件大小的设置
不管你怎么设置,自定义组件似乎总是match_parent。我们需要覆盖onMeasure方法,并在其内正确设置组件大小。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //注意:为尽可能简洁本文只讨论EXACTLY和AT_MOST模式 int mode = MeasureSpec.getMode(widthMeasureSpec); if(mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) { this.widthMeasureSpec = widthMeasureSpec; this.heightMeasureSpec = heightMeasureSpec; int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(width, height); } else if(mode == MeasureSpec.UNSPECIFIED) { Log.d("WOGU", "mode=UNSPECIFIED"); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }二、自定义组件内的组件的大小设置
除了覆盖onMeasure还需覆盖onLayout,在期内设置子元素的大小。
public class MyViewGroup extends ViewGroup { protected int widthMeasureSpec, heightMeasureSpec; @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if(changed) { View view = getChildAt(0); //measure将触发View的onMeasure。 //widthMeasureSpec包括尺寸和模式 //int width = MeasureSpec.getSize(widthMeasureSpec); //int height = MeasureSpec.getSize(heightMeasureSpec); //int mode = MeasureSpec.getMode(widthMeasureSpec); //mode的可能值MeasureSpec.AT_MOST,MeasureSpec.EXACTLY,MeasureSpec.UNSPECIFIED //这里只是简单的在onMeasure中记录widthMeasureSpec和heightMeasureSpec //其实MeasureSpec提供了根据sizet和模式合成widthMeasureSpec,heightMeasureSpec的方法 //int widthMeasureSpec = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY); //int heightMeasureSpec = MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY); view.measure(widthMeasureSpec, heightMeasureSpec); view.layout(0, 0, getWidth(), getHeight()); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { this.widthMeasureSpec = widthMeasureSpec; this.heightMeasureSpec = heightMeasureSpec; ... } }