Android Studio 学习记录-操作后台服务

  • Post author:
  • Post category:其他



目录


服务的启动和停止


服务的绑定与解绑


活动与服务之间的交互


本文介绍了Android四大组件之一Service的基本概念和常见用法。包括服务的生命周期及其两种启停方式——普通方式和绑定方式(含立即绑定和延迟绑定),还介绍了如何在活动和服务之间交互数据。

服务的启动和停止

既然Android自带了系统服务,App也可以拥有自己的服务。Service与Activity相比,不同之处在于没有对应的页面,相同之处在于都有生命周期。想要用好服务,就要弄清楚它的生命周期。

Service与生命周期有关的说明如下:

  • onCreate:创建服务。
  • onStart:开始服务,Android 2.0以下版本使用,现已废弃。
  • onStartCommand:开始服务,Android 2.0及以上版本使用。该方法的返回值说明见表
服务启动的返回值说明
返回值类型 返回值说明
START_STICKY 黏性的服务。如果服务进程被杀掉,就保留服务的状态为开始状态,但不保留传送的Intent对象。随后系统尝试重新创建服务,由于服务状态为开始状态,因此创建服务后一定会调用onStartCommand方法。如果在此期间没有任何启动命令传送给服务器,参数Intent就为空值
START_NOT_STICKY 非黏性服务。使用这个返回值时,如果服务被异常杀掉,系统就不会自动重启该服务
START_REDELIEVER_INTENT 重传Intent的服务。使用这个返回值时,如果服务被异常杀掉,系统就会自动重启该服务,并传入Intent的原值
START_STICKY_COMPATIBILITY START_STICKY的兼容版本,但不保证服务被杀掉后一定能重启
  • onDestroy:销毁服务。
  • onBind:绑定服务。
  • onUnbind:解除绑定。返回值为true表示允许再次绑定,之后在绑定服务时,不会调用onBind方法而是调用onRebind方法;返回值为false表示只能绑定一次,不能再次绑定。
  • onRebind:重新绑定。只有上次的onUnbind方法返回true时,再次绑定服务才会调用onRebind方法。

服务的生命周期也存在好几个环节,除了必须的onCreate方法和onDestroy方法,还有其他几种生命周期方法。接下来已普通启停为例,讲解服务的生命周期过程。

首先在Java代码包下面创建名为service的新包,右击该包并在右键菜单中依次选择New→Service→Service,弹出下示服务创建对话框。

在服务创建对话框的Class Name一栏填写服务名称,比如NormalService ,再点击Finish按钮,Android Studio便自动再service包下生成NormalService .java,同时在AndroidManifest.xml的application节点内部添加如下的服务注册配置:

        <service
            android:name=".service.NormalService"
            android:enabled="true"
            android:exported="true" />

打开NormalService .java,为了方便观察服务的生命周期过程,需要重写该服务的所有周期方法,给每个方法都打印相应的运行日志。

public class NormalService extends Service {
    private static final String TAG = "NormalService";

    private void refresh(String text) {
        Log.d(TAG, text);
        Service1.showText(text);
    }

    @Override
    public void onCreate() { // 创建服务
        super.onCreate();
        refresh("onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startid) { // 启动服务,Android2.0以上使用
        Log.d(TAG, "测试服务到此一游!");
        refresh("onStartCommand. flags=" + flags);
        return START_STICKY;
    }

    @Override
    public void onDestroy() { // 销毁服务
        super.onDestroy();
        refresh("onDestroy");
    }

    @Override
    public IBinder onBind(Intent intent) { // 绑定服务。普通服务不存在绑定和解绑流程
        refresh("onBind");
        return null;
    }

    @Override
    public void onRebind(Intent intent) { // 重新绑定服务
        super.onRebind(intent);
        refresh("onRebind");
    }

    @Override
    public boolean onUnbind(Intent intent) { // 解绑服务
        refresh("onUnbind");
        return true; // 返回false表示只能绑定一次,返回true表示允许多次绑定
    }
}

启停服务很简单,只要创建一个指向服务的意图,然后调用startService方法即可启动服务,若要停止服务,调用stopService方法即可停止指定意图的服务。

//创建一个通往普通服务的意图
mIntent = new Intent(this, NormalService.class);

//启动指定意图的服务
 startService(mIntent);

//停止指定意图的服务
stopService(mIntent);

服务的绑定与解绑

Android还提供了另一种启停方式,也就是绑定服务和解绑服务。对于服务来说,要求提供黏合剂Binder指定服务的绑定关系,同时黏合剂还负责再两个组件或者在两个进程之间的交流通信。此时增加了黏合剂的服务代码示例如下:

public class BindImmediateService extends Service {
    private static final String TAG = "BindImmediateService";
    private final IBinder mBinder = new LocalBinder(); // 创建一个粘合剂对象

    // 定义一个当前服务的粘合剂,用于将该服务黏到活动页面的进程中
    public class LocalBinder extends Binder {
        public BindImmediateService getService() {
            return BindImmediateService.this;
        }
    }

    private void refresh(String text) {
        Log.d(TAG, text);
        Service2.showText(text);
    }

    @Override
    public void onCreate() { // 创建服务
        super.onCreate();
        refresh("onCreate");
    }

    @Override
    public void onDestroy() { // 销毁服务
        super.onDestroy();
        refresh("onDestroy");
    }

    @Override
    public IBinder onBind(Intent intent) { // 绑定服务。返回该服务的粘合剂对象
        Log.d(TAG, "绑定服务开始旅程!");
        refresh("onBind");
        return mBinder;
    }

    @Override
    public void onRebind(Intent intent) { // 重新绑定服务
        super.onRebind(intent);
        refresh("onRebind");
    }

