Android (一) Service基础

为一种应用组件,Service可以长时间地在后台执行而不用给用户提供UI接口(不同于Activity)。其他的组件可以启动某个service,但是这个service并不会与启动它的应用组件绑定在一起。及时应用组件退出,而service还是会继续在后台运行,除非组件选择与这个service绑定(通过bindService()方法)。组件选择绑定到目标组件的目的是可以与service进行IPC。所以我们一般可以将service设计为进行后台I/O操作或者网络操作的模块。一个Service质上有两种的形式:

Started: 当一个应用组件通过startService()启动目标service后,service就会处于这种状态。一旦service启动,那么service就可以无限地在后台运行,即使启动它的组件已经退出了。通常这种started service用于单向的逻辑,不需要返回结果,例如下载图片。当任务完成后,service需要暂停自己。

Bound: 当一个应用组件调用bindService()后,目service就会处于bound态。Bound service会提供一个用于client-server交互的接口,即IBinder对象。其他组件通过IBinder来与目service进行交互。只要bind到目service组件还运行着,那么目标service就会持续运行。一旦这些组件都destroyed或者unbind,那么目service就会被destroyed

虽然列出了上述两种形式,但是service有可能同时运行在这两种形式的。只不过你需要通过实现onStartCommand()方法来运行其他组件启动服务以及实现onBind()方法来运行绑定。启动service还是以Intent的形式。同时也可以将serviceManifest.xml文件中声明private这样就会阻止其他应用的访问。service认是运行在应用进程的main thread中的。所以如果service处理消耗CPU较大的任务时,建议另外启动新的线程。这样可以避免出现ANR问题(但是如何将service放到新的service中,我目前能想到的就是AsyncTask)。

Basics

为了创建一个service,首先要扩展Service这个类或者使用它已有的子类。如果是使用自定义的Service子类,那么就需要实现一些与Service生命周期相关的回调方法(callback methods)以及与交互绑定相关的机制。主要的回调方法如下:

onStartCommand() 当其他的应用组件(例如activity)来请求启动service时,系统就会调用这个方法。一旦这个方法被调用,service就会启动同时可以在后台无休止地运行。所以开发者需要在完成任务之后手动地停止service这个主要是通过调用stopSelf()stopService()两个方法来实现。如果开发者只想提供binding的机制,那么就没有必要实现onStartCommand()这个方法

onBind() 当其他的应用组件通过调用bindService()想要绑定到目标service时,系统就会调用serviceonBind()方法。在实现这个方法中,开发者需要返回一个用于与service交互的接口,即IBinder对象。开发者必须要实现这个方法,如果不提供绑定的机制,那就直接返回null

onCreate():当第一次service创建时,系统就会调用这个方法来提供一次setup过程。这个方法会在onStartCommand()方法和onBind()方法之前被调用。如果service经在running,那么这个方法就不会被调用。

onDestroy():当这个service不再需要的时候,系统就会通过调用这个方法来销毁service。开发者可以通过实现这个方法在结束之前清空一些被使用的资源,例如线程、listenersreceivers等。

一旦service启动,那么就会一直运行直到service自己调用stopSelf()暂停自己或者其他的process调用stopService()暂停service。系统只有在内存过低以及必须通过回收资源才能保证用户关注的activity良好运行的情况下,才会强制停止服务。如果service是被绑定到上述类型的activity中,那么被杀死的概率比较低。如果service被声明为运行在前台,那么就永远不会被杀死。随着时间的增长,系统会降低长时间运行的service优先级,也就是会在background tasks进行迁移。所以运行时间越长的service越容易被杀死。这样就要求开发者能够处理各种由系统引起的service启事件。因为只要系统的资源充裕了,系统又会重启service。下面我们来讨论一下具体如何使用Service

Manifest中声明Service

activity样,都需要在Manifest.xml文件中声明所有在应用中的service。声明service,只需要在<application>下声明<service>元素即可。<service>元素中必然还包括一些属性,其中包括启动service需要的权限以及service运行所在的process等。而只有android:name这个属性是必须要提供的,它指定了service对应的类名。类似于activityservice可以通过定义intent filters来允许其它组件调用service,不论这个组件是来自于哪个application。如果只是现在自有应用中使用,service就不必提供intent filters这样在启动service时候,只需要在intent中指出目service的类名即可。此外为了保证service私有于自有应用,可以将android:exported属性设置为false这样即使提供了intent filter,其他应用的组件也无法使用该service

创建一个Service

启动service时指定的Intent需要指出目service以及传递给目标service的数据。而目serviceonStartCommand()中接收到Intent对象。在创建新的Service时,可以通过扩展以下两个类来实现:

Service 所有service的基础类。默认情况下,通过扩展这个类的service运行在应用进程的主线程中。所以一般情况下,需要开发者去创建一个新的线程。??

IntentService 它是Service的子类,但是它会使用一个worker thread处理所有start requests对于负载不高的情况下,扩展IntentService是最好的选择。开发者需要做的只是实现onHandleIntent()方法来接收发送过来的intent对象。(那么对于高并发的情况,又该怎么处理呢??)

扩展IntentService

IntentService创建默认的woker thread执行onStartCommand()而来的所有intent请求。同时会创建一个woker queue来排队所有的intent请求,并按照这个队列发给onHanldeIntent()执行。所以这样就避免了线程安全问题。当所有的请求完成处理之后,就会自动停止而不需要手动地调用stopSelf()方法。并且提供了返回值为nullonBind()认实现。当然也默认实现了onStartCommand()方法,只是在这个方法中将intent请求加入到work queue中,并由onHandleIntent()方法来处理。所以开发者只需要实现onHandleIntent()方法即可。当然也可重写其他的方法,但是要记得调用父类的方法即可。这样才能正确的处理这类service的生命周期。由于onBind()方法是一个返回了null的空实现,所以如果要重写,没有必要调用父类方法。

扩展Service

在高负载的情况下,那么就需要通过多线程来提高性能。这种情况下IntentService经不能满足要求了。因为IntentService在同一时间只能处理一个请求。如下就是扩展Service类的示例代码:

public class DemoService extends Service{


    private Looper demoLooper;

    private ServiceHandler demoHandler;


    private final class ServiceHandler extends Handler{

        public ServiceHandler(Looper looper){

            super(looper);

        }


        public void handleMessage(Message msg){

            // we can do something here like some I/O operations


        }

    }


    @Override

    public void onCreate(){


        // start a new thread to run the service rather than in the main thread

        HandlerThread thread = new HandlerThread("DemoServiceArguments", Process.THREAD_PRIORITY_BACKGROUND);

        thread.start();


        // get the HandlerThread's looper

        demoLooper = thread.getLooper();

        demoHandler = new ServiceHandler(demoLooper);

    }


    @Override

    public int onStartCommand(Intent intent, int flags, int startId){

        // create an empty message when receive a intent request

        // then deliver to the handler

        Message msg = demoHandler.obtainMessage();

        msg.arg1 = startId;

        demoHandler.sendMessage(msg);


        return START_STICKY;

    }


    @Override

    public IBinder onBind(Intent intent){

        return null; // don't provide binding

    }


    @Override

    public void onDestroy(){

        // Service finishes its work

    }

}

由于onStartCommand()处理每个请求的入口方法,所以可以同时处理多个请求。不同于IntentServiceonStartCommand()方法中将请求加入到一个work queue中。当然可以在onStartCommand()方法中为每一个请求创建一个thread,而不是像上述代码中生成一个message,然后加入到Looper中由Handler处理。其实这种机制和IntentService在本质上没有什么区别,但是明显具有后续扩展上的灵活性。值得注意的是onStartCommand()须返回一个表示运行状态的整型变量。系统将根据这个变量来判断在杀死service之后,是否要继续运行service。返回的状态常量有以下几个:

START_STICKY 执行完onStartCommand()方法之后系统杀死了service,那么就会重新创建service并且调用onStartCommand(),但是不会重新发送上一次的intent这时候的intent会是个null值,除非这时候就有待发送的intent。媒体播放器就是适用于这种情况。

START_NO_STICKY 相比于START_STICKY,不同在于杀死service之后系统不会主动地重新创建service。而是等到有新的intent时候。这种是比较安全和节省资源的方式,

START_REDELIVER_INTENT 相比于START_STICKY,不同在于会重新发送一次最后的intent这种比较适用与需要立即重启的情况,例如文件的下载等。

开始Service

