首页 > 代码库 > Android Api Component---翻译Service

Android Api Component---翻译Service

一个service是一个在后台执行长时间的操作并且不需要提供一个用户接口的应用程序组件。另一个应用程序能够开启service并且继续运行在后台甚至如果用户切换到另一个应用程序。此外,一个组件可以绑定到service与它交互而且甚至可以执行进程间通信(IPC)。例如一个service也许操作网络事务,播放音乐,执行文件I/O或者跟一个内容提供交互,所有那些都是在后台执行的。


一个service本质上可以接受两种组成:


Started

    当应用程序通过调用startService()的时候,一个service被“started”。一旦被开启了,service可以独立的运行在后台甚至开启它的组件被销毁。通常一个被开启的service执行单个操作并且不会给调用者返回结果。例如也许通过网络下载或者上传文件。当操作完成的时候,这个service应该停止它自己。


Bound

    当应用程序组件通过调用bindService()的时候,一个service就被“bound”了。一个被绑定的service提供了允许这个组件与service交互,发送请求,获得结果甚至用进程间通信访问进程的client-server接口。只要另一个应用程序组件被绑定给它,那么这个service就开始运行。多个组件可以立刻绑定到service,当是当它们都被解开了之后,这个service就被销毁。


尽管文档普遍的将这两种类型分开讨论,但是你的service可以用两种方式工作-它能被开启(独立的运行)并且也允许绑定。简单的事情是看你有没有实现一组回调方法:onStartCommand()允许组件开启它并且onBind()允许绑定。


不管你的应用程序组件是否被开启,绑定或者做了两者,任何的应用程序组件都可以使用这个service(甚至于不同的应用程序),用相同的方式任何组件可以使用一个用intent开启的activity。但是,你可以在manifest文件中定义私有的service并且阻塞来自于另一个应用程序的访问。这个会在Declaring the service in the manifest中讨论更多。


警告:一个service运行在它的主进程的主线程中-service不会创建它自己的线程并且不会运行在不同的进程中(除非你指定了)。这就意味着你的service应该做的是使任务CPU加强工作的或者阻塞操作(像mp3回放或者网络),你就应该在service中创建一个新的线程来做这个事情。通过使用分隔的线程,你将减少(ANR)错误的风险并且应用程序的主线程任然专注于用户跟activity的交互体验。


基础


为了创建一个service,你必须创建一个Service的子类(或者已经存在的它的子类)。在你的实现中,如何合适的话,你需要实现一些回调方法,这些方法是service的生命周期的刚要并且给绑定到service的组件提供了一种机制。你应该覆盖的最重要的方法是:


onStartCommand()

    当另一个像activity的组件通过调用startService()来要求那个service启动的时候系统会调用这个方法。一旦这个方法执行了,service被开始并且可能无限期的运行在后台。如果你实现了这个,那么你有责任在这个service工作完成之后通过调用onStop()或者stopService()停止这个service(如果你只想提供绑定,那你就不需要实现这个方法)。


onBind()

    当另一个组件通过调用bindService()想要跟这个service绑定的时候系统调用这个方法。在你的这个方法的实现中,你必须通过返回一个IBind提供一个客户端和你的service通信的接口。你必须总是实现这个方法,但是如果你不允许绑定,那么你应该返回null。


onCreate()

    当service第一次被创建的时候调用,它是为了执行前期的设置程序(它在onStartCommand()或者onBind()之前调用),如果service已经在运行了,那么这个方法不会被调用。


onDestroy()

    当service不再被使用并且被销毁的时候系统会调用这个方法。你的service应该实现这个方法来清除像线程,被注册的监听器,接收器等。这是service最后接收的调用。


如果一个组件通过调用startService()(它导致一个onStartCommand()的调用)启动一个service,那么service任然在运行,直到它用stopSelf()或者另一个组件通过调用stopService()停止它自己。


如果一个组件调用bindService()来创建这个service(并且onStartCommand()不被调用),只要组件被绑定给它,那么service就启动了。一旦这个service从所有的客户端被解开,那么系统就销毁它。


