在Activity和Service之间使用Binder和回调接口进行通信

Activity和Service之间的通信方式有多种:通过broadcast、扩展Binder类、使用Messenger、使用AIDL等。


扩展Binder类使用场景

如果你的服务仅供本地应用使用,不需要跨进程工作,则可以实现扩展Binder 类,让你的客户端通过该类直接访问服务中的公共方法。此方法只有在客户端和服务位于同一应用和进程内这一最常见的情况下方才有效。例如,对于需要将 Activity 绑定到在后台播放音乐的自有服务的音乐应用,此方法非常有效。


下面讲一下怎么样扩展Binder类实现Activity和Service的通信。

1、在Service中创建一个Binder实例,然后再onBind()方法中返回这个实例。

2、在客户端中使用bindService从 onServiceConnected() 回调方法接收Binder实例,这个Binder实例就是Service的回调方法onBind()方法返回的Binder,然后使用这个Binder提供的方法调用绑定服务,得到Service实例。

3、然后就可以使用这个Service实例来调用其方法,这样就可以对Sevice服务端进行操作。

4、如果Service端想要给客户端返回数据咋办呢?这个简单,既然已经获得了Service实例,那么就可以在Service当中设置一个接口,然后在客户端实现这个接口,这样Service端就可以通过这个接口给客户端发送消息了!


以下是Service类:

package com.easyliu.demo.binderdemo;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;

