Android学习笔记18——Bound Services

本文深入探讨了Android应用中的BoundService概念,包括其作为服务器的功能、与客户端交互的方法、实现细节以及三种主要的接口定义方式:继承Binder、使用Messager和采用AIDL。文章详细解释了每种方式的特点和适用场景,特别强调了BoundService在应用中的独特价值和实现技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

         一个Bound Service在客户端和服务器接口中是一个服务器。Bound Service允许组件绑定到这个Service,发送请求,接受响应,甚至表现出进程间通信。Bound Service仅仅当它服务于另一个应用程序组件时才会存活,并且不能独立的运行在后台。

        Bound Service是一个Service类的实现,允许其他应用程序绑定他并且和他进行交互。为了绑定一个Service,你必须实现onBind回调方法,这个方法返回一个IBind对象,这个IBind对象定义了客户端和服务器交互的程序接口。

        一个客户端能够调用bindService方法绑定到Service,当它连接的时候,它必须提供一个ServiceConnection的实现,它控制着与这个Service的连接。这个bindService方法不会返回任何值,但是当系统创建客户端与服务器的连接时,它会调用ServiceConnecte类的onServiceConnected()方法,客户端能够用IBinder和这个Service通讯。

         在同一时间,可以有多个客户端连接到这个Service,然而仅仅当第一个客户端和这个Service绑定的时候系统才会调用Service的onBind方法获取IBind对象。系统将会发送同样的IBind到其他要绑定这个Service的客户端,而不是再次调用oBind方法。

        当最后一个客户端和这个Service解除绑定的时候,系统就会销毁这个Service。

        当我们实现我们的Bound Service的时候,最重要的部分是定义我们Service的onBind回调方法返回的接口。我们可以有一下三种方式来定义这个接口。

 第一、继承Binder这个类

         如果我们的Service是对于我们的应用程序是私有的并且和客户端运行在同一个进程中,我们应该继承Binder这个类来创建我们的接口,并且从onBind方法中返回一个Binder的实例。当我们要为我们的应用程序创建一个后台的工作时,这种做法是一种比较好的做法。

第二、使用一个Messager

         如果我们希望我们的接口能够跨进行进行工作,我们可以用Messager来为我们的Service创建一个接口。用这种方法,我们定义一个Handler去响应不同类型的Messager对象。这个Hanlder是一个Messager对象和一个客户端分享IBinder的基础,运行客户端用Messager对象发送命令给这个Service。另外,客户端也可以定义自己的Messgaer对象,因此这个Service也能够发送Messager给客户端。

第三、使用AIDL

        AIDL(Android Interface Define Language)所做的工作是把对象分解成操作系统能够识别的原生的语言,并且安排他们跨进程进行进程间的通讯。以上所说的Messager技术,就是基于AIDL。正如前面所提到过的,Messager对象创建了在线程中为所有客户端请求创建一个请求队列,因此这个Service一次仅能接收一个请求。然而,如果我们想要我们的Service能够一次接受多个请求,我们可以直接使用AIDL,在这种情况下,你的Service必须能够胜任多线程并且实现安全的。

    我们为了使用AIDL,我们需要定义一个.aidl文件,这个文件定义了程序的接口。Android SDK工具用这个文件生成了一个抽象类,这个抽象类定义了定义了接口和操作IPC的实现,在我们的Service中我们可以继承这个抽象类。

注意:大多数情况下,我们一般来说不会使用AIDL来创建一个Bound Service,因为它可能需要一个多线程的兼容并且导致一个更加复杂的实现。正因为如此,AIDL不太适合大多数的应用程序,所以我们不会在这里进行讲解关于AIDL的用法。

继承Binder类的讲解

如果我们的Service仅仅需要在我们自己的应用程序中使用并且不需要跨进程工作,那么我们可以通过继承Binder来实现我们自己的Binder,这个Binder可以让客户端直接访问我们的Service。

注意,这种方法仅仅适用于客户端和服务端在同一个应用程序和进程中,例如,一个音乐播放应用程序需要把一个Activty和一个在后台播放音乐的Service进行绑定的时候,这种方式将会是一个很好做法。

1、首先,在我们的Service中我们可以创建一个IBinder的实例通过一下几种方式

      1>包含这个客户端能够访问的公共方法;

      2>返回当前Service的实例,这个实例中的公共方法客户端可以访问;

      3>或者,返回被这个Service主导的另一个类的实例,这个实例包含了可以被客户端访问的公共方法;

2、从onBinder这个回调方法中返回Binder的实例;

3、在客户端中接受到来自onServiceConnected()回调方法;

public class LocalService extends Service {
    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();
    // Random number generator
    private final Random mGenerator = new Random();

    /**
     * Class used for the client Binder.  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 this instance of LocalService so clients can call public methods
            return LocalService.this;
        }
    }

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

    /** method for clients */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}
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();
        // Bind to LocalService
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    /** Called when a button is clicked (the button in the layout file attaches to
      * this method with the android:onClick attribute) */
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call were something that might hang, then this request should
            // occur in a separate thread to avoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** Defines callbacks for service binding, passed to bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

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

