0
点赞
收藏
分享

微信扫一扫

Pro Android学习笔记(一零二):BroadcastReceiver(6):长时间处理通知小例子(下)

在前面我们已经详细分析wake lock如何在小例子中应用,本次学习将完成其余部分,即接收器接收到通知,然后开启服务,通过服务的worker线程来进行长时间的处理。很值得在此重贴小例子的设计图(也不枉我花了点时间)。

Pro Android学习笔记(一零二):BroadcastReceiver(6):长时间处理通知小例子(下)_生命周期

接收器的处理

作为通用的小例子,希望能提供通用的解决方案,下面是通用的长时间处理的通知接收器的抽象类。

public abstract class ALongRunningReceiver extends BroadcastReceiver{  
     @Override 
    public void onReceive(Context context, Intent intent) {  
         LightedGreenRoom.setup(context);  //收到通知后,就要开始确保持有wakelock(进行setup)来保证后续代码处理        startService(context,intent);  
     } 
     //开启IntentService,将通知(intent)作为开启Service的Intent所携带的传输进行传递,以便Service中,完全获知通知内容。其余的和开启普通IntentService没有什么区别。至于是开启哪个Service,用abstract来确定,有实现类具体指明 
     private void startService(Context context, Intent intent){ 
         Intent serviceIntent  = new Intent(context,getLRSClass());
         serviceIntent.putExtra("original_intent", intent);  //将通知以Parcelable的形式在extra中进行传递  
         context.startService(serviceIntent); 
     } 
     // 通用的长时间处理接收器需要获取具体处理的Sevice名称。 
     public abstract Class getLRSClass(); 
 }小例子的具体实现如下,很简单,用于明确开启哪个IntentService:
 public class Test60SecBCR extends ALongRunningReceiver{ 
     @Override 
    public Class getLRSClass() {  
         return Test60SecBCRService.class; 
     } 
 }

IntentService的处理

作为通用了例子,对于IntentService同样给出一个抽象类,用于wakelock的处理。

public abstract class ALongRunningNonStickyBroadcastService extends IntentService{
     public static String tag = "ALongRunningNonStickyBroadcastService"; 
   //具体的通知处理,其中参数为通知的内容,如此才能称之为一个好的抽象封装,使用时就如同普通的通知处理。
    protected abstract void handleBroadcastIntent(Intent broastIntent);     
     public ALongRunningNonStickyBroadcastService(String name){ 
         super(name); 
     } 
     
