Android—服务+通知=>前台服务

文章目录

1、Android服务

1.1、定义

  • 服务(Service)是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务
  • 服务并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。
  • 服务并不会自动开启线程,所有的代码都是默认运行在主线程当中的。 耗时操作需另启线程(如HandlerThread或AsyncTask)

1.2、基本用法

1.2.1、定义一个服务

  • 要创建服务,您必须创建 Service 的子类或使用其现有子类之一
  • onBind()方法。这个方法是Service中唯一的一个抽象方法,所以必须要在子类里实现。
  • onCreate()方法会在服务创建的时候调用,onStartCommand()方法会在每次服务启动的时候调用,onDestroy()方法会在服务销毁的时候调用。
  • 如果希望服务一旦启动就立刻去执行某个动作,就可以将逻辑写在onStartCommand()方法里
java 复制代码
public class MyService extends Service {
    private static final String TAG = "MyService";
    private Handler handler;
    private Runnable task;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "服务创建");
        // 初始化Handler和定时任务(示例:每秒打印日志)
        handler = new Handler(Looper.getMainLooper());
        task = new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "服务运行中: " + System.currentTimeMillis());
                handler.postDelayed(this, 1000); // 每隔1秒执行一次
            }
        };
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "服务启动");
        handler.post(task); // 启动定时任务
        return START_STICKY; // 服务被终止后自动重启
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "服务销毁");
        handler.removeCallbacks(task); // 停止任务,避免内存泄漏
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null; 
    }
}

1.2.2、服务注册

  • 每一个服务都需要在AndroidManifest.xml文件中进行注册才能生效
xml 复制代码
<application>
    <service android:name=".MyService" android:enabled="true" />
</application>

1.2.3、启动和停止服务

  • 启动和停止的方法主要是借助Intent来实现的
  • startService()和stopService()方法都是定义在Context类中的,所以我们在活动里可以直接调用这两个方法。
  • onCreate()方法是在服务第一次创建的时候调用的,而onStartCommand()方法则在每次启动服务的时候都会调用。
  • 服务应在工作完成后通过调用 stopSelf() 停止自身,或者另一个组件可以通过调用 stopService() 停止它。
java 复制代码
//从Activity启动/停止服务​
// 启动服务
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);

// 停止服务
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);

1.2.4、活动和服务进行通信

  • 应用组件(例如 activity)可以通过调用 startService() 并传入指定服务并包含服务要使用的任何数据的 Intent 来启动服务。服务会在 onStartCommand() 方法中接收此 Intent。
  • 当一个活动和服务绑定了之后,就可以调用该服务里的Binder提供的方法了

1.3、带绑定的服务示例

  • 用于活动和服务进行通信,比如在活动中指挥服务去看什么,服务就去干什么,主要用onBind()方法
  • 当一个活动和服务绑定之后,就可以调用该服务里的Binder提供的方法了。

示例说明:

  1. 在服务类中定义Binder的内部类,此内部类定义获取服务实例的函数,以供活动中调用;
  2. 在活动中定义ServiceConnection匿名类在重写的方法中服务的实例;
  3. 获取到服务实例后,就可以调用服务定义的方法。

1.3.1、定义服务类

java 复制代码
public class MyService extends Service {
    private static final String TAG = "MyService";
    private Handler handler;
    private Runnable task;
    private int counter = 0; // 示例:用于客户端交互的计数器

    // 1. 定义 Binder 类
    public class MyBinder extends Binder {
        public MyService getService() {
            return MyService.this; // 返回当前服务实例
        }
    }

    private final IBinder mBinder = new MyBinder(); // Binder 对象

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "服务创建");
        handler = new Handler(Looper.getMainLooper());
        task = new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "服务运行中: " + System.currentTimeMillis());
                handler.postDelayed(this, 1000);
            }
        };
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "服务启动");
        handler.post(task);
        return START_STICKY;
    }

    // 2. 实现绑定功能
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "客户端绑定");
        return mBinder; // 返回 Binder 对象
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "所有客户端解绑");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "服务销毁");
        handler.removeCallbacks(task);
    }

    // 3. 定义客户端可调用的方法
    public int getCounter() {
        return counter++;
    }

    public String formatMessage(String input) {
        return "服务处理: " + input.toUpperCase();
    }
}

1.3.2、客户端(Activity)绑定与交互​

java 复制代码
public class MainActivity extends AppCompatActivity {
    private MyService myService;
    private boolean isBound = false;

    // 1. 定义 ServiceConnection
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            MyService.MyBinder binder = (MyService.MyBinder) service;
            myService = binder.getService(); // 获取服务实例
            isBound = true;
            Log.d("Client", "绑定成功,计数器值: " + myService.getCounter());
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
        }
    };

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

        // 2. 绑定服务
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    // 3. 调用服务方法(示例:按钮点击事件)
    public void onButtonClick(View view) {
        if (isBound) {
            String result = myService.formatMessage("hello");
            Toast.makeText(this, result + " 计数:" + myService.getCounter(), 
                          Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 4. 解绑服务
        if (isBound) {
            unbindService(connection);
            isBound = false;
        }
    }
}

1.3.3、AndroidManifest.xml 注册​

xml 复制代码
<service android:name=".MyService" android:enabled="true" />

1.3.4、关键点

  • ​​Binder 机制​​

    • 通过继承 Binder类实现内部类,提供 getService()方法返回服务实例。
    • 客户端通过 ServiceConnection获取 IBinder对象并转换为具体类型。
  • ​​生命周期管理​​

    • 绑定触发顺序:onCreate()→ onBind();解绑触发 onUnbind()→ onDestroy()(若无其他绑定)。
    • 必须调用 unbindService()避免内存泄漏(通常在 onDestroy()中处理)。
  • ​​线程安全​​

    • 服务方法默认在主线程执行,若需耗时操作应另启线程(如 HandlerThread)

2、Android通知

2.1、创建通知渠道(Android 8.0+ 必需)​

java 复制代码
private void createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        String channelId = "my_channel_id";
        CharSequence name = "My Channel";
        String description = "Default Notification Channel";
        int importance = NotificationManager.IMPORTANCE_DEFAULT;

        NotificationChannel channel = new NotificationChannel(channelId, name, importance);
        channel.setDescription(description);
        
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(channel);
    }
}

