Service 详解

翻译一下Service.java的API文档----哎哟,我去,公司终于可以发文章了

 

简介

service是应用相关组件,主要用于应用要做一个长时间运行的动作,或者不需要跟用户交互或为其他应用提供功能。每一个service,都必须在AndroidManafest.xml中定义,

一个service,通过Context.startService()或Context.bindService()被启动。

 

service在main thread中运行,这意味着service,可以做CPU密集型操作(MP3后台播放)或阻塞操作(比如网络操作),service可以自己创建线程去完成它的任务。

更新信息,可以在 ApplicationFundamentals:Processes and Threads中找到。比如IntentService.java是一个标准的利用它自己的线程去实现其功能的service实现类

主题

1. 什么是service

2. service生命周期

3. permissions

4. Process生命周期

5. 本地 service 范例

6. 远程信息service范例

 

一、什么是service

大多数关于service的疑问都围绕着它不是什么

1. 服务不是一个单独的进程。服务对象本身并不意味着它在自己的进程中运行。除非有其他说明,它同在一个进程中运行,作为应用程序的一部分。

2. 服务不是线程。它不可以脱离主线程进行工作的(以避免应用程序无响应错误)

 

其实服务本身很简单,它只提供了两个主要功能:

1. 应用要在后台做什么事情(甚至用户并没有跟应用直接交互)。这就调用Context.startService(),这要求系统安排工作,直到运行到service或者人为的停止

2. 将其一部分功能应用到其他应用上。这就调用Context.bindService()。这允许长时间保持连接为了服务与它交互。

 

当service组件被创建之后,不管是通过bindService或者startService创建的,系统都将会实例化service,并调用onCreate,以及其他适当的回调。这取决于service实现了什么样的功能行为,比如创建一个在service内部创建了二级线程

 

注意:service本身是简单,你可以用local java object来对待它,或者通过aidl远程调用这种复杂的方式。

二、service生命周期

service的运行有两种方式。Context.startService()或者Context.bindService().

1.Context.startService:系统将会取回service(这可能会创建它同时调用onCreate()方法),然后调用onStartCommand(Intent,int, int)方法(参数由客户端提供)。service会一直运动到Context.stopService()或stopSErlf()被调用。

注意:多次调用Context.startService不会启动多个service,但是这会多次调用onStartCommand(),一旦Context.stop()或stopSelf()被调用,就会立即停止service。

stopSelf()会保证onStartCommand中的方法被执行完毕,才会停止service。

在启动service后,service主要有两种操作模式,这取决于onStartCommand的返回值。

START_STICKY:显式开启关闭service

START_NOT_STICKY|START_REDELIVER_INTENT:用于仅在接受到的指令时才运行。

 

2.Context.bindService:这会获得一个长久的连接服务。如果service没在运行(那会调用onCreate),但不会调用onStartCommand。service会通过onBind(Intent)函数返回一个IBinder给客户端,这允许客户端做一些连接创建后的回调。只要连接被建立,ervice会保持运行(不管客户端还是否保留服务的IBinder)。通常IBinder会被写在一个复杂的interface  AIDL中。

 

service可以同时被started和有多个跟它相连的连接。在这种情况下,只要它被started有一个或多个用Context.BIND_AUTO_CREATE标签的连接,service会一直运行。一旦不存在连接或被stop,系统最终会调用onDestroy(),这样service就正式有效终止。所有的清理动作(stopping threads, unregisteringreceivers)应该在onDestroy之前被处理。

Permissions

全局访问一个service,需要在manifest中定义<service>标签。其他应用需要添加相应的<uses-permission>。

在android2.3版本中,若使用Context.startService(),需要同时设置intent的flag成Intent.FLAG_GRANT_READ_URI_PERMISSION and/orIntent.FLAG_GRANT_WRITE_URI_PERMISSION。

这会给让service有暂时访问指定URI的权限。权限会一直保持到service调用stopSelf,或者一直到service完全被系统停止。

这种方式可以给予对其他apps中未添加permission保护的service的访问权限(even when the service is not exported at all)。

 

此外,通过调用checkcallingpermission(string),service可以限制个别IPC 对它的调用权限。

进程优先级

android系统会一直保持 持有service的进程运行,只要service一直被启动或者有客户端接连上。当内存不足需要杀进程时,持有service的进程的有以下几种可能。

1. 如果service正在执行onCreate,onStartCommand,onDestory函数,这进程会被确保执行完毕

 

2. service已被启动,但优先级比正与用户交互的可见进程低,比不可见的进程高。那么,这个进程就不应该被killed,除非达到extreme low memory的情况

 

