通过前面的学习,可以发现使用 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