只有当内存变低并且Android系统必须回收系统资源给用户焦点的activity的时候,Android系统将会强制停止一个service。如果service被绑定到一个有用户焦点的activity,那么它很少情况下会被杀死,而且如果这个service被定义运行在后台,那么它将几乎永远背会被杀死。否则,如果这个被开始的服务要运行很长时间,那么久而久之系统将在后台任务列表中降低它的位置,并且服务将变的对杀死高度敏感-如果你的service被开启,那么你必须设计它优雅的被系统重启。如果系统杀死了你的服务,在资源再一次变得有效的时候,它将尽可能快的开启它(尽管这也依赖于你从onStartCommand()众返回的值,以后会被讨论)。关于系统什么时候销毁一个服务的更多信息,看Processes and Threading文档。


在下面的段落中,你将看到你如何能创建每一个service的类型并且如何在其它的应用程序组件中使用它。


在mainfest中定义一个service

像activity一样(还有其它组件),你必须在你的manifest文件中定义所有的服务。


为了定义你的服务,给<application>元素添加一个<service>子元素,例如:

<manifest ...>
    ...
    <application ...>
        <service android:name=".ExampleService"/>
        ...
    </application>
</manifest>

看<service>元素更多的关于在你的manifest文件中定义你的service的信息。


你能包含其它属性在<service>元素中来定义属性像开启service的权限请求以及在service中处理应该运行哪一个service。android:name属性是必须的属性--它指定了service的类名。一旦你发布了你的应用程序,你就不应该改变这个名字,因为如果你这样做了,由于依赖的显式intent开启或者绑定了service,你就冒了终端代码的风险。


为了确保你的app是安全的,当开启或者绑定你的Service的时候,总是使用一个显式的intent并且不要给service的intent定义过滤器。如果对于你容许像要运行哪一个service是模糊的,你可以给你的service提供intent过滤器并且从intent中剔除组件名称,但是你任然必须使用setPackage()给intent设置包,设置包给目标service提供了足够的消岐。


此外,你可以通过包含android:exported属性并且将其设置为false来让你的service只对你的app有效。这甚至在使用一个显式的intent的时候有效的组织了其它app开启你的service。


创建一个被开启的Service


一个被开启的service是另一个组件通过调用startService()开始的,这导致了一个对service的onStartCommand()方法的调用。


当一个service被开启的时候,它有一个独立于开启它的组件的生命周期并且这个service可以独立的运行在后台,甚至如果开启它的组件被销毁了。同样的,当service的工作完成的时候,它应该通过调用stopSelf()停止它自己或者另一个组件通过调用stopService()停止它。


像activity这样的应用程序组件可以通过调用startService()启动这个service并且给指定的service传递一个带有一些数据使用的Intent。这个service在onStartCommand()方法中接收这个Intent。


例如,加入一个activity需要给线上数据库保存一些数据。这个activity可能开一个管家服务并且通过给startService()传递一个intent来给它传递要保存的数据。service在onStartCommand()中接收这个intent,连接到网络并且执行数据库事务。当这个事务完成了的时候,服务停止它自己随之被销毁。


警告:默认的,一个service被运行在它被定义的相同进程的那个应用程序的主线程中。因此,当用户跟来自于同意个应用程序的activity交互的时候,如果你的service执行密集的或者阻塞的操作,那么service将使activity的性能速度变慢。为了避免影响应用程序的性能,你应该在service中开启一个新线程。


习惯上,有两个你可以扩展的类来创建一个service:


Service

    这是所有service的基类。当你继承这个类的时候,重要的是你在这个类中创建一个新的线程来执行所有service的工作,因为这个service默认的是使用你的主线程的,在主线程中会让你的正在运行的应用程序的任何activity的性能非常慢。


IntentService

    这是Service的子类,它使用一个工作者线程在同一时间一次一个的来处理所有开始请求,如果你不需要你的service同时处理多个请求,那么这个类就是最好的选项。你需要做的就是实现OnHandleIntent(),这个方法接收开始请求的Intent,因此你可以在后台做这些事情。


下面的段落描述了你如何使用两个中的其中一个类实现你的service。


扩展IntentService类

由于大多数被开启的service不需要同时处理多个请求(多个线程方案确实是危险的),那如果你使用IntentService类实现你的service可能是最好的。


这个IntentService做下面的事情:

    

    创建一个默认的工作者线程从你的应用程序的主线程中分离出来执行所有传递给onStartCommand()中的intent。

    创建一个工作队列在同一时间给你的onHandleIntent()实现传递一个intent,因此你永远不需要担心多线程。

    在所有的开始请求已经被处理完成之后停止service,因此你永远不需要调用stopSelf()。

    提供默认的返回null的onBind()实现。

    提供onStartCommand()的默认实现来给工作队列发送这个intent并且然后传递给你的onHandleIntent()实现。


