Android Service 详解(上)

相信大家对 Service 这个单词并不陌生,每一个程序员在开发中或多或少会接触 Service。Service 作为 Android 四大组件在 Android 有着举足轻重的作用。Service 主要用来在后台处理一些耗时任务,或者长时间执行的任务,有时甚至在程序退出的情况下,让 Service 继续在后台运行执行任务。

不过,虽然 Service 在工作中被大家广泛使用,但并不是所有人都对 Service 的各个知识点掌握的非常透彻。那么今天就带着大家对 Service 进行一次全面深入的分析,希望大家在阅读本篇文章之后,能对 Service 有个更深刻的理解。

Service 的基本用法

关于 Service 的基本用法那就是如何启动 Service,启动 Service 和启动 Activity 一样都是通过 Intent 来实现,下面就通过一个简单的例子来演示一下。

本文选择 Android7.1 的API,先新建一个工程,取名 ServiceTest。

我们自定义 MyService 继承自 Service,重写 onCreate, onStartCommand,onBind,onDestroy 方法。代码如下:

public class MyService extends Service {

    private static final String TAG = "MyService";
    @Override
    public void onCreate() {
        super.onCreate();
        Log.w(TAG, "onCreate() called");
    }

    @Override
    public int onStartCommand(Intent intent ,int flags, int startId) {
        Log.w(TAG, "onStartCommand() called with: intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]");
        return super.onStartCommand(intent, flags, startId);

    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.w(TAG, "onBind() called with: intent = [" + intent + "]");
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.w(TAG, "onDestroy() called");
    }
}

可以看到,我们只在 onCreate(),onStartCommand(),onBind(),onDestroy() 方法中,并没有在什么逻辑操作,仅仅增加了一句打印。

新建一个 activity_main 作为程序的主布局文件,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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"
    tools:context="com.example.bradley.myapplication.MainActivity">

    <Button
        android:id="@+id/btn_start_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开启Service"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="28dp" />

    <Button
        android:id="@+id/btn_stop_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="停止Service"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginRight="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="25dp"
        app:layout_constraintTop_toBottomOf="@+id/btn_start_service" />
</android.support.constraint.ConstraintLayout>

在布局文件中,放置两个 Button,一个 Button 用于启动 Service,一个 Button 用于停止 Service。

新建 MainActivity 继承 AppCompatActivity 作为程序的入口 Activity,在里面添加启动 Service 和 停止Service 的逻辑,代码如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btnStart = (Button) findViewById(R.id.btn_start_service);
        Button btnStop = (Button) findViewById(R.id.btn_stop_service);
        btnStart.setOnClickListener(this);
        btnStop.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_start_service:
                Intent intent = new Intent(this,MyService.class);
                startService(intent);
                break;
            case R.id.btn_stop_service:
                Intent intent2 = new Intent(this,MyService.class);
                stopService(intent2);
                break;
        }
    }
}

通过代码可以看到,在 btn_start_service 按钮点击事件里,我们构建了一个 Intent 用于启动 Service,同理,在 btn_stop_service 按钮点击事件里,也构建了一个 Intent 用于停止 Service,代码逻辑比较简单,不再赘述。

我们知道,Activity 和 Service 作为 Android 四大组件是需要在 AndroidManifest.xml 注册才能使用,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.bradley.myapplication">

    <application android:allowBackup="true" android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true" android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".MyService"/>
    </application>

</manifest> 

OK,至此,一个简单的 service 启动程序就写完了,将程序运行起来,点击 btn_start_service ,观察 log 如下:

7-07 00:02:22.290 8870-8870/com.example.bradley.myapplication W/MyService: onCreate() called
07-07 00:02:22.290 8870-8870/com.example.bradley.myapplication W/MyService: onStartCommand() called with: intent = [Intent { cmp=com.example.bradley.myapplication/.MyService }], flags = [0], startId = [1]

通过 log 可以看到,启动 Service 时,系统会回调 Service 的 onCreate 和 onStartCommand 方法。

如果我们再次点击 btn_start_service 会怎样呢?

8870-8870/com.example.bradley.myapplication W/MyService: onStartCommand() called with: intent = [Intent { cmp=com.example.bradley.myapplication/.MyService }], flags = [0], startId = [2]

可以看到,再次点击 btn_start_service 按钮,此时只有 onStartCommand 会被调用,而 onCreate 并不会被调用,为什么会这样呢?因为 onCreate 只有在 Service 第一次创建时才会被调用,如果当前 Service 已经存在了,不管 startService 调用多少次,onCreate 都不会被调用。

