原创 以 bind 方式创建与绑定 Service实例

2021-10-21 17:22 2080 15 3 分类: 软件与OS 文集: android studio

通过前面的学习,可以发现使用 start方式开启 Service 非常简单,但是它有一个技大的弊端,没有办法与开启者进行通信。为了解决这个问题,我们一般会使用 bind方式来定 Service。以bind方式创建与绑定 Service 的步骤在之前也已讲述,此处将一个实例来进行讲解。

1创建一个继承Service 类的子类 MyService类,由于是采用 bind方式绑定Service 因此 MyService 类与前文以start方式开启Service而创建的继承Service 类的子类有很大不同。

前文说过,采用此种方式绑定 Servce 时会回调 onBind()方法,此方法会返回一个IBind 类的对象。一般情况下,如果我们在某个Activity 中绑定了此 Service,就会从ServiceConnection onServiceConnected(ComponentName nameIBinder service)方法中获取到 onBind()方法返IBind类的对象。ActivityService可以进行通信,利用的就是这个IBind 类的对象。

所以,我们可以建立一个IBind类的子类,并在该类中封装 Service 对象,并在 onBind()方法中返回此类的对象。这样一来,在 Activity中就可以对 Service进行操作了。MyService类代码如下

  1. package com.rfstar.servicetest2;</span></p><p class="p" style="margin-top:0.0000pt;margin-right:0.0000pt;margin-bottom:0.0000pt;
  2. margin-left:0.0000pt;text-indent:24.0000pt;mso-char-indent-count:2.0000;
  3. mso-pagination:widow-orphan;">import android.app.Service;
  4. import android.content.Intent;
  5. import android.os.Binder;
  6. import android.os.IBinder;
  7. import android.util.Log;
  8. public class MyService extends Service {
  9. private String message;
  10. private boolean isrunning = true;
  11. private IBinder binder = new MyBinder();
  12. private MyService.ServiceThread serviceThread=new MyService.ServiceThread();
  13. private Thread thread;
  14. @Override
  15. public IBinder onBind(Intent intent) {
  16. Log.i("service", "onBind");
  17. thread = new Thread(serviceThread);
  18. //开启一个线程
  19. thread.start();
  20. /**返回一个可以在Activity的onServiceConnected()方法中接收的binder对象
  21. * 它是Activity和Service通信的桥梁
  22. * 在Activity中通过这个bind对象可以得到Service的实例引用
  23. * 通过获取的Service实例就可以调用相关方法和属性
  24. */
  25. return binder;
  26. }
  27. @Override
  28. public void onCreate() {
  29. Log.i("service", "oncreate");
  30. super.onCreate();
  31. }
  32. @Override
  33. public int onStartCommand(Intent intent, int flags, int startId) {
  34. Log.i("service", "onStartCommand");
  35. return super.onStartCommand(intent, flags, startId);
  36. }
  37. @Override
  38. public boolean onUnbind(Intent intent) {
  39. Log.i("service", "onUnbind");
  40. return super.onUnbind(intent);
  41. }
  42. @Override
  43. public void onDestroy() {
  44. super.onDestroy();
  45. //结束run方法的循环
  46. serviceThread.flag = false;
  47. Log.i("service", "onDestroy");
  48. }
  49. class ServiceThread implements Runnable {
  50. //用volatile修饰保证变量在线程间的可见性
  51. volatile boolean flag = true;
  52. @Override
  53. public void run() {
  54. Log.i("service", "thread开始运行");
  55. int i = 1;
  56. while (flag) {
  57. if (mOnDataCallback != null) {
  58. //通过线程模拟真实场景,循环改变数据
  59. mOnDataCallback.onDataChange(message + i);
  60. }
  61. i++;
  62. try {
  63. //间隔2秒调用一次函数
  64. Thread.sleep(2000);
  65. } catch (InterruptedException e) {
  66. e.printStackTrace();
  67. }
  68. }
  69. }
  70. }
  71. public class MyBinder extends Binder{
  72. public void setData(String message)
  73. {
  74. //从Activity传入message值
  75. MyService.this.message=message;
  76. }
  77. public MyService getService()
  78. {
  79. /**
  80. * 返回当前MyService对象
  81. * 当Activity中获取binder类的实例后
  82. * 可以通过此方法获取Service类实例
  83. */
  84. return MyService.this;
  85. }
  86. }
  87. private OnDataCallback mOnDataCallback=null;
  88. public void setmOnDataCallback(OnDataCallback mOnDataCallback) {
  89. this.mOnDataCallback = mOnDataCallback;
  90. }
  91. public interface OnDataCallback{
  92. void onDataChange(String message);
  93. }
  94. }

