zoukankan      html  css  js  c++  java
  • Service服务

    Service 服务
    Service是一个能够在后台运行的服务,能够长时间运行,优先级相对较高;当然系统也有可能在前台程序需要内存的时候会将服务杀掉,但是这种事很极端的情况,一般不会发生;
    服务并不需要用户界面,我们可以使用Service来做一些后台的任务,比如监控某些信息,播放音乐等等;
     
    服务的启动和生命周期:
    服务可以通过两种方式启动,一种是通过 Content.startService()来启动,另外一种是通过 Context.bindService()来启动;
     
    在一个包含Service的app中,我们需要在AndroidManifest.xml的application节点中增加对应的服务
    <service name=".MyFirstService" />
     
    MyFirstService是我们app中的一个类,继承自Service;我们通过重写 onCreate,onStart,onDestroy来处理服务各个时刻要处理的情况;
    Service的生命周期:
     
    通过 startService启动时候:
    onCreate() (如果为首次启动) ->onStart() -> 运行期 -> stopService()  -> onDestroy();
     
    通过 bindService方式的时候:
    onCreate()(如果首次启动) -> onBind() -> 运行期 ->  调用者退出或者调用者使用 unbindService时候  -> onUnBind() -> onDestroy();
     
    对于一个只是同一个app中其他的Actvity才会调用的服务来说,一般来说实现onStart和onDestroy的函数,并且在 onBind的时候返回null即可;
     
    注意:
    在Android2.0之后,已经不太推荐重写 onStart 了,而是使用重写 onStartCommand这个函数;
    针对新的onStartCommand函数,我们重写的时候,在返回值上面,返回的值对于服务有直接影响:
     
    START_STICKY: 服务被kill掉后,保留service为开始状态,不保留启动时候的intent;系统会尝试重启服务,并且传入的intent为null;  (需要显式的启动或者停止)
    START_NOT_STICKY: 服务被kill掉后,系统不会尝试重启;(只有在收到command的时候,才会处于运行状态)
    START_REDELIVER_INTENT: 与START_STICKY类似,系统会重启服务,但是会保留intent重新启动时候传入;(只有在收到command的时候,才会处于运行状态)
    START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
    当然上面几种方式,在用户主动退出服务的情况下,系统不会尝试重启;
     
    服务处理周期:
    • onCreate(), onStartCommand(), or onDestroy() 这几个函数中,不能被kill掉,确保这几个函数中的代码都被执行;
    • 服务优先级:低于任何可见的app进程,高于任务不可见的app;
    • 服务的优先级永远高于连接到服务的进程优先级,即如果有可见的app连接到服务,那么服务也相当于是可见的
    • 服务可以使用 startForeground(int, Notification)API来使得服务处于前台状态,使得不会被kill掉(很极端很极端的情况下还是会被kill,但是不需要考虑这种情况)
     
    本地服务:
    一般来说服务最常用的方式是作为app的一个次要的组件来运行的,即它是作为一个本地服务运行的,它会在app的同一个包的进程内运行,除非显式的指定其他状态,否则都会这么做;
    这个时候,我们需要其实并不需要关系很多的服务交互问题,只需要实现一些基本的服务处理;
     
    下面是本地服务的一个实现,我们要做的,只是在onCreate,onStartCommand,onDestroy里面做一些我们服务启停时候要做的事情即可;
    public class LocalService extends Service {
    private NotificationManager mNM;

    /**
    * 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(R.string.local_service_started);

    // 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 the expanded 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 this notification
    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.
    // We use a layout id because it is a unique number. We use it later to cancel.
    mNM.notify(R.string.local_service_started, notification);
    }
    }
    客户端调用的代码如下,比如下面的代码放在某个 Activity下面:
     
    private LocalService mBoundService;

    private ServiceConnection mConnection = new ServiceConnection() {
    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 same process, 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();
    }
    可以看到的是,我们以上的客户端代码,只是启停服务而已,并不做过多的其他处理;
     
    远程服务:
    有时候我们的服务,可以是开放的,其他的app也能够调用到我们的服务,并且发消息给到服务进行处理;可以使用Messenger类来替换AIDL文件实现的远程调用;
     
    下面是一个使用Messenger作为客户接口的代码,首先是服务代码,在被绑定的时候,发布一个Messenger给到内部的Handler;
    public class MessengerService extends Service {
    /** 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's replyTo 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 receiving callbacks
    * from the service. The Message's replyTo 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 is dead. Remove it from the list;
    // we are going through the list from back to front
    // so this is safe to 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 the expanded 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 our activity 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);
    }
    }
    一个远程服务的话,需要在AndroidManifest.xml里面,加上对应的设置,这样就会使得这个服务时运行在一个远程进程里,跟apk的包不是运行在同一个进程;
    <service android:name=".app.MessengerService"
    android:process=":remote" />
    remote这个字其实可以使用任意字符代替,:的前缀是将包的标准进程名添加进去;
    完成上面的工作之后,客户端可以绑定到服务,并且发送消息给它;同时也允许用户注册来接收返回的消息;
     
    /** Messenger for communicating with service. */
    Messenger mService = null;
    /** Flag indicating whether we have called bind on the service. */
    boolean mIsBound;
    /** Some text view we are using to show state 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 = new Messenger(new IncomingHandler());

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

    private ServiceConnection mConnection = new ServiceConnection() {
    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 we need to do if the service
    // has crashed.

    }
    }

    // Detach our existing connection.
    unbindService(mConnection);
    mIsBound = false;
    mCallbackText.setText("Unbinding.");
    }
    }
    上面的本地服务和远程服务的代码,都可以在android的api文档中找到;
  • 相关阅读:
    Windows常用命令
    Linux常用命令
    SpringBoot实现动态数据源切换及单库事务控制
    ArrayList与LinkedList
    java学习笔记之集合—ArrayList源码解析
    java学习笔记之反射—反射和工厂模式
    java学习笔记之反射—Class类实例化和对象的反射实例化
    java学习笔记之IO编程—对象序列化
    java学习笔记之IO编程—打印流和BufferedReader
    java学习笔记之IO编程—内存流、管道流、随机流
  • 原文地址:https://www.cnblogs.com/coldforce/p/4979550.html
Copyright © 2011-2022 走看看