2.2、发送基础通知​

java 复制代码
private void sendSimpleNotification(Context context) {
    // 1. 获取NotificationManager
    NotificationManager notificationManager = 
        (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

    // 2. 创建点击通知后跳转的Intent
    Intent intent = new Intent(context, TargetActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    PendingIntent pendingIntent = PendingIntent.getActivity(
        context, 0, intent, PendingIntent.FLAG_IMMUTABLE);

    // 3. 构建通知
    NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "my_channel_id")
        .setSmallIcon(R.drawable.ic_notification) // 必需的小图标
        .setContentTitle("新消息提醒")              // 通知标题
        .setContentText("这是一条简单的通知示例")   // 通知内容
        .setPriority(NotificationCompat.PRIORITY_DEFAULT) // 优先级
        .setContentIntent(pendingIntent)         // 设置点击行为
        .setAutoCancel(true);                    // 点击后自动消失

    // 4. 发送通知(ID唯一,可用于更新或取消)
    notificationManager.notify(1, builder.build());
}

2.3、在Activity中调用

java 复制代码
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    createNotificationChannel(); // 初始化通知渠道
    findViewById(R.id.btn_send).setOnClickListener(v -> {
        sendSimpleNotification(this); // 点击按钮发送通知
    });
}

3、Android前台服务

  • Android 8.0+限制:后台服务需使用startForeground()并显示通知,否则会被系统回收

3.1、创建前台服务类(MyForegroundService.java)​

java 复制代码
public class MyForegroundService extends Service {
    private static final String TAG = "MyForegroundService";
    private static final String CHANNEL_ID = "foreground_service_channel";
    private static final int NOTIFICATION_ID = 1;

    @Override
    public void onCreate() {
        super.onCreate();
        createNotificationChannel(); // 创建通知渠道(Android 8.0+必需)
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 构建通知
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("前台服务运行中")
                .setContentText("正在执行后台任务...")
                .setSmallIcon(R.drawable.ic_notification) // 必需的小图标
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .build();

        // 将服务提升为前台服务
        startForeground(NOTIFICATION_ID, notification);
        Log.d(TAG, "前台服务已启动");

        // 模拟任务(实际可替换为下载、播放等逻辑)
        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                    Log.d(TAG, "任务执行中...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        return START_STICKY; // 服务被终止后自动重启
    }

    // 创建通知渠道(Android 8.0+)
    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID,
                    "前台服务通知",
                    NotificationManager.IMPORTANCE_DEFAULT
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null; // 不提供绑定功能
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "服务已停止");
    }
}

3.2、在 AndroidManifest.xml 中注册服务和权限​

java 复制代码
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<application>
    <service
        android:name=".MyForegroundService"
        android:enabled="true"
        android:exported="false" />
</application>

3.3、从 Activity 启动服务​

java 复制代码
// 启动前台服务(兼容 Android 8.0+)
Intent serviceIntent = new Intent(this, MyForegroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(serviceIntent); // Android 8.0+ 专用方法
} else {
    startService(serviceIntent);
}

3.4、停止服务

java 复制代码
// 停止服务
Intent stopIntent = new Intent(this, MyForegroundService.class);
stopService(stopIntent);

3.5、注意事项​​

  • 通知渠道:Android 8.0 及以上版本必须创建通知渠道,否则通知无法显示。
  • 5秒规则:调用 startForegroundService()后需在5秒内调用 startForeground(),否则会触发 ANR。
  • 权限:Android 10+ 需在清单中声明 FOREGROUND_SERVICE权限。
  • 线程管理:前台服务默认在主线程运行,耗时操作需另启线程(如示例中的 Thread)。
相关推荐
CYRUS_STUDIO4 分钟前
Android 源码如何导入 Android Studio?踩坑与解决方案详解
android·android studio·源码阅读
前端赵哈哈1 小时前
初学者入门:Android 实现 Tab 点击切换(TabLayout + ViewPager2)
android·java·android studio
一条上岸小咸鱼4 小时前
Kotlin 控制流(二):返回和跳转
android·kotlin
Jasonakeke4 小时前
【重学 MySQL】九十二、 MySQL8 密码强度评估与配置指南
android·数据库·mysql
Mertrix_ITCH4 小时前
在 Android Studio 中修改 APK 启动图标(2025826)
android·ide·android studio
荏苒追寻5 小时前
Android OpenGL基础1——常用概念及方法解释
android
人生游戏牛马NPC1号5 小时前
学习 Android (十七) 学习 OpenCV (二)
android·opencv·学习
恋猫de小郭5 小时前
谷歌开启 Android 开发者身份验证,明年可能开始禁止“未经验证”应用的侧载,要求所有开发者向谷歌表明身份
android·前端·flutter
用户096 小时前
Gradle声明式构建总结
android
用户096 小时前
Gradle插件开发实践总结
android