除了上面所说的 IBind 类之外,在此 Service类中还建立了一个接口 OnDataCallback,用进行数据的回调。

    2AndroidManifest.xml 中配置 Service 与前例相同,在AndroidManifest.xml中加入如下代码即可

3、通过 Context 调用 bindService(Intent intent,ServiceConnection serviceConnectionint flags)方法来绑定 Service.调用 unBindService(ServiceConnection serviceConnection)方法来解绑 Service

可以发现不管是绑定服务还是解绑服务都需传入一个 ServiceConnection 类的对象作为参。ServiceConnection 类有两个比较重要的回调方法∶onServiceConnected(ComponentName name,IBinder service)onServiceDisconnected(ComponentName name),这两个方法中前者是当ActviyService 绑定时的回调方法,后者是解绑时的回调方法。一般情况下我们会在前个方法中获取 IBinder类的对象,并通过该对象获取Service类的实例,然后对 Service进行操作,而在后一个方法中主要就是做一些清理性工作,比如销毁对象。

本实例中为了展现出 ServiceActivity 真正在做通信,加入了一个TextView,当MainActity 获取 Service 中的数据时,对该 TextView 的值进行修改。实例中布局文件在前例的acivity_main.xml文件基础上修改,大同小异,代码如下

 

  1. <?xml version="1.0" encoding="utf-8"?></p><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:app="http://schemas.android.com/apk/res-auto"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:orientation="vertical"
  7. tools:context=".MainActivity">
  8. <Button
  9. android:id="@+id/bind_service"
  10. android:layout_width="match_parent"
  11. android:layout_height="wrap_content"
  12. android:text="绑定服务"
  13. android:textSize="25dp"/>
  14. <Button
  15. android:id="@+id/unbind_service"
  16. android:layout_width="match_parent"
  17. android:layout_height="wrap_content"
  18. android:text="解绑服务"
  19. android:textSize="25dp"/>
  20. <TextView
  21. android:id="@+id/textview"
  22. android:layout_width="match_parent"
  23. android:layout_height="wrap_content"
  24. android:gravity="center"
  25. android:text="无数据"
  26. android:textSize="25dp"/>
  27. </LinearLayout>

下面是本实例MainActivity代码,为了方便读者理解,加入了一些注释

  1. package com.rfstar.servicetest2;
  2. import androidx.appcompat.app.AppCompatActivity;
  3. import android.content.ComponentName;
  4. import android.content.Context;
  5. import android.content.Intent;
  6. import android.content.ServiceConnection;
  7. import android.os.Bundle;
  8. import android.os.IBinder;
  9. import android.view.View;
  10. import android.widget.Button;
  11. import android.widget.TextView;
  12. public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  13. private Button bindService;
  14. private Button unbindService;
  15. private TextView textView;
  16. @Override
  17. protected void onCreate(Bundle savedInstanceState) {
  18. super.onCreate(savedInstanceState);
  19. setContentView(R.layout.activity_main);
  20. initView();
  21. }
  22. private void initView() {
  23. bindService=(Button)findViewById(R.id.bind_service);
  24. unbindService=(Button)findViewById(R.id.unbind_service);
  25. textView=(TextView)findViewById(R.id.textview);
  26. bindService.setOnClickListener(this);
  27. unbindService.setOnClickListener(this);
  28. }
  29. @Override
  30. public void onClick(View view) {
  31. switch (view.getId()){
  32. case R.id.bind_service:
  33. //创建意图
  34. Intent bindIntent=new Intent(this,MyService.class);
  35. //绑定服务
  36. bindService(bindIntent,serviceConnection,Context.BIND_AUTO_CREATE);
  37. break;
  38. case R.id.unbind_service:
  39. unbindService(serviceConnection);
  40. break;
  41. }
  42. }
  43. @Override
  44. protected void onDestroy()
  45. {
  46. super.onDestroy();
  47. }
  48. private MyService.MyBinder binder;
  49. private MyService myService;
  50. private ServiceConnection serviceConnection=new ServiceConnection() {
  51. @Override
  52. public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
  53. //得到binder实例
  54. binder=(MyService.MyBinder)iBinder;
  55. //给iBinder中的message设置一个值
  56. binder.setData("MainActivity:");
  57. //得到Service实例
  58. myService=(MyService)binder.getService();
  59. //设置接口回调获取Service中的数据
  60. myService.setmOnDataCallback(new MyService.OnDataCallback() {
  61. @Override
  62. public void onDataChange(final String message) {
  63. //在非UI线程想要修改UI界面的内容时,使用此方法
  64. runOnUiThread(new Runnable() {
  65. @Override
  66. public void run() {
  67. textView.setText(message);
  68. }
  69. });
  70. }
  71. });
  72. }
  73. @Override
  74. public void onServiceDisconnected(ComponentName componentName) {
  75. myService=null;
  76. }
  77. };
  78. }