    @Override
    public boolean onUnbind(Intent intent) { // 解绑服务
        Log.d(TAG, "绑定服务结束旅程!");
        refresh("onUnbind");
        return true; // 返回false表示只能绑定一次,返回true表示允许多次绑定
    }
}

对于绑定了黏合剂的服务,它的绑定和解绑操作与普通方式不同:首先要定义一个ServiceConnection的服务连接对象,然后调用bindService方法绑定服务,绑定之后再择机调用unbindService方法解绑服务,具体的活动代码示例如下:

public class Service2 extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "BindImmediateActivity";
    private static TextView tv_immediate; // 声明一个文本视图对象
    private Intent mIntent; // 声明一个意图对象
    private static String mDesc; // 日志描述

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_service2);
        tv_immediate = findViewById(R.id.tv_result);
        findViewById(R.id.btn_start_bind).setOnClickListener(this);
        findViewById(R.id.btn_unbind).setOnClickListener(this);
        mDesc = "";
        // 创建一个通往立即绑定服务的意图
        mIntent = new Intent(this, BindImmediateService.class);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_start_bind) { // 点击了绑定服务按钮
            // 绑定服务。如果服务未启动,则系统先启动该服务再进行绑定
            boolean bindFlag = bindService(mIntent, mServiceConn, Context.BIND_AUTO_CREATE);
            Log.d(TAG, "bindFlag=" + bindFlag);
        } else if (v.getId() == R.id.btn_unbind) { // 点击了解绑服务按钮
            if (mBindService != null) {
                // 解绑服务。如果先前服务立即绑定,则此时解绑之后自动停止服务
                unbindService(mServiceConn);
            }
        }
    }

    public static void showText(String desc) {
        if (tv_immediate != null) {
            mDesc = String.format("%s%s %s\n", mDesc, DateUtil.getNowTime(), desc);
            tv_immediate.setText(mDesc);
        }
    }

    private BindImmediateService mBindService; // 声明一个服务对象
    private ServiceConnection mServiceConn = new ServiceConnection() {
        // 获取服务对象时的操作
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 如果服务运行于另外一个进程,则不能直接强制转换类型,否则会报错
            mBindService = ((BindImmediateService.LocalBinder) service).getService();
            Log.d(TAG, "onServiceConnected");
        }

        // 无法获取到服务对象时的操作
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBindService = null;
            Log.d(TAG, "onServiceDisconnected");
        }
    };
}

为了更好的说明绑定方式的优势,Android还提供了另一种延迟绑定。延迟绑定与立即绑定的区别在于:延迟绑定要先通过startService方法启动服务,再通过bindService方法绑定已存在服务;同理延迟解绑要先通过unbindService方法解绑服务,再通过stopService方法停止服务。这样一来因为启动操作在先、绑定操作在后,所以解绑操作只能撤销绑定操作,而不能撤销启动操作。由于解绑操作不能销毁服务,因此存在再次绑定的可能。

从上面两个日志界面可知,延迟绑定和立即绑定这两种方式的生命周期区别在于:

  1. 延迟绑定的首次绑定操作只触发onBind方法,再次绑定操纵只触发onRebind方法(是否允许再次绑定要看上次onUnbind方法的返回值)。
  2. 延迟绑定的解绑操作只触发onUnbind方法。

活动与服务之间的交互

若想及时获取服务的运行情况,活动就得主动打探消息,此时需要有个信使承担消息传输的任务,这个信使便是绑定方式用到的服务黏合剂——IBinder。

由于IBinder是个接口,它的实现类名叫Binder,因此每个服务的黏合剂都得从Binder派生而来。除了定义getService方法返回当前的服务对象之外,黏合剂还可以定义一般的数据交互方法,用于同活动代码往来通信。

public class DataService extends Service {
    private static final String TAG = "DataService";
    private final IBinder mBinder = new LocalBinder(); // 创建一个粘合剂对象
    // 定义一个当前服务的粘合剂,用于将该服务黏到活动页面的进程中
    public class LocalBinder extends Binder {
        public DataService getService() {
            return DataService.this;
        }

        // 获取数字描述
        public String getNumber(int number) {
            return "我收到了数字"+number;
        }
    }

    @Override
    public IBinder onBind(Intent intent) { // 绑定服务。返回该服务的粘合剂对象
        Log.d(TAG, "绑定服务开始旅程!");
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) { // 解绑服务
        Log.d(TAG, "绑定服务结束旅程!");
        return true; // 返回false表示只能绑定一次,返回true表示允许多次绑定
    }

活动代码在调用bindService方法时,第二个参数为ServiceConnection类型,表示绑定结果的连接对象。这个连接对象来自接口ServiceConnection,它的onServiceConnected方法在连接成功时回调,onServiceDisconnected方法在连接断开时回调。重写ServiceConnection的onServiceConnected方法,即可拿到已绑定服务的黏合剂对象。有了服务的黏合剂,才能通过黏合剂获取服务的内部情况。

  private DataService.LocalBinder mBinder; // 声明一个粘合剂对象
    private ServiceConnection mServiceConn = new ServiceConnection() {
        // 获取服务对象时的操作
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 如果服务运行于另外一个进程,则不能直接强制转换类型,否则会报错
            mBinder = (DataService.LocalBinder) service;
            // 活动代码通过粘合剂与服务代码通信
            String response = mBinder.getNumber(new Random().nextInt(100));
            tv_result.setText(DateUtil.getNowTime()+" 绑定服务应答:"+response);
            Log.d(TAG, "onServiceConnected");
        }

        // 无法获取到服务对象时的操作
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBinder = null;
            Log.d(TAG, "onServiceDisconnected");
        }
    };



版权声明:本文为demon_dog原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。