•概念

  Service(服务)是一个长期运行在后台,没有用户界面的应用组件,即使切换到另一个应用程序或者后台,服务也可以正常运行;

  因此,服务适合执行一些不需要显示界面的后台耗时操作,比如下载网络数据,播放音乐等。

•定义一个服务

  新建一个 ServiceTest 项目,然后右击  com.example.servicetest->New->Service->Service ;

  会弹出如下图所示的窗口:

  可以看到,这里我们将服务命名为 MyService(由于我之前创建过,所以左下角提示名字重复):

  • exported :表示是否允许除了当前程序之外的其他程序访问这个服务

    • 如果设置为 true,则能够被调用或交互,通常如果一个服务需要跨进程使用需要这么设置,否则不能
    • 设置为 false 时,只有同一个应用程序的组件或带有相同用户 ID 的应用程序才能启动或绑定该服务
  • enabled :指该服务是否能够被实例化

    • 如果设置为 true,则能够被实例化,否则不能被实例化,默认值是 true
    • 一般情况下,我们都会需要实例化,所以也可以选择不设置

  需要注意的是,每一个服务都需要在 AndroidManifest.xml 文件中进行注册,

  因为我们是通过 Android Studio New 的 Service ,

  所以 Android Studio 默认帮我们在清单文件中添加对该 Service 的注册;

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.servicetest"> <application
......> <service
android:name=".MyService"
android:enabled="true"
android:exported="true" /> <activity android:name=".MainActivity">
......
</activity> </application> </manifest>

  现在观察 MyService 中的代码,如下所示:

public class MyService2 extends Service {
public MyService2() {
} @Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}

  可以看到,MyService 是继承自 Service 类的,说明这是一个服务;

  目前,MyService 中可以算是空空如也,但有一个  onBind()  方法特别醒目,

  这个方法是 Service 中唯一的一个抽象方法,所以必须要在子类里实现,后面会提及该方法的用法;

  既然是定义一个服务,自然应该在服务中去处理一些事情,这时就要重写 Service 中的另外一些方法了;

MyService.java

public class MyService extends Service {

    public final static String TAG = "MyService";
public MyService() {
} @Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
} @Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: ");
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
} @Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
}
}

  可以看到,在该代码中重写了  onCreate(),onStartCommand() , onDestroy()  方法,并分别在它们的方法体中打印Log日志;

•启动和停止服务

  启动和停止服务主要是借助 Intent 来实现的,下面就让我们在 ServiceTest 项目中尝试去启动以及停止 MyService;

  首先修改 activity_main.xml 中的代码;

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp"> <Button
android:id="@+id/start_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Service"
android:textAllCaps="false"
/> <Button
android:id="@+id/stop_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stop Service"
android:textAllCaps="false"
/> </LinearLayout>

  在该代码中,仅仅添加了两个按钮,分别是用于启动服务和停止服务的;

  然后,修改 MainActivity.java 中的代码;

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private Button startService;
private Button stopService; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); startService = findViewById(R.id.start_service);
startService.setOnClickListener(this); stopService = findViewById(R.id.stop_service);
stopService.setOnClickListener(this); } @Override
public void onClick(View v) {
Intent intent = new Intent(this,MyService.class);
switch(v.getId()){
case R.id.start_service:
startService(intent);
break;
case R.id.stop_service:
stopService(intent);
break;
default:
break;
}
}
}

  可以看到,这里在  onCreate() 方法中分别获取到了 startService 按钮 和 stopService 按钮 的实例,

  并给他们注册了点击事件;

  然后在点击事件里构建出了一个 Intent 对象,并通过调用  startService(intent) 方法来启动 MyService 服务,

  调用  stopService(intent) 来停止 MyService 服务;

   startService() 和 stopService() 方法都是定义在 Context 类中的,所以我们在活动里可以直接调用这两个方法。

运行效果

  通过观察日志文件可以看到:

  • onCreate() :服务第一次被创建的时候调用,只执行一次
  • onStartCommand() :每次服务启动的时候调用
    • 在第二次点击 Start Service 按钮的时候,只打印了 onStartCommand: 语句
    • 如果我们希望服务一旦启动就立刻去执行某个动作,就可以将逻辑写在 onStartCommand() 中
  • onDestory() :服务被销毁的时候调用,只执行一次

  需要注意的是,如果在活动中只点击了 startService 按钮,而没有点击 stopService 按钮,那么服务会一直处于运行状态,

  如果想要 MyService 服务停止运行,除了点击 stopService 按钮外,

  还可以通过在 MyService.java 中任何一个方法中调用  stopSelf()  方法,即可将该服务停止;

  例如,我在  onStartCommand() 中调用  stopSelf() 方法,观察 Logcat 的打印语句;

运行效果

  果不其然,打印完  onStartCommand: 语句后紧跟着打印了 onDestroy: 语句。

