service常见用法及相关知识

来源:互联网 发布:360浏览器mac版下载 编辑:程序博客网 时间:2024/05/22 17:44

Service组件

身为四大组件之一的Service在程序中使用频率是比较高的,主要用来处理一些不需要UI的耗时的后台操作,或者在后台对外提供接口,供其他组件调用。Service的实现是比较典型的C/S模式,后文介绍用法时会有体会。

两种常见的Service

  • IntentService:适合同一时间只处理一个任务,代码少,使用简单
    是Service类的子类,默认会开启一个工作线程,你需要覆盖onHandleIntent方法,用来处理startService传过来的Intent,在一个生命周期内,如果多次调用startService只会进一次onCreate(),但是会进对应次数的onHandleIntent,在同一时间只能处理一个Intent,也就是说不管调用多少次startService只有一个工作线程在工作,其余的排队等待,一旦所有的Intent处理完成,自动调用onDestroy方法,无须手动调用stopService或者stopSelf

    IntentService的示例

    public class MyIntentService extends IntentService {    private static final String TAG = "MyIntentService";    public static final String ACTION_ONE = "action_one";    public static final String ACTION_TWO = "action_two";    /**     * A constructor is required, and must call the super IntentService(String)     * constructor with a name for the worker thread.     */    public MyIntentService() {        super("MyIntentService");    }    @Override    protected void onHandleIntent(Intent intent) {        if (intent != null) {            final String action = intent.getAction();            if (ACTION_ONE.equals(action)) {                handleActionOne();            } else if (ACTION_TWO.equals(action)) {                handleActionTwo();            }        }    }    private void handleActionOne(){        for (int i = 0;i<3;i++){            Log.i(TAG,"handleActionOne");            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    private void handleActionTwo(){        for (int i = 0;i<3;i++){            Log.i(TAG,"handleActionTwo");            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    @Override    public void onCreate() {        super.onCreate();        Log.i(TAG,"onCreate");    }    @Override    public void onDestroy() {        super.onDestroy();        Log.i(TAG,"onDestroy");    }}

    可以通过如下方法调用多次同时调用

    Intent intent = new Intent(this,MyIntentService.class); intent.setAction(MyIntentService.ACTION_ONE);//intent.setAction(MyIntentService.ACTION_TWO);startService(intent);

    调用后我们会发现IntentService不需要我们另起线程来执行耗时的操作,同时你无论触发多少次ACTION_ONE或者ACTION_TWO动作,它们都会按触发的顺序顺序执行,且任务一旦执行完成无需我们调用stopService或者stopSelf就会自己进入onDestory

  • Service:适合并发请求,代码较多,较复杂,更灵活
    需要手动调用stopService或者执行完任务后调用stopSelf ,即使Service销毁了,其中的线程如果没有执行完会继续执行,如果不掉用的话即使启动该Service的activity销毁了,该Service仍然存在,系统内存不够时会销毁后台的service,service存在时间越长,被销毁的可能性越大

    Service的示例

    public class MyService extends Service {    private static final String TAG = "MyService";    public MyService() {    }    @Override    public IBinder onBind(Intent intent) {        //暂时用不到        return null;    }    @Override    public void onCreate() {        super.onCreate();        Log.i(TAG,"onCreate");    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        MyServiceThread thread = new MyServiceThread(startId);        thread.start();        // If we get killed, after returning from here, restart        return START_STICKY;    }    @Override    public void onDestroy() {        super.onDestroy();        Log.i(TAG,"onDestroy");    }    class MyServiceThread extends Thread{        int startId = 0;        public MyServiceThread(int startId){            this.startId = startId;        }        @Override        public void run() {            for(int i = 0;i<3;i++){                Log.i(TAG,"my startId:"+startId);                try {                    sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            //根据startId停止,能保证最近一次的请求执行完才销毁            stopSelf(startId);        }    }}

    我们可以通过startService多次调用,但运行完需要显式的调用stopSelf来结束自己,在这里例子中我们可以看到在Service中需要我们自己启动线程来执行耗时的任务,可以同时并行的执行多个任务,还能看到startId的用处,可以用来保证最近的一次请求执行完才onDestory

service使用方式

  • startService

    可以直接看上面的两个例子

  • bindService

    • 继承binder类

      如果只是想利用Service作为你应用的一个后台工作线程的话,这是首选的实现方式,除非你的Service需要被其他应用使用或者跨进程通讯。

      先看Service代码

          public class MyLocalService extends Service {    private static final String TAG = "MyLocalService";    // Binder given to clients    private final IBinder mBinder = new LocalBinder();    // Random number generator    private final Random mGenerator = new Random();    //实现Binder类,是用来在onBind返回,客户端可以在    //onServiceConnected()回调方法内获取该对象的实例,    //进而通过该实例的getService()方法获得Service的引用    //最后就可以通过调用Service的Public方法来和Service通讯    public class LocalBinder extends Binder{        MyLocalService getService(){            // Return this instance of LocalService so clients can call public methods            return MyLocalService.this;        }    }    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }    /** method for clients */    public int getRandomNumber(){        return mGenerator.nextInt(100);    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i(TAG,"onStartCommand");        return super.onStartCommand(intent, flags, startId);    }    @Override    public boolean onUnbind(Intent intent) {        Log.i(TAG,"onUnbind");        return super.onUnbind(intent);    }    @Override    public void onRebind(Intent intent) {        Log.i(TAG,"onRebind");        super.onRebind(intent);    }    @Override    public void onCreate() {        Log.i(TAG,"onCreate");        super.onCreate();    }    @Override    public void onDestroy() {        Log.i(TAG,"onDestroy");        super.onDestroy();    }}

      再看activity代码

          public class ServiceMainActivity extends Activity implements View.OnClickListener{private final static String TAG = "ServiceMainActivity";//报存获取的Service的引用MyLocalService mService;boolean mBound = false;@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_service_main);    btnStartBind.setOnClickListener(this);    btnStopBind.setOnClickListener(this);    btnGetData.setOnClickListener(this);}@Overridepublic void onClick(View v) {    switch (v.getId()){        case R.id.btnStartBind:            startBind();            return;        case R.id.btnStopBind:            stopBind();            return;        case R.id.btnGetBindData:            getBindData();            return;    }}/** 定义Service绑定的回调方法,就是通过这个回调方法来实现与Service的绑定的 */private ServiceConnection mConnection = new ServiceConnection() {    @Override    public void onServiceConnected(ComponentName name, IBinder service) {        // We've bound to LocalService, cast the IBinder and get LocalService instance        Log.i(TAG,"onServiceConnected");        MyLocalService.LocalBinder binder = (MyLocalService.LocalBinder)service;        mService = binder.getService();        mBound = true;    }    @Override    public void onServiceDisconnected(ComponentName name) {        Log.i(TAG,"onServiceDisconnected");        mBound = false;    }};private void startBind(){    Intent intent = new Intent(this,MyLocalService.class);    bindService(intent,mConnection, Context.BIND_AUTO_CREATE);}private void stopBind(){    if (mBound){        unbindService(mConnection);        mBound = false;    }}private void getBindData(){    if(mBound){        // 调用Service的方法        // 这里注意要避免调用Service里可能导致挂起的方法        //如果要调用的话需要在独立的线程里调用,避免影响UI线程的性能        int random = mService.getRandomNumber();        Toast.makeText(this,"random number:"+random,Toast.LENGTH_SHORT).show();    }}}

      使用的步骤总结

      1. 在你的Service中创建一个Binder的实现,包含一个可以被客户端调用的公共方法,这个公共方法需要返回这个Service的实例或者Service中的其他内部类的实例

      2. onBind 方法中返回你实现的Binder的实例

      3. 客户端通过onServiceConnected() 回调方法获取你实现的Binder实例后就可以调用在Service中提供的方法了
    • 使用Messenger

      这是一种简单的实现跨进程通讯方式,因为使用 Messenger queues来处理所有的请求,而所有的请求都在一个线程中,所以你不需要关心Service的线程安全的问题。但是这所有的请求也都是顺序被执行,这种方式应该能满足绝大部分的需求。

      先看Service的代码

      public class MessengerService extends Service {    private static final String TAG = "MessengerService";    /** Command to the service to display a message */    static final int MSG_SAY_HELLO = 1;    /**     * 用于存放客户端的Messenger,可以通过这个实例响应客户端,这里只响应一个客户端     * 可以用集合存储,响应多个客户端     */    Messenger clientMessenger;    /**     * 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 = 2;    /**     * Handler of incoming messages from clients.     */    class IncomingHandler extends Handler{        @Override        public void handleMessage(Message msg) {            switch (msg.what){                case MSG_SAY_HELLO://                    Toast.makeText(getApplicationContext(),"Hello!",Toast.LENGTH_SHORT).show();                    Log.i(TAG,"Hello!");                    if(clientMessenger != null){                        Message message = Message.obtain(null,MSG_SAY_HELLO);                        try {                            clientMessenger.send(message);                        } catch (RemoteException e) {                            e.printStackTrace();                        }                    }                    break;                case MSG_REGISTER_CLIENT:                    clientMessenger = msg.replyTo;                    break;                default:                    super.handleMessage(msg);            }        }    }    /**     * Target we publish for clients to send messages to IncomingHandler.     */    final Messenger mMessenger = new Messenger(new IncomingHandler());    /**     * When binding to the service, we return an interface to our messenger     * for sending messages to the service.     */    @Override    public IBinder onBind(Intent intent) {        Log.i(TAG,"binding");        return mMessenger.getBinder();    }}

      activity的代码

      public class MessengerActivity extends ActionBarActivity implements View.OnClickListener{    private final static String TAG = "MessengerActivity";    /** Messenger for communicating with the service. */    Messenger mService = null;    /** Flag indicating whether we have called bind on the service. */    boolean mBound;    class IncomingHandler extends Handler{        @Override        public void handleMessage(Message msg) {            switch (msg.what){                case MessengerService.MSG_SAY_HELLO:                    Log.i(TAG,"response Hello!");                    break;                default:                    super.handleMessage(msg);            }        }    }    final Messenger mMessenger = new Messenger(new IncomingHandler());    /**     * Class for interacting with the main interface of the service.     */    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            // This is called when the connection with the service has been            // established, giving us the object we can use to            // interact with the service.  We are communicating with the            // service using a Messenger, so here we get a client-side            // representation of that from the raw IBinder object.            mService = new Messenger(service);            mBound = true;            Message msg = Message.obtain(null,MessengerService.MSG_REGISTER_CLIENT);            msg.replyTo = mMessenger;            try {                mService.send(msg);            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {            mService = null;            mBound = false;        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_messenger);        Button btnBindService = (Button)findViewById(R.id.btnBindService);        Button btnSayHello = (Button) findViewById(R.id.btnSayHello);        Button btnUnbindService = (Button) findViewById(R.id.btnUnbindService);        btnBindService.setOnClickListener(this);        btnSayHello.setOnClickListener(this);        btnUnbindService.setOnClickListener(this);    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.menu_messenger, menu);        return true;    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        // Handle action bar item clicks here. The action bar will        // automatically handle clicks on the Home/Up button, so long        // as you specify a parent activity in AndroidManifest.xml.        int id = item.getItemId();        //noinspection SimplifiableIfStatement        if (id == R.id.action_settings) {            return true;        }        return super.onOptionsItemSelected(item);    }    @Override    public void onClick(View v) {        switch (v.getId()){            case R.id.btnBindService:                doBindService();                break;            case R.id.btnSayHello:                sayHello();                break;            case R.id.btnUnbindService:                doUnbindService();                break;        }    }    private void sayHello(){        if(!mBound) return;        // Create and send a message to the service, using a supported 'what' value        Message msg = Message.obtain(null,MessengerService.MSG_SAY_HELLO);        try {            mService.send(msg);        } catch (RemoteException e) {            e.printStackTrace();        }    }    private void doBindService(){        Intent intent = new Intent(this,MessengerService.class);        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);        mBound = true;    }    private void doUnbindService(){        if(mBound){            unbindService(mConnection);            mBound = false;        }    }}

      这里实现了activity和service通过Messenger来进行通讯,在Manifest对应的Service中添加android:process=”:remote”,发现依然可以通讯,证明了这是一种可以跨进程的通讯方式。

      使用步骤总结

      1. Service实现一个Handler,接收客户端发过来的Message
      2. 上面实现的Handler被用来创建一个Messenger对象,这个对象其实就是Handler的引用
      3. 这个Messenger对象可以用来在onBind() 方法中创建一个IBinder对象用于return。
      4. 客户端在onServiceConnected() 回调方法中利用IBinder来实例化客户端中的Messenger,然后就可以调用Messenger实例的send方法,来发送Message到绑定的Service了
      5. Service可以在实现的Handler类中的handleMessage() 方法更具msg.what 来处理客户端不同类型的Message了
      6. 以上只是客户端->Service单向的通讯,如果想让客户端接收Service响应的消息,可以在客户端中创建一个Handler,并用这个Handler创建Messenger对象,在onServiceConnected() 回调方法中通过Message的replyTo发送给Service,Service保留收到的客户端的Messenger,利用该对象的send方法给客户端发送Message,客户端可以在Handler的回调方法handleMessage() 中根据msg.what来不处理不同类型的消息

      需要注意的是:只有activity,service和content provider能够绑定服务,而broadcast receiver 不能够绑定

      客户端使用步骤总结

      1. 实现ServiceConnection类,覆写其中的两个回调方法
        • onServiceConnected() 当和Service绑定后会回调这个方法,需要注意的是bindService() 方法是异步的,Service中的onBind() 方法return的IBinder是通着 onServiceConnected() 传递到客户端的
        • onServiceDisconnected() 这个方法是当Service意外的丢失了而由android系统调用的,比如Service崩溃了或者被系统kill掉了,当客户端调用unbindService()方法该回调方法是不会被调用的
      2. 调用bindService() 方法
      3. onServiceConnected() 方法调用后你就与Service建立了联系,可以调用Service提供的接口跟Service进行通讯了
      4. 调用unbindService() 完成通讯

      以上代码大部分是参照的google提供的示例,自己修改部分代码及注释。个人感觉先看代码,能知道每个方法是做什么用的,最后再来总结一下实现方式,能加深理解记忆

    • 使用AIDL

      其实上面的Messenger底层也是基于AIDL实现的,使用这种方式就更灵活,适合于处理多个需要同时运行的请求,但也带来了复杂性,需要你自己维护Service的线程安全。这里先不详细介绍该用法了。

  • startService and bindService
    就是Service即实现了onBind() 又实现了onStartCommand() 方法,使用的话把上面的两个例子合起来就行了。这里唯一要注意的就是Service的生命周期,下文中会提到。

Service和Thread

  • 使用场景

    • Service通常是运行在后台的,即使用户与应用没有任何交互。且Service可以供多个客户端使用,甚至是其他进程的其他应用。
    • 如果你仅仅是在用户与应用交互的时候需要在主线程外执行一些操作, 这个时候可以用Thread。比如说,只有当你的activity运行的时候才需要播放一段音乐,这个时候你就可以在onCreate() 方法中创建线程,onStop()方法中关闭线程,在Android一般不使用原生的Thread,因为android给我们准备了更好用的 AsyncTask 或者 HandlerThread类供我们使用

你启动的Service默认是运行在应用进程的主线程中的(remote service是运行在自己的进程中),所以不要直接在service中运行资源密集或者阻塞操作,而是需要在service中启动一个线程去执行这些操作

生命周期

  • 先引用一下官网的图
    周期1
    可以看到不管是startService还是bindService,通用的都有onCreate()onDestory 且在一个生命周期内只会调用一次onCreate()onDestory

  • 以表格形式总结一下

service onCreate onStartCommand onbind onDestory start Service startService触发,只调用一次 startService触发,多次启动调用多次 / stopService或stopSelf或被系统kill触发,只调用一次 bind Service bindService触发,只调用一次 / bindService触发,一个客户端只触发一次 所有bind的客户端都调用了unbindService或者所有的客户端都不存在了触发,只触发一次 start&bind Service bindService或startService触发,只调用一次 startService触发,多次启动调用多次 bindService触发,一个客户端只触发一次 调用stopService且所有bind的客户端都调用了unbindService或者所有的客户端都不存在了才触发,只触发一次
  • bindService更详细的生命周期
    如果你想让客户端下次再绑定Service的时候调用 onRebind() 方法的话,可以覆写onUnbind()方法,使其返回true,这样下次客户端再调用bindService的时候就会进 onUnbind()方法,而不再进onBind() 方法了,虽然onUnbind()方法是renrun void的,但是客户端仍然能够通过onServiceConnected()回调方法获取到IBinder对象。在官网上有更详细的生命周期配图,如下
    周期2

参考

MessengerService示例代码
谷歌Service Guide

1 0