结合注释,代码还是不难理解的。这里需要注意的是,修改 TextView的值使用runOnUiThreadThread thread)方法。有的读者可能会问,为何要在这个方法内修改TextView的值,而不是在 onDataChange(final String mesage)方法内直接修改。这涉及 Android 开发一个原则,那就是普通线程不可以修改UI界面,只有UI线程也就是 Activity所在线程可以 UI界面。如果在普通线程中需要修改 UI界面了,那么只能使用 Android 中的消息处理机制,也就是我们常说的 Handler 机制。此处使用的runOnUiThreadRunnable action)方法本质上Handler机制的应用。至于什么是Handler 机制,下一篇技术博客将详细讲解。

步骤一、理解了代码之后,运行程序,效果如图1-1所示。

                                              1-1

步骤二、当点击"绑定服务"按钮绑定完此服务后,会发现当前界面中的TextView被改了,效果如图1-2所示。

                                                   1-2

步骤三、当点击"解绑服务"按钮解绑服务后,发现界面中TextView停止了变化,定在了一个数字上,效果如1-3所示。

                                                       1-3

此时观察Log的输出,效果如下:

发现bind方式绑定Service的生命周期正如前面所描述的那样,它的生命周期方法是按照onCreate()-onBind()-Service running----调用context.unbindService()-onUnbind()-onDestroy()这样的路径执行的。

android studio工具及手机模拟器以及更多工程源代码下载请前往微信公众号:大鸟科创空间,回复:android studio即可获取。

作者: 大鸟科创空间, 来源:面包板社区

链接: https://mbb.eet-china.com/blog/uid-me-3949041.html

版权声明:本文为博主原创,未经本人允许,禁止转载!

文章评论2条评论)

登录后参与讨论

yzw92 2021-10-22 06:15

感谢楼主的分享

curton 2021-10-21 21:10

学习了
相关推荐阅读
大鸟科创空间 2022-02-14 17:42
生日快乐HTML浪漫网页制作源码
对象生日快到了,赶快下载去给ta一个惊喜吧。解压后里面有整个项目和使用说明。这个代码有背景音乐(可以自己更改),有自转相册(可以改成对象照片)如下是效果截图,用鼠标滑动界面会有爱心划过,下滑可以循环播...
大鸟科创空间 2022-01-18 18:06
Android之WebView用法
除了HTTP通信与 Socket 通信两种主要的网络技术外,在 Android 中还提供了一种加载和显示网页的技术—WebView。这可以让我们去处理一些特殊的需求,比如像微信那样在应用程序里展示网页...
大鸟科创空间 2022-01-07 14:18
Android之Socket实例
    Socket(套接字)是对 TCP/IP 协议的封装和应用,根据底层封装协议的不同,Socket 的类型可以分为流套接字(streamsocket)和数据报套接字(data...
大鸟科创空间 2021-12-24 14:38
Android之本地广播的使用
前面我们发送和接收的广播全部属于系统全局广播,即发出的广播可以被其他任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样就很容易会引起安全性的问题,比如说我们发送的一些携带关键性数...
大鸟科创空间 2021-12-15 12:50
自定义广播实例
我们应该已经学会了通过广播接收者来接收系统广播的内容,但是在实际开发中,仍需要自定义一些广播。下面我们就来讲解如何在应用程序中发送自定义的广播。发送广播很简单,只需要声明一个意图,然后使用Contex...
大鸟科创空间 2021-12-07 13:04
Android之动态注册广播实例
Android内置了很多系统级别的广播,我们可以在应用序中通过监听这些广播来得到各种系统的状态信息,比如手机开机完成后会发出一条广播、电池的电最发生变化会发出一条广播、时间或时区发生改变也会发出一条广...
我要评论
2
15
关闭 站长推荐上一条 /2 下一条