专栏首页指点的专栏Android Service基础

Android Service基础

Service 作为Android的四大组件之一,如果没听过Service,怎么能说能说自己是一个Android开发者呢,实际上,Service 在Android程序中扮演者不可或缺的角色,很多应用在从服务器获取数据、进行后台工作(播放音乐)是都是使用的 Serive 来完成,服务就相当于一个没有UI界面的Activity,作为Android的四大组件之一,我们先看一下服务的生命周期:

通过这张图,我们可以清楚的看到,服务的启动有两种方式,一种是通过调用 startService 方法,另一种是调用 bindService 方法,服务的结束可以由系统结束或者由我们调用方法来结束,根据服务的两个启动方法,我们也有两个对应的结束服务的方法 : stopService 方法和 unBindService 方法,下面我们用一个简单的例子来进一步理解服务的生命周期问题: 新建一个Android工程: activity_main.xm:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal"
    tools:context="com.example.administrator.blogservice.MainActivity">

    <Button
        android:id="@+id/startServiceButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开启服务" />
    <Button
        android:id="@+id/stopServiceButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="关闭服务"/>

</LinearLayout>

布局中两个按钮分别用于启动服务和关闭服务,Ok,那么我们既然要用到服务,那么自然我们要在 AndroidManifest.xml 文件中注册它了。我们新建一个类MyService,继承于Service类,并且在AndroidManifest.xml 文件中注册这个服务, 下面是MyService.java:

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

/**
 * Created by Administrator on 2017/3/2.
 */

public class MyService extends Service {

    private String TAG = "MyService";

    /*
     * 服务被创建的时候会调用这个方法
     */
    @Override
    public void onCreate() {
        Log.i(TAG, "onCreate");
    }

    /*
     * 服务被创建之后,这个方法会被立即调用,如果我们需要服务一经创建就执行某些功能,
     * 我们可以把要执行的功能的逻辑代码写在这个方法里面
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    /*
     * 这个方法当服务被绑定的时候会调用,这里我们先不用管
     */
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind");
        return new MyTask();
    }

    /*
     * 这个方法当服务被结束的时候(被系统杀死或者被主动结束)调用
     */
    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy");
    }



    private class MyTask extends Binder { // 自定义的内部类来完成对应功能

    }
}

在这个类中我们重写了父类Service类中的几个方法。并且在对应的方法中都打上了 Tag,这里我们注意到MyService 类中必须重写的一个方法是 onBind 方法,这个方法会在当前服务对象和 Activity 对象绑定的时候调用,但是在这里它不是主角,我们接下来先看看MainActivity.java:

import android.content.Intent;
import android.net.sip.SipAudioCall;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private Button button = null;
    private Intent intent = null;

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

        button = (Button) findViewById(R.id.startServiceButton);
        button.setOnClickListener(listener);
        button = (Button) findViewById(R.id.stopServiceButton);
        button.setOnClickListener(listener);
    }

    private View.OnClickListener listener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.startServiceButton:
                    intent = new Intent(MainActivity.this, MyService.class);
                    /*
                     *  这里调用了 startService 方法来启动一个服务,服务一旦启动,必须主动关闭,
                     *  否则的话只要当前进程一直在运行,服务就会一直处于执行状态,
                     *  所以当我们不需要使用服务的时候,我们应该及时停止服务,节省资源
                     */
                    startService(intent);
                    break;
                case R.id.stopServiceButton:
                    if(intent == null) {
                        intent = new Intent(MainActivity.this, MyService.class);
                    }
                    /*
                     * 调用 stopService 方法来停止一个服务,这个方法对应于 startService 方法启动的服务,
                     * 参数也是一个 Intent 对象
                     */
                    stopService(intent);
                    break;
            }
        }
    };

}

在主函数中,我们对两个按钮进行了单击事件监听,监听事件分别对应于开始服务和停止服务,好了,让我们来看看效果:

我们把LogCat中的信息清除,单击“开启服务”按钮:

我们看到,MyService 类中的 onCreate 方法和 onStartCommand 方法分别调用了一次。证明 onStartCommand 方法在服务创建之后就会立即被调用,我们再点击“开启服务”按钮试试:

我们可以看到,即使我们单击了两次“开启服务”按钮, MyService 中的 onCreate 方法仍然没有被调用。只是调用了 onStartCommand 方法,因为同一个服务只能存在一个实例,因此,当一个服务已经被创建的时候,再去创建这个服务只会调用这个服务的 onStartCommand 方法。接下来我们点击“关闭服务”按钮来结束服务:

OK, 服务成功的被销毁。

