Github:https://github.com/MADMAX110/Odometer
启动式服务对于后台操作很合适,不过需要一个更有交互性的服务。
接下来构建这样一个应用:
1、创建一个绑定式服务的基本版本,名为OdometerService
我们要为它增加一个方法getDistance(),这个方法会返回一个随机数
2、让活动MainActivity绑定到OdometerService,并调用它的getDistance方法。
将每秒调用一次这个方法,在MainActivity中用得到的结果更新一个文本视图。
3、更新OdometerService,改为使用Android的位置服务。
这个服务将得到用户当前位置的更新,并使用这些更新计算所走过的距离。
要创建一个绑定式服务,需要扩展Service类。
如图所示,在com.hfad.odometer中创建一个服务。
不要选中Exported,因为只有当希望这个应用之外的服务能够访问这个服务时才需要将它设置为true。
确保选中Enabled,不选中的话活动将无法运行应用。
package com.hafd.odometer;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class OdometerService extends Service {
@Override
public IBinder onBind(Intent intent) {
}
}
上面的代码实现了一个方法onBind,当一个组件希望绑定到这个服务时,会调用这个方法。这个方法有一个参数Intent,并返回一个IBinder对象。
IBinder是一个接口,用来将你的服务绑定到活动,需要在服务代码中提供它的一个实现。
定义绑定器:
public class OdometerBinder extends Binder {
OdometerService getOdometer() {
return OdometerService.this;
}
}
更新OdometerService.java代码并增加一个getDistance方法
package com.hafd.odometer;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import java.util.Random;
public class OdometerService extends Service {
private final IBinder binder = new OdometerBinder();
private final Random random = new Random();
//创建一个绑定式服务时,需要提供一个Binder实现
public class OdometerBinder extends Binder {
//活动将使用这个方法得到OdometerService的一个引用
OdometerService getOdometer() {
return OdometerService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public double getDistance() {
return random.nextDouble();
}
}
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/distance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="48sp"
android:layout_gravity="center_horizontal"
android:textAppearance="?android:attr/textAppearanceLarge" />
LinearLayout>
ServiceConnection是一个接口,允许将活动绑定到一个服务。
它有两个方法需要你定义:
onServiceConnected():建立与服务的连接时会调用
onServiceDisconnected():断开服务连接时会调用
更新MainActivity.java
package com.hafd.odometer;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
public class MainActivity extends AppCompatActivity {
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
在活动和服务之间建立连接时会调用这个方法。
有两个参数:一个ComponentName对象和一个IBinder对象,ComponentName对象描述所要连接的服务,IBinder对象由服务定义。
我们使用onServiceConnected方法做两件事:
1、使用它的IBinder参数得到所连接服务的一个引用,在这里就是OdometerService的引用。为此可以将IBinder强制转换为一个OdometerService.OdometerBinder,并调用它的getOdometer方法。
2、记录活动绑定到这个服务。
private OdometerService odometer;
private boolean bound = false;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
OdometerService.OdometerBinder odometerBinder = (OdometerService.OdometerBinder) iBinder;
odometer = odometerBinder.getOdometer(); //使用IBinder得到服务的一个引用
bound = true;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
bound = false;
}
};
记录活动不再绑定到服务。
public void onServiceDisconnected(ComponentName componentName) {
bound = false;
}
通常会在两个地方将活动绑定到服务:
活动变得可见时,在活动的onStart方法中,如果只需要在活动可见时与活动交互,在这个方法中绑定服务就很合适。
活动创建时,在活动的onCreate方法中,如果需要从服务接受更新,即使活动已经停止也要继续接受更新,就要在这个方法中绑定服务。
在这里我们只需要在MainActivity可见时显示从OdometerService得到的更新,所以要在活动的onStart方法中绑定服务。
要绑定这个服务首先要创建一个显式意图,指定想要绑定的服务。然后使用活动的bindService方法绑定服务,传入这个意图,服务定义的ServiceConnection对象以及描述希望如何绑定的一个标志。
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, OdometerService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
调用OdometerService的getDistance方法
一旦活动绑定到服务,我们要每秒调用一次OdometerService的getDistance方法,并将其值更新MainActivity的文本视图。
为MainActivity增加一个displayDistance方法:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
displayDistance();
}
private void displayDistance() {
final TextView distanceView = (TextView) findViewById(R.id.distance);
final Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
double distance = 0.0;
//如果得到OdometerService的一个引用,而且绑定到这个服务,则调用getDistance。
if (bound && odometer != null)distance = odometer.getDistance();
String distanceStr = String.format(Locale.getDefault(), "%1$,.2f miles", distance);
distanceView.setText(distanceStr);
//每秒运行一次
handler.postDelayed(this, 1000);
}
});
}
将活动与服务解除绑定时,通常会把相应的代码增加到活动的onStop或onDestroy方法中。使用哪一个方法取决于你的bindService代码放在哪里:
如果在活动的onStart方法中绑定服务,则在onStop方法中解除绑定。
如果在onCreate方法中绑定服务,则在onDestroy方法中解除绑定。
所以这里在onStop方法与服务解除绑定。这个方法有一个参数,即ServiceConnection对象。需要向MainActivity增加以下代码:
protected void onStop() {
super.onStop();
if (bound) {
unbindService(connection);
bound = false;
}
}
运行这个应用时会在MainActivity显示一个随机数,这个数每秒改变一次。
1、服务创建。
2、onCreate(): 当服务第一次被创建时执行。这个方法可以在服务创建时进行一些初始化操作。
3、onBind(): 当其他组件通过调用bindService()方法与服务绑定时执行。在这个方法中,服务会返回一个IBinder接口实例,客户端可以通过这个接口与服务进行通信。
4、服务绑定。
5、onUnbind(): 当其他组件通过调用unbindService()方法与服务解绑时执行。在这个方法中,服务可以执行一些清理操作,例如停止在后台运行的任务等。
6、onDestroy(): 当服务不再被使用或被销毁时执行。注意,如果服务同时被启动和绑定,那么即使所有的客户端都解绑了服务,服务也不会被销毁,除非服务用stopSelf()方法或其他组件调用stopService()方法来终止自己。
7、服务撤销。
总的来说,绑定式服务的生命周期与启动式服务的生命周期略有不同,主要区别在于绑定式服务需要与其他组件进行绑定和解绑操作,并且服务的生命周期会受到这些操作的影响。