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

2021-10-21 17:22 2135 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类代码如下

package com.rfstar.servicetest2;</span></p><p class="p" style="margin-top:0.0000pt;margin-right:0.0000pt;margin-bottom:0.0000pt;
  • margin-left:0.0000pt;text-indent:24.0000pt;mso-char-indent-count:2.0000;
  • mso-pagination:widow-orphan;">import android.app.Service;
  • import android.content.Intent;
  • import android.os.Binder;
  • import android.os.IBinder;
  • import android.util.Log;
  • public class MyService extends Service {
  • private String message;
  • private boolean isrunning = true;
  • private IBinder binder = new MyBinder();
  • private MyService.ServiceThread serviceThread=new MyService.ServiceThread();
  • private Thread thread;
  • @Override
  • public IBinder onBind(Intent intent) {
  • Log.i("service", "onBind");
  • thread = new Thread(serviceThread);
  • //开启一个线程
  • thread.start();
  • /**返回一个可以在Activity的onServiceConnected()方法中接收的binder对象
  • * 它是Activity和Service通信的桥梁
  • * 在Activity中通过这个bind对象可以得到Service的实例引用
  • * 通过获取的Service实例就可以调用相关方法和属性
  • */
  • return binder;
  • }
  • @Override
  • public void onCreate() {
  • Log.i("service", "oncreate");
  • super.onCreate();
  • }
  • @Override
  • public int onStartCommand(Intent intent, int flags, int startId) {
  • Log.i("service", "onStartCommand");
  • return super.onStartCommand(intent, flags, startId);
  • }
  • @Override
  • public boolean onUnbind(Intent intent) {
  • Log.i("service", "onUnbind");
  • return super.onUnbind(intent);
  • }
  • @Override
  • public void onDestroy() {
  • super.onDestroy();
  • //结束run方法的循环
  • serviceThread.flag = false;
  • Log.i("service", "onDestroy");
  • }
  • class ServiceThread implements Runnable {
  • //用volatile修饰保证变量在线程间的可见性
  • volatile boolean flag = true;
  • @Override
  • public void run() {
  • Log.i("service", "thread开始运行");
  • int i = 1;
  • while (flag) {
  • if (mOnDataCallback != null) {
  • //通过线程模拟真实场景,循环改变数据
  • mOnDataCallback.onDataChange(message + i);
  • }
  • i++;
  • try {
  • //间隔2秒调用一次函数
  • Thread.sleep(2000);
  • } catch (InterruptedException e) {
  • e.printStackTrace();
  • }
  • }
  • }
  • }
  • public class MyBinder extends Binder{
  • public void setData(String message)
  • {
  • //从Activity传入message值
  • MyService.this.message=message;
  • }
  • public MyService getService()
  • {
  • /**
  • * 返回当前MyService对象
  • * 当Activity中获取binder类的实例后
  • * 可以通过此方法获取Service类实例
  • */
  • return MyService.this;
  • }
  • }
  • private OnDataCallback mOnDataCallback=null;
  • public void setmOnDataCallback(OnDataCallback mOnDataCallback) {
  • this.mOnDataCallback = mOnDataCallback;
  • }
  • public interface OnDataCallback{
  • void onDataChange(String message);
  • }
  • }
  • 复制代码

    除了上面所说的 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文件基础上修改,大同小异,代码如下

     

    <?xml version="1.0" encoding="utf-8"?></p><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  • xmlns:app="http://schemas.android.com/apk/res-auto"
  • xmlns:tools="http://schemas.android.com/tools"
  • android:layout_width="match_parent"
  • android:layout_height="match_parent"
  • android:orientation="vertical"
  • tools:context=".MainActivity">
  • <Button
  • android:id="@+id/bind_service"
  • android:layout_width="match_parent"
  • android:layout_height="wrap_content"
  • android:text="绑定服务"
  • android:textSize="25dp"/>
  • <Button
  • android:id="@+id/unbind_service"
  • android:layout_width="match_parent"
  • android:layout_height="wrap_content"
  • android:text="解绑服务"
  • android:textSize="25dp"/>
  • <TextView
  • android:id="@+id/textview"
  • android:layout_width="match_parent"
  • android:layout_height="wrap_content"
  • android:gravity="center"
  • android:text="无数据"
  • android:textSize="25dp"/>
  • </LinearLayout>
  • 复制代码

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

    package com.rfstar.servicetest2;
  • import androidx.appcompat.app.AppCompatActivity;
  • import android.content.ComponentName;
  • import android.content.Context;
  • import android.content.Intent;
  • import android.content.ServiceConnection;
  • import android.os.Bundle;
  • import android.os.IBinder;
  • import android.view.View;
  • import android.widget.Button;
  • import android.widget.TextView;
  • public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  • private Button bindService;
  • private Button unbindService;
  • private TextView textView;
  • @Override
  • protected void onCreate(Bundle savedInstanceState) {
  • super.onCreate(savedInstanceState);
  • setContentView(R.layout.activity_main);
  • initView();
  • }
  • private void initView() {
  • bindService=(Button)findViewById(R.id.bind_service);
  • unbindService=(Button)findViewById(R.id.unbind_service);
  • textView=(TextView)findViewById(R.id.textview);
  • bindService.setOnClickListener(this);
  • unbindService.setOnClickListener(this);
  • }
  • @Override
  • public void onClick(View view) {
  • switch (view.getId()){
  • case R.id.bind_service:
  • //创建意图
  • Intent bindIntent=new Intent(this,MyService.class);
  • //绑定服务
  • bindService(bindIntent,serviceConnection,Context.BIND_AUTO_CREATE);
  • break;
  • case R.id.unbind_service:
  • unbindService(serviceConnection);
  • break;
  • }
  • }
  • @Override
  • protected void onDestroy()
  • {
  • super.onDestroy();
  • }
  • private MyService.MyBinder binder;
  • private MyService myService;
  • private ServiceConnection serviceConnection=new ServiceConnection() {
  • @Override
  • public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
  • //得到binder实例
  • binder=(MyService.MyBinder)iBinder;
  • //给iBinder中的message设置一个值
  • binder.setData("MainActivity:");
  • //得到Service实例
  • myService=(MyService)binder.getService();
  • //设置接口回调获取Service中的数据
  • myService.setmOnDataCallback(new MyService.OnDataCallback() {
  • @Override
  • public void onDataChange(final String message) {
  • //在非UI线程想要修改UI界面的内容时,使用此方法
  • runOnUiThread(new Runnable() {
  • @Override
  • public void run() {
  • textView.setText(message);
  • }
  • });
  • }
  • });
  • }
  • @Override
  • public void onServiceDisconnected(ComponentName componentName) {
  • myService=null;
  • }
  • };
  • }
  • 复制代码

    结合注释,代码还是不难理解的。这里需要注意的是,修改 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

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

    PARTNER CONTENT

    文章评论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
    关闭 站长推荐上一条 /3 下一条