一.服务基本上分为两种形式:
启动
当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。 操作完成后,服务会自行停止运行。
绑定
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
二.启动Service
1.新建名为Service的Project
java文件(MainActivity.java(主Activity),HelloIntentService.java,HelloService.java)
xml文件(activity_main.xml)
2.Strings.xml
<resources>
<string name="app_name">Service</string>
<string name="button1_name">StartService</string>
<string name="button2_name">StopService</string>
</resources>
3.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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/button1_name"/>
<Button
android:id="@+id/button2"
android:text="@string/button2_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
4.MainActivity.java
此处使用OnClickListener接口实现Button监听事件
package com.example.administrator.service;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1=findViewById(R.id.button1);
Button button2=findViewById(R.id.button2);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Intent intent=new Intent(this,HelloIntentService.class);
switch (v.getId()){
case R.id.button1:startService(intent);break;
case R.id.button2:stopService(intent);break;}
}
}
5.启动Service
5.1从传统上讲,可以扩展两个类来创建启动服务:
Service
这是适用于所有服务的基类。扩展此类时,必须创建一个用于执行所有服务工作的新线程,因为默认情况下,服务将使用应用的主线程,这会降低应用正在运行的所有 Activity 的性能。
IntentService
这是 Service 的子类,它使用工作线程逐一处理所有启动请求。如果您不要求服务同时处理多个请求,这是最好的选择。 您只需实现 onHandleIntent() 方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。
5.2扩展 IntentService 类来启动服务
(特点:通过工作队列处理启动请求)
IntentService执行以下操作:
1.创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。
2.创建工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样您就永远不必担心多线程问题。
3.在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()。
4.提供 onBind() 的默认实现(返回 null)。(因此此处无需有public IBinder onBind(Intent intent) {return null; }
)
5.提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。(若有需要可重写其他回调方法(如 onCreate()、onStartCommand() 或 onDestroy()),请确保调用超类实现,以便 IntentService 能够妥善处理工作线程的生命周期。)
综上所述,只需实现 onHandleIntent() 来完成客户端提供的工作即可。(不过,您还需要为服务提供小型构造函数。)
HelloIntentService.java
package com.example.administrator.service;
import android.app.IntentService;
import android.content.Intent;
import android.widget.Toast;
public class HelloIntentService extends IntentService {
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
@Override
protected void onHandleIntent( Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
try {
Thread.sleep(5000);
System.out.println("Thread are sleeping");
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
}
//onStartCommand() 必须返回默认实现(即,如何将 Intent 传递给 onHandleIntent()):
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}
@Override
public void onDestroy() {
System.out.println("Service onDestroy");
super.onDestroy();
}
}
5.3扩展 Service 类来启动服务
(特点:服务执行多线程处理每个 Intent)
5.3.1在Strings.xml新增
<string name="button3_name">StartService1</string>
<string name="button4_name">StopService1</string>
5.3.2在activity_main.xml中新增
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/button3"
android:text="@string/button3_name"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/button4"
android:text="@string/button4_name"/>
5.3.3MainActivity.java
package com.example.administrator.service;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1=findViewById(R.id.button1);
Button button2=findViewById(R.id.button2);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
//新增部分
Button button3=findViewById(R.id.button3);
Button button4=findViewById(R.id.button4);
button3.setOnClickListener(new buttonclick(this));
button4.setOnClickListener(new buttonclick(this));
}
@Override
public void onClick(View v) {
Intent intent=new Intent(this,HelloIntentService.class);
switch (v.getId()){
case R.id.button1:startService(intent);break;
case R.id.button2:stopService(intent);break; }
}
//新增:此处为外部类(独立类)实现Button监听事件
public class buttonclick implements View.OnClickListener{
private Context context;
public buttonclick(Context ct){this.context=ct;}
Intent intent=new Intent(MainActivity.this,HelloService.class);
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.button3:startService(intent);break;
case R.id.button4:stopService(intent);break;}
}
}
}
5.3.4HelloService.java
package com.example.administrator.service;
import android.app.Service;
import android.content.Intent;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.widget.Toast;
public class HelloService extends Service {
private ServiceHandler mServiceHandler;
private Looper mServiceLooper;
// Handler that receives messages from the thread
private final class ServiceHandler extends android.os.Handler {
public ServiceHandler(Looper looper) {
super(looper); }
@Override
public void handleMessage(Message msg){
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
try { Thread.sleep(5000); }
catch (InterruptedException e){
// Restore interrupt status.
Thread.currentThread().interrupt();}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
/**Start up the thread running the service. Note that we create a
separate thread because the service normally runs in the process's
main thread, which we don't want to block. We also make it
background priority so CPU-intensive work will not disrupt our UI.*/
HandlerThread thread=new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper=thread.getLooper();
mServiceHandler=new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg=mServiceHandler.obtainMessage();
msg.arg1=startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
// We don't provide binding, so return null
@Override
public IBinder onBind(Intent intent){
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
super.onDestroy(); }
}
与使用 IntentService 相比,这需要执行更多工作。
但是,因为是由自己处理对 onStartCommand() 的每个调用,因此可以同时执行多个请求。此示例并未这样做,此示例此时只有一个请求,但如果希望同时执行多个请求,则可为每个请求创建一个新线程,然后立即运行这些线程(而不是等待上一个请求完成)。
5.3.5onStartCommand() 方法返回值
请注意,onStartCommand() 方法必须返回整型数。整型数是一个值,用于描述系统应该如何在服务终止的情况下继续运行服务(如上所述,IntentService 的默认实现将为您处理这种情况,不过您可以对其进行修改)。从 onStartCommand() 返回的值必须是以下常量之一:
START_NOT_STICKY
如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
START_STICKY
如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。
START_REDELIVER_INTENT
如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
6.执行
点击StartService/StartService1按钮,睡眠5秒后会自动停止
若点击StartService/StartService1按钮,又马上点击StopService/StopService1按钮,也会直接停止运行。
注:每一个实例的代码都会附上相应的代码片或者图片,保证代码完整展示在博客中。
附:&Android开发思维导图一张思维导图,告诉你Android新手如何快速入门 - CSDN博客 https://blog.csdn.net/vanpersie_9987/article/details/52518782
&大佬的博客https://blog.csdn.net/javazejian/article/details/52709857
&官方文档https://developer.android.google.cn/guide/components/services
&Button监听事件的几种设置方法https://www.cnblogs.com/xiao-chuan/p/6074075.html
&学习教程视频地址:第6章 四大组件之Servicehttps://www.imooc.com/learn/179