如果你的应用需要持续、稳定地执行后台任务(比如音乐播放、运动轨迹记录),那么将你的Service设置为前台服务是最高优先级的方案。前台服务会显示一个持续的通知,告诉用户你的应用正在运行。系统会将其视为用户正在交互的应用,从而极大地降低被杀死或限制的概率。
我的示例程序是创建一个前台服务并启动。服务里运行一个定时器,定时更新通知并与主界面进行交互。该示例程序适合于Android13及以上版本。
前台服务程序代码:
java
package com.example.train;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import androidx.core.app.NotificationCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.util.Locale;
public class TimerForegroundService extends Service {
private static final int NOTIF_ID = 1; // 全局唯一的通知标识
String NOTIFICATION_CHANNEL_ID = "foreground_service_channel";
String CHANNEL_NAME = "前台服务通知";
String text = "通知内容";
private boolean isRunning = false;
private int count = 0;//定时器计数
private Handler handler = new Handler(Looper.getMainLooper());//消息队列
private Runnable runnable;//线程同步
public TimerForegroundService() {
}
@Override
public void onCreate() {
super.onCreate();
// 初始化代码
//创建通知模板
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!isRunning) {
isRunning = true;
startTimer();
}
// 这里启动前台服务,在此之前不应执行任何耗时操作
startForeground(NOTIF_ID, buildNotification(text)); // 1是通知ID,必须是唯一的正整数
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
//throw new UnsupportedOperationException("Not yet implemented");
return null;
}
private Notification buildNotification(String contentText) {
//Notification 点击跳转需通过 PendingIntent 封装 Intent 并传入 Notification
//Android 12+ 必须显式指定 FLAG_IMMUTABLE 或 FLAG_MUTABLE,否则应用崩溃 。
Intent intent = new Intent(this, MainActivity.class);
int flag = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
? PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
: PendingIntent.FLAG_UPDATE_CURRENT;
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, flag);
return new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.train)
.setContentTitle("前台服务")
.setContentText(contentText)
.setContentIntent(pendingIntent)// 绑定点击事件(可选)
.setAutoCancel(true)// 点击后自动消失(可选)
.setOngoing(true)
.build();
}
private void updateNotification(String newContent) {
Notification updatedNotification = buildNotification(newContent);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(NOTIF_ID, updatedNotification);
Intent intent = new Intent("com.example.broadcast.MY_BROADCAST");
intent.putExtra("data", newContent);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
private void startTimer() {
runnable = new Runnable() {
@Override
public void run() {
if (isRunning) {
// 执行任务,例如更新时间
count++;
updateNotification(String.format(Locale.getDefault(), "定时器已执行次%d", count));
// 延迟1秒后再次执行,形成循环
handler.postDelayed(this, 1000);
}
}
};
handler.post(runnable);
}
private void stopTimer() {
if (handler != null && runnable != null) {
handler.removeCallbacks(runnable);
}
}
// 在 Activity 的 onDestroy 中务必调用 stopTimer()
@Override
public void onDestroy() {
super.onDestroy();
stopTimer();
}
}
主程序代码:(除了启动前台服务并接收消息外,还另有段测试google TTS语音的代码)
java
package com.example.train;
import android.Manifest;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.*;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.speech.tts.TextToSpeech;
import java.util.Locale;
import android.util.Log;
import android.content.Context;
import android.media.AudioManager;
public class MainActivity extends AppCompatActivity {
android.widget.Button button = null;
private TextToSpeech tts;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
//android 13及以上系统动态获取通知权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
checkPostNotificationPermission();
}
tts = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
int result = tts.setLanguage(Locale.CHINA); // 设置中文
if (result == TextToSpeech.LANG_MISSING_DATA
|| result == TextToSpeech.LANG_NOT_SUPPORTED) {
Log.e("TTS", "中文语言包不可用");
}
else{
AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
am.setStreamVolume(AudioManager.STREAM_MUSIC, (int)(am.getStreamMaxVolume(AudioManager.STREAM_MUSIC) * 0.8), 0);
EditText edit = findViewById(R.id.editTextText);
edit.setText("语音初始化成功2");
}
}
}
});
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EditText edit = findViewById(R.id.editTextText);
//Toast.makeText(MainActivity.this,edit.getText() , Toast.LENGTH_SHORT).show();
tts.setPitch(1.2f);// 音高调节(0.5-2.0,默认1.0)
tts.setSpeechRate(0.9f);// 语速调节(0.5-2.0,默认1.0)
tts.speak(edit.getText(), TextToSpeech.QUEUE_FLUSH, null, null);
}
});
LocalBroadcastManager.getInstance(this).registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String data = intent.getStringExtra("data");
// 处理数据
EditText edit = findViewById(R.id.editTextText);
edit.setText(data);
}
}, new IntentFilter("com.example.broadcast.MY_BROADCAST"));
// Android 8.0+ 必须使用此方法启动前台服务
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent = new Intent(this, TimerForegroundService.class);
startForegroundService(intent);//启动前台服务
}
}
private void checkPostNotificationPermission() {
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions((Activity) this, new String[]{
Manifest.permission.POST_NOTIFICATIONS}, 200);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 200) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//允许了通知权限
} else {
Toast.makeText(this, "您拒绝了通知权限", Toast.LENGTH_SHORT).show();
}
}
}
}
权限申请:
包括前台服务的申明,前台服务通知权限申请,TTS权限申请,位置服务申请(暂时用不上)
XML
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<queries>
<intent>
<action android:name="android.intent.action.TTS_SERVICE" />
</intent>
</queries>
<!-- 1. 声明基础及细分权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Train">
<!-- 2. 在 Service 中声明类型 -->
<service
android:name=".TimerForegroundService"
android:foregroundServiceType="location"
android:enabled="true"
android:exported="true" /><!--组件仅能被应用内部组件(如其他Activity、Service等)启动或绑定。-->
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
在模拟器中的表现:

