将服务重新附加到活动?
Posted
技术标签:
【中文标题】将服务重新附加到活动?【英文标题】:Reattach Service to Activity? 【发布时间】:2018-09-03 18:58:21 【问题描述】:当我第一次打开 Activity 时,它会创建一个 MediaBrowserService。当我尝试重新打开它时,我想重新连接到它。我尝试了多种方法:
override fun onResume()
mMediaBrowser.connect()
buildTransportControls()
当我尝试这样做时,它总是会带来错误消息:
connect() called while not disconnected (state=CONNECT_STATE_CONNECTING)
即使我调用了onStop()
函数mMediaBrowser.disconnect()
,这怎么可能?或者这只是错误的方式?当我通过 BackButton 返回时,这一切都会发生。
我的完整代码:
class MusicPlayer : AppCompatActivity()
private var songAdapter : RecyclerView.Adapter<MusicRecyclerSongAdapter.ViewHolder>? = null
private var albumAdapter: RecyclerView.Adapter<MusicRecyclerAlbumAdapter.ViewHolder>? = null
val context:Context = this
//Media Browser
private lateinit var mMediaBrowser: MediaBrowserCompat
//prepare variables for late init
lateinit var songUri:ArrayList<String>
lateinit var album_name:ArrayList<String>
lateinit var mediaController : MediaControllerCompat
//------------------------------------------------------------------------------
fun buildTransportControls()
mMediaBrowser.subscribe(mMediaBrowser.root,object: MediaBrowserCompat.SubscriptionCallback())
mediaController = MediaControllerCompat.getMediaController(this@MusicPlayer)
//Show Init state
var metadat = mediaController.metadata
var pbState = mediaController.playbackState
//Register Callback to stay synced
mediaController.registerCallback(controllerCallback)
//------------------------------------------------------------------------------
val controllerCallback = object : MediaControllerCompat.Callback()
override fun onMetadataChanged(metadat: MediaMetadataCompat)
Log.i("Musik",metadat.toString())
override fun onPlaybackStateChanged(state :PlaybackStateCompat)
//------------------------------------------------------------------------------
private val mConnectionCallbacks = object : MediaBrowserCompat.ConnectionCallback()
override fun onConnected()
Log.i("Connection","Connecting")
// Get the token for the MediaSession
val token = mMediaBrowser.sessionToken
// Create a MediaControllerCompat
val mediaController = MediaControllerCompat(this@MusicPlayer,token)
// Save the controller
MediaControllerCompat.setMediaController(this@MusicPlayer, mediaController)
// Finish building the UI
buildTransportControls()
override fun onConnectionSuspended()
// The Service has crashed. Disable transport controls until it automatically reconnects
Log.i("ERROR","Connection Suspended")
override fun onConnectionFailed()
// The Service has refused our connection
Log.i("ERROR","Connection Failed")
//------------------------------------------------------------------------------
fun startSong(position: Int)
if(songAdapter == null)
songAdapter = MusicRecyclerSongAdapter(context, album_name[position])
//Getting the Location of the Songs
songUri = GetMusic.uris
recyclerViewMusic.adapter = songAdapter
else
var pbSate = MediaControllerCompat.getMediaController(this@MusicPlayer).playbackState.playbackState
MediaControllerCompat.getMediaController(this@MusicPlayer).transportControls.playFromUri(Uri.parse(songUri[position]), null)
//TODO Testing if no song is playing already
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_music_player)
//Initialize MediaBrowserServiceCompat
if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
//Create MediaBrowser Service
createMediaBrowser()
else
permissionRequest()
//Create Recycler View and get Adapter
//val recyclerViewMusic: RecyclerView = findViewById(R.id.recyclerViewMusic)
recyclerViewMusic.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL,false)
albumAdapter = MusicRecyclerAlbumAdapter(context)
//Getting values from Companion Object
album_name = GetAlbum.albums
//Apply Adapter to Recyclerview and change Backgroundcolor
recyclerViewMusic.adapter = albumAdapter
recyclerViewMusic.setBackgroundColor(Color.BLUE)
recyclerViewMusic.addOnItemTouchListener(
RecyclerItemClickListener(context, recyclerViewMusic, object : RecyclerItemClickListener.OnItemClickListener
override fun onItemClick(view: View, position: Int)
//Change to Song selection
startSong(position)
override fun onLongItemClick(view: View, position: Int)
//Change to Song selection
startSong(position)
)
)
musicprogressbar.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean)
if(fromUser)
//mplayer.seekTo(progress)
override fun onStartTrackingTouch(seekBar: SeekBar?)
//if(::mplayer.isInitialized)
// mplayer.pause()
//
override fun onStopTrackingTouch(seekBar: SeekBar?)
//if(::mplayer.isInitialized)
// mplayer.start()
)
override fun onStart()
super.onStart()
mMediaBrowser.connect()
override fun onResume()
super.onResume()
Log.i("I","RE OPEN")
Log.i("MediaBrowserStatus",mMediaBrowser.isConnected.toString())
if(!mMediaBrowser.isConnected)
mMediaBrowser.connect()
buildTransportControls()
volumeControlStream = AudioManager.STREAM_MUSIC
override fun onStop()
super.onStop()
Log.i("STOP","STOP")
if(MediaControllerCompat.getMediaController(this@MusicPlayer) != null)
MediaControllerCompat.getMediaController(this@MusicPlayer).unregisterCallback(controllerCallback)
mMediaBrowser.disconnect()
fun permissionRequest()
val permissions = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
if(permissions != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(this, arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE
),101)
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray)
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
createMediaBrowser()
fun createMediaBrowser()
Log.i("HI","HI")
mMediaBrowser = MediaBrowserCompat(this,
ComponentName(this, MediaPlaybackService::class.java),
mConnectionCallbacks,
null)
fun musicControls(view: View)
//applying functions to buttons
if (mMediaBrowser.isConnected)
when (view.id)
//Pause or Start Music
R.id.musicStart -> if (mediaController.playbackState.playbackState != null)
mediaController.transportControls.play()
Log.i("TEST",mediaController.playbackState.playbackState.toString())
R.id.musicPause -> if (mediaController.playbackState.playbackState != null)
mediaController.transportControls.pause()
Log.i("TEST",mediaController.playbackState.playbackState.toString())
/* //Forward/Backward Music
R.id.musicbackward ->
mplayer.pause()
mplayer.seekTo(mplayer.currentPosition - 5000)
mplayer.start()
R.id.musicforward ->
mplayer.pause()
mplayer.seekTo(mplayer.currentPosition + 5000)
mplayer.start()
*/
override fun onBackPressed()
super.onBackPressed()
//EDIT 询问服务的代码:
class MediaPlaybackService: MediaBrowserServiceCompat()
//private val MY_MEDIA_ROOT_ID = "MediaStore.Audio.Media.EXTERNAL_CONTENT_URI"
private val MY_MEDIA_ROOT_ID ="root"
private lateinit var mMediaSession: MediaSessionCompat
private lateinit var mStateBuilder : PlaybackStateCompat.Builder
private lateinit var MySessionCallback : MediaSessionCompat.Callback
private lateinit var mMediaPlayer: MediaPlayer
override fun onCreate()
super.onCreate()
initMediaPlayer()
//Create the MediaSession
mMediaSession = MediaSessionCompat(this,"PLAYER")
//Setting the necessary Flags (Media Buttons)
mMediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
//Set an inital PlaybackStatewith ACTION_BUTTONS, so Media buttons can start the player
mStateBuilder = PlaybackStateCompat.Builder().setActions(
PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PAUSE)
//Set our PLaybackState for the MediaSession
mMediaSession.setPlaybackState(mStateBuilder.build())
MySessionCallback = object : MediaSessionCompat.Callback()
override fun onPlayFromUri(uri: Uri?, extras: Bundle?)
super.onPlayFromUri(uri, extras)
if(!mMediaPlayer.isPlaying)
mMediaPlayer.setDataSource(application.applicationContext, uri)
mMediaPlayer.setOnPreparedListener
mMediaPlayer.start()
Log.i("DURATION",mMediaPlayer.duration.toString())
mMediaPlayer.prepareAsync()
override fun onPause()
super.onPause()
if(mMediaPlayer.isPlaying)
mMediaPlayer.pause()
override fun onPlay()
super.onPlay()
mMediaPlayer.start()
//Handles callbacks from Media Controller MySessionCalback is a PlaeHolder
mMediaSession.setCallback(MySessionCallback)
mMediaSession.isActive = true
//Set SessionToken so Activites can communicate with it
setSessionToken(mMediaSession.getSessionToken());
override fun onLoadChildren(parentId: String, result: Result<MutableList<MediaBrowserCompat.MediaItem>>)
var mediaItems:ArrayList<MediaBrowserCompat.MediaItem> = ArrayList()
/*
var albums = MusicLibrary(this@MediaPlaybackService).getMusic()
for(item in MusicLibrary.MusicFiles)
val songList = MediaBrowserCompat.MediaItem(item,
MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
mediaItems.add(songList)
Log.i("MEDIA_ITEMS",mediaItems.toString())
*/
result.sendResult(mediaItems)
override fun onGetRoot(clientPackageName: String, clientUid: Int, rootHints: Bundle?): BrowserRoot?
return BrowserRoot(MY_MEDIA_ROOT_ID,null)
fun initMediaPlayer()
mMediaPlayer = MediaPlayer()
【问题讨论】:
【参考方案1】:您看到的问题是由于连接和断开连接的调用是异步的。当您拨打connect()
时,不会立即建立连接。您只是“请求连接”。只有在回调 onConnected()
被调用后,您才有连接。
因此,当用户从另一个Activity
返回时,首先您在onStart()
中调用connect()
,然后几乎立即调用onResume()
。在onResume()
中调用isConnected()
时,这将返回false
,因为尚未建立连接(即:尚未调用回调onConnected()
)。然后,您调用 connect()
失败并出现给定的异常。你甚至可以从异常信息中看到:
connect() called while not disconnected (state=CONNECT_STATE_CONNECTING)
当前状态不是“CONNECTED”而是“CONNECTING”。
在与MediaBrowser
交互之前,您需要等待异步回调到达。
【讨论】:
好的。到现在为止还挺好。但是我的问题仍然存在,那就是总是创建一个 My MediaBrowserService 的新对象。如何重新连接到预先存在的 Servicec? 请通过编辑问题并将代码添加到您的MediaBrowserService
发布代码。是否有可能您的 Service
正在崩溃,这就是为什么 android 总是在您断开连接和连接时创建一个新实例?
我在代码中看不到任何明显的东西。如果您扫描 logcat(不要过滤它),您是否看到有关 Service
的任何错误或消息?
我对@987654337@ 的工作方式不是很熟悉。如果创建了新实例,为什么会出现问题?我假设当所有客户端断开连接时,Android 会终止 Service
。如果没有连接的客户端,为什么Service
需要继续运行?您还可以通过覆盖onDestroy()
并调用super.onDestroy()
以及将日志消息写入logcat 来查看Android 是否正在关闭Service
。如果调用onDestroy()
,Android 将关闭Service
。否则,它正在崩溃或被 Android 杀死。
我无法相信该服务已被杀死,因为音乐仍在继续播放。但我找到了灵魂。我创建了一个连接类。此类连接而不是 Activity,然后将 mediaController asynchrone 发送到 Activity(如果它已打开)以上是关于将服务重新附加到活动?的主要内容,如果未能解决你的问题,请参考以下文章