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、改变输入框内容,点击同步数据,界面和对应的日志如下:
因本人水平有限,如在文中发现错误或者描述不当的地方,敬请指正,感激不尽!
声明:欢迎转载,转载是请注明本文链接,谢谢!
作者:CodingBlock
出处:http://www.cnblogs.com/codingblock/
本文版权归作者和共博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
如果文中有什么错误,欢迎指出。以免更多的人被误导。