使用Messager对象

如果我们想要和远程的Service进行通讯,你能使用Messager对象来来为你的Service提供接口。这种技术允许你不必通过AIDL来表现进程间通讯。

下面是一些如何使用Messager对象的总结:

1、Service实现一个Handler,这个Handler接收每一个来自客户端请求的回调;

2、这个Handler被用来创造一个Messager对象;

3、而这个Meeager对象创建一个IBinder对象,这个Binder对象从这个Service的onBind()方法中返回到客户端;

4、客户端用这个IBinder实例化这个Messager,客户端用这个IBinder给这个Service发送一个Messager对象;

5、这个Service在它的Handler对象的handleMessage()方法中接收没一个请求。

<span style="font-size:12px;">public class MessengerService extends Service {
    /** Command to the service to display a message */
    static final int MSG_SAY_HELLO = 1;

    /**
     * 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();
                    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) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }
}</span>
<span style="font-size:12px;">public class ActivityMessenger extends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;

    /** Flag indicating whether we have called bind on the service. */
    boolean mBound;

    /**
     * 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 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;
        }

        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;
            mBound = false;
        }
    };

    public void sayHello(View v) {
        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, 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();
        // Bind to the service
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}</span>


<think>好的,我现在需要帮助用户理解在结构体中使用lower_bound的方法。首先,我得回忆一下lower_bound在C++中的基本用法。lower_bound通常用于在有序区间中查找第一个不小于给定值的元素,返回的是迭代器。 用户提到的是结构体中的数据,所以可能需要自定义比较函数或者重载运算符。因为结构体默认无法直接比较,需要明确指定比较的规则。这时候应该考虑两种情况:一种是使用自定义的比较函数作为参数传递给lower_bound,另一种是在结构体中重载小于运算符,这样lower_bound就可以直接使用了。 接下来,我需要思考如何具体实现这两种方法。比如,结构体可能包含多个字段,比如学生的姓名和分数。如果我们要根据分数来查找,就需要在比较的时候只考虑分数字段。这时候,自定义的比较函数应该接收两个结构体实例,比较它们的分数。或者在结构体中重载<运算符时,只比较分数部分。 然后,我需要考虑示例代码的结构。首先定义结构体Student,包含name和score。然后创建一个存储Student的vector,并按照score排序。使用lower_bound时,需要注意传递一个临时对象作为查找的值,这个临时对象只需要设置score字段,因为比较的时候只关心这个字段。 另外,需要注意lower_bound的正确使用条件:区间必须是有序的,而且排序的方式必须和比较函数一致。如果vector是按照score升序排列的,那么lower_bound也会基于升序来查找。如果排序方式不同,结果可能不正确。 可能用户在实际使用中会遇到的问题包括:忘记排序导致结果错误,或者比较函数与排序方式不一致。这时候需要在回答中提醒用户确保数据已经正确排序,并且比较逻辑一致。 最后,我需要组织语言,分步骤说明两种方法,并给出示例代码。同时指出注意事项,帮助用户避免常见错误。这样用户就能清楚地理解如何在结构体中使用lower_bound了。</think>在 C++ 中,当结构体数据存储在有序容器(如 vector)时,使用 `lower_bound` 需要明确指定比较规则。以下是两种典型用法(示例以查找学生分数为例): ```cpp #include <algorithm> #include <vector> using namespace std; struct Student { string name; int score; // 方法1:重载小于运算符 bool operator<(const Student& other) const { return score < other.score; } }; // 方法2:自定义比较函数 struct Compare { bool operator()(const Student& a, const Student& b) { return a.score < b.score; } bool operator()(const Student& a, int val) { return a.score < val; } }; int main() { vector<Student> students = {{"Alice", 85}, {"Bob", 90}, {"Charlie", 78}}; // 必须先排序(排序方式须与比较规则一致) sort(students.begin(), students.end()); // 方法1:直接使用(依赖重载的<运算符) auto it1 = lower_bound(students.begin(), students.end(), Student{"", 80}); // 方法2:显式指定比较规则 auto it2 = lower_bound(students.begin(), students.end(), 80, Compare()); // 通过迭代器访问元素 if(it1 != students.end()) { cout << "找到第一个不小于80的学生:" << it1->name; } return 0; } ``` **关键点说明:** 1. 必须预先排序,且排序规则要与比较规则完全一致 2. 比较维度要明确(示例中仅比较score字段) 3. 方法2更灵活,可处理多字段排序场景 4. 返回值是迭代器,需判断是否找到(it != end()) **典型应用场景:** - 在游戏玩家排行榜中快速定位段位 - 学生成绩查询系统 - 商品价格区间快速检索 **易错点提醒:** 1. 未排序直接使用会导致未定义行为 2. 比较函数与排序规则不一致会产生错误结果 3. 结构体包含多个字段时需明确比较维度 4. 注意处理边界情况(查找值大于所有元素的情况)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值