Android学习笔记(九)一个例子弄清Service与Activity通信

  上一篇博文主要整理了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、改变输入框内容,点击同步数据,界面和对应的日志如下:

   

  因本人水平有限,如在文中发现错误或者描述不当的地方,敬请指正,感激不尽!

  声明:欢迎转载,转载是请注明本文链接,谢谢!

posted @ 2015-09-30 23:45  codingblock  阅读(16590)  评论(2编辑  收藏  举报