通过前面的学习,可以发现使用 start方式开启 Service 非常简单,但是它有一个技大的弊端,没有办法与开启者进行通信。为了解决这个问题,我们一般会使用 bind方式来定 Service。以bind方式创建与绑定 Service 的步骤在之前也已讲述,此处将用一个实例来进行讲解。
1、创建一个继承Service 类的子类 MyService类,由于是采用 bind方式绑定Service, 因此 MyService 类与前文以start方式开启Service而创建的继承Service 类的子类有很大不同。
前文说过,采用此种方式绑定 Servce 时会回调 onBind()方法,此方法会返回一个IBind 类的对象。一般情况下,如果我们在某个Activity 中绑定了此 Service,就会从ServiceConnection 类 onServiceConnected(ComponentName name, IBinder service)方法中获取到 onBind()方法返回的IBind类的对象。Activity与Service可以进行通信,利用的就是这个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,用来进行数据的回调。
2、在AndroidManifest.xml 中配置 Service 与前例相同,在AndroidManifest.xml中加入如下代码即可∶
3、通过 Context 调用 bindService(Intent intent,ServiceConnection serviceConnection, int flags)方法来绑定 Service.调用 unBindService(ServiceConnection serviceConnection)方法来解绑 Service。
可以发现不管是绑定服务还是解绑服务都需传入一个 ServiceConnection 类的对象作为参。ServiceConnection 类有两个比较重要的回调方法∶onServiceConnected(ComponentName name,IBinder service)与onServiceDisconnected(ComponentName name),这两个方法中前者是当Actviy与 Service 绑定时的回调方法,后者是解绑时的回调方法。一般情况下我们会在前个方法中获取 IBinder类的对象,并通过该对象获取Service类的实例,然后对 Service进行操作,而在后一个方法中主要就是做一些清理性工作,比如销毁对象。
本实例中为了展现出 Service与Activity 真正在做通信,加入了一个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的值使用runOnUiThread(Thread thread)方法。有的读者可能会问,为何要在这个方法内修改TextView的值,而不是在 onDataChange(final String mesage)方法内直接修改。这涉及 Android 开发中一个原则,那就是普通线程不可以修改UI界面,只有UI线程也就是 Activity所在线程可以修改 UI界面。如果在普通线程中需要修改 UI界面了,那么只能使用 Android 中的消息处理机制,也就是我们常说的 Handler 机制。此处使用的runOnUiThread(Runnable 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即可获取。
yzw92 2021-10-22 06:15
curton 2021-10-21 21:10