Android-Mediasession-播放状态监控

张开发
2026/4/14 1:50:36 15 分钟阅读

分享文章

Android-Mediasession-播放状态监控
Android 监控 MediaSession 播放状态并打印包名的 Java 实现下面是一个完整的 Java 示例展示如何系统级监控所有应用的 MediaSession 播放状态并打印当前正在播放的应用包名。 一、核心原理通过 MediaSessionManager 获取所有活跃的 MediaSession监听其状态变化并提取对应的包名。️ 二、完整实现代码权限配置 (AndroidManifest.xml)?xml version1.0 encodingutf-8?!-- 必须权限监听媒体按键和会话 -- uses-permission android:nameandroid.permission.MEDIA_CONTENT_CONTROL / uses-permission android:nameandroid.permission.ACCESS_NOTIFICATION_POLICY / !-- ⚠️ 仅系统应用可获得 MEDIA_CONTENT_CONTROL 权限 -- !-- 普通应用需用户手动授权或使用无障碍服务 -- application service android:name.GlobalMediaMonitorService android:enabledtrue android:exportedtrue android:permissionandroid.permission.BIND_NOTIFICATION_LISTENER_SERVICE intent-filter action android:nameandroid.service.notification.NotificationListenerService / /intent-filter /service /application全局媒体监控服务 (GlobalMediaMonitorService.java)import android.app.Service;import android.content.Context;import android.media.MediaController;import android.media.MediaSessionManager;import android.media.session.MediaController.Callback;import android.media.session.MediaSession;import android.media.session.PlaybackState;import android.os.Binder;import android.os.IBinder;import android.util.Log;import java.util.ArrayList;import java.util.HashSet;import java.util.List;import java.util.Set;public class GlobalMediaMonitorService extends Service {private static final String TAG “MediaMonitor”;private MediaSessionManager mMediaSessionManager; private SetMediaController mActiveControllers new HashSet(); private final ListMediaController.Callback mControllerCallbacks new ArrayList(); // 记录最后一次播放状态避免重复打印 private String mLastPlayingPackage ; private int mLastKnownState PlaybackState.STATE_NONE; Override public void onCreate() { super.onCreate(); Log.d(TAG, 全局媒体监控服务启动); mMediaSessionManager (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE); startMonitoring(); } /** * 核心开始监控所有活跃会话 */ private void startMonitoring() { try { // 获取所有活跃的 MediaSession ListMediaController controllers mMediaSessionManager.getActiveSessions(null); for (MediaController controller : controllers) { if (controller ! null) { registerControllerCallback(controller); mActiveControllers.add(controller); // 初始检查 checkInitialState(controller); } } // 监听会话变化Android 5.0 mMediaSessionManager.addOnActiveSessionsChangedListener(this::onActiveSessionsChanged, null); } catch (SecurityException e) { Log.e(TAG, 权限不足无法访问 MediaSession: e.getMessage()); } } /** * 活跃会话列表变化回调 */ private void onActiveSessionsChanged(ListMediaController controllers) { Log.d(TAG, 活跃会话数量变化: controllers.size()); // 清理旧的 for (MediaController oldController : mActiveControllers) { if (!controllers.contains(oldController)) { unregisterControllerCallback(oldController); } } // 添加新的 for (MediaController newController : controllers) { if (!mActiveControllers.contains(newController)) { registerControllerCallback(newController); mActiveControllers.add(newController); } } } /** * 为单个控制器注册回调 */ private void registerControllerCallback(MediaController controller) { String pkg controller.getPackageName(); Log.d(TAG, 开始监控应用: pkg); MediaController.Callback callback new MediaController.Callback() { Override public void onPlaybackStateChanged(PlaybackState state) { super.onPlaybackStateChanged(state); handlePlaybackStateChange(pkg, state); } Override public void onSessionDestroyed() { super.onSessionDestroyed(); Log.d(TAG, 会话销毁: pkg); mActiveControllers.remove(controller); } }; controller.registerCallback(callback); mControllerCallbacks.add(callback); } /** * 取消回调注册 */ private void unregisterControllerCallback(MediaController controller) { controller.unregisterCallback(mControllerCallbacks.get(mActiveControllers.indexOf(controller))); } /** * 处理播放状态变化 */ private void handlePlaybackStateChange(String packageName, PlaybackState state) { if (state null) return; int stateCode state.getState(); String stateName getStateName(stateCode); // 过滤只有状态变化或包名变化时才打印 boolean shouldPrint !packageName.equals(mLastPlayingPackage) || stateCode ! mLastKnownState; if (shouldPrint) { Log.i(TAG, String.format( [%s] %s - %s (位置: %d), packageName, getAppNameFromPackage(packageName), stateName, state.getPosition() )); // 记录最后状态 mLastPlayingPackage packageName; mLastKnownState stateCode; } // 特别关注正在播放的应用 if (stateCode PlaybackState.STATE_PLAYING) { onAppStartedPlaying(packageName); } else if (stateCode PlaybackState.STATE_STOPPED || stateCode PlaybackState.STATE_NONE) { onAppStoppedPlaying(packageName); } } /** * 初始状态检查 */ private void checkInitialState(MediaController controller) { PlaybackState state controller.getPlaybackState(); if (state ! null state.getState() PlaybackState.STATE_PLAYING) { String pkg controller.getPackageName(); Log.d(TAG, 发现正在播放的应用: pkg); onAppStartedPlaying(pkg); } } /** * 应用开始播放的处理 */ private void onAppStartedPlaying(String packageName) { // 这里可以加入你的业务逻辑 Log.w(TAG, ⚠️ 检测到播放开始: packageName); // 示例限制某些应用播放 if (isRestrictedApp(packageName)) { Log.e(TAG, 受限应用正在播放: packageName); } } /** * 应用停止播放的处理 */ private void onAppStoppedPlaying(String packageName) { Log.w(TAG, ⏹️ 检测到播放停止: packageName); } /** * 获取状态名称 */ private String getStateName(int state) { switch (state) { case PlaybackState.STATE_NONE: return 无状态; case PlaybackState.STATE_STOPPED: return 已停止; case PlaybackState.STATE_PAUSED: return 已暂停; case PlaybackState.STATE_PLAYING: return 正在播放; case PlaybackState.STATE_FAST_FORWARDING: return 快进中; case PlaybackState.STATE_REWINDING: return 快退中; case PlaybackState.STATE_BUFFERING: return 缓冲中; case PlaybackState.STATE_ERROR: return 错误; case PlaybackState.STATE_CONNECTING: return 连接中; case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: return 切上一首; case PlaybackState.STATE_SKIPPING_TO_NEXT: return 切下一首; default: return 未知状态( state ); } } /** * 根据包名获取应用名称 */ private String getAppNameFromPackage(String packageName) { try { return getPackageManager().getApplicationLabel( getPackageManager().getApplicationInfo(packageName, 0) ).toString(); } catch (Exception e) { return packageName; } } /** * 判断是否为受限应用示例 */ private boolean isRestrictedApp(String packageName) { // 示例黑名单 String[] restrictedApps {com.example.restricted1, com.example.restricted2}; for (String restricted : restrictedApps) { if (restricted.equals(packageName)) return true; } return false; } Override public IBinder onBind(Intent intent) { return new MonitorBinder(); } public class MonitorBinder extends Binder { public GlobalMediaMonitorService getService() { return GlobalMediaMonitorService.this; } } Override public void onDestroy() { super.onDestroy(); Log.d(TAG, 监控服务销毁); // 清理资源 for (MediaController controller : mActiveControllers) { unregisterControllerCallback(controller); } mActiveControllers.clear(); mControllerCallbacks.clear(); }}启动监控的 Activity (MonitorActivity.java)import android.content.ComponentName;import android.content.Intent;import android.os.Bundle;import androidx.appcompat.app.AppCompatActivity;public class MonitorActivity extends AppCompatActivity {private static final String TAG “MonitorActivity”;Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_monitor); // 启动监控服务 startMonitorService(); // 检查权限 checkPermissions(); } private void startMonitorService() { try { Intent serviceIntent new Intent(this, GlobalMediaMonitorService.class); startService(serviceIntent); Log.d(TAG, 媒体监控服务已启动); } catch (SecurityException e) { Log.e(TAG, 启动服务失败可能需要系统权限: e.getMessage()); } } private void checkPermissions() { // 检查必要的权限 if (!hasMediaControlPermission()) { Log.w(TAG, 缺少 MEDIA_CONTENT_CONTROL 权限监控可能受限); // 引导用户到设置页面 openSettingsForPermission(); } } private boolean hasMediaControlPermission() { // 检查权限的逻辑 return checkSelfPermission(android.permission.MEDIA_CONTENT_CONTROL) PackageManager.PERMISSION_GRANTED; } private void openSettingsForPermission() { // 打开系统设置页面 Intent intent new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS); startActivity(intent); } Override protected void onDestroy() { super.onDestroy(); // 可选停止服务 // stopService(new Intent(this, GlobalMediaMonitorService.class)); }}简化的无障碍服务方案 (普通应用备用方案)如果无法获取 MEDIA_CONTENT_CONTROL 权限可以使用无障碍服务间接监控public class MediaAccessibilityService extends AccessibilityService {Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {if (event.getEventType() AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {// 通过窗口变化推断播放状态String packageName event.getPackageName() ! null? event.getPackageName().toString() : “”;// 检查是否有媒体通知 if (hasMediaNotification(packageName)) { Log.d(AccessibilityMedia, 检测到媒体应用: packageName); } } } private boolean hasMediaNotification(String packageName) { // 这里实现通知扫描逻辑 return false; // 简化实现 } Override public void onInterrupt() {}} 三、运行效果示例当不同应用播放时Logcat 会输出I/MediaMonitor: [com.spotify.music] Spotify - 正在播放 (位置: 125000)I/MediaMonitor: [com.google.android.youtube] YouTube - 已暂停 (位置: 45000)I/MediaMonitor: [com.netflix.ninja] Netflix - 正在播放 (位置: 89000)⚠️ 四、重要注意事项事项 说明权限限制 MEDIA_CONTENT_CONTROL 是系统级权限普通应用难以获取系统应用 此方案最适合系统内置应用或拥有平台签名的应用备用方案 普通应用可考虑通过通知监听或无障碍服务间接实现Android 版本 API 21 (Android 5.0 Lollipop) 及以上支持性能考量 监控大量会话时注意资源消耗 五、调试命令查看当前活跃的 MediaSessionadb shell dumpsys media_session强制杀死某个播放器测试状态变化adb shell am force-stop com.spotify.music模拟按键事件adb shell input keyevent KEYCODE_MEDIA_PLAY这个方案提供了一个系统级的 MediaSession 监控框架能够准确捕捉各个应用的播放状态变化并输出包名信息。

更多文章