•活动和服务进行通信

  在上面的介绍中,我们只是实现了在 Activity 中开启和关闭一个服务,

  然而服务开启后就和这个 Activity 没什么联系了;

  其实二者是可以继续保持联系的,还记得前面提到的一个  onBind() 方法吧,其实它就是 Service 与 Activity 之间建立通信的桥梁;

  比如说,我们希望 MyService 里提供一个下载功能,然后在活动中可以决定何时开始下载,以及随时查看下载进度;

  实现这个功能的思路是创建一个专门的 Binder 对象来对下载功能进行管理;

  修改 MyService.java 中的代码;

MyService.java

public class MyService extends Service {

    public static final String TAG = "MyService";
private DownloadBinder mBinder = new DownloadBinder(); class DownloadBinder extends Binder { public void startDownload(){//开始下载
Log.d(TAG, "startDownload: ");
} public int getProgress(){//获取下载进度
Log.d(TAG, "getProgress: ");
return 0;
}
} @Override
public IBinder onBind(Intent intent) {
return mBinder;
} public MyService() {} @Override
public void onCreate() {...} @Override
public int onStartCommand(Intent intent, int flags, int startId) {...} @Override
public void onDestroy() {...}
}

  可以看到,这里我们新建了一个 DownloadBinder 类,并让他继承自 Binder;

  然后在他的内部提供了开始下载以及查看下载进度的方法,

  当然,这只是两个模拟方法,仅仅是在这两个方法中打印了一行日志;

  接着在 MyService.java 中创建了 DownloadBinder 实例,然后再  onBind() 方法中返回了这个实例;

  这样,MyService 中的工作就全部完成了;

  下面就要看一看,在活动中如何去调用服务里的这些方法;

  首先在布局文件中新增两个按钮;

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp"> <Button .../> <Button .../> <Button
android:id="@+id/bind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Bind Service"
android:textAllCaps="false"
/>
<Button
android:id="@+id/unbind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Unbind Service"
android:textAllCaps="false"
/> </LinearLayout>

  这两个按钮分别是用于绑定服务和取消绑定服务的;

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    ......

    private Button bindService;
private Button unbindService;
private MyService.DownloadBinder downloadBinder; private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
} @Override
public void onServiceDisconnected(ComponentName name) { }
}; @Override
protected void onCreate(Bundle savedInstanceState) { ...... bindService = findViewById(R.id.bind_service);
bindService.setOnClickListener(this);
unbindService = findViewById(R.id.unbind_service);
unbindService.setOnClickListener(this);
} @Override
public void onClick(View v) {
Intent intent = new Intent(this,MyService.class);
switch(v.getId()){ ...... case R.id.bind_service:
bindService(intent,connection,BIND_AUTO_CREATE);
break;
case R.id.unbind_service:
unbindService(connection);
break;
default:
break;
}
}
}

  可以看到,这里我们首先创建了一个 ServiceConnection 的匿名类,

  在里面重写了 onServiceConnected() 方法和 onServiceDisconnected() 方法,

  这两个方法分别会在活动与服务成功绑定以及解除绑定的时候调用;

  在  onServiceConnected() 方法中,我们又通过向下转型得到了 DownloadBinder 实例;

  然后调用了 DownloadBinder 的  startDownload() 和  getProgress() 方法。

  当然,现在活动和服务还没进行绑定呢,这个功能是在 bindService 按钮 的点击事件里完成的;

  在点击事件里,通过调用  bindService() 方法将 MainActivity 和 MyService 进行绑定,

  调用  unbindService() 方法解除绑定;

  其中, bindService(Intent service,ServiceConnection conn,int flags) 传递了三个参数:

  • Intent :用于指定要启动的 Service

  • ServiceConnection :用于监听调用者与 Service 之间的连接状态

    • 当调用者与Service连接成功时,将回调该对象的  onServiceConnected()  方法
    • 断开连接时,将回调该对象的  onServiceDisconnected() 方法
  • flag :指绑定时是否自动创建 Service(如果 Service 还未创建)

    • 可指定为 0,即不自动创建
    • 也可指定为  BIND_AUTO_CREATE  ,即自动创建

  现在,让我们重新运行一下程序;

运行效果

  可以看到,再点击 Bind Service 按钮 的时候,首先是 MyService 的  onCreate()  方法得到了执行,

  然后  startDownload()  和  getProgress()  方法都得到了执行;

  另外需要注意的是,任何一个服务在整个应用程序范围内都是通用的,即 MyService 不仅可以和 MainActivity 绑定,

  还可以和任何一个其他的活动进行绑定,而且在绑定完成后,他们都可以获取到相同的 DownloadBinder 实例。

•服务的生命周期

服务的启动方式

  通过上面的学习,可以看到服务有两种启动方式:

  • 通过  startService(Intent intent)  方式启动服务

    • 通过 startService 方式启动服务,服务会长期在后台运行
    • 并且服务的状态与开启者的状态没有关系,即便启动服务的组件已经被销毁,服务也依旧会运行
  • 通过 bindService 方式启动服务

    • 通过 bindService 方式启动服务,服务会与组件进行绑定
    • 一个被绑定的服务提供一个客户端与服务器接口,允许组件与服务进行交互,发送请求,得到结果
    • 多个组件可以绑定一个服务,当调用  onUnbind()  方法时,这个服务就被销毁了,即调用  onDestory()  方法

