I've tried following this example for creating and using a BoundService: https://developer.android.com/guide/components/bound-services#Binder
The issue is, that it won't connect to the service for me. I have an existing project with some fragment stuff in it, but in case this messes something up I've also copied it. That's my MainActivity.kt:
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.view.MenuItem
import android.view.View
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.Fragment
import com.google.android.material.navigation.NavigationView
import com.google.android.material.tabs.TabLayout
class MainActivity : AppCompatActivity() {
lateinit var toggle : ActionBarDrawerToggle
lateinit var drawerLayout: DrawerLayout
private lateinit var mService: LocalService
private var mBound: Boolean = false
/** Defines callbacks for service binding, passed to bindService() */
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
Log.w("test", "test")
val binder = service as LocalService.LocalBinder
mService = binder.getService()
mBound = true
}
override fun onServiceDisconnected(arg0: ComponentName) {
mBound = false
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
drawerLayout = findViewById(R.id.drawerLayout)
val navView : NavigationView = findViewById(R.id.nav_view)
toggle = ActionBarDrawerToggle(this, drawerLayout, R.string.open, R.string.close)
drawerLayout.addDrawerListener(toggle)
toggle.syncState()
supportActionBar?.setDisplayHomeAsUpEnabled(true)
navView.setNavigationItemSelectedListener {
// Highlight the current selected Item
it.isChecked = true
// Switch Fragments when user clicks on Items in NavDrawer
when(it.itemId) {
R.id.nav_home -> replaceFragment(HomeFragment(), it.title.toString())
R.id.nav_import -> replaceFragment(ImportFragment(), it.title.toString())
R.id.nav_export -> replaceFragment(ExportFragment(), it.title.toString())
R.id.nav_share -> replaceFragment(ShareFragment(), it.title.toString())
}
true
}
// Default Page is Home
replaceFragment(HomeFragment(), "Home")
}
// Method for replacing fragments
private fun replaceFragment(fragment: Fragment, title : String) {
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.frameLayout, fragment)
fragmentTransaction.commit()
// Close Drawer when user clicks on it
drawerLayout.closeDrawers()
// Set the correct title of current fragment
setTitle(title)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if(toggle.onOptionsItemSelected(item)) {
return true
}
return super.onOptionsItemSelected(item)
}
override fun onStart() {
super.onStart()
// Bind to LocalService
Intent(this, LocalService::class.java).also { intent ->
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
}
override fun onStop() {
super.onStop()
unbindService(connection)
mBound = false
}
/** Called when a button is clicked (the button in the layout file attaches to
* this method with the android:onClick attribute) */
fun onButtonClick(v: View) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
val num: Int = mService.randomNumber
Toast.makeText(this, "number: $num", Toast.LENGTH_SHORT).show()
}
}
}
The Button with the onButtonClick Listener is in the Home Fragment and calls the function correctly (I assume as e.g. a Toast with a testmessage then gets displayed). However, mBound always keeps the value "false" and as a result the onButtonClick function does nothing. Where is my error?
To make it completet, that's the LocalService.kt:
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import java.util.*
class LocalService : Service() {
// Binder given to clients
private val binder = LocalBinder()
// Random number generator
private val mGenerator = Random()
/** method for clients */
val randomNumber: Int
get() = mGenerator.nextInt(100)
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
inner class LocalBinder : Binder() {
// Return this instance of LocalService so clients can call public methods
fun getService(): LocalService = this@LocalService
}
override fun onBind(intent: Intent): IBinder {
return binder
}
}
CodePudding user response:
Did you declare the Service in your Android Manifest file? Try to use getApplicationContext().bindService instead of just bindService on your activity. It is using the higher level application context. I hope help you.