     @Override //对于worker线程,获取具体通知,然后进行具体处理。
     final protected void onHandleIntent(Intent intent) {  
         try{ 
             Intent brocastIntent = intent.getParcelableExtra("original_intent"); 
             handleBroadcastIntent(brocastIntent); 
         }finally{ 
             LightedGreenRoom.s_leave(); //处理结束要离开房间(如果无其他处理,则关灯)        } 
     } 

     @Override 
     public void onCreate() {  
         super.onCreate(); 
         LightedGreenRoom.setup(this.getApplicationContext());//再次确保开灯,setup可以调用多次,为何在接收器进行了调用,在service中仍需要,因为service可能会被重新唤醒(因内存不足等原因service被回收,由于存在为处理的intent,在资源可行时,将重新会唤醒)。
        LightedGreenRoom.s_registerClient();//可能有多个Service,进行注册,以便没有Service时光灯和复位
     } 

     @Override 

    /** 我奇怪为何不用super.onStartCommand(intent, flags, startId);,而用super.onStart(),在我们过往的学习中,Service,无论是local service还是remote service,生命周期中都没有onStart()一项。于是去查了参考,参考说onStart()已经是deprecate,接着查了Android4.4的android.app.IntentService,相关代码如下,其中onStart()是将消息加入到looper队列中,而onStartCommand()调用了onStart()。对于本例,采用super.onStart()或者super.onStartCommand()的效果是一样的。 
     public void onStart(Intent intent, int startId) { 
         Message msg = mServiceHandler.obtainMessage(); 
         msg.arg1 = startId; 
         msg.obj = intent; 
         mServiceHandler.sendMessage(msg); 
     } 

     @Override 
     public int onStartCommand(Intent intent, int flags, int startId) { 
         onStart(intent, startId); 
         return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; 
     } 
      * 因此使用哪个都没有什么不同。 
      */ 
     public int onStartCommand(Intent intent, int flags, int startId) {   
         super.onStart(intent, startId); 
         LightedGreenRoom.s_enter();//要进入房间,准备开启线程处理
        return Service.START_NOT_STICKY; //表明如果没有挂起的intent,不要重启service    } 
     
     @Override //如果Android回收这个进程,或者以StopSerivce()的方式强制结束,或者处理中出现异常退出,需要确保能释放wakelock
     public void onDestroy() { 
         super.onDestroy(); 
         LightedGreenRoom.s_unregisterClient();
     }  
     
 }

有了这个通用的抽闲类,加入wakelock处理的IntentService,我们在具体的实现例子中只需进行具体的通知处理即可,本例简单地sleep()一分钟,可以证明通过worker线程,即使处理的时间超过10秒,也不会触发ANR异常。

public class Test60SecBCRService extends ALongRunningNonStickyBroadcastService{ 
     public Test60SecBCRService(){ 
         super("cn.flowingflying.Test60SecBCRService"); 
     } 
     
     @Override 
     protected void handleBroadcastIntent(Intent broastIntent) {  
        try{ 
             Thread.sleep(1000*60); 
         }catch(Exception e){ 
             
         } 
         String message = broastIntent.getStringExtra("message"); 
         Log.d("WEI","Job completed with message :" + message); 
     } 
 }

进一步了解Service

唤起一个service,使用startService(),将触发onStartCommand(),service的生命周期将由onStartCommand()的返回值决定。服务进程在内容和在运行是两个不同的概念,服务运行是在响应startService(),如果没有执行,并不意味这服务进程不驻入内存。有时候,这两个概念也会混淆表述,在内存中驻留(获得某些资源但并不实际运行),有人也称之为running,例如Android声称其保持service running。onStartCommand()是在主线程中运行的,当其超过5秒或者10秒,会产生ANR异常。

Android会尽可能在内存中存放service,但是在内存紧张时,也会进行回收(触发onDestroy())。和activity不同,service如果不是被精确关闭(例如通过stopSelf(),stopService()),当资源许可时,如果还有挂起未处理的startService intent在队列,service将被restart,当然会先执行onCreate()。这种自动重启的机制是一个典型的sticky方式。

对于通过stopService()的方式进行明确关闭的情况,要看看有多少client连接service,如果是被最后一个client进行stop,则service的生命周期已经完毕,不会restart。然而这里给出的是正常处理的情况,但是如果出现异常,intent处理的时候异常退出了,即生命周期并非完整,仍会触发restart。在Android 2.0后,可以设置Service.START_NOT_STICK模式,通过onStartCommand()返回。

对于Service.START_STICKY,表明即使没有intent也会被重启,这是会调用onCreate()和onStartCommand(Intent null),这给service一个正常stopSelf()的机会,然后再去执行挂起的intent(如果有)。我们回头看看IntentService的内部类ServiceHandler的源代码,handleMessagge()最后会调用stopSelf();

private final class ServiceHandler extends Handler {
     public ServiceHandler(Looper looper) { 
         super(looper); 
     } 

     @Override 
     public void handleMessage(Message msg) { 
         onHandleIntent((Intent)msg.obj); 
         stopSelf(msg.arg1);     } 
 }

在IntentService的代码中,我们可以看到onStart()和stopSelf()一个是将消息加入队列,一个是将消息清除出队列,正常的是成对出现的。

如果service处理一个intent的时间很长,例如半小时,这是service会被回收,如果我们是用nonsticky,service并不会被唤醒,也就不会被调用stopSelf()。一般而言,这不会带来什么麻烦,但是我们一定要求执行onStart()和stopSelf(),可通过Service.START_REDELIVER,这保证只要不调用stopSelf(),就会确保队列中有一个挂起的intent,只有在stopSelf()才会清出队列。

小例子代码在:Pro Android学习:Broadcast小例子


举报

相关推荐

0 条评论