第5章 后台服务与系统服务 ?5.1 后台服务 Android系统的后台服务(Service)是一种类似于Activity的组件,但Service没有用户操作界面,也不能自己启动,其主要作用是提供后台服务调用。Service不像Activity那样当用户关闭应用界面就停止运行,Service会一直在后台运行,除非明确命令其停止。 通常使用Service为应用程序提供一些只需在后台运行的服务或不需要界面的功能,如从Internet下载文件、控制Video播放器等。 Service的生命周期中只有3个阶段,即onCreate、onStartCommand、onDestroy。Service的常用方法如表5-1所示。 表5-1 Service的常用方法 方 法 说 明 onCreate() 创建后台服务 onStartCommand (Intent intent, int flags, int startId) 启动后台服务 onDestroy() 销毁后台服务,并删除所有调用 sendBroadcast(Intent intent) 继承父类Context的sendBroadcast()方法,实现发送广播机制的消息 onBind(Intent intent) 与服务通信的信道进行绑定,服务程序必须实现该方法 onUnbind(Intent intent) 撤销与服务信道的绑定 通常Service要在一个Activity中启动,调用Activity的startService(Intent)方法启动Service。若要停止正在运行的Service,则调用Activity的stopService(Intent)方法关闭 Service。startService()和stopService()方法均继承于Activity及Service共同的父类android.content.Context。 一个服务只能创建一次,销毁一次,但可以开始多次,即onCreate()和onDestroy()方法只会被调用一次,而onStartCommand()方法可以被调用多次。后台服务的具体操作一般应该放在onStartCommand()方法里面。如果Service已经启动,当再次启动Service时则不调用onCreate()而直接调用onStartCommand()。 设计一个后台服务的应用程序大致有以下几个步骤。 (1)创建Service的子类。 ① 编写onCreate()方法,创建后台服务; ② 编写onStartCommand()方法,启动后台服务; ③ 编写onDestroy()方法,终止后台服务,并删除所有调用。 (2)创建启动和控制Service的Activity。 ① 创建Intent对象,建立Activity与Service的关联; ② 调用Activity的startService(Intent)方法启动Service后台服务; ③ 调用Activity的stopService(Intent)方法关闭Service后台服务。 (3)修改配置文件AndroidManifest.xml。 在配置文件AndroidManifest.xml的<application>标签中添加以下代码: <service android:enabled="true" android:name=".AudioSrv" /> 【例5-1】 一个简单的后台音频服务程序示例。 本例通过一个按钮启动后台服务,在服务程序中播放音频文件,演示服务程序的创 建、启动,再通过另一个按钮演示服务程序的销毁过程。新建项目ex5_1后,将音频文件mtest.mp3复制到应用程序新建的res\raw目录下。 (1)设计界面布局。 在界面布局中,设置两个按钮,分别用于“启动后台服务程序”和“关闭后台服务程序”;再设置一个文本标签,用于显示后台服务程序的运行状态。 (2)设计后台服务程序。 新建一个后台服务程序,命名为AudioSrv.java,其代码如下。 1 package com.example.ex5_1; 2 import android.app.Service; 3 import android.content.Intent; 4 import android.media.MediaPlayer; 5 import android.os.IBinder; 6 import android.widget.Toast; 7 8 public class AudioSrv extends Service 9 { 10 MediaPlayer play; 11 @Override 12 public IBinder onBind(Intent intent) 13 { 14 return null; 15 } 16 public void onCreate() 17 { 18 super.onCreate(); 19 play = MediaPlayer.create(this, R.raw.mtest); 20 Toast.makeText(this, "创建后台服务...", Toast.LENGTH_LONG).show(); 21 } 22 public int onStartCommand(Intent intent, int flags, int startId) 23 { 24 super.onStartCommand(intent, flags, startId); 25 play.start(); 26 Toast.makeText(this, "启动后台服务程序,播放音乐...", 27 Toast.LENGTH_LONG).show(); 28 return START_STICKY; 29 } 30 public void onDestroy() 31 { 32 play.release(); 33 super.onDestroy(); 34 Toast.makeText(this, "销毁后台服务!", Toast.LENGTH_LONG).show(); 35 } 36 } (3)启动后台服务的主控程序MainActivity.java。 1 package com.example.ex5_1; 2 import androidx.appcompat.app.AppCompatActivity; 3 import android.os.Bundle; 4 import android.content.Intent; 5 import android.view.View; 6 import android.view.View.OnClickListener; 7 import android.widget.Button; 8 import android.widget.TextView; 9 public class MainActivity extends Activity 10 { 11 Button startbtn, stopbtn; 12 Intent intent; 13 static TextView txt; 14 @Override 15 public void onCreate(Bundle savedInstanceState) 16 { 17 super.onCreate(savedInstanceState); 18 setContentView(R.layout.main); 19 startbtn=(Button)findViewById(R.id.butn1); 20 stopbtn=(Button)findViewById(R.id.butn2); 21 startbtn.setOnClickListener(new mClick()); 22 stopbtn.setOnClickListener(new mClick()); 23 txt=(TextView)findViewById(R.id.text1); 24 intent = new Intent(MainActivity.this, AudioSrv.class); 25 } 26 class mClick implements OnClickListener //定义一个类实现监听接口 27 { 28 public void onClick(View v) 29 { 30 if(v == startbtn) 31 { 32 MainActivity.this.startService(intent); 33 txt.setText("start service ......."); 34 } 35 else if(v == stopbtn) 36 { 37 MainActivity.this.stopService(intent); 38 } 39 } 40 } 41 } (4)修改配置文件AndroidManifest.xml。 在配置文件AndroidManifest.xml的<application>标签中添加以下代码: <service android:enabled="true" android:name=".AudioSrv" /> 修改后完整的AndroidManifest.xml文件如下: 1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.example.ex5_1" /> 4 <application 5 android:allowBackup="true" 6 android:icon="@mipmap/ic_launcher" 7 android:label="ex5_1 " 8 android:roundIcon="@mipmap/ic_launcher_round" 9 android:supportsRtl="true" 10 android:theme="@style/Theme.ex5_1"> 11 <activity 12 android:name=".MainActivity" > 13 <intent-filter> 14 <action android:name="android.intent.action.MAIN" /> 15 <category android:name="android.intent.category.LAUNCHER" /> 16 </intent-filter> 17 </activity> 18 <!-- 添加AudioSrv服务程序 --> 19 <service android:enabled="true" android:name=".AudioSrv" /> 20 </application> 21 </manifest> 程序运行结果如图5.1所示。 图5.1 启动后台服务程序 ?5.2 信息广播机制 Broadcast是Android系统应用程序之间传递信息的一种机制。当系统之间需要传递某些信息时,不是通过诸如单击按钮之类的组件来触发事件,而是由系统自身通过系统调用来引发事件。这种系统调用是由BroadcastReceiver类实现的,把这种系统调用称为广播。BroadcastReceiver是“广播接收者”的意思,顾名思义,它用来接收来自系统和应用中的广播信息。 在Android系统中有很多广播信息。例如,当开机时,系统会产生一条广播信息,接收到这条广播信息就能实现开机启动服务的功能;当网络状态改变时,系统会产生一条广播信息,接收到这条广播信息就能及时地做出提示和保存数据等操作;当电池电量改变 时,系统会产生一条广播信息,接收到这条广播信息就能在电量低时告知用户及时保存进度等。 下面通过简单的应用示例来说明信息广播的设计方法。 【例5-2】 一个简单的信息广播程序示例。 (1)设计主控文件MainActivity.java。 1 package com.example.ex5_2; 2 import androidx.appcompat.app.AppCompatActivity; 3 import android.os.Bundle; 4 import android.content.Intent; 5 import android.view.View; 6 import android.widget.Button; 7 import android.widget.TextView; 8 public class MainActivity extends Activity 9 { 10 static TextView txt; 11 private TestReceiver recevier; 12 private IntentFilter intentFilter; 13 @Override 14 public void onCreate(Bundle savedInstanceState) 15 { 16 super.onCreate(savedInstanceState); 17 setContentView(R.layout.main); 18 txt = (TextView)findViewById(R.id.txtView); 19 Button btn=(Button)findViewById(R.id.button); 20 btn.setOnClickListener(new mClick()); 21 } 22 class mClick implements View.OnClickListener 23 { 24 @Override 25 public void onClick(View v) 26 { 27 recevier = new TestReceiver(); 28 intentFilter = new IntentFilter(); 29 intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); 30 registerReceiver(recevier, intentFilter); 31 System.out.println("广播已经发送"); 32 } 33 } 34 } (2)设计广播接收器文件TestReceiver.java。 1 package com.ex5_2; 2 import android.content.BroadcastReceiver; 3 import android.content.Context; 4 import android.content.Intent; 5 6 public class TestReceiver extends BroadcastReceiver 7 { 8 @Override 9 public void onReceive(Context context, Intent intent) 10 { 11 String str = intent.getExtras().getString("hello"); 12 MainActivity.txt.setText(str); 13 } 14 } (3)设计配置文件AndroidManifest.xml。 1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.example.ex5_2"> 4 <application 5 android:allowBackup="true" 6 android:icon="@mipmap/ic_launcher" 7 android:label="@string/app_name" 8 android:roundIcon="@mipmap/ic_launcher_round" 9 android:supportsRtl="true" 10 android:theme="@style/Theme.ex5_2"> 11 <activity android:name=".MainActivity"> 12 <intent-filter> 13 <action android:name="android.intent.action.MAIN" /> 14 <category android:name="android.intent.category.LAUNCHER" /> 15 </intent-filter> 16 </activity> 17 <!-- 注册对应的广播接收类 --> 18 <receiver android:name=".TestReceiver"> 19 </receiver> 20 </application> 21 </manifest> 程序运行结果如图5.2所示。 图5.2 简单的广播示例 【例5-3】 由一个后台服务广播音乐的播放、暂停或停止信息,接收器接收到信息后,执行改变用户界面按钮上文本的操作。 在本例中创建了3个类,即MainActivity、AudioService和Broadcast,MainActivity负责用户的交互界面,并启动后台服务;AudioService是Service的子类,在后台提供播放、暂停或停止音乐等功能,同时发送改变交互界面的广播信息;Broadcast是BroadcastReceiver的子类,负责接收广播信息,更改交互界面。 (1)设计界面布局。 在界面布局中,设置一个文本标签和?3?个按钮,如图?5.3 所示。 (2)设计主控文件MainActivity.java。 1 package com.example.ex5_3; 2 import androidx.appcompat.app.AppCompatActivity; 3 import android.os.Bundle; 4 import android.content.Intent; 5 import android.view.View; 6 import android.widget.Button; 7 import android.widget.TextView; 8 public class MainActivity extends AppCompatActivity 9 { 10 static Button startbtn, stopbtn, paubtn; 11 static TextView txt; 12 Intent intent; 13 @Override 14 public void onCreate(Bundle savedInstanceState) 15 { 16 super.onCreate(savedInstanceState); 17 setContentView(R.layout.main); 18 startbtn=(Button)findViewById(R.id.button); 19 paubtn = (Button)findViewById(R.id.button2); 20 stopbtn=(Button)findViewById(R.id.button3); 21 txt=(TextView)findViewById(R.id.textView); 22 intent = new Intent(MainActivity.this, AudioSrv.class); 23 startbtn.setOnClickListener(new mClick()); 24 paubtn.setOnClickListener(new mClick()); 25 stopbtn.setOnClickListener(new mClick()); 26 } 27 class mClick implements View.OnClickListener //定义实现监听接口类 28 { 29 public void onClick(View v) 30 { 31 if(v == startbtn) 32 { 33 intent.putExtra("play",1); 34 MainActivity.this.startService(intent);//开启服务 35 MainActivity.this.startService(intent); 36 System.out.println("(1)启动音乐"); 37 } 38 else if(v == paubtn) 39 { 40 intent.putExtra("play",2); 41 MainActivity.this.startService(intent); 42 System.out.println("(2)暂停音乐"); 43 } 44 else if(v == stopbtn) 45 { 46 intent.putExtra("play",3); 47 MainActivity.this.startService(intent); 48 System.out.println("(3)停止音乐"); 49 } 50 } 51 } 52 } (3)设计后台服务程序AudioService.java。 1 package com.example.ex5_3; 2 import android.app.Service; 3 import android.content.Intent; 4 import android.content.IntentFilter; 5 import android.media.MediaPlayer; 6 import android.os.IBinder; 7 import android.widget.Toast; 8 public class AudioService extends Service 9 { 10 public MediaPlayer play; 11 private AudioReceiver receiver; //接收广播类 12 private IntentFilter intentFilter; 13 public IBinder onBind(Intent intent) 14 { 15 return null; 16 } 17 public void onCreate() //创建后台服务 18 { 19 super.onCreate(); 20 play = MediaPlayer.create(this, R.raw.mtest); 21 Toast.makeText(this, "创建后台服务...", Toast.LENGTH_LONG).show(); 22 } 23 void on_Start(){ //创建广播 24 receiver = new AudioReceiver(); 25 intentFilter = new IntentFilter(); 26 intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); 27 registerReceiver(receiver, intentFilter); 28 } 29 public int onStartCommand(Intent intent, int flags, int startId) 30 { 31 super.onStartCommand(intent, flags, startId); 32 switch (intent.getIntExtra("play",-1)){ 33 case 1: 34 play.start(); //开始 35 on_Start(); //启动广播 36 Toast.makeText(this,"启动后台服务,播放音乐...", 37 Toast.LENGTH_SHORT).show();//提示 38 break; 39 case 2: 40 if (play!=null&&play.isPlaying()){ 41 play.pause();//暂停 42 Toast.makeText(this,"暂停...",Toast.LENGTH_SHORT).show(); 43 } 44 else { 45 play.start();//继续 46 Toast.makeText(this,"继续...",Toast.LENGTH_SHORT).show(); 47 } 48 break; 49 case 3: 50 play.stop();//停止 51 play.release();//释放内存 52 Toast.makeText(this,"销毁后台服务...",Toast.LENGTH_SHORT).show(); 53 break; 54 } 55 return START_STICKY; 56 } 57 public void onDestroy() 58 { 59 play.release(); 60 super.onDestroy(); 61 Toast.makeText(this, "销毁后台服务!", Toast.LENGTH_LONG).show(); 62 } 63 } (4)设计广播接收器程序Broadcast.java。 1 package com.example.ex5_3; 2 import android.content.BroadcastReceiver; 3 import android.content.Context; 4 import android.content.Intent; 5 /* 广播接收器 */ 6 class Broadcast extends BroadcastReceiver 7 { 8 @Override 9 public void onReceive(Context context, Intent intent) 10 { 11 String action = intent.getAction(); 12 if(action != null) { 13 String str = "正在播放音乐"; 14 System.out.println(str);