import java.util.Random;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class LocalService extends Service {
    private static final String TAG = LocalService.class.getSimpleName();
    private final IBinder mBinder = new LocalBinder();
    // Random number generator
    private final Random mGenerator = new Random();
    private OnDataArrivedListener mOnDataArrivedListener;
    private ScheduledExecutorService mThreadPool;//定时器线程池
    private int mCount = 0;

    /**
     * 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() {
    }

    @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() {
        Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
    }

    /**
     * 定时任务
     */
    final TimerTask task = new TimerTask() {

        @Override
        public void run() {
            if (mOnDataArrivedListener != null) {
                mCount++;
                if (mCount == 1000) {
                    mCount = 0;
                }
                mOnDataArrivedListener.onDataArrived(mCount);
            }
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        //开启定时任务
        mThreadPool = Executors.newScheduledThreadPool(1);
        mThreadPool.scheduleAtFixedRate(task, 0, 1000, TimeUnit.MILLISECONDS);
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        //关闭定时器
        if (mThreadPool != null) {
            mThreadPool.shutdownNow();
        }
        return true;
    }

    /**
     * method for clients
     */
    public int getRandomNumber() {
        return mGenerator.nextInt(100);
    }

    /**
     * 设置监听
     *
     * @param onDataArrivedListener
     */
    public void setOnDataArrivedListener(OnDataArrivedListener onDataArrivedListener) {
        this.mOnDataArrivedListener = onDataArrivedListener;
    }
}

在以上的Service当中,在onBind()方法中开启了一个定时器,定时器的任务就是每隔一段时间通过接口:

public interface OnDataArrivedListener {
    public void onDataArrived(int data);
}
发送数据,在客户端获取到此Service实例之后调用以下方法,就可以接收到Service返回的数据了。

 /**
     * 设置监听
     *
     * @param onDataArrivedListener
     */
    public void setOnDataArrivedListener(OnDataArrivedListener onDataArrivedListener) {
        this.mOnDataArrivedListener = onDataArrivedListener;
    }

下面是客户端代码实现:

package com.easyliu.demo.binderdemo;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class LocalServiceActivity extends AppCompatActivity {
    private boolean mIsBound;
    private LocalService mBoundService;
    private Button btn_bound;
    private Button btn_unbound;
    private Button btn_call_func;
    private TextView tv_display;
    private IncomingHandler mHandler;
    private static final int MSG_FROM_SERVICE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
        mHandler = new IncomingHandler();
    }

    private void initViews() {
        btn_bound = (Button) findViewById(R.id.btn_bound);
        btn_unbound = (Button) findViewById(R.id.btn_ubound);
        btn_call_func = (Button) findViewById(R.id.btn_call_func);
        btn_bound.setOnClickListener(mListener);
        btn_unbound.setOnClickListener(mListener);
        btn_call_func.setOnClickListener(mListener);
        tv_display = (TextView) findViewById(R.id.tv_display);
    }

    /**
     * 接收信息
     */
    private final class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_FROM_SERVICE:
                    tv_display.setText(msg.obj + "");
                    break;
            }
        }
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mBoundService = ((LocalService.LocalBinder) service).getService();
            //回调监听
            mBoundService.setOnDataArrivedListener(new OnDataArrivedListener() {
                @Override
                public void onDataArrived(int data) {
                    //不是在主线程,不能直接操作主UI,切换到主线程
                    mHandler.obtainMessage(MSG_FROM_SERVICE, data).sendToTarget();
                }
            });
            // Tell the user about this for our demo.
            Toast.makeText(LocalServiceActivity.this, R.string.local_service_connected,
                    Toast.LENGTH_SHORT).show();
        }

        public void onServiceDisconnected(ComponentName className) {
            mBoundService = null;
            Toast.makeText(LocalServiceActivity.this, R.string.local_service_disconnected,
                    Toast.LENGTH_SHORT).show();
        }
    };

    /**
     * 绑定服务
     */
    void doBindService() {
        bindService(new Intent(LocalServiceActivity.this,
                LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }

    /**
     * 解除绑定
     */
    void doUnbindService() {
        if (mIsBound) {
            unbindService(mConnection);
            mIsBound = false;
        }
    }

    private OnClickListener mListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_bound:
                    doBindService();
                    break;
                case R.id.btn_ubound:
                    doUnbindService();
                    break;
                case R.id.btn_call_func:
                    Toast.makeText(LocalServiceActivity.this, "number:" + mBoundService.getRandomNumber(), Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        doUnbindService();
    }
}

onServiceConnected回调方法中实现以下接口,当接收到消息之后就把消息发送给主UI进行显示。注意在这里不能直接操作主UI的控件,因为回调接口是在Service中的线程池里面执行的,也就是以下方法onDataArrived的执行不在主UI,所以不能直接操作主UI的控件。

  mBoundService.setOnDataArrivedListener(new OnDataArrivedListener() {
                @Override
                public void onDataArrived(int data) {
                    //不是在主线程,不能直接操作主UI,切换到主线程
                    mHandler.obtainMessage(MSG_FROM_SERVICE, data).sendToTarget();
                }
            });


下面是界面,当点击绑定之后,下面的数字就会隔一秒加1(这个可以用来音乐播放器后台Service更新主UI的进度条),可以点击解除绑定。同时可以点击调用服务中的方法,就会Toast一个随机数字。



关于绑定服务

绑定服务是客户端-服务器接口中的服务器。绑定服务可让组件(例如 Activity)绑定到服务、发送请求、接收响应,甚至执行进程间通信 (IPC)。绑定服务通常只在为其他应用组件服务时处于活动状态,不会无限期在后台运行。

绑定服务是 Service 类的实现,可让其他应用与其绑定和交互。要提供服务绑定,你必须实现 onBind() 回调方法。该方法返回的 IBinder 对象定义了客户端用来与服务进行交互的编程接口。

 

绑定到已启动服务

正如服务文档中所述,你可以创建同时具有已启动和绑定两种状态的服务。也就是说,可通过调用 startService() 启动该服务,让服务无限期运行;此外,还可通过调用 bindService() 使客户端绑定到服务。

 

如果你确实允许服务同时具有已启动和绑定状态,则服务启动后,系统“绝对不会”在所有客户端都取消绑定时销毁服务。为此,你必须通过调用 stopSelf() 或 stopService() 显式停止服务。

 

尽管你通常应该实现 onBind() 或 onStartCommand(),但有时需要同时实现这两者。例如,音乐播放器可能发现让其服务无限期运行并同时提供绑定很有用处。这样一来,Activity 便可启动服务进行音乐播放,即使用户离开应用,音乐播放也不会停止。然后,当用户返回应用时,Activity 可绑定到服务,重新获得回放控制权。

 

客户端可通过调用 bindService() 绑定到服务。调用时,它必须提供 ServiceConnection 的实现,后者会监控与服务的连接。bindService() 方法会立即无值返回,但当 Android 系统创建客户端与服务之间的连接时,会调用 ServiceConnection 上的 onServiceConnected(),向客户端传递用来与服务通信的 IBinder。

 

多个客户端可同时连接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder。系统随后无需再次调用 onBind(),便可将同一 IBinder 传递至任何其他绑定的客户端。

 

当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非 startService() 也启动了该服务)。


当你实现绑定服务时,最重要的环节是定义你的 onBind() 回调方法返回的接口。


参考:https://developer.android.com/guide/components/bound-services.html#Creating




  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值