- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在尝试构建一个极其简单的广播流媒体应用程序,它存储一个网络广播 URL 列表,可以选择这些 URL 来流式传输音频;使用服务允许在应用未激活时继续播放 + 从通知控制。
我需要的控件非常简单:播放/暂停和停止,它们应该终止服务并在清除通知或按下应用内的停止按钮时触发。
对于大量代码,我深表歉意,但这就是我现在的位置:
public class StreamingService extends Service
implements MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener {
// .. snipped out fields
private AudioManager.OnAudioFocusChangeListener mOnAudioFocusChangeListener =
new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// set mCurrentAudioFocusState field
}
if (mMediaPlayer != null)
configurePlayerState();
}
};
private int mCurrentAudioFocusState = AUDIO_NO_FOCUS_NO_DUCK;
private final IntentFilter mAudioNoisyIntentFilter =
new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private BroadcastReceiver mNoisyReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Pause when headphones unplugged
mMediaPlayer.pause();
}
};
private boolean mAudioNoisyReceiverRegistered = false;
@Override
public void onCreate() {
super.onCreate();
AudioManager mAudioManager = (AudioManager)
getSystemService(Context.AUDIO_SERVICE);
int result = mAudioManager.requestAudioFocus(
mOnAudioFocusChangeListener,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN
);
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
stopSelf();
} else {
mCurrentAudioFocusState = AUDIO_FOCUSED;
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.setOnErrorListener(this);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
WifiManager.WifiLock wifiLock =
((WifiManager) Objects.requireNonNull(
getApplicationContext().getSystemService(Context.WIFI_SERVICE)))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "wifi_lock");
wifiLock.acquire();
try {
mMediaPlayer.setDataSource(intent.getStringExtra(STREAM_URI));
} catch (IOException e) {
e.printStackTrace();
}
mMediaPlayer.prepareAsync();
onStartIntent = intent;
return Service.START_STICKY;
}
@Override
public void onDestroy() {
mMediaPlayer.release();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
mMediaPlayer.reset();
return true;
}
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
handleIntent(onStartIntent);
}
private void handleIntent(Intent intent) {
String action = intent.getAction();
String command = intent.getStringExtra(CMD_NAME);
if (ACTION_CMD.equals(action)) {
switch (command) {
case CMD_PLAY:
registerAudioNoisyReceiver();
mMediaPlayer.start();
startForeground(NOTIFICATION_ID, buildNotification());
case CMD_PAUSE:
unregisterAudioNoisyReceiver();
mMediaPlayer.pause();
startForeground(NOTIFICATION_ID, buildNotification());
case CMD_STOP:
unregisterAudioNoisyReceiver();
mMediaPlayer.stop();
stopSelf();
}
}
}
private Notification buildNotification() {
createNotificationChannel();
NotificationCompat.Builder builder =
new NotificationCompat.Builder(getApplicationContext(), NOTIFICATION_CHANNEL);
builder
.setContentTitle(onStartIntent.getStringExtra(STREAM_TITLE))
.setContentIntent(PendingIntent.getActivity(
this,
0,
new Intent(getApplicationContext(), MainActivity.class),
0))
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setDeleteIntent(getActionIntent(CMD_STOP));
builder
.setSmallIcon(android.R.drawable.ic_media_play)
.setColor(ContextCompat.getColor(this, R.color.colorPrimaryDark));
builder
.addAction(new NotificationCompat.Action(
android.R.drawable.ic_media_pause, getString(R.string.pause),
getActionIntent(CMD_PAUSE)));
builder
.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
.setShowActionsInCompactView(0)
.setShowCancelButton(true)
.setCancelButtonIntent(
getActionIntent(CMD_STOP)));
return builder.build();
}
private PendingIntent getActionIntent(String action) {
Intent s = new Intent(getApplicationContext(), StreamingService.class);
s.putExtra(
STREAM_TITLE,
onStartIntent.getStringExtra(STREAM_TITLE)
);
s.putExtra(
STREAM_URI,
onStartIntent.getStringExtra(STREAM_URI)
);
s.setAction(ACTION_CMD);
s.putExtra(
CMD_NAME,
action
);
s.setPackage(getApplicationContext().getPackageName());
return PendingIntent.getService(
getApplicationContext(), 0, s, 0);
}
// snipped methods to register and unregister noisy receiver
private void configurePlayerState() {
switch(mCurrentAudioFocusState) {
case AUDIO_NO_FOCUS_CAN_DUCK:
registerAudioNoisyReceiver();
mMediaPlayer.setVolume(VOLUME_DUCK, VOLUME_DUCK);
case AUDIO_NO_FOCUS_LOST:
unregisterAudioNoisyReceiver();
mMediaPlayer.stop();
case AUDIO_NO_FOCUS_NO_DUCK:
unregisterAudioNoisyReceiver();
mMediaPlayer.pause();
case AUDIO_FOCUSED:
registerAudioNoisyReceiver();
mMediaPlayer.setVolume(VOLUME_NORMAL, VOLUME_NORMAL);
}
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.channel_name);
String description = getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel =
new NotificationChannel(NOTIFICATION_CHANNEL, name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
assert notificationManager != null;
notificationManager.createNotificationChannel(channel);
}
}
}
这是根据使用 Google 关于媒体播放的讲座、Android 文档以及 UAMP 和其他在线示例等示例应用的想法整理而成。
目前的代码:启动,似乎设置了音频,但随后似乎暂停、停止和销毁,同时销毁了通知。应用程序内不会出现通知,也不会播放音频。这是一个日志:
05-06 12:41:21.407 1903 1994 I ActivityManager: Displayed com.ojm.pinstream/.activities.MainActivity: +727ms
05-06 12:41:23.955 1903 2517 D AudioService: Stream muted, skip playback
05-06 12:41:23.962 1903 3205 I ActivityManager: START u0 {cmp=com.ojm.pinstream/.activities.PlayActivity} from uid 10191
05-06 12:41:23.979 12786 12786 W AudioManager: Use of stream types is deprecated for operations other than volume control
05-06 12:41:23.979 12786 12786 W AudioManager: See the documentation of requestAudioFocus() for what to use instead with android.media.AudioAttributes to qualify your playback use case
05-06 12:41:23.980 1903 3205 I MediaFocusControl: requestAudioFocus() from uid/pid 10191/12786 clientId=android.media.AudioManager@6badb4bcom.ojm.pinstream.services.StreamingService$1@3626928 callingPack=com.ojm.pinstream req=1 flags=0x0 sdk=27
05-06 12:41:23.986 12786 12786 W MediaPlayer: Use of stream types is deprecated for operations other than volume control
05-06 12:41:23.986 12786 12786 W MediaPlayer: See the documentation of setAudioStreamType() for what to use instead with android.media.AudioAttributes to qualify your playback use case
05-06 12:41:23.990 12786 12786 V MediaHTTPService: MediaHTTPService(android.media.MediaHTTPService@9e12641): Cookies: null
05-06 12:41:23.992 1808 25066 D NuPlayerDriver: NuPlayerDriver(0xe8513800) created, clientPid(12786)
05-06 12:41:23.996 12786 12808 V MediaHTTPService: makeHTTPConnection: CookieManager created: java.net.CookieManager@5cb47e6
05-06 12:41:23.997 12786 12808 V MediaHTTPService: makeHTTPConnection(android.media.MediaHTTPService@9e12641): cookieHandler: java.net.CookieManager@5cb47e6 Cookies: null
05-06 12:41:24.005 12786 12808 D NetworkSecurityConfig: No Network Security Config specified, using platform default
05-06 12:41:24.053 1903 4685 E NotificationService: Suppressing notification from package by user request.
05-06 12:41:24.056 1903 1966 E NotificationService: Suppressing notification from package by user request.
05-06 12:41:24.076 12786 12791 I zygote64: Do partial code cache collection, code=60KB, data=45KB
05-06 12:41:24.076 12786 12791 I zygote64: After code cache collection, code=60KB, data=45KB
05-06 12:41:24.078 12786 12791 I zygote64: Increasing code cache capacity to 256KB
05-06 12:41:24.203 1903 1994 I ActivityManager: Displayed com.ojm.pinstream/.activities.PlayActivity: +203ms
05-06 12:41:24.227 12786 12807 D OpenGLRenderer: endAllActiveAnimators on 0x7bd8b64c00 (ListView) with handle 0x7be64b8340
05-06 12:41:27.025 1903 8861 E NotificationService: Suppressing notification from package by user request.
05-06 12:41:27.031 1903 1966 E NotificationService: Suppressing notification from package by user request.
05-06 12:41:28.257 5051 5051 V ApiRequest: Performing request to https://127.0.0.1:8384/rest/events?since=0&limit=1
05-06 12:41:28.322 5051 5051 D EventProcessor: Reading events starting with id 1675
05-06 12:41:28.322 5051 5051 V ApiRequest: Performing request to https://127.0.0.1:8384/rest/events?since=1675&limit=0
05-06 12:41:28.733 1903 8861 D WificondControl: Scan result ready event
05-06 12:41:29.020 1808 12827 D GenericSource: stopBufferingIfNecessary_l, mBuffering=0
05-06 12:41:29.020 1808 12818 D NuPlayerDriver: notifyListener_l(0xe8513800), (1, 0, 0, -1), loop setting(0, 0)
05-06 12:41:29.039 1903 3205 V MediaRouterService: restoreBluetoothA2dp(false)
05-06 12:41:29.039 1711 6225 D AudioPolicyManagerCustom: setForceUse() usage 1, config 10, mPhoneState 0
05-06 12:41:29.040 1808 2811 D NuPlayerDriver: start(0xe8513800), state is 4, eos is 0
05-06 12:41:29.041 1808 12818 I GenericSource: start
05-06 12:41:29.061 1808 12834 I OMXClient: Treble IOmx obtained
05-06 12:41:29.061 1812 1902 I OMXMaster: makeComponentInstance(OMX.google.mp3.decoder) in omx@1.0-service process
05-06 12:41:29.067 1812 1902 E OMXNodeInstance: setConfig(0xf362a720:google.mp3.decoder, ConfigPriority(0x6f800002)) ERROR: Undefined(0x80001001)
05-06 12:41:29.068 1808 12834 I ACodec : codec does not support config priority (err -2147483648)
05-06 12:41:29.068 1812 6179 E OMXNodeInstance: getConfig(0xf362a720:google.mp3.decoder, ConfigAndroidVendorExtension(0x6f100004)) ERROR: Undefined(0x80001001)
05-06 12:41:29.069 1808 12834 I MediaCodec: MediaCodec will operate in async mode
05-06 12:41:29.081 1808 2811 D NuPlayerDriver: pause(0xe8513800)
05-06 12:41:29.081 1808 2811 D NuPlayerDriver: notifyListener_l(0xe8513800), (7, 0, 0, -1), loop setting(0, 0)
05-06 12:41:29.082 1903 1966 E NotificationService: Suppressing notification from package by user request.
05-06 12:41:29.082 1903 8861 V MediaRouterService: restoreBluetoothA2dp(false)
05-06 12:41:29.084 1711 6225 D AudioPolicyManagerCustom: setForceUse() usage 1, config 10, mPhoneState 0
05-06 12:41:29.097 1808 2811 D NuPlayerDriver: stop(0xe8513800)
05-06 12:41:29.097 1808 2811 D NuPlayerDriver: notifyListener_l(0xe8513800), (8, 0, 0, -1), loop setting(0, 0)
05-06 12:41:29.101 12786 12786 V MediaPlayer: resetDrmState: mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false
05-06 12:41:29.102 12786 12786 V MediaPlayer: cleanDrmObj: mDrmObj=null mDrmSessionId=null
05-06 12:41:29.102 1808 2811 D NuPlayerDriver: reset(0xe8513800) at state 8
05-06 12:41:29.103 1903 1903 I NotificationService: Cannot find enqueued record for key: 0|com.ojm.pinstream|576|null|10191
05-06 12:41:29.108 1808 12826 I NuCachedSource2: caching reached eos.
05-06 12:41:29.108 1903 1966 E NotificationService: Suppressing notification from package by user request.
05-06 12:41:29.117 1903 3205 E NotificationService: Suppressing notification from package by user request.
05-06 12:41:29.117 1808 12818 D NuPlayerDriver: notifyResetComplete(0xe8513800)
05-06 12:41:29.121 1903 1966 E NotificationService: Suppressing notification from package by user request.
05-06 12:41:29.123 2663 2663 W StatusBar: removeNotification for unknown key: 0|com.ojm.pinstream|576|null|10191
我在 Android 开发方面经验不足。如果有人可以提供任何帮助,我们将不胜感激。
最佳答案
这是在您的代码中调用的方法链:
startForeground(NOTIFICATION_ID, buildNotification())
方法被调用buildNotification()
方法通过 getActionIntent(CMD_PAUSE)
getActionIntent()
方法调用方法PendingIntent.getService(getApplicationContext(), 0, s, 0)
问题是,根据文档,您是通过一种立即启动服务的方法获取 PendingIntent - PendingIntent.getService()
:
Retrieve a PendingIntent that will start a service, like calling {@link Context#startService Context.startService()}. The start arguments given to the service will come from the extras of the Intent.
当您的音频开始播放时,它会创建通知,获取 CMD_PAUSE 操作的挂起 Intent ,这个挂起的 Intent 启动服务,handleIntent()
方法通过待定 Intent ,然后您的音频将暂停...
根据个人经验,您应该使用以下方法进行调查:
MediaButtonReceiver.buildMediaButtonPendingIntent(context,
PlaybackStateCompat.ACTION_PLAY)
参见 MediaButtonReceiver documentation了解更多详情。
onStartCommand() 在您的媒体服务发生媒体事件(例如按下暂停按钮)时被调用 - 因此您应该实现一个简单的回调来处理 Intent :
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
MediaButtonReceiver.handleIntent(mSession, intent)
return super.onStartCommand(intent, flags, startId)
}
您需要找到一种不同的方法来将新的 URI 传递给您的服务,例如使用 MediaBrowser - 或者更简单的方法,绑定(bind)到服务并调用一种方法来从您的 Activity 中刷新 URI。您不应该从触发 onStartCommand() 的 Activity 中调用 startService()。
关于java - 如何为带有交互式通知的网络广播流创建 Android 前台服务?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50189004/
我正在关注 Android guide在媒体播放器应用程序中使用 MediaBrowserServiceCompat,但该服务在应用程序退出时被销毁。
我使用System.Windows.Window.IsActive来检测窗口是否位于前台,并且在某些情况下有效。但我发现了一些情况,它没有,我想知道是否有任何方法可以检测到它。 最佳答案 除非仅后台进
我需要为 EditText 的一部分设置样式。我希望文本为白色,背景为灰色。看起来很简单,但事实并非如此。 spanRange.setSpan(new BackgroundColorSpan(Colo
我决定在一个帖子中发布两个问题,因为这是完全相同的问题。 我需要知道屏幕何时打开或关闭,以便我可以打开 LED。第二个我需要知道我的应用程序是在后台还是在前台,以便在应用程序处于后台时管理在某些操作上
关于双高红色录音状态栏有很多问题( here , here ),但是当应用程序退出到后台时,所有这些问题都引用闪烁。我得到了一个闪光,我假设来自 AVCaptureSession设置,而应用程序在前台
我有一个奇怪的案例。我的 swift ios 应用程序已连接到 Cloudkit。如果应用程序未运行(后台状态),我每次都会收到通知徽章和警报!如果应用程序正在运行,则不会收到任何通知!我知道它没有击
我是 firebase 和 android 的新手,我想在我的应用程序中包含实时聊天。但我对 firebase 有以下疑问。请帮忙。 1) 如果应用程序在前台,系统托盘中是否会有默认通知,还是我必须在
我正在为 Excel 编写 VSTO 加载项,我注意到,如果我锁定工作表并使用密码保护它(因此只有我的加载项可以写入它,但用户可以查看它),如果用户尝试编辑工作表时,他们收到“此工作表已锁定”弹出窗口
我正在开发一个 iOS 应用程序,此应用程序允许其用户添加他稍后必须执行的任务。完成添加此任务后,它将发送到服务器以保存在服务器端。现在我对某些情况感到困惑:我的用户在输入任务详细信息时有什么电话..
我正在阅读内存不足 (OOM) killer ,以及 Android 如何确定进程的优先级 (https://developer.android.com/guide/components/proces
我已经在我的新应用程序中启动了一项服务。该服务是前台的,带有通知。当它在 AVD 2.1 API Level 7 中运行时,一切正常。但是当它在运行 Gingerbread 的 Samsung Gal
我的 Laravel 应用程序的结构需要帮助。 我想要的基本上是这个结构: 应用程序接口(interface) 管理面板 公共(public)网站 我开始构建我认为非常正确的文件夹结构: app/
我正在尝试用 CardView 填充我的 RecyclerView,CardView 使用 Android 数据绑定(bind)来设置属性,例如 TextView 中的文本。在未完成喷射的项目上,我想
我想使用 Window Script Host(WSH) 查找当前事件(具有焦点)的窗口的标题,因为我希望我的 WSH 脚本仅在所需窗口处于事件状态时才发送键。 注意*我无法使用替代方案,即在调用 s
如何调试 react-scripts 启动? 这工作正常,我不知道发生了什么变化(我没有改变任何东西) 看来 react-scripts start 无法作为前台进程继续运行。 我的 Dockerfi
我想制作像endonmondo那样的秒表。当我们启动应用程序时,它应该计算时间并更新主 Activity 中的textView(实际上它是一个 fragment )。 我做了后台服务(如 here )
我遇到了一个需求,但我无法获得正确的实现方式,因此需要您的帮助。 我想做什么? - 我想根据收到的通知执行操作,如下所示: 当应用程序打开并位于前台时,即对用户可见并且我收到通知时,我只是显示一个弹出
在 Android 10 中,我注意到我从操作系统收到一条 Toast 消息,说明“此 NFC 标签不支持应用程序”或“此 NFC 标签不支持应用程序”(取决于设备): 奇怪的是,我在 enableR
我是一名优秀的程序员,十分优秀!