在上一篇博客中http://blog.youkuaiyun.com/redoq/article/details/52142868,我们对Service进行了粗略的讲解,这一篇博客我将介绍Service的通信机制,不足之处望指正。
由于Service的启动分为两种,所以我把Service的通信分为两种介绍
一:对于通过startService启动的服务
我们可以通过下面的代码将数据发送到Service中
启动服务的Activity中的代码
Intent i = new Intent(this,MyService.class);
i.putExtra("data","send data");
...
startService(i);
MyService中的代码,由于使用的是startService启动服务,所以这里用onStartCommand来接收数据
private String data = "";
@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
data = intent.getStringExtra("data"); //通过onStartCommand中的intent参数获取数据
System.out.println("接收到的数据为:" + data);
return super.onStartCommand(intent, flags, startId);
}
输出结果 接收到的数据为:send data
通过startService传递数据虽然简单方便,但是不能将MyService中的数据返回到调用它的Activity,除非使用广播,我将在后面对广播进行讲解。
=============================================================================
二、通过bindService绑定服务传递数据
1、首先,我们重写MyService中的onBind()并新建一个类继承Binder
public class MyService extends Service {
private String data = "";
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder(); //将MyBinder对象返回给启动MyService的Activity
}
public class MyBinder extends Binder { //Binder类实现了IBinder接口
public void setData(String data) {
MyService.this.data = data; //修改data的值
}
}
}
2、然后我们使用bindService绑定服务并重写onServiceConnected()和onServiceDisconnected()
MainActivity代码
private MyService.MyBinder binder = null; //新建一个MyBinder对象,用于接收MyService返回的MyBinder对象
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
binder.setData("修改MyService中data的值 ");
}
});
bindService(new Intent(this,MyService.class),this,Context.BIND_AUTO_CREATE);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
binder = (MyService.MyBinder)service; //接收MyService返回的MyBinder对象
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
通过上面的绑定服务来直接调用方法就可以将数据传递给Service,这种方法比startService更加方便同时也更加高效。
===========================================================================================
三、如何将服务中状态的改变通知给外界,比如Service执行某些操作之后想要更新主UI线程
我们知道,在辅线程中是不能进行主UI线程更新操作的,有些人可能听过这样的口诀“主线程不做耗时操作,子线程不更新UI”
1、使用接口回调的方式更新UI线程
我们在MyService中写一个接口
public static interface Callback{
void onDataChange(String data);
}
为了大家能直观的理解,我把MyService的代码全列出
public class MyService extends Service {
private boolean running = false;
private CallBack mCallBack = null;
public MyService() {
}
@Override
public void onCreate() {
super.onCreate();
running = true;
new Thread() {
@Override
public void run() {
super.run();
int i = 0;
while (running) {
i++;
if (mCallBack != null) {
mCallBack.onDataChange(i + ""); //把更新的内容传出去
}
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
@Override
public void onDestroy() {
super.onDestroy();
running = false;
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
public class MyBinder extends Binder{
public MyService getMyService(){
return MyService.this;
}
}
public CallBack getCallBack() {
return mCallBack;
}
public void setCallBack(CallBack callBack) {
mCallBack = callBack;
}
public static interface CallBack {
void onDataChange(String data);
}
}
下面的内容是Activity中的部分代码,控件的初始化代码没给
private MyService.MyBinder binder = null;
private TextView tv;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
binder = (MyService.MyBinder) service; //获取MyService中onBind返回的 MyBinder对象
binder.getMyService().setCallBack(new MyService.CallBack() {
@Override
public void onDataChange(String data) { //这里不能直接更新UI,因为辅线程不能更新UI主线程
Message message = new Message();
Bundle b = new Bundle();
b.putString("data", data);
message.setData(b);
mHandler.sendMessage(message); //把数据通过Message发送到Handler中
}
});
}
private Handler mHandler = new Handler() { //定义一个Handler对象,用于更新UI线程
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tv.setText(msg.getData().getString("data")); //更新UI
}
};
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bindService:
bindService(new Intent(MainActivity.this, MyService.class), this, Context.BIND_AUTO_CREATE);
break;
}
}
2、使用广播(broadcast)将MyService中的数据传到外界,更新UI
当MyService中的数据变化时就发送一条广播,然后在Activity中已经注册的广播接收器就能接收到广播并获取内容进行UI更新。
(1)首先在Activity中注册广播
public class MainActivity extends Activity implements View.OnClickListener {
private TextView tv;
private MsgReceiver msgReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
findViewById(R.id.startService).setOnClickListener(this);
findViewById(R.id.stopService).setOnClickListener(this);
}
//广播接收器
public class MsgReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
tv.setText(intent.getIntExtra("data", 0)+""); //接收到广播数据后更新UI
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.startService:
msgReceiver = new MsgReceiver(); //定义一个广播接收器
// IntentFilter intentFilter = new IntentFilter(MyService.ACTION); //也可像下面那样写
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(MyService.ACTION); //添加action,要和发送广播的Intent中的action一致
registerReceiver(msgReceiver,intentFilter); //注册广播
startService(new Intent(this, MyService.class)); //启动服务
break;
case R.id.stopService:
stopService(new Intent(this, MyService.class)); //停止服务
unregisterReceiver(msgReceiver); //注销广播
}
}
}
(2)、在MyService中将需要传出的数据通过广播发送出去
public class MyService extends Service {
private boolean running = false;
public static final String ACTION = "包名.intent.action.MyService";
@Override
public void onCreate() {
super.onCreate();
running = true;
new Thread() {
@Override
public void run() {
super.run();
int i = 0;
while (running) {
i++;
Intent intent = new Intent( ACTION ); //添加action,要和Activity中接收广播的action一致
intent.putExtra("data", i);
sendBroadcast(intent); //每次内容更改都发送一条广播
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
@Override
public void onDestroy() {
super.onDestroy();
running = false;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
}