【问题标题】:Android Studio - Kotlin - How to make the reference to the service null?Android Studio - Kotlin - 如何使对服务的引用为空?
【发布时间】:2020-02-27 11:04:30
【问题描述】:

我正在尝试将 Google 的 LocationsUpdatesForegroundService 示例改编为 Kotlin 以在我的应用中使用。现在,一切都很好,直到我需要引用一个等于 null 的服务。这不会在它起源的 Java 代码中造成任何问题,但是,当我尝试在 Kotlin 中实现它时,即使使用 null !!,当我尝试运行应用程序并且应用程序崩溃时,我也会收到 KotlinNullPointerException。我不太确定如何避免这种情况或以不同的方式设置它。我在这上面花了几个小时,有时浏览 StackOverFlow 却没有真正找到解决方案。如果有人可以帮助我,将不胜感激。我已经附上了我要离开这里的代码的链接:

https://github.com/android/location-samples/blob/master/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/MainActivity.java#L127

...以及我在下面使用的必要代码。

我的主要活动中的相关代码:

private var lservice : LocService = null!! // A reference to the service to get location updates
private var bound = false // Tracks the bound state of the service

// Monitors the state of the connection to the service.
private val mServiceConnection = object:ServiceConnection {
    override fun onServiceConnected(name:ComponentName, service: IBinder) {
        val binder : LocService.LocalBinder = service as LocService.LocalBinder
        lservice = binder.getService()
        bound = true
    }

    override fun onServiceDisconnected(name: ComponentName) {
        lservice = null!!
        bound = false
    }
}

我的服务类,对于帮助调试此错误可能需要也可能不需要:

class LocService : Service() {

private val PACKAGE_NAME = "com.example.localization"

private val TAG = LocService::class.java!!.getSimpleName()

val ACTION_BROADCAST = PACKAGE_NAME + ".broadcast"

private val EXTRA_STARTED_FROM_NOTIFICATION = PACKAGE_NAME + ".started_from_notification"

// To return a current instance of the service
private val binder = LocalBinder()

// To check if the bounded activity has actually gone away
// and not unbound as part of an orientation change
private var changingConfig = false

private lateinit var fusedLocClient: FusedLocationProviderClient // For FusedLocationProvider API
private lateinit var locRequest : LocationRequest // Parameters for FusedLocationProvider
// Callback for changes in location
private lateinit var locCallback: LocationCallback
private lateinit var serviceHandler : Handler

private lateinit var notificationManager : NotificationManager // Notification Manager
private lateinit var loc : Location // The current location

// The identifier for the notification displayed for the foreground service
private val NOTIFICATION_ID = 12345678

// Set up when the service is created
override fun onCreate()
{
    // An instance of Fused Location Provider Client
    fusedLocClient = LocationServices.getFusedLocationProviderClient(this)

    // Obtains location callback
    locCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult?) {
            super.onLocationResult(locationResult)
            loc = locationResult!!.getLastLocation() // Obtains last location

            // Send location information to any broadcast receivers
            val intention = Intent(ACTION_BROADCAST)
            intention.putExtra("Coordinates", locationResult!!.getLastLocation())
            intention.putExtra("Address", getAddress(locationResult))
            intention.putExtra("Time", SimpleDateFormat("MM/dd/yyyy 'at' HH:mm").format(Date()))
            LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intention)

            // Change notification content if the service is running in the foreground
            if (serviceIsRunningInForeground(this@LocService))
            {
                notificationManager.notify(NOTIFICATION_ID, getNotification())
            }
        }
    }

    // Create location request and get the last location
    getLastLocation()
    buildLocReq()

    // Creates a HandlerThread, which is an extension of Thread and works
    // with a Looper, meaning it's meant to handle multiple jobs in the background
    // thread. The Looper is what keeps the thread alive. Notification Manager
    // is there to notify the user of the notification service
    val handlerThread = HandlerThread(TAG)
    handlerThread.start()
    serviceHandler = Handler(handlerThread.getLooper())
    notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
}

// Called whenever the client starts the service using startService()
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    val startedFromNotification = intent!!.getBooleanExtra(EXTRA_STARTED_FROM_NOTIFICATION, false)

    return START_NOT_STICKY // Don't recreate the service after it's killed
}

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    changingConfig = true
}

// Called when the client comes to the foreground and binds
// with this service. The service will stop being a foreground
// service when that happens
override fun onBind(intent: Intent): IBinder {
    stopForeground(true)
    changingConfig = false
    return binder
}

// Called when the client returns to the foreground
// and binds once again with this service. The service will
// stop being a foreground service when that happens
override fun onRebind(intent: Intent?) {
    stopForeground(true)
    changingConfig = false
    super.onRebind(intent)
}

// Called when the client unbinds with the service. If it's called
// with a configuration change, do nothing. Otherwise, make the service
// a foreground service
override fun onUnbind(intent: Intent?): Boolean {
    if (!changingConfig && requestingLocationUpdates(this))
    {
        startForeground(NOTIFICATION_ID, getNotification())
    }

    return true
}

// Called when service is destroyed
override fun onDestroy() {
    serviceHandler.removeCallbacksAndMessages(null)
}

