Android手把手编写儿童手机远程监控App之前台服务

引言

本文将详细介绍如何在Android应用中实现前台服务,以确保应用在后台持续运行并执行重要任务。前台服务是一种高优先级的服务,能够在用户注意到的情况下持续运行,并且不容易被系统终止。这对于需要长时间保持连接的应用(如儿童手机远程监控)尤为重要。

什么是服务(Service)

服务(Service) 是Android中用于在后台执行长时间运行操作的一种组件。它可以独立于用户界面运行,即使应用被切换到后台或用户打开其他应用,服务仍然能够正常运行。服务主要分为两种类型:

  1. 后台服务:执行用户不会直接注意到的操作,如数据处理、日志清理等。当系统资源不足时,后台服务可能会被系统终止。
  2. 前台服务:执行用户能注意到的操作,必须显示一个通知,优先级高,系统不容易主动终止它。

由于现代Android系统对功耗的严格管理,后台服务在锁屏后可能会被暂停。因此,为了确保应用在后台持续运行,通常需要使用前台服务。

创建后台服务

项目结构

假设我们有一个名为DemoApp的项目,目前包含MainActivity页面。我们将删除其他不必要的内容,并创建一个后台服务。

创建MyService

  1. 右键点击app目录,选择New > Service > Service。
  2. 在弹出的对话框中,填写以下信息:
    • 自定义类名:MyService
    • Exported:选择“否”,表示该服务不会被外部应用调用。
    • Enabled:选择“是”,表示该服务默认启用。
    • Source Language:选择开发语言(例如Java)。
    • Target Source Set:选择创建后台服务代码存放的位置。

点击“Finish”完成后台服务的创建。IDE会自动生成MyService类,并在AndroidManifest.xml文件中声明该服务。

MyService源码

package com.example.demoapp;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class MyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        // 返回与服务通信的通道
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

AndroidManifest文件代码

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.demoapp">
    <application
        android:allowBackup="true"
        android:icon="@drawable/app_icon"
        android:label="@string/app_name"
        android:roundIcon="@drawable/app_icon_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:targetApi="31">
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="false"></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>

重载onCreate、onStartCommand、onDestroy函数

在MainActivity中添加按钮

为了方便测试,我们在MainActivity中添加两个按钮,分别用于启动和停止服务。

package com.example.demoapp;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

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

        Button startBtn = findViewById(R.id.start_btn);
        Button stopBtn = findViewById(R.id.stop_btn);

        startBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, MyService.class);
                startService(intent);
                Toast.makeText(MainActivity.this, "服务已启动", Toast.LENGTH_SHORT).show();
            }
        });

        stopBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, MyService.class);
                stopService(intent);
                Toast.makeText(MainActivity.this, "服务已停止", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

重载MyService中的方法

在MyService类中,我们需要重载onCreate、onStartCommand和onDestroy方法,以便在服务的不同生命周期阶段执行相应的操作。

package com.example.demoapp;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;

public class MyService extends Service {

    private static final String CHANNEL_ID = "ForegroundServiceChannel";

    @Override
    public void onCreate() {
        super.onCreate();
        createNotificationChannel();
        Notification notification = new Notification.Builder(this, CHANNEL_ID)
                .setContentTitle("前台服务")
                .setContentText("服务正在运行")
                .setSmallIcon(R.drawable.ic_notification)
                .build();
        startForeground(1, notification);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 执行长时间运行的任务
        return START_STICKY; // 服务被杀死后会重新创建
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 清理资源
    }

    @Override
    public IBinder onBind(Intent intent) {
        // 返回与服务通信的通道
        return null;
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel serviceChannel = new NotificationChannel(
                    CHANNEL_ID,
                    "前台服务",
                    NotificationManager.IMPORTANCE_DEFAULT
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(serviceChannel);
        }
    }
}

解释关键代码

  • createNotificationChannel:创建通知渠道,适用于Android 8.0及以上版本。
  • startForeground:将服务设置为前台服务,并显示通知。
  • onStartCommand:返回START_STICKY,表示如果服务被系统杀死,系统会尝试重新创建服务。
  • onDestroy:在服务被销毁时清理资源。

总结

本文详细介绍了如何在Android应用中创建和使用前台服务。通过创建前台服务,可以确保应用在后台持续运行并执行重要任务,从而提高应用的稳定性和用户体验。建议在开发需要长时间后台运行的应用时,优先考虑使用前台服务。

希望本文对你有所帮助,如果你有任何问题或建议,欢迎在评论区留言交流。

创建前台服务

在上一部分中,我们介绍了如何创建一个简单的后台服务。接下来,我们将进一步扩展这个功能,使其成为一个前台服务。前台服务在运行时会显示一个通知,这样用户可以随时了解服务的状态,并且系统也不会轻易将其终止。

添加前台服务权限

首先,我们需要在AndroidManifest.xml文件中添加前台服务所需的权限。这一步是必不可少的,因为从Android 8.0(API级别26)开始,所有前台服务都需要显式声明这一权限。

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

创建通知渠道

为了在Android 8.0及以上版本中显示通知,我们需要创建一个通知渠道。通知渠道允许用户管理和控制不同类型的通知。

private void createNotificationChannel() {
    // Android 8.0+ 需要创建通知渠道
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel channel = new NotificationChannel(
                "SERVICE_CHANNEL",
                "Service Channel",
                NotificationManager.IMPORTANCE_LOW
        );
        NotificationManager manager = getSystemService(NotificationManager.class);
        manager.createNotificationChannel(channel);
    }
}