其他的应用组件通过传递intentstartService()启动service实例代码如下

Intent intent = new Intent(this, DemoService,class);

startService(intent);

随后就会马上调用serviceonStartCommand()方法。如果这个service之前没有被创建,那么在此之前先调用onCreate()方法。如果想要收到service的回馈同时又不想提供binding机制,那么可以使用PendingIntent实现最后service执行结果的广播(getBroadcast())值得注意的是,多个的intent请求就会导致onStartCommand()方法的多次调用。当然stopSelf()stopService只要一次。

暂停Service

一旦调用了stopSelf()或者stopService()方法,系统就会立即销毁service。但是在暂停service时可能会遇上仍然有request处理的情况,特别是多线程的环境。这是由于在暂停service过程,还会接收到新的request这时候可能就需要stopSelf(int)方法来保证暂停请求之前的intent请求都尽可能地得到处理。stop(int)中的int对应的是onStartCommand()方法中的startId这样在调用stopSelf(int)之前新接受的请求id就会与stopSelf(int)中的startId不匹配,service还会继续运行。相反的是调用stopSelf(int)之后的request就不会被执行。

需要注意的是,stopSelf(int)stopSelfResult(int)的旧版本。前者没有返回值,后者返回一个boolean变量。这两个方法的目的都是为了避免出现暂停时仍有pendingintent请求。发送给方法的IDs一定要按照接收到的顺序。如果传入的是最新接收到的id,那么service就会立即停止。stopSelfResult(int)如果传入的id是最新启动的request,那么就会返回true并且暂停service。后者返回falseservice不会暂停。

创建Bound Service

为了和service建立一个长期的交互连接,其他应用组件可以通过调用bindService()方法来获得与service交互的接口IBinder对象。可以有多个的client组件bindingservice中。而当所有的client组件都解绑定后(通过调用unbindService()),系统就会销毁service.所以对于bound service而言,是不需要开发者手动地暂停service对于service启动也不通过startService()方法。具体如何通IBinder进行交互,有待后续的分析和完善???

给用户发送通知

一旦service开始运行,service就可以通Toast Notifications或者Status Bar Notifications来通知用户事件。一个toast通知是出现在当前window surface上的消息,时间比较短暂。但是status bar通知提供了一个iconstatus bar中,这样用户就可以通过选择icon来触发某个行为,例如开始一个activity。一般来status bar notification是最好的通知用户的方式。(具体如何实现,有待完善 ??)

在前台运行Service

运行在前台的service是需要与用户交互的,所以被系统杀死的概率很低。但是这种service须要提供一个在status bar中的notification这个notification将出现在“Ongoing”下,从而不能消失知道service暂停了,例如音乐播放器。

为了让serivce在前台运行,需要调用startForeground()这个方法使用两个参数,一个唯一表示notificationinteger以及status bar中的Notification对象。 示例代码如下所示:

Notification notif = new Notification(R.drawable.icon, getText(R.string.ticket), System.currentTimeMillis());

Intent notifIntent = new Intent(this, ExampleActivity.class);

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifIntent, 0);

notif.setLastestEventInfo(this, getText(R.string.notification_title), getText(R.string.notification_message), pendingIntent);

startForeground(ONGOING_NOTIFICATION, notif);

PendingIntent是用于进行broadcast请求。这段代码主要是启动目标ExampleActivity.class service让其在前台运行。为了将service从前台移除, 就需要调用stopForground()方法。这个方法需要有一个boolean参数来表明是否要从status bar中移除这个notification这个方法并不会暂停service暂停service还是需要通过stopService()

管理Service的生命周期

主要的生命周期方法:

@Override

public void onCreate() {

    // The service is being created

}

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

    // The service is starting, due to a call to startService()

    return mStartMode;

}

@Override

public IBinder onBind(Intent intent) {

    // A client is binding to the service with bindService()

    return mBinder;

}

@Override

public boolean onUnbind(Intent intent) {

    // All clients have unbound with unbindService()

    return mAllowRebind;

}

@Override

public void onRebind(Intent intent) {

    // A client is binding to the service with bindService(),

    // after onUnbind() has already been called

}

@Override

public void onDestroy() {

    // The service is no longer used and is being destroyed

}

需要注意的是onRebind()的方法。这个可详细查看Bound Service文档。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值