这里的服务,我们仔细思考后可以发现,当这个服务启动之后,我们是无法对其进行具体的操作的,什么意思呢,就是我们启动了这个服务之后,这个服务就去做自己的事情了,我们无法知道服务在做什么事情,或者是做的事情的进度是多少了,我们只能启动和结束这个服务,从某些方面来说,这显然是不合理的。那么怎么解决呢,还记得MyService 类中的 onBind 方法吗,我们之前说过,这个方法当当前服务和 Activity 绑定的时候会调用。OK,下面让我们来看看怎么通过这个方法来实现 Activity 和服务的绑定, 修改相关代码:

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal"
    tools:context="com.example.administrator.blogservice.MainActivity">

    <Button
        android:id="@+id/startServiceButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开启服务" />
    <Button
        android:id="@+id/stopServiceButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="关闭服务"/>
    <Button
        android:id="@+id/bindServiceButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定服务"/>
    <Button
        android:id="@+id/unBindServiceButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="解绑服务"/>

</LinearLayout>

在 activity_main.xml 布局文件中,我们增加了两个按钮用于绑定服务和解绑服务,接下来我们来看一下 MyService 类:

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

/**
 * Created by Administrator on 2017/3/2.
 */

public class MyService extends Service {

    private String TAG = "MyService";

    /*
     * 服务被创建的时候会调用这个方法
     */
    @Override
    public void onCreate() {
        Log.i(TAG, "onCreate");
    }

    /*
     * 服务被创建之后,这个方法会被立即调用,如果我们需要服务一经创建就执行某些功能,
     * 我们可以把要执行的功能的逻辑代码写在这个方法里面
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    /*
     * 这个方法是当服务和 Activity 绑定的时候会调用的方法,返回的是一个 IBinder 对象,
     * 一般我们会新建一个内部类继承于 Binder 类,一般我们会把要执行的逻辑放在这个
     * 当 onBinder 方法被调用的时候,我们就可以返回这个类的实例化对象,
     * 这里说一下 Binder 类实现了 IBinder 接口,因此 Binder 类的对象同时也是 IBinder 对象
     * 那么绑定这个 Service 的 Activity 就可以得到这个内部类的实例的引用
     * 之后我们可以在Activity 中可以通过得到的这个内部类的引用来获取我们正在做的事情的信息,
     * 并且可以通过这个内部类提供的方法加以控制,这样就实现了 Service 和绑定的 Activity 之间的通信
     */
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind");
        return new MyTask();
    }

    /*
     * 这个方法当服务被结束的时候(被系统杀死或者被主动结束)调用
     */
    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy");
    }

    /*
     * 这里我们新建了一个内部类,我们把我们想做的事情写在这个内部类中,
     * 并且一般我们会实现一些公用方法用于让绑定这个 Service 的 Activity 可以控制我们要做的事情
     */
    public class MyTask extends Binder { // 自定义的内部类来完成对应功能

    }
}

那么在这里 onBind 方法就是主角了。简单来说,这个方法在 Activity 和 Service 绑定的时候被调用,并且返回一个 IBinder 对象给 Activity, 之后 Activity 就可以通过这 IBinder 对象进行和绑定的服务之间的通信。最后我们来看一下 MainActivity.java:

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

public class MainActivity extends AppCompatActivity {

    private Button button = null;
    private Intent intent = null;
    private MyService.MyTask myBind = null;

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

        button = (Button) findViewById(R.id.startServiceButton);
        button.setOnClickListener(listener);
        button = (Button) findViewById(R.id.stopServiceButton);
        button.setOnClickListener(listener);
        button = (Button) findViewById(R.id.bindServiceButton);
        button.setOnClickListener(listener);
        button = (Button) findViewById(R.id.unBindServiceButton);
        button.setOnClickListener(listener);
    }

    /*
     * 新建一个 ServiceConnection 匿名类对象并且重写里面的抽象方法,
     * 用于当前 Activity 绑定服务和解绑服务调用
     */
    private ServiceConnection connection = new ServiceConnection() {
        // 这个方法会在 Activity 绑定服务成功的时候调用
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBind = (MyService.MyTask) service; // 获取 MyService 中 onBind 方法返回的 IBinder 对象
            Toast.makeText(MainActivity.this, "绑定服务" ,Toast.LENGTH_SHORT).show();
        }

        /*
         * 这个方法在服务被异常结束(被系统回收)的时候调用,
         * 当服务于 Activity 正常解绑的时候,这个方法是不会被调用的
         */
        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    private View.OnClickListener listener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.startServiceButton:
                    intent = new Intent(MainActivity.this, MyService.class);
                    /*
                     *  这里调用了 startService 方法来启动一个服务,服务一旦启动,必须主动关闭,
                     *  否则的话只要当前进程一直在运行,服务就会一直处于执行状态,
                     *  所以当我们不需要使用服务的时候,我们应该及时停止服务,节省资源
                     */
                    startService(intent);
                    break;
                case R.id.stopServiceButton:
                    if(intent == null) {
                        intent = new Intent(MainActivity.this, MyService.class);
                    }
                    /*
                     * 调用 stopService 方法来停止一个服务,这个方法对应于 startService 方法启动的服务,
                     * 参数也是一个 Intent 对象
                     */
                    stopService(intent);
                    break;
                case R.id.bindServiceButton:
                    onBindServie();
                    break;
                case R.id.unBindServiceButton:
                    onUnBindService();
                    break;
            }
        }
    };

    /*
     * 绑定服务的方法
     */
    private void onBindServie() {
        if(intent == null) {
            intent = new Intent(this, MyService.class);
        } else {
            intent.setClass(this, MyService.class);
        }
        // 调用bindService 方法,并且通过 Intent 对象绑定服务,第三个参数代表服务绑定成功的时候自动创建绑定成功的服务对象
        bindService(intent, connection, BIND_AUTO_CREATE);
    }

    /*
     * 解绑服务的方法
     */
    private void onUnBindService() {
        // 通过 unBinderService 方法来解绑服务,这个方法和 bindService  方法对应
        unbindService(connection);
    }

}

