Android Studio - Kotlin - Как сделать ссылку на сервис нулевой? - PullRequest
0 голосов
/ 01 ноября 2019

Я пытаюсь адаптировать пример Google LocationsUpdatesForegroundService в Kotlin для использования в моем приложении. Теперь все идет хорошо, пока мне не нужно сделать ссылку на сервис, равный нулю. Это не вызывает никаких проблем в 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)

1 Ответ

1 голос
/ 01 ноября 2019

Вы объявляете lservice как:

private var lservice: LocService

Это означает, что это не обнуляемо;Kotlin не позволит вам установить его в null.

(В частности, обратите внимание, что null!! будет всегда генерировать исключение: оператор !! указывает компилятору обрабатыватьВыражение как ненулевое, или для исключения, если оно есть. А поскольку null, очевидно, равно null, вы гарантировано исключение!)

Если вы хотите разрешитьДля того чтобы служба была нулевой, вам нужно объявить ее как:

private var lservice: LocService?

* * * * * * * * * * * * * в типе означает, что он может иметь значение NULL. В результате вы сможете установить его на ноль без каких-либо исключений. Тем не менее, вам нужно проверить, является ли он нулевым, когда вы используете , чтобы предотвратить NullPointerException.

Обнуляемость довольно проста для Kotlin. Все это объясняется в документации по языку Kotlin .

...