所有这些加起来被通过客户端提供的其实是你只需要做的就是实现onHandleIntent()来做这个工作。(当然,你还需要提供一个service的小构造器)。


下面的例子是IntentService的实现:

public class HelloIntentService extends IntentService {
    
    /**
    * A constructor is required  and must call the super IntentService(String)
    * constructor with a name for the worker thread.
    */
    public HelloIntentService() {
        super("HelloIntentService");
    }
    
    /**
    * The IntentService calls this method from the default worker thread with 
    * the intent that started the service. When this method returns, IntentService
    * stops the service, as appropriate.
    */
    @Override
    protected void onHandleIntent(Intent intent) {
        //Normally we would do some work here, like download a file.
        // For our sample, we just sleep for 5 seconds
        long endTime = System.currentTimeMillis() + 5*1000;
        while(System.currentTimeMillis() < endTime) {
            synchronized(this) {
                try {
                    wait(endTime-System.currentTimeMillis());
                } catch(Exception e) {
                }
            }
        }
    }
}


所有你要做的:一个构造器和实现onHandleIntent()。


如果你也决定要覆盖其它的回调方法,像onCreate(),onStartCommand(),或者onDestroy(),那么确保调用了super实现,这样为的是IntentService可以适当的处理工作线程的生命。


例如,onStartCommand()必须返回默认实现(实现的是如何得到一个被传递的intent):

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    return super.onStartCommand(intent, flags, startId);
}


除了onHandleIntent(),只有onBind()方法是你不需要从超类中调用的(但是你只需要实现你的service允许绑定的)。


在下面的段落中,你将看到当扩展了基类Service的时候相同种类的service是如何被实现的,这会有更多的代码,但是如果你需要同时处理开始请求那它也许是合适的。


扩展Service类

正如你在前面看到的,使用IntentService时你的开启service变的非常简单。如果,然而,你需要你的service执行多线程(代替通过一个工作队列处理开启请求),那么你可以继承这个Service来处理每一个intent。


例如,下面的Service类的代码实现执行精确的相同工作与上面使用IntentService的例子一样。也就是说,对于每一个开启请求,它使用一个工作线程处理这个工作并且在同一时间只处理一个请求。

public class HelloService extends Service {
    private Looper mServiceLooper;
    private ServiceHandler mServiceHandler;
    
    //Handler that receives messages from the thread
    private final class ServiceHandle extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        
        @Override
        public void handleMessage(Message msg) {
            //Normally we would do some work here, like download a file.
            //For our sample, we just sleep for 5 seconds
            long endTime = System.currentTimeMillis() + 5*1000;
            while(System.currentTimeMillis() < endTime) {
                synchronized(this) {
                    try {
                        wait(endTime - System.currentTimeMillis());
                    } catch(Excetpion e) {
                    
                    }
                }
            }
            
            //Stop the service using the startId, so that we don‘t stop
            // the service in the middle of handling another job
            stopSelf(msg.arg1);
        }
    }
    
    @Override
    public void onCreate() {
        //Start up the thread running the service. Note that we create a 
        //separate thread because the service normally runs in the process‘s
        //main thread, which we don‘t want to block. We also make it
        // background priority so CPU-intensive work will not disrupt our UI.
        HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
        
        //Get the HandlerThread‘s Looper and use it for our Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
        
        
        
       //For each start request, send a message to start a job and deliver the 
       //start ID so we know which request we‘re stoping when we finish the job
       Message msg = mServiceHandler.obtainMessage();
       msg.arg1 = startId;
       mServiceHandler.sendMessage(msg);
       
       //If we get killed after returning from here, restart
       return START_STICKY;
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        //we don‘t provide binding , so return null
        return null;
    }
    
    @Override
    public void onDestroy() {
        Tast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
    }
}


正如你看到的,它比使用IntentService多了很多代码。


然而,因为你处理每一个你自己的onStartCommand()调用,因此你可以同时执行多个请求。这不是这个例子做的,但是如果你想做就可以做,然后你可以创建给每一个请求创建一个新线程并且立刻运行它们(代替前面的等待请求去完成)。