在 MainActivity 中我们新建了一个 ServiceConnection 匿名类对象,这个对象中的 onServiceConnected 方法在服务和当前 Activity 绑定成功的时候会被调用。最后,让我们来运行一下:

Nice,成功的显示出了绑定服务的 Toast 提示信息,并且LogCat 中的显示也证明 MyService 类中的 onBind 方法确实调用了。那么下面我们单击“解绑服务”按钮:

服务成功的被摧毁!

那么这里还有一个问题:如果同时调用了 startService 方法和 bindService 方法之后要怎么结束这个服务呢?我们还是通过实验来看:

我们可以看到,当我们同时点击了 “开启服务”按钮和“绑定服务”按钮的时候,我们必须要同时点击 “关闭服务”按钮和“解绑服务”按钮才能将服务摧毁,这样是因为这个服务被创建了两次吗,其实并不是,如果这个服务被创建了两次,那么应该打印两次 onCreate Log 信息,但是并没有。这里我们可以理解成开启服务的两个方法(startService、bindService)之间并不互通,一个开启方法对应一个结束方法,只有两个开启的方法对应的结束方法都被调用了才可以结束这个服务。

关于服务的用法这里介绍的只是入门级别的,要想对服务理解更深,日后还得多加学习。

如果博客中有什么不正确的地方,还请多多指点。 谢谢观看。。。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android中ContentProvider的用法

    在Android中,如果要将一个程序的数据共享给另一个程序,在之前的Android版本,我们可以设置Android文件和SharedPreferences操作的...

    指点
  • Android的ListView和RecyclerView的基本用法

    在Android 5.0 版本之前,为了方便的显示多行数据,形如QQ聊天信息主界面,最常用的选择无非是ListView控件,但是ListView控件本身就有很大...

    指点
  • Android中的通知和自定义通知布局

    Android中的通知(Notification)是Android中的重要一部分,应用程序通过通知来提醒用户或者向用户传达信息,下面让我们来看一下怎么在我们的程...

    指点
  • Json概述以及python对json的相关操作

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScr...

    bear_fish
  • Spring Cloud学习(1)——单节点Eureka Server

    服务治理可以说是微服务架构中最为核心和基础的模块,主要用来实现各个微服务实例的自动化注册与发现。

    胡了了
  • mView has no focus+DecorView setVisiblity: visibility = 4+Finishing stop of ActivityRecord

    09-24 18:22:23.692: E/AndroidRuntime(22703): FATAL EXCEPTION: main 09-24 18:22...

    wust小吴
  • 安卓入门-第三章-安卓常用控件的使用方式

     TextView可以说是Android中最简单的一个控件了,你在前面其实已经和它打过一些交道了。它主要用于在界面上显示一段文本信息,比如你在第1章看到的“He...

    Fisherman渔夫
  • 采纳运行在Kubernetes上的Istio服务网格的利弊分析

    IT 团队能否只使用一种工具,使开发人员能够专注于编写应用程序代码,使管理员只专注于 IT 资源的管理?使用 Istio 可以实现,尽管如此,采纳 Istio ...

    CNCF
  • S006SELinux(SEAndroid)是个什么呀

    SEAndroid 是一套安全机制,实现的主要目的是为了是Android系统更安全。 SELinux是被设计为一个灵活的可配置的MAC机制。 SEAndro...

    上善若水.夏
  • 【JS】512- JS 自定义事件如此简单!

    在前端开发世界中,JavaScript 和 HTML 之间往往通过 事件 来实现交互。其中多数为内置事件,本文主要介绍 JS自定义事件概念和实现方式,并结合案例...

    pingan8787

扫码关注云+社区

领取腾讯云代金券