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

文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处以及作者@恺风Wei。

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

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

接收器的处理

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

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小例子

相关链接:我的Android开发相关文章

你可能感兴趣的:(Pro Android学习笔记(一零二):BroadcastReceiver(6):长时间处理通知小例子(下))