注意onStartCommand()方法必须返回一个整数。这个整数是一个值,这个值描述了这个系统应该如何在杀掉事件中继续这个service(像在上面讨论的一样,IntentService的默认实现给你处理了这些,尽管你可以修改它)。从onStartCommand()中返回的值必须是下面的其中之一常量:


START_NOT_STICKY

    如果系统在onStartCommand()然会后杀死了这个service,不要重新创建这个service,除非有intent传递的挂起。这是为了避免当不需要并且当你的应用程序可能简单的重新开始任何未完成的工作的时候运行你的service的最安全的选项。


START_STICKY

    如果在onStartCommand()返回之后系统杀掉了service,重新创建这个service并且调用onStartCommand(),但是不会重新传递最后的intent。替代的,系统使用一个空intent调用onStartCommand(),除非对开启的service有挂起的intent,在它的例子中,那些intent被传递。这适合于不执行命令但是独立运行并且等待工作的媒体播放器(或者类似服务)。


START_REDELIVER_INTENT

    如果onStartCommand()返回之后系统杀死了这个service,使用传递给这个service的intent重新创建这个service并且调用onStartCommand()。任何挂起的intent一次呗传递。这适合于那些应该立即被重新开始的积极执行工作的service,像下载文件。


更多关于常量的描述,请看文档。


开启一个Service

你可以从一个activity或者其它应用程序组件通过传递一个Intent到startService()中来开启一个service。Android系统调用service的onStartCommand()方法并且给它传递这个Intent。(你应该永远不要直接调用onStartCommand())。


例如,在前面的段落(HelloService)中一个activity开启service的例子使用了一个显式的intent给startService():

Intent intent = new Intent(this, HelloService.class);
startService(intent);


这个startService立即返回并且Android系统调用service的onStartCommand()方法。如果service还没有运行,系统首先调用onCreate(),然后调用onStartCommand()。


如果service也不提供绑定,这个传递给startService的intent仅仅是应用程序组件和service之间的通信模式。然而,如果你想让service发送一个返回结果,那么开启这个service的客户端可以创建一个PendingIntent给广播(使用getBroadcast())并且把它传递给在开启这个service的Intent中的service。这个service然后就可以使用广播传递一个结果了。


多个开启service的请求结果用多个回应调用service的onStartCommand()。然而,只有一个被请求的service请求停止这个service(使用stopSelf()或者stopService())。


停止一个service

一个被开启的service必须管理自己的声明周期。也就是说,系统不停止它或者销毁这个service,除非它必须回收系统内存并且这个系统继续在onStartCommand()之后运行。因此,这个service必须通过调用stopSelf()停止它自己或者另一个组件通过调用stopService()停止它。


一旦使用stopSelf()或者stopService()请求停止,系统会尽可能快的销毁这个service。


然而,如果你的service给当前的onStartCommand()处理多个请求,那么当你处理了一个开启请求完成的时候你不应当停止这个service,因为也许从接受一个新开启请求开始你还有其它的请求(在第一个请求完成后停止会中断第二个请求)。为了避免这种问题,你可以使用stopSelf(int)来确保你的停止这个service的请求总是基于最近开始的请求。也就是说,当你调用stopSelf(int),你给你的停止请求响应的那个service的开启请求传递一个ID(传递个onStartCommand()的startId)。然后如果在你能调用stopSelf(int)之前,这个service接收一个新的开启请求,那么将会停止跟这个ID匹配的service。


警告:当它完成工作的时候你的应用程序停止它的service是重要的,为了避免浪费系统资源和消耗电池。如果需要,其他的组件可以通过调用stopService()停止这个service.甚至如果你给这个service开启了绑定,如果它前面接收了一个调用到onStartCommand(),你必须总是自己停止这个service。


关于更多关于service的声明周期信息,看下面的章节Manging the Lifecycle of a Service。


创建一个绑定Service



一个被绑定的service是通过调用bindService()来允许应用程序按照创建一个长期存在的连接顺序绑定它(并且通常不允许组件通过调用startService()开启它)。


当你想跟来自于activity和你的应用程序中的其它组件的service交互或者给其它的应用程序通过进程间通信(IPC)暴露你的应用程序的功能的时候,你应该创建一个绑定的service。


为了创建一个绑定的service,你必须实现onBind()回调方法来返回一个定义了用service给通信定义的接口IBinder。其它的应用程序然后调用bindService()来接收这个接口并且在这个service中开始调用这个方法。这个service只存活于服务的绑定给这个service的应用程序组件,因此当没有组件绑定到service的时候,系统销毁它(当service通过onStartCommand()开始的时候,你不需要使用你的方式停止一个service)。


