上一篇博文主要整理了Service的创建、绑定过程,本篇主要整理一下Service与Activity的通信方式。包括在启动一个Service时向它传递数据、怎样改变运行中的Service中得数据和侦听Service内数据的改变。
本篇将写一个demo来说明以下三个问题:
1、怎样在启动一个Service时向它传递数据
关键点:Intent传值,onStartCommand()接收。
2、怎样向运行的Service中同步数据
关键点:通过onBind()获取Service实例,然后再调用Binder中的相关方法。
3、怎样侦听Service中数据变化
关键点:通过回调函数达到目的。
一、准备Service
先贴出Service的详细代码,然后再慢慢分析
1 public class MyService extends Service {
2 private String data = "默认消息";
3 private boolean serviceRunning = false;
4
5 // 必须实现的方法,用于返回Binder对象
6 @Override
7 public IBinder onBind(Intent intent) {
8 System.out.println("--onBind()--");
9 return new MyBinder();
10 }
11
12 public class MyBinder extends Binder {
13 MyService getService() {
14 return MyService.this;
15 }
16
17 public void setData(String data) {
18 MyService.this.data = data;
19 }
20 }
21
22 // 创建Service时调用该方法,只调用一次
23 @Override
24 public void onCreate() {
25 super.onCreate();
26 System.out.println("--onCreate()--");
27 serviceRunning = true;
28 new Thread() {
29 @Override
30 public void run() {
31 int n = 0;
32 while (serviceRunning) {
33 n++;
34 String str = n + data;
35 System.out.println(str);
36 if (dataCallback != null) {
37 dataCallback.dataChanged(str);
38 }
39 try {
40 sleep(1000);
41 } catch (InterruptedException e) {
42 e.printStackTrace();
43 }
44 }
45 };
46 }.start();
47 }
48
49 // 每次启动Servcie时都会调用该方法
50 @Override
51 public int onStartCommand(Intent intent, int flags, int startId) {
52 System.out.println("--onStartCommand()--");
53 data = intent.getStringExtra("data");
54 return super.onStartCommand(intent, flags, startId);
55 }
56
57 // 解绑Servcie调用该方法
58 @Override
59 public boolean onUnbind(Intent intent) {
60 System.out.println("--onUnbind()--");
61 return super.onUnbind(intent);
62 }
63
64 // 退出或者销毁时调用该方法
65 @Override
66 public void onDestroy() {
67 serviceRunning = false;
68 System.out.println("--onDestroy()--");
69 super.onDestroy();
70 }
71
72 DataCallback dataCallback = null;
73
74 public DataCallback getDataCallback() {
75 return dataCallback;
76 }
77
78 public void setDataCallback(DataCallback dataCallback) {
79 this.dataCallback = dataCallback;
80 }
81
82 // 通过回调机制,将Service内部的变化传递到外部
83 public interface DataCallback {
84 void dataChanged(String str);
85 }
86
87 }
代码分析:我们都知道,通过startService启动一个Service时,Service会调用生命周期函数onStartCommand(),在代码中创建一个Service,在onStartCommand()方法中获取从Activity传递过来的数据,并在Service的onCreate()方法中开启一个新的线程,使其循环调用回调函数,以达到通知外界信息改变的目的。并在Service中通过Binder类,将Service与Activity链接起来,以实现信息同步。
二、准备布局文件
布局文件比较简单,直接贴出,就不分析了,activity_main.xml如下:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 android:orientation="vertical">
6
7 <TextView
8 android:id="@+id/tv_out"
9 android:layout_width="fill_parent"
10 android:layout_height="wrap_content"
11 android:text="显示区域" />
12
13 <EditText
14 android:id="@+id/et_data"
15 android:layout_width="match_parent"
16 android:layout_height="wrap_content"
17 android:ems="10" >
18
19 <requestFocus />
20 </EditText>
21
22 <Button
23 android:id="@+id/btn_start_service"
24 android:layout_width="fill_parent"
25 android:layout_height="wrap_content"
26 android:text="startService" />
27
28 <Button
29 android:id="@+id/btn_stop_service"
30 android:layout_width="fill_parent"
31 android:layout_height="wrap_content"
32 android:text="stopService" />
33
34 <Button
35 android:id="@+id/btn_bind_service"
36 android:layout_width="fill_parent"
37 android:layout_height="wrap_content"
38 android:text="bindService" />
39
40 <Button
41 android:id="@+id/btn_unbind_service"
42 android:layout_width="fill_parent"
43 android:layout_height="wrap_content"
44 android:text="unbindService" />
45
46 <Button
47 android:id="@+id/btn_sync_data"
48 android:layout_width="fill_parent"
49 android:layout_height="wrap_content"
50 android:text="同步数据" />
51
52 </LinearLayout>
三、准备Activity
MainActivity代码如下:
1 public class MainActivity extends Activity implements OnClickListener {
2
3 private Intent intent = null;
4 private Button btn_start_service;
5 private Button btn_stop_service;
6 private Button btn_bind_service;
7 private Button btn_unbind_service;
8 private Button btn_sync_data;
9 private EditText et_data;
10 private TextView tv_out;
11 MyServiceConn myServiceConn;
12 MyService.MyBinder binder = null;
13
14 @Override
15 protected void onCreate(Bundle savedInstanceState) {
16 super.onCreate(savedInstanceState);
17 setContentView(R.layout.activity_main);
18 intent = new Intent(this, MyService.class);
19 myServiceConn = new MyServiceConn();
20 setOnClick();
21 }
22
23 @Override
24 public void onClick(View v) {
25 switch (v.getId()) {
26 case R.id.btn_start_service:
27 //用intent启动Service并传值
28 intent.putExtra("data", et_data.getText().toString());
29 startService(intent);
30 break;
31 case R.id.btn_stop_service:
32 //停止Service
33 stopService(intent);
34 break;
35 case R.id.btn_bind_service:
36 //绑定Service
37 bindService(intent, myServiceConn, Context.BIND_AUTO_CREATE);
38 break;
39 case R.id.btn_unbind_service:
40 //解绑Service
41 if (binder != null) {
42 unbindService(myServiceConn);
43 }
44 break;
45 case R.id.btn_sync_data:
46 //注意:需要先绑定,才能同步数据
47 if (binder != null) {
48 binder.setData(et_data.getText().toString());
49 }
50 break;
51 default:
52 break;
53 }
54 }
55
56 class MyServiceConn implements ServiceConnection {
57 // 服务被绑定成功之后执行
58 @Override
59 public void onServiceConnected(ComponentName name, IBinder service) {
60 // IBinder service为onBind方法返回的Service实例
61 binder = (MyService.MyBinder) service;
62 binder.getService().setDataCallback(new MyService.DataCallback() {
63 //执行回调函数
64 @Override
65 public void dataChanged(String str) {
66 Message msg = new Message();
67 Bundle bundle = new Bundle();
68 bundle.putString("str", str);
69 msg.setData(bundle);
70 //发送通知
71 handler.sendMessage(msg);
72 }
73 });
74 }
75
76 @SuppressLint("HandlerLeak")
77 Handler handler = new Handler() {
78 public void handleMessage(android.os.Message msg) {
79 //在handler中更新UI
80 tv_out.setText(msg.getData().getString("str"));
81 };
82 };
83
84 // 服务奔溃或者被杀掉执行
85 @Override
86 public void onServiceDisconnected(ComponentName name) {
87 binder = null;
88 }
89 }
90
91 private void loadUI() {
92 btn_start_service = (Button) findViewById(R.id.btn_start_service);
93 btn_stop_service = (Button) findViewById(R.id.btn_stop_service);
94 btn_bind_service = (Button) findViewById(R.id.btn_bind_service);
95 btn_unbind_service = (Button) findViewById(R.id.btn_unbind_service);
96 btn_sync_data = (Button) findViewById(R.id.btn_sync_data);
97 et_data = (EditText) findViewById(R.id.et_data);
98 tv_out = (TextView) findViewById(R.id.tv_out);
99 }
100
101 private void setOnClick() {
102 loadUI();
103 btn_start_service.setOnClickListener(this);
104 btn_stop_service.setOnClickListener(this);
105 btn_bind_service.setOnClickListener(this);
106 btn_unbind_service.setOnClickListener(this);
107 btn_sync_data.setOnClickListener(this);
108 }
109
110 }
代码分析:
1、加载UI,初始化变量啥的跳过了,主要说一下关键代码,在第28代码中,与启动一个Activity类似,通过Intent想要启动的Service传递参数。
2、在37行通过bindService绑定Service,然后在ServiceConnection中获取Service类中onBind方法返回的实例,获取实例Service实例后,我们就可以通过调用Service中MyBinder的setData()方法对Service进行同步数据,如48行所示。
3、整个过程,在Service的onCreate方法中都会循环调用回调函数,同时我们在MainActivity中重写回调方法以实现更新UI。
四、测试
1、启动示例后,在输入框输入你好,然后点击startService,界面和对应的日志如下:
看了下面的代码后就会知道,此时因为没有绑定service,所以办法执行回调函数更新UI,所以显示区域没有更新。
2、点击bindService后,界面如下:
当执行bindService后,在ServiceConnection方法中就会执行执行回调函数更新UI,此时显示区域开始更新。
3、改变输入框内容,点击同步数据,界面和对应的日志如下:
因本人水平有限,如在文中发现错误或者描述不当的地方,敬请指正,感激不尽!
声明:欢迎转载,转载是请注明本文链接,谢谢!