Android Service 官方解释


Service 官方解释。

Class Overview

service 是实现一个需要长时间运行的操作,并提供结果给应用,而且不去影响用户其他操作的应用组件。每一个Service都必须在AndroidMainifest.xml中进行声明。Services可以使用Context.startservice()和Context.bindservice()进行启动。

注意Service,和其他应用对象一样。运行在所在进程的主线程中。这样就意味着如果你的Service需要执行一些耗时的操作如:音乐播放、网络连接,就需要开辟一个新的线程去完成这些操作。IntentService(一个子类)就是一个标准的由独立的工作线程去完成工作的Service


What is a Service?

要了解Service,首先要确定Service不是什么:
1.Service不是一个从应用中分离出来的进程,一个Service不是在暗示它是工作在一个自己的新线程中。除非特殊情况下,Service是工作在应用的同一线程内作为应用的一部分。
2.Service不是一个线程,所以它不是脱离主线程在独立工作(所以耗时操作需要开辟独立线程)

Service其实非常简单,适用于两种主要的场景:
1.应用中的一个设备去告诉操作系统有些东西希望在后台运行(当用户不直接与应用进行交互的时候),这时候去调用Context.startservice(),让系统把工作安排给Service,直到Service或其他线程等去明确的停止它(Service的优先级比较高,一般情况下不会被系统自动停止)
2.应用中的一个设备去把本应用的功能暴露给其他应用去使用,这时候去调用Context.bindService(),允许其他应用与Service进行一个长连接Servic进行交互

当一个Service由于上面的原因被当做应用组件创建的时候,操作系统实际上做得就是实例化Service并调用他的onCreate(),并且在主线程中添加上一些适当的回调操作,这些操作取决于Service本身,例如创建另一个工作线程。

注意虽然Service本身非常简单,但是你可以更改和Service之间的交互操作使其变得复杂或者简单,就像把Service当做一个local java object 去修改它里面的方法,然后使用AIDL提供一个完整的接口。


Service Lifecycle

一个Service运行在系统中有两个原因,一个是调用了Context.startService(),然后系统就会新建Service(创建Service并且调用onCreate() ),之后调用onStartConmmand(Intent, int, int)方法其中参数由client提供,这时Service会一直运行直到调用Context.stopService() 或者stopSelf()。注意多次调用Context.Service()不会多次创建(即使多次调用了onStartConmmend()),所以无论调用了几次Service,一旦调用Context.stopService(),或者stopSelf(),Service就会被停止。可是,Service本身可以通过调用stopSelf(int)来确定在启动的Intent被处理前,Service没有停止。

启动一个Service,你有两种运行模式可以进行选择,取决于onStartCommand()的返回值,START_STICKY表示一个Service需要明确的Started和Stoped,而START_NOT_STICKY或者START_REDELIVER_INTENT表示Service需要保持一直运行的状态等待进程发来请求。

Clients可以通过Context.bindService()获取一个Service的持续的连接,如果Service还没有启动通过调用它也可以创建该Service(同时调用onCreate()),但是还没有调用onStartConmmand()。Clients会接收到一个从Service 的onBinde(Intent)方法返回的IBinder对象,允许Clients在之后对Service的回调。Service会保持运行只要连接被建立(无论Clients是否持有Service 的 IBinder的引用)。通常返回的IBinder复杂接口已经写在了AIDL里面。

一个Service可以同时被启动和绑定。在这种情况下,操作系统会保持Service运行只要它被启动,或者有一个或多个连接(通过Context.BIND_AUTO_CREATE查看连接数)。一旦两种情况都不存在,Service会调用onDestroy()方法,终止Service。清理所有(停止线程,注销接收器),在得到onDestory()返回值之前完成。

Permissions

在manifest中<service>标签中可以把Service声明为全局变量。这样做之后,其他应用需要声明一个相应的<users-permission>在他们自己的manifest中,用来启动、停止、绑定这个Service。
Android 2.3  之后,当你使用Context.startService(Intent),你可以在Intent中设置Intent.FLAG_GRANDT_READ_URI_PREMISSION和/或Intent.FLAG_GRANT_WRITE_URI_PREMISSION 这样会获得临时访问特殊URIs的权限,权限一直保持到Service调用stopSelf(int),或者知道Service完全停止,这样可以去访问那些没有权限访问限制的Apps,甚至当Service没有暴露给他们时。
另外,一个Service可以通过权限来保护自己,当IPC中被调用的时候,通过调用checkCallingPermission(String)方法查看是否允许被调用。

Process Lifecycle

操作系统会尽力去保持一个含有Service的进程,只要一个Service已经开启或者绑定。如果系统运行到内存不够的时候,含有Service的进程在可以选的Kill掉的进程中有更高的优先级,也就是会更晚被Kill掉(虽然很少会被Kill掉)。
如果Service正在执行onCreate(),onStartCommand(),或者onDestory() 方法。宿主进程会变成一个前台进程用来保证执行而不会被Kill。
如果Service已经被开启,那么他的宿主进程被认为比用户可见进程的优先级要低,但是比一些其他的不可见进程要高。因为只有很少进程是被用户可见的,这样意味着Service不会被Kill,除非是在内存十分紧张的时候。
如果有Clients绑定到Service,那么Service的宿主进程一定不会比Client的优先级低。如果Client被认为是用户可见的,那么Service本身也被认为是用户可见的。
一个开启的Service可以通过使用startForeground(int, Notification)接口设置为前台状态,那样操作系统就认为他是用户关注的,内存低时就不会去Kill这个Service了(在理论上依然有可能在内存极低的情况下去Kil掉,但是在实际上不用去担心这个问题)。

根据这些描述说明,大多数是时间上你的Service是在运行的,在内存极低的情况下可能被操作系统回收。如果发生这样的情况,操作系统之后会重启Service。一个重要的结论是如果你使用onStartConmmand()去安排一些异步或者在其他线程中的工作,你需要使用START_FLAG_REDELIVERY去让操作系统重新发送一个Intent给你(当Service被Kill后启动的时候)。这样就会防止在Service处理它时被Kil掉造成的数据丢失了。

其他应用组件也能和Service运行在同一进程(例如Activity),当然,包含其他组件的进程会比只有Service本身重要性更高。

Local Service Sample


一个Service最普通的用法,就是把它作为应用中运行在“一边”的部件(注意:不是两个进程),与应用的其他部件运行在同一个进程当中。除非特殊说明所有APK的其他部件运行在同一个进程中,这是一个定性的情况。
在这种情况下,通过确认部件在同一进程,你可以十分简单地让他们进行交互:Service的Clients可以通过IBinder获取到Service的类。

一个这种使用方法下的例子,首先是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 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.
        mNM.notify(NOTIFICATION, notification);
    }
}
Service完成以后,一个可以直接访问Service的Client的代码,例如:

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();
}

Remote Messenger Service Sample(通过Message远程管理Service的例子)

如果你需要一个Service可以和Client进行跨进程的复杂的交互(除了简单地使用Context.startService去发送commands),然后你可以通过一个Messenges类,而不是去写一个AIDL文件。

一个使用Messenges作为与Client交互接口的Service。首先是Service,当绑定的时候返回一个核心的Handler所需要的Messenges类:
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);
    }
}
如果我们想让这个Service运行在远程的进程里(在本身的apk 中),我们可以在manifest中进行标识:
<service android:name=".app.MessengerService"
        android:process=":remote" />
注意 ":remote"在这只是一个随意起的名字,你可以为你的另一个进程起其他的名字,“:”表示一个包中的标准进程。

完成那些以后,Clients现在可以绑定Service并且向他发送Messenges。注意这里也允许Clients接收到Messenges后再进行注册:
/** 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.");
    }
}


猜你喜欢

转载自blog.csdn.net/u011414158/article/details/60136535