为了创建一个绑定的service,你必须做的第一件事情就是定义一个指定客户端如何跟service通信的接口。这个接口是在这个service和客户端之间的必须是IBinder的实现并且必须是从onBind()回调方法中返回的你的service。一旦客户端接收了这个IBinder,那它可能就会通过那个接口跟service开始交互。


多个客户端可能立刻绑定到这个service。当一个客户端跟service交互完成的时候,它就调用unbindService()来解开。一旦没有客户端再绑定到这个service,那么系统就销毁这个service。


有多种方式实现一个绑定service并且并且这种实现比被开启的service更复杂,因此讨论的这种绑定service是一个分开的文档在Bound Service。


给用户发送通知


service一旦运行,service可能使用Toast Notifications或者Status Bar Notifications通知事件的用户。


toast通知是在当前的窗口的界面出现的短暂的然后消失的消息,然而状态条通知在状态条中提供了一个带有消息的图标,用户可以选择这个图标来采取一个动作(比如开启一个activity)。


通常,一个状态条通知是当一些后台工作完成的时候可选的最好的技术(像文件下载完成)并且用户可以按照它来做一些事情。当用户选择了来自于被扩展的视图的通知的时候,这个通知可以开启一个activity(像看一个被下载的文件)。


关于更多信息,可以看Tost Notications 或者Status Bar Notifications开发文档。


在前台运行service


前台service是考虑到用户知道是活动的并且因此当系统内存低的时候它不是一个要被杀死的候补者。前台service必须给状态条提供一个通知,它被防止在“进行中”头部,也就是意味着除非这个service要么被停止要么从前台被移除才会消失。


例如,一个音乐播放器从一个service中播放音乐就应该被设置在前台,因为用户明显的知道它的操作。在状态条中的这个通知可以辨别当前的歌曲并且允许用户运行一个activity跟这个音乐播放器交互。


为了请求运行在前台的你的service,就调用startForground()。这个方法有两个参数:一个是一个标识这个通知的整数唯一值和给状态条的Notification。例如:

Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title), getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);


警告:你给startForeground()的整数ID必须是0。


为了从前台移除这个service,调用stopForeground()。这个方法有一个boolean,标明是否也移除状态条通知。这个方法不会停止service。然而,如果当它运行在前台的时候你停止了这个service,那么通知也就被移除了。


更多关于notification的信息,看Creating Status Bar Notifications。


管理service的生命周期


一个service的声明周期比activity的更简单。然而,关注你的service是如何被创建和销毁的是更重要的,因为service可能在用户没有意识到的情况下运行在后台。


service的生命周期-从它被创建到它被销毁-可能有小面两个不同的方式:

A 开始service

    当另一个组件调用startService的时候这个service被创建。然后独立的运行并且必须通过调用stopSelf()停止它自己。另一个组件也可能通过调用stopService来停止这个service。当service被停止的时候,系统销毁它。


A 绑定service

    当另一个组件调用bindService()的时候这个service被创建。客户端然后通过一个IBinder接口跟这个service通信。客户端可能通过调用unbindService()关闭连接。当多个客户端可能绑定相同的service,当所有它们被解开的时候,系统销毁这个service(这个service不需要停止他自己)。


那两种方式是完全不同的。也就是说,你可能绑定的service已经是用startService()开始的。例如,一个后台标示音乐播放的intent音乐服务可能通过调用startService()被开启了。然后,可能当用户想在播放器上操作一些管理的或者得到关于当前歌曲的信息的时候,一个activity可能通过调用bindService()绑定了这个service。假如是这样的,stopService()或者stopSelf()事实上不会停止这个service直到所有客户端被解开。


实现回调的生命周期

像activity,service都有你可以调用来监视service的状态和在合适的时间执行工作的生命周期回调方法。下面的骨架service证明了每一个生命周期方法:

public class ExampleService extends Service {
    int mStartMode;       // indicates how to behave if the service is killed
    IBinder mBinder;      // interface for clients that bind
    boolean mAllowRebind; // indicates whether onRebind should be used

    @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
    }
}


注意:你不需要像activity生命周期回调方法一些样调用超累的回调方法实现。


技术分享


?


Android Api Component---翻译Service