图示

文字描述

——《第一行代码》

•声明

参考资料

Android学习之服务初体验的更多相关文章

  1. Android学习之Broadcast初体验

    •何为 Broadcast ? Broadcast 直译广播,接下来举个形象的例子来理解下 Broadcast: 上学的时候,每个班级都会有一个挂在墙上的大喇叭,用来广播一些通知,比如,开学要去搬书, ...

  2. 第三次随笔--安装虚拟机及学习linux系统初体验

    第三次随笔--安装虚拟机及学习linux系统初体验 ·学习基于VirtualBox虚拟机安装Ubuntu图文教程在自己笔记本上安装Linux操作系统 首先按照老师的提示步骤进行VirtualBox虚拟 ...

  3. Spring Boot 学习笔记1——初体验之3分钟启动你的Web应用[z]

    前言 早在去年就简单的使用了一下Spring Boot,当时就被其便捷的功能所震惊.但是那是也没有深入的研究,随着其在业界被应用的越来越广泛,因此决定好好地深入学习一下,将自己的学习心得在此记录,本文 ...

  4. 算法学习:并行化初体验_JAVA实现并行化归并算法

    这个系列包括算法导论学习过程的记录. 最初学习归并算法,对不会使其具体跑在不同的核上报有深深地怨念,刚好算倒重温了这个算法,闲来无事,利用java的thread来体验一下并行归并算法.理论上开的thr ...

  5. Yaf学习(二)----Yaf初体验

    1.hello world 1.1 用yaf输出hello world 1.首先配置host,nginx 2.host不用多说,指向虚拟机IP即可 1.2 重点说一下nginx (只说server块) ...

  6. Android学习笔记--服务(Service)

    1.服务概述 1.服务是Android四大组件之一,在使用上可以分为本地服务和远程服务,本地服务是指在不影响用户操作的情况下在后台默默的执行一个耗时操作,例如下载,音频播放等.远程服务是指可以供其他应 ...

  7. Android学习--探究服务(一)

    什么是服务? 服务(service)是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务.服务的运行不依赖任何的用户界面,即使应用被切换到后台或者 ...

  8. Visual Studio Code 学习.net core初体验

    一,安装 最近在用 Visual Studio Code 学习.net core ,记录下学习的过程,首先去官网下载最新的.net core2.1安装包,有windows 和mac,根据自己的开发环境 ...

  9. docker从零开始(三)服务初体验docker compose

    决条件 安装Docker 1.13或更高版本. 获取Docker Compose.在适用于Mac的Docker和适用于Windows的Docker上,它已预先安装,因此您可以随意使用.在Linux系统 ...

随机推荐

  1. I ❤️ W3C : Secure Contexts

    I ️ W3C : Secure Contexts Secure Contexts W3C Candidate Recommendation, 15 September 2016 https://ww ...

  2. JSON-LD 结构化数据

    JSON-LD 结构化数据 SEO JSON-LD JSON for Linking Data JSON 链接数据 https://json-ld.org/ https://en.wikipedia. ...

  3. PWA & Service Workers 版本更新 bug

    PWA & Service Workers 版本更新 bug PWA & Service Worker https://developer.mozilla.org/zh-CN/docs ...

  4. after upgrade macOS Catalina bugs

    after upgrade macOS Catalina bugs 升级了macOS catalina后,碰到的 bugs? macOS 10.15.5 https://www.apple.com/m ...

  5. 运行Chrome浏览器如何添加Options

    原文档 Options Windows: 退出Chrome的所有正在运行的实例. 右键单击"Chrome"快捷方式 选择属性 在"目标:"行的末尾添加命令行标志 ...

  6. Flutter: 下拉刷新,上拉加载更多

    import 'package:flutter/material.dart'; import 'package:english_words/english_words.dart'; void main ...

  7. NGK Global英国路演落下帷幕,区块链赋能大数据取得新突破

    NGK全球巡回路演于7月25日在英国圆满举行,此次路演是由NGK英国社区主办,旨在探讨当前大数据爆炸的形式下,区块链如何赋能,解决行业痛点.会上,行业精英.区块链爱好者.各实体产业代表以及科技人员纷纷 ...

  8. MySQL的简单使用方法备忘

    这只是一篇我的个人备忘录,写的是我常用的命令.具体可以参考"菜鸟教程" https://www.runoob.com/mysql/mysql-tutorial.html 登录(用户 ...

  9. display: inline、block、inline-block、flex和inline-flex

    inline 共享一行 不能修改width.height属性,大小由内容撑开 padding属性 top.right.bottom.left设置都有效:margin属性只有left.right设置有效 ...

  10. Ctf_show Web[1-8]

    CTFshow_web1: F12查看JS即在代码里 CTFshow_web2: 进入网址后页面如下: ①:尝试使用admin登陆,发现页面无回显 ②:尝试单引号闭合,并且添加' or 1=1#,此时 ...