启动前台服务

在MyService类中,我们需要重写onStartCommand方法,并在其中调用startForeground方法来启动前台服务。startForeground方法需要两个参数:一个唯一的ID和一个Notification对象。

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(this, "前台服务onStartCommand", Toast.LENGTH_SHORT).show();
    startForeground(10001, createNotification());
    return super.onStartCommand(intent, flags, startId);
}

创建通知

我们还需要创建一个Notification对象,该对象将在前台服务运行时显示。这个通知可以包含一些基本信息,如标题、内容和图标。此外,我们还可以设置点击通知时的行为,例如返回到主活动页面。

private Notification createNotification() {
    // 点击通知返回应用
    Intent intent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(
            this, 0, intent,
            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
    );

    return new NotificationCompat.Builder(this, "SERVICE_CHANNEL")
            .setContentTitle("儿童手机远程监控")
            .setContentText("正在守护孩子的安全...")
            .setSmallIcon(android.R.drawable.ic_menu_info_details)
            .setContentIntent(pendingIntent)
            .setOngoing(true)  // 不可滑动删除
            .build();
}

更新主活动代码

在MainActivity中,我们需要更新按钮的点击事件,以便启动和停止前台服务。这里我们使用startForegroundService方法来启动前台服务,而不是startService。

package com.example.childmonitor;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

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

        Button startBtn = findViewById(R.id.startbtn);
        startBtn.setOnClickListener(v -> {
            Intent intent = new Intent(this, MyService.class);
            startForegroundService(intent);
        });

        Button stopBtn = findViewById(R.id.stopbtn);
        stopBtn.setOnClickListener(v -> {
            Intent intent = new Intent(this, MyService.class);
            stopService(intent);
        });
    }
}

完整的MyService代码

以下是完整的MyService类代码,包含了所有必要的方法和逻辑:

package com.example.childmonitor;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.widget.Toast;

import androidx.core.app.NotificationCompat;

public class MyService extends Service {
    public MyService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(this, "前台服务onCreate", Toast.LENGTH_SHORT).show();
        createNotificationChannel();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast.makeText(this, "前台服务onStartCommand", Toast.LENGTH_SHORT).show();
        startForeground(10001, createNotification());
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Toast.makeText(this, "前台服务onDestroy", Toast.LENGTH_SHORT).show();
        super.onDestroy();
    }

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

    private void createNotificationChannel() {
        // Android 8.0+ 需要创建通知渠道
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    "SERVICE_CHANNEL",
                    "Service Channel",
                    NotificationManager.IMPORTANCE_LOW
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }
    }

    private Notification createNotification() {
        // 点击通知返回应用
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this, 0, intent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
        );

        return new NotificationCompat.Builder(this, "SERVICE_CHANNEL")
                .setContentTitle("儿童手机远程监控")
                .setContentText("正在守护孩子的安全...")
                .setSmallIcon(android.R.drawable.ic_menu_info_details)
                .setContentIntent(pendingIntent)
                .setOngoing(true)  // 不可滑动删除
                .build();
    }
}

运行效果

完成以上步骤后,运行应用程序,点击“启动”按钮,你会看到一个通知出现在通知栏中,表示前台服务已经启动。点击“停止”按钮,通知将消失,前台服务也会被终止。

前台服务运行效果

至此,我们已经成功地创建了一个前台服务,用于儿童手机的远程监控。希望这篇文章对你有所帮助!如果有任何问题或建议,请在评论区留言。