Activity 与 Service 进行通信

上面我们学习了启动Service,在启动 Service 之后,就可以让 Service 在 onCreate 或 onStartCommand 执行一些任务,不过此时 Activity 与 Service 关系并不是很大,只是 Activity 通知 Service “你可以启动了”,那有没有什么办法,可以让 Activity 指定 Service 做某些任务呢?

在 MyService 方法中,有一个 onBind 方法并没有被使用,而它就是和 Activity 建立关联的,修改代码如下:

package com.example.bradley.myapplication;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * Created by qianweili on 2018/7/26.
 */

public class MyService extends Service {

    private static final String TAG = "MyService";
    @Override
    public void onCreate() {
        super.onCreate();
        Log.w(TAG, "onCreate() called");
    }

    @Override
    public int onStartCommand(Intent intent ,int flags, int startId) {
        Log.w(TAG, "onStartCommand() called with: intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]");
        return super.onStartCommand(intent, flags, startId);

    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.w(TAG, "onBind() called with: intent = [" + intent + "]");
        return new Mybind();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.w(TAG, "onDestroy() called");
    }

    class Mybind extends Binder{
        void hello(){
            Log.w(TAG, "hello() called");
        }
    }
}

在代码中,我们新建了一个内部类 Mybind 继承自 Binder,在 Mybind 仅仅增加一个 hello 函数,因为只是做调试用,所以在里面并没有执行什么任务,仅仅打印了一个 log 而已。

我们继续修改 activity_main.xml 文件,在里面添加 bindService 和 unBindService 的按钮,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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"
    tools:context="com.example.bradley.myapplication.MainActivity">

    <Button
        android:id="@+id/btn_start_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开启Service"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="28dp" />

    <Button
        android:id="@+id/btn_stop_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="停止Service"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginRight="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="25dp"
        app:layout_constraintTop_toBottomOf="@+id/btn_start_service" />

    <Button
        android:id="@+id/btn_bind_seervice"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定 Service"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginTop="25dp"
        app:layout_constraintTop_toBottomOf="@+id/btn_stop_service" />

    <Button
        android:id="@+id/btn_unbind_seervice"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="解除绑定"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginTop="25dp"
        app:layout_constraintTop_toBottomOf="@+id/btn_bind_seervice" />
</android.support.constraint.ConstraintLayout>

接下来继续修改 MainActivity 代码,让 Activity 和 Service 建立关联,代码如下:

package com.example.bradley.myapplication;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import static com.example.bradley.myapplication.R.id.btn_start_service;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btnStart = (Button) findViewById(R.id.btn_start_service);
        Button btnStop = (Button) findViewById(R.id.btn_stop_service);
        Button btnBind = (Button) findViewById(R.id.btn_bind_seervice);
        Button btnUnbind = (Button) findViewById(R.id.btn_unbind_seervice);
        btnStart.setOnClickListener(this);
        btnStop.setOnClickListener(this);
        btnBind.setOnClickListener(this);
        btnUnbind.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_start_service:
                Intent intent = new Intent(this,MyService.class);
                startService(intent);
                break;
            case R.id.btn_stop_service:
                Intent intent2 = new Intent(this,MyService.class);
                stopService(intent2);
                break;
            case R.id.btn_bind_seervice:
                Intent intent3 = new Intent(this,MyService.class);
                bindService(intent3,mConn, Context.BIND_AUTO_CREATE);
                break;
            case R.id.btn_unbind_seervice:
                Intent intent4 = new Intent(this,MyService.class);
                unbindService(mConn);
                break;
        }
    }

    private ServiceConnection mConn = new ServiceConnection(){

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.w(TAG, "onServiceConnected() called with: name = [" + name + "], service = [" + service + "]");
            MyService.Mybind myBind = ( MyService.Mybind)service;
            myBind.hello();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.w(TAG, "onServiceDisconnected() called with: name = [" + name + "]");
        }
    };
}

在代码中,我们创建了一个类型为 ServiceConnection 的变量,重写了 onServiceConnected 和 onServiceDisconnected 方法,onServiceConnected 指在 Activity 与 Service 建立关联之后调用,在方法里,我们将传递传来的 IBinder 向下转型为 MyService.Mybind ,此时 Activity 就可以通过调用 myBind 里的 public 方法来指定 Service 做任何任务。

让我们运行一下程序,点击 btn_bind_seervice 按钮,log如下:

07-07 00:53:09.010 18926-18926/com.example.bradley.myapplication W/MyService: onCreate() called
07-07 00:53:09.010 18926-18926/com.example.bradley.myapplication W/MyService: onBind() called with: intent = [Intent { cmp=com.example.bradley.myapplication/.MyService }]
07-07 00:53:09.030 18926-18926/com.example.bradley.myapplication W/MainActivity: onServiceConnected() called with: name = [ComponentInfo{com.example.bradley.myapplication/com.example.bradley.myapplication.MyService}], service = [com.example.bradley.myapplication.MyService$Mybind@4186bf50]
07-07 00:53:09.030 18926-18926/com.example.bradley.myapplication W/MyService: hello() called

销毁 Service

在这一部分,我们通过 startService 启动 Service,再通过 stopService 销毁 Service ,log如下:

07-07 00:57:09.840 5580-5580/com.example.bradley.myapplication W/MyService: onCreate() called
07-07 00:57:09.850 5580-5580/com.example.bradley.myapplication W/MyService: onStartCommand() called with: intent = [Intent { cmp=com.example.bradley.myapplication/.MyService }], flags = [0], startId = [1]
07-07 00:57:12.500 5580-5580/com.example.bradley.myapplication W/MyService: onDestroy() called

如果我们是通过 bindService 启动的service呢?很简单,只需要调用 unbindService 解除绑定就可以了,log 如下:

07-07 00:58:06.610 5580-5580/com.example.bradley.myapplication W/MyService: onCreate() called
07-07 00:58:06.610 5580-5580/com.example.bradley.myapplication W/MyService: onBind() called with: intent = [Intent { cmp=com.example.bradley.myapplication/.MyService }]
07-07 00:58:06.610 5580-5580/com.example.bradley.myapplication W/MainActivity: onServiceConnected() called with: name = [ComponentInfo{com.example.bradley.myapplication/com.example.bradley.myapplication.MyService}], service = [com.example.bradley.myapplication.MyService$Mybind@41872ae0]
07-07 00:58:06.610 5580-5580/com.example.bradley.myapplication W/MyService: hello() called
07-07 00:58:09.020 5580-5580/com.example.bradley.myapplication W/MyService: onDestroy() called

如果我们既通过 startService 启动 Service,又通过 bindService 绑定 Service,那又该这样销毁 Service呢?可以发现,无论我们怎么单独点击 stopService 或者 unbindService 都不会销毁 Service,只有同时点击了 stopService 和 unbindService 才能销毁 Service。也就是说,stopService 只会让 Service 停止,unbindService 只能解除 Activity 和 Service 的关联,一个 Service 只有和任何 Activity 没有关联并且处于停止状态才能被销毁。

07-07 01:04:30.800 7368-7368/com.example.bradley.myapplication W/MainActivity: 启动 Service
07-07 01:04:30.810 7368-7368/com.example.bradley.myapplication W/MyService: onCreate() called
07-07 01:04:30.820 7368-7368/com.example.bradley.myapplication W/MyService: onStartCommand() called with: intent = [Intent { cmp=com.example.bradley.myapplication/.MyService }], flags = [0], startId = [1]
07-07 01:04:33.400 7368-7368/com.example.bradley.myapplication W/MainActivity: 绑定 Service
07-07 01:04:33.400 7368-7368/com.example.bradley.myapplication W/MyService: onBind() called with: intent = [Intent { cmp=com.example.bradley.myapplication/.MyService }]
07-07 01:04:33.400 7368-7368/com.example.bradley.myapplication W/MainActivity: onServiceConnected() called with: name = [ComponentInfo{com.example.bradley.myapplication/com.example.bradley.myapplication.MyService}], service = [com.example.bradley.myapplication.MyService$Mybind@4186f2d0]
07-07 01:04:33.400 7368-7368/com.example.bradley.myapplication W/MyService: hello() called
07-07 01:04:44.040 7368-7368/com.example.bradley.myapplication W/MainActivity: 停止 Service
07-07 01:04:45.230 7368-7368/com.example.bradley.myapplication W/MainActivity: 解除绑定 Service
07-07 01:04:45.240 7368-7368/com.example.bradley.myapplication W/MyService: onDestroy() called

切记,在 Service 里的 onDestroy 方法里清理掉那些不再使用的资源,防止 Service 被销毁后还有一些不再使用的资源占用着内存,浪费资源。

猜你喜欢

转载自blog.csdn.net/liqianwei1230/article/details/81222942