inner class LocalBinder : Binder()
{
    fun getService() : LocService
    {
        return this@LocService
    }
}

// For obtaining location request
private fun buildLocReq()
{
    // Create a location request to store parameters for the requests
    locRequest = LocationRequest.create()

    // Sets priority, interval, and --smallest displacement-- for requests
    locRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
    locRequest.interval = 5000
    // locRequest.smallestDisplacement = 10f
}

private fun getLastLocation() {
    try
    {
        fusedLocClient.getLastLocation()
            .addOnCompleteListener(object:OnCompleteListener<Location>
            {
                override fun onComplete(@NonNull task:Task<Location>) {
                    if (task.isSuccessful() && task.getResult() != null)
                    {
                        loc = task.getResult() as Location
                    }
                    else
                    {
                        Log.w(TAG, "Failed to get location.")
                    }
                }
            })
    }
    catch (unlikely:SecurityException) {
        Log.e(TAG, "Lost location permission." + unlikely)
    }
}

fun requestLocationUpdates()
{
    setRequestingLocationUpdates(this, true)
    startService(Intent(getApplicationContext(), LocService::class.java))

    try
    {
        fusedLocClient.requestLocationUpdates(locRequest, locCallback, Looper.myLooper())
    } catch (unlikely:SecurityException)
    {
        setRequestingLocationUpdates(this, false)
        Log.e(TAG, "Lost location permission. Couldn't request updates. " + unlikely)
    }
}

// Obtain address via GeoCoder class
private fun getAddress(locResult: LocationResult?): String {
    var address = ""
    var geoCoder = Geocoder(this, Locale.getDefault())

    var loc1 = locResult!!.locations.get(locResult.locations.size-1)

    try {
        var addresses:ArrayList<Address> = geoCoder.getFromLocation(loc1.latitude, loc1.longitude, 1) as ArrayList<Address>
        address = addresses.get(0).getAddressLine(0)
    } catch (e: IOException) {
        e.printStackTrace()
    }

    return address
}

private fun getNotification(): Notification {
    val intent = Intent(this, LocService::class.java)

    val text = getLocationText(loc)

    val builder = NotificationCompat.Builder(this)
        .setContentText(text)
        .setOngoing(true)
        .setPriority(Notification.PRIORITY_HIGH)
        .setTicker(text)
        .setWhen(System.currentTimeMillis())

    return builder.build()
}

// Checks to see if the service is running in the foreground or not
fun serviceIsRunningInForeground(context: Context) : Boolean
{
    val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager

    for (service in manager.getRunningServices(Integer.MAX_VALUE))
    {
        if (javaClass.getName().equals(service.service.getClassName()))
        {
            if (service.foreground)
            {
                return true
            }
        }
    }

    return false
}

val KEY_REQUESTING_LOCATION_UPDATES = "requesting_locaction_updates"

// Returns true if the requesting location updates, else false
fun requestingLocationUpdates(context: Context): Boolean {
    return PreferenceManager.getDefaultSharedPreferences(context)
        .getBoolean(KEY_REQUESTING_LOCATION_UPDATES, false)
}

// Stores the location updates state in SharedPreferences
fun setRequestingLocationUpdates(context: Context, requestingLocationUpdates: Boolean)
{
    PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(KEY_REQUESTING_LOCATION_UPDATES, requestingLocationUpdates).apply()
}

// Returns the coordinates as a string for the notifications
fun getLocationText(loc: Location) : String
{
    if (loc == null) {
        return "Unknown Location"
    } else {
        return "Latitude: " + loc.longitude.toString() + " | Longitude: " + loc.longitude.toString()
    }
}
}

这是错误:

11-01 00:27:36.923 15995-15995/com.example.localization E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.localization, PID: 15995
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.localization/com.example.localization.MainActivity}: kotlin.KotlinNullPointerException
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2327)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
    at android.app.ActivityThread.-wrap11(ActivityThread.java)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
 Caused by: kotlin.KotlinNullPointerException
    at com.example.localization.MainActivity.<init>(MainActivity.kt:40)
    at java.lang.Class.newInstance(Native Method)
    at android.app.Instrumentation.newActivity(Instrumentation.java:1067)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2317)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) 
    at android.app.ActivityThread.-wrap11(ActivityThread.java) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:148) 
    at android.app.ActivityThread.main(ActivityThread.java:5417) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

【问题讨论】:

标签: kotlin service nullpointerexception


【解决方案1】:

您将lservice 声明为:

private var lservice: LocService

这意味着它不能为空; Kotlin 不允许您将其设置为 null。

(特别注意null!!总是抛出异常:!! 运算符告诉编译器将表达式视为非空,或者如果它是则抛出异常.  既然null 显然为空,你肯定是个例外!)

如果您想允许服务为空,您必须将其声明为:

private var lservice: LocService?

类型中的? 表示它可以为空。因此,您可以毫无例外地将其设置为 null。但是,使用时需要检查它是否为空,以防止出现NullPointerException

可空性对于 Kotlin 来说是非常基本的。在Kotlin language docs中都有解释。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-06
    • 1970-01-01
    • 1970-01-01
    • 2022-08-18
    • 2022-12-05
    • 1970-01-01
    相关资源
    最近更新 更多