3. 如果客户端绑定上来service,那么持有service的进程就跟最重要的进程一样重要,因为只要有一个客户端是可见的,service就应该被视为可见的

 

4. 当系统考虑将service是活跃的同时不是可杀的候选进程,那么已启动的service可以用startForeground函数将service改成前显得状态。(在极端的情况下,这样也仍有可能被杀掉)

 

注意:在内存压力比较大的时候,即便service大多数时间在运行的,也会被杀掉。如果发生这种情况,系统会稍后尝试重新启动service。如果你正在onStartCommand()中安排任务,那么你就可能需要添加START_FLAG_REDELIVERY让系统在重新启动service再次派发这个intent。这样确保service在执行这个intent指令时被杀掉后还能重新执行。

其他的应用组件(比如Activity)也会增加整个进程的重要性。

 

本地Service范例

service其中一种比较常用的典型方法就是应用程序的辅助组件。APK的所有组件都运行在同一个进程中,除非另有明确规定。

采用这种方式,会大大简化应用程序组件之间的交互复杂性:app的组件可以通过IBinder轻松的交互。

 

这种方式的service使用范例如下。首先是service本身,在绑定的时候发布一个自定义类

public class LocalService extends Service {

   private NotificationManager mNM;

 

   // Unique Identification Number for the Notification.

   // We use it on Notification start, and to cancel it.

   private int NOTIFICATION = R.string.local_service_started;

 

   /**

    * Class for clients to access. Because we know this service always

    * runs in the same process as its clients, we don't need to deal with

    * IPC.

    */

   public class LocalBinder extends Binder {

       LocalService getService() {

           return LocalService.this;

       }

    }

 

   @Override

   public void onCreate() {

       mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

 

       // Display a notification about us starting.  We put an icon in the status bar.

       showNotification();

    }

 

   @Override

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

       Log.i("LocalService", "Received start id " + startId+ ": " + intent);

       // We want this service to continue running until it is explicitly

       // stopped, so return sticky.

       return START_STICKY;

    }

 

   @Override

   public void onDestroy() {

       // Cancel the persistent notification.

       mNM.cancel(NOTIFICATION);

 

       // Tell the user we stopped.

       Toast.makeText(this, R.string.local_service_stopped,Toast.LENGTH_SHORT).show();

    }

 

   @Override

   public IBinder onBind(Intent intent) {

       return mBinder;

    }

 

   // This is the object that receives interactions from clients.  See

   // RemoteService for a more complete example.

   private final IBinder mBinder = new LocalBinder();

 

   /**

    * Show a notification while this service is running.

    */

   private void showNotification() {

       // In this sample, we'll use the same text for the ticker and theexpanded notification

       CharSequence text = getText(R.string.local_service_started);

 

       // Set the icon, scrolling text and timestamp

       Notification notification = new Notification(R.drawable.stat_sample,text,

                System.currentTimeMillis());

 

       // The PendingIntent to launch our activity if the user selects thisnotification

       PendingIntent contentIntent = PendingIntent.getActivity(this, 0,

                new Intent(this,LocalServiceActivities.Controller.class), 0);

 

       // Set the info for the views that show in the notification panel.

       notification.setLatestEventInfo(this,getText(R.string.local_service_label),

                       text, contentIntent);

 

       // Send the notification.

       mNM.notify(NOTIFICATION, notification);

    }

}

第一步完成后,客户端代码可以直接访问运行中的service

private LocalService mBoundService;

 

private ServiceConnection mConnection = newServiceConnection() {

   public void onServiceConnected(ComponentName className, IBinder service){

       // This is called when the connection with the service has been

       // established, giving us the service object we can use to

       // interact with the service. Because we have bound to a explicit

       // service that we know is running in our own process, we can

       // cast its IBinder to a concrete class and directly access it.

       mBoundService = ((LocalService.LocalBinder)service).getService();

 

       // Tell the user about this for our demo.

       Toast.makeText(Binding.this, R.string.local_service_connected,

                Toast.LENGTH_SHORT).show();

    }

 

   public void onServiceDisconnected(ComponentName className) {

       // This is called when the connection with the service has been

       // unexpectedly disconnected -- that is, its process crashed.

        // Because it is running in our sameprocess, we should never

       // see this happen.

       mBoundService = null;

       Toast.makeText(Binding.this, R.string.local_service_disconnected,

                Toast.LENGTH_SHORT).show();

    }

};

 

void doBindService() {

   // Establish a connection with the service.  We use an explicit

   // class name because we want a specific service implementation that

   // we know will be running in our own process (and thus won't be

   // supporting component replacement by other applications).

   bindService(new Intent(Binding.this,

           LocalService.class), mConnection, Context.BIND_AUTO_CREATE);

   mIsBound = true;

}

 

void doUnbindService() {

   if (mIsBound) {

       // Detach our existing connection.

       unbindService(mConnection);

       mIsBound = false;

    }

}

 

@Override

protected void onDestroy() {

   super.onDestroy();

   doUnbindService();

}

Remote Messenger Service 范例

如果你需要写一个service与远程客户端通信(不仅仅是context.startservice发送命令的使用),也可以用Message类代替AIDL

 

以下就是使用Messager作为clients接口的范例。首先是service本身在绑定时发布一个messager到内部handler。(额外加,其实这里的arraylist只是保存ibinder)

public class MessengerService extendsService {

   /** For showing and hiding our notification. */

   NotificationManager mNM;

   /** Keeps track of all current registered clients. */

   ArrayList<Messenger> mClients = new ArrayList<Messenger>();

   /** Holds last value set by a client. */

   int mValue = 0;

 

   /**

    * Command to the service to register a client, receiving callbacks

    * from the service.  The Message'sreplyTo field must be a Messenger of

    * the client where callbacks should be sent.

    */

   static final int MSG_REGISTER_CLIENT = 1;

 

   /**

    * Command to the service to unregister a client, ot stop receivingcallbacks

    * from the service.  The Message'sreplyTo field must be a Messenger of

    * the client as previously given with MSG_REGISTER_CLIENT.

    */

   static final int MSG_UNREGISTER_CLIENT = 2;

 

   /**

    * Command to service to set a new value. This can be sent to the

    * service to supply a new value, and will be sent by the service to

    * any registered clients with the new value.

    */

   static final int MSG_SET_VALUE = 3;

 

   /**

    * Handler of incoming messages from clients.

    */

   class IncomingHandler extends Handler {

       @Override

       public void handleMessage(Message msg) {

           switch (msg.what) {

                case MSG_REGISTER_CLIENT:

                    mClients.add(msg.replyTo);

                    break;

                case MSG_UNREGISTER_CLIENT:

                   mClients.remove(msg.replyTo);

                    break;

                case MSG_SET_VALUE:

                    mValue = msg.arg1;

                    for (int i=mClients.size()-1;i>=0; i--) {

                        try {

                           mClients.get(i).send(Message.obtain(null,

                                   MSG_SET_VALUE, mValue, 0));

                        } catch(RemoteException e) {

                           // The client isdead.  Remove it from the list;

                            // we are goingthrough the list from back to front

                            // so this is safeto do inside the loop.

                            mClients.remove(i);

                        }

                    }

                    break;

                default:

                    super.handleMessage(msg);

           }

       }

    }

 

   /**

    * Target we publish for clients to send messages to IncomingHandler.

    */

   final Messenger mMessenger = new Messenger(new IncomingHandler());

 

   @Override

   public void onCreate() {

       mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

 

       // Display a notification about us starting.

       showNotification();

    }

 

   @Override

   public void onDestroy() {

       // Cancel the persistent notification.

       mNM.cancel(R.string.remote_service_started);

 

       // Tell the user we stopped.

       Toast.makeText(this, R.string.remote_service_stopped,Toast.LENGTH_SHORT).show();

    }

 

   /**

    * When binding to the service, we return an interface to our messenger

    * for sending messages to the service.

    */

   @Override

   public IBinder onBind(Intent intent) {

       return mMessenger.getBinder();

    }

 

   /**

    * Show a notification while this service is running.

    */

   private void showNotification() {

       // In this sample, we'll use the same text for the ticker and theexpanded notification

       CharSequence text = getText(R.string.remote_service_started);

 

       // Set the icon, scrolling text and timestamp

       Notification notification = new Notification(R.drawable.stat_sample,text,

                System.currentTimeMillis());

 

        // The PendingIntent to launch ouractivity if the user selects this notification

       PendingIntent contentIntent = PendingIntent.getActivity(this, 0,

                new Intent(this,Controller.class), 0);

 

       // Set the info for the views that show in the notification panel.

       notification.setLatestEventInfo(this,getText(R.string.remote_service_label),

                       text, contentIntent);

 

       // Send the notification.

       // We use a string id because it is a unique number.  We use it later to cancel.

       mNM.notify(R.string.remote_service_started, notification);

    }

}

 

如果我们想要让这个service运行在远程进程中(而不是apk本身),我们就需要在manifest中标记。android:process

<serviceandroid:name=".app.MessengerService"

       android:process=":remote" />

                  

这里的remote的名字是任意的。如果你想要其他进程,需要使用其他名字。: 前缀附加你package的标准进程名字。

这样,客户端就可以绑定上service,同时允许收发信息。

 

/** Messenger for communicating withservice. */

Messenger mService = null;

/** Flag indicating whether we have calledbind on the service. */

boolean mIsBound;

/** Some text view we are using to showstate information. */

TextView mCallbackText;

 

/**

 *Handler of incoming messages from service.

 */

class IncomingHandler extends Handler {

   @Override

   public void handleMessage(Message msg) {

       switch (msg.what) {

           case MessengerService.MSG_SET_VALUE:

               mCallbackText.setText("Received from service: " + msg.arg1);

                break;

           default:

                super.handleMessage(msg);

       }

    }

}

 

/**

 *Target we publish for clients to send messages to IncomingHandler.

 */

final Messenger mMessenger = newMessenger(new IncomingHandler());

 

/**

 *Class for interacting with the main interface of the service.

 */

private ServiceConnection mConnection = newServiceConnection() {

   public void onServiceConnected(ComponentName className,

           IBinder service) {

       // This is called when the connection with the service has been

       // established, giving us the service object we can use to

        // interact with the service.  We are communicating with our

       // service through an IDL interface, so get a client-side

       // representation of that from the raw service object.

       mService = new Messenger(service);

       mCallbackText.setText("Attached.");

 

       // We want to monitor the service for as long as we are

       // connected to it.

       try {

           Message msg = Message.obtain(null,

                   MessengerService.MSG_REGISTER_CLIENT);

           msg.replyTo = mMessenger;

           mService.send(msg);

 

           // Give it some value as an example.

           msg = Message.obtain(null,

                   MessengerService.MSG_SET_VALUE, this.hashCode(), 0);

           mService.send(msg);

       } catch (RemoteException e) {

           // In this case the service has crashed before we could even

           // do anything with it; we can count on soon being

           // disconnected (and then reconnected if it can be restarted)

           // so there is no need to do anything here.

       }

 

       // As part of the sample, tell the user what happened.

       Toast.makeText(Binding.this, R.string.remote_service_connected,

                Toast.LENGTH_SHORT).show();

    }

 

   public void onServiceDisconnected(ComponentName className) {

       // This is called when the connection with the service has been

       // unexpectedly disconnected -- that is, its process crashed.

       mService = null;

       mCallbackText.setText("Disconnected.");

 

       // As part of the sample, tell the user what happened.

       Toast.makeText(Binding.this, R.string.remote_service_disconnected,

                Toast.LENGTH_SHORT).show();

    }

};

 

void doBindService() {

   // Establish a connection with the service.  We use an explicit

   // class name because there is no reason to be able to let other

   // applications replace our component.

   bindService(new Intent(Binding.this,

           MessengerService.class), mConnection, Context.BIND_AUTO_CREATE);

   mIsBound = true;

   mCallbackText.setText("Binding.");

}

 

void doUnbindService() {

   if (mIsBound) {

       // If we have received the service, and hence registered with

       // it, then now is the time to unregister.

       if (mService != null) {

           try {

                Message msg =Message.obtain(null,

                       MessengerService.MSG_UNREGISTER_CLIENT);

                msg.replyTo = mMessenger;

                mService.send(msg);

           } catch (RemoteException e) {

                // There is nothing special weneed to do if the service

                // has crashed.

           }

       }

 

       // Detach our existing connection.

       unbindService(mConnection);

       mIsBound = false;

       mCallbackText.setText("Unbinding.");

    }

}

 

总结:

Context.startService和Context.bindService的侧重点是不一样的。

区别:

1. 侧重点不一样

Context.startService(): 在后台执行的任务上

Context.bindService():  将一部分功能扩展到其他应用上。

2. 生命周期不同

onCreate--->onStartCommand--->stopService/stopSelf

多次startService--->多次onStartCommand--->1个stopService/stopSelf

onCreate--->onBind--->onUnBind--->onDestroy

只要被started或者有多个绑定,就会一直运行,直到被stop或者没有连接

 

共同点:都是后台长时间运行

 

全局访问的service,要在manifest中定义,其他应用要访问需要有相应的permission。

service可以通过checkCallingPermission来限制个别IPC对它的访问。

 

service的在执行onCreate onStartCommand onDestroy时不会被终止。

service的优先级取决了绑定它的clients

可以通过startForeground来保持service的不被杀掉,但是在内存压力很大的时候依旧会被杀,可以通过FLAG可以保证service在系统内存不那么紧张的时候,会恢复。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值