Android开发——Service的学习(下)

WangWY 2014-05-19

        Bound Service 是客户端-服务器模式的服务,它允许允许组件(比如activity)对其进行绑定、发送请求、接收响应、甚至进行进程间通信(IPC)。为了提供绑定,开发人员必须实现onBind()毁掉方法,该方法返回IBinder对象,它定义了客户端用来与服务交互的程序接口。

        客户端通过bindService()方法绑定到服务。此时,客户端必须提供ServiceConnection接口的实现类,它监视客户端和服务之间的连接。多个客户端能够同时连接服务器。然而仅当第一个客户端绑定时,系统调用服务的onBind()方法来获取IBinder对象。然后,系统会向后续请求绑定的客户端传送这同一个 IBinder ,而不再调用 onBind() 。

        当最后一个客户端解除绑定后,系统会销毁服务(除非服务同时是通过 startService() 启动的)。

       一:创建Bound服务

        当你实现自己的bound服务时,最重要的工作就是定义 onBind() 回调方法所返回的接口。 有一下三种方式:

        (1)继承Binder类

        如果服务是你的应用程序所私有的,并且与客户端运行于同一个进程中(通常都是如此),你应该通过j继承 Binder 类来创建你的接口,并从 onBind() 返回一个它的实例。客户端接收该 Binder 对象并用它来直接访问 Binder 实现类或者 Service 中可用的公共(public)方法。

        如果服务只应用于私有应用程序,那这就是首选的技术方案。如果服务要被其它应用程序使用或者访问独立进程时,则不能使用该技术。

        仅当客户端与服务位于同一个应用程序和进程时,才可以。例如音乐播放我绑定activity到自己的服务来在后台播放音乐。

        

        实现步骤如下:

  • 在你的服务中,创建一个 Binder 的实例,实现以下三者之一:

             (1)包含客户端能调用的公共方法

             (2)返回当前service实例,其中包含客户端能够调用的公共方法

             (3)返回服务管理的其他类的实例,其中包含客户端能够调用的公共方法

  • 从回调方法 onBind() 中返回 Binder 类的实例。
  • 在客户端中,在回调方法 onServiceConnected() 中接收 Binder 类实例并用所提供的方法对绑定的服务进行调用。

      以下是一个服务的示例,它通过实现一个 Binder 来为客户端访问它内部的方法提供支持:

public class LocalService extends Service {
    // 给客户端的Binder
    private final IBinder mBinder = new LocalBinder();
    // 产生随机数
    private final Random mGenerator = new Random();


    public class LocalBinder extends Binder {
        LocalService getService() {
            // 返回LocalService 的实例,其中包含客户端可以调用的方法
            return LocalService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    /**客户端可以调用的方法 */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

         LocalBinder为客户端提供了getService()方法,用于返回当前LocalService的实例。 这就让客户端可以调用服务中的公共方法。比如,客户端可以调用服务中的getRandomNumber()。

        以下是一个绑定到LocalService的activity,当点击按钮时,它会调用getRandomNumber():

public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // 绑定到 LocalService
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // 解除绑定的服务
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    /** 点击Button时,调用此方法 */
    public void onButtonClick(View v) {
        if (mBound) {
            // 调用LocalService中的方法。
            // 不过,如果该调用会导致某些操作的挂起,那么调用应该放入单独的线程中进行,
            // 以免降低activity的性能。
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** 定义服务绑定的回调方法,将其传给bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // 我们已经绑定了服务, 将IBinder转型获得LocalService实例
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}

       上述例子展示了客户端如何利用 ServiceConnection 和 onServiceConnected() 回调方法绑定到服务。

       

       (2)使用Messenger类

        如果你的服务需要与远程进程进行通信,那你可以使用一个 Messenger 来提供服务的接口。这种技术能让你无需使用AIDL就能进行进程间通信(IPC),这是执行进程间通信(IPC)最为简便的方式。

       

        以下是使用方法:

  •  服务实现一个 Handler ,用于客户端每次调用时接收回调。
  • 此 Handler 用于创建一个 Messenger 对象(它是一个对 Handler 的引用)。
  • 此 Messenger 对象创建一个 IBinder ,服务在 onBind() 中把它返回给客户端。
  • 客户端用 IBinder 将 Messenger (引用服务的 Handler )实例化,客户端用它向服务发送消息对象 Message 。
  • 服务接收 Handler 中的每个消息 Message ——确切的说,是在 handleMessage() 方法中接收。

        通过这种方式,客户端不需要调用服务中的“方法”。取而代之的是,客户端发送“消息”( Message 对象),服务则接收位于 Handler 中的这个消息。

public class MessengerService extends Service {
   
    static final int MSG_SAY_HELLO = 1;

    /**
     * 服务处理客户端发来的消息
     * 服务实现了一个Handler
     */
    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();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * 给客户端使用的用来发送消息给 IncomingHandler.即为,第二步。
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    /**
     * 返回一个接口用来给服务发送消息,即为,第三步
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }
}

        客户端要做的全部工作就是根据服务返回的 IBinder 创建一个 Messenger ,并用 send() 方法发送一个消息。例如,以下是一个activity示例,它绑定到上述服务,并向服务发送 MSG_SAY_HELLO消息:

        

public class ActivityMessenger extends Activity {
    /** 和service沟通的Messenger. */
    Messenger mService = null;

    /** 是否已经绑定 */
    boolean mBound;

    /**
     * 与服务进行交互
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // 与服务建立联接后将会调用本方法,
            // 给出用于和服务交互的对象。
            // 我们将用一个Messenger来与服务进行通信,
            // 因此这里我们获取到一个原始IBinder对象的客户端实例。
            mService = new Messenger(service);
            mBound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            // 当与服务的联接被意外中断时——也就是说服务的进程崩溃了,
            // 将会调用本方法。
            mService = null;
            mBound = false;
        }
    };

    public void sayHello(View v) {
        if (!mBound) return;
        //  创建并向服务发送一个消息,用到了已约定的'what'值
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        //绑定到服务
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // 解除绑定
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

         二:绑定到服务

        应用程序组件(客户端)可以通过调用 bindService() 来绑定服务。然后Android系统会调用服务的 onBind() 方法,返回一个用于和服务进行交互的 IBinder 。

       绑定是异步进行的。 bindService() 将立即返回,并不会向客户端返回 IBinder 。为了接收 IBinder ,客户端必须创建一个 ServiceConnection 的实例,并把它传给 bindService() 。 ServiceConnection 包含了一个回调方法,系统将会调用该方法来传递客户端所需的那个 IBinder 。

       注意: 只有activity、service和content provider才可以绑定到服务上——broadcast receiver不能绑定服务。

      因此,要把客户端绑定到服务上,你必须:

  • 实现 ServiceConnection 。你的实现代码必须重写两个回调方法:onServiceConnected()系统调用该方法来传递服务的 onBind() 方法所返回的 IBinder 。onServiceDisconnected()当与服务的联接发生意外中断时,比如服务崩溃或者被杀死时,Android系统将会调用该方法。客户端解除绑定时,不会调用该方法。
  • 调用 bindService() ,传入已实现的 ServiceConnection 。
  • 当系统调用你的 onServiceConnected() 回调方法时,你可以利用接口中定义的方法开始对服务的调用。
  • 要断开与服务的联接,请调用 unbindService() 。

        当客户端被销毁时,与服务的绑定也将解除。但与服务交互完毕后,或者你的activity进入pause状态时,你都应该确保解除绑定,以便服务能够在用完后及时关闭。 

LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
    // 与服务的联接建立之后将会调用
    public void onServiceConnected(ComponentName className, IBinder service) {
        // 因为我们已经与明显是运行于同一进程中的服务建立了联接,
        // 我们就可以把它的IBinder转换为一个实体类并直接访问它。
      LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    // 与服务的联接意外中断时将会调用
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "onServiceDisconnected");
        mBound = false;
    }
};

 利用这个 ServiceConnection ,客户端就能够把它传入 bindService() 完成与服务的绑定。

Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

相关推荐