How to fix "lateinit property YYY has not been initialized" - android-studio

I have a class that makes bluetooth connection and gets a PID from an OBD bluetooth dongle that its connected in the car.
But when calling the method for getting RPM then I have an Exception about not initialazing that property.
class BluetoothOBDManager : OBDManager {
lateinit var context: Context
private lateinit var input: InputStream
private lateinit var output: OutputStream
private lateinit var rpmCommand: RPMCommand
private lateinit var coolantTempCommand: EngineCoolantTemperatureCommand
private lateinit var airIntakeTempCommand: AirIntakeTemperatureCommand
private lateinit var mmSocket : BluetoothSocket
fun connect(deviceString: String) {
var adapter: BluetoothAdapter = getDefaultAdapter()
val device = adapter.getRemoteDevice(deviceString)
val uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
val mmSocket:BluetoothSocket=device.createRf....Record(uuid)
mmSocket.let { socket ->
socket.connect()
}
input = mmSocket.inputStream
output = mmSocket.outputStream
if (mmSocket.isConnected) {
println("output.write =1")
output.write(1)
}
rpmCommand = RPMCommand() //This is the INIT of the propert
}
override fun getCurrentRpm(): Int {
rpmCommand.run(input, output) // This line throughs the EXCEPTION
return rpmCommand.rpm
}
}
This is how I call the method from another class
BluetoothOBDManager().connect(deviceString)
var rpm = BluetoothOBDManager().getCurrentRpm()
rpmTextView.setText(rpm)
The exception
E/AndroidRuntime: FATAL EXCEPTION: main
Process: gr.obd.logger, PID: 17486
kotlin.UninitializedPropertyAccessException: lateinit property rpmCommand has not been initialized
I have tried to INIT the property inside the method but no luck
override fun getCurrentRpm(): Int {
rpmCommand = RPMCommand()
rpmCommand.run(input, output) // This line throughs the EXCEPTION
return rpmCommand.rpm
}

BluetoothOBDManager().connect(deviceString)
var rpm = BluetoothOBDManager().getCurrentRpm()
Your code creates a BluetoothOBDManager, connects it (initializing rpmCommand), then throws it away, creates a new BluetoothOBDManager with everything uninitialized and tries to call getCurrentRpm on it. It likely should be
val obdManager = BluetoothOBDManager()
obdManager.connect(deviceString)
val rpm = obdManager.getCurrentRpm()
This doesn't explain why you would get
I have tried to INIT the property inside the method but no luck
but that really shouldn't happen; I expect something else is going wrong.

Related

lateinit property mAdapter has not been initialized. How to solve this problem?

MainActivity.kt
class MainActivity : AppCompatActivity(), NewsItemclicked {
private lateinit var mAdapter: NewsListAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
fetchData()
val adapter = NewsListAdapter( this)
recyclerView.adapter = mAdapter
}
private fun fetchData(){
val url = "https://saurav.tech/NewsAPI/top-headlines/category/sports/in.json"
val jsonObjectRequest = JsonObjectRequest(
Request.Method.GET,
url,
null,
Response.Listener {
val newsJsonArray = it.getJSONArray("articles")
val newsArray = ArrayList<News>()
for(i in 0 until newsJsonArray.length()){
val newsJsonObject = newsJsonArray.getJSONObject(i)
val news = News(
newsJsonObject.getString("title"),
newsJsonObject.getString("author"),
newsJsonObject.getString("url"),
newsJsonObject.getString("urlToImage")
)
newsArray.add(news)
}
mAdapter.updateNews(newsArray)
},
Response.ErrorListener{
}
)
MySingleton.getInstance(this).addToRequestQueue(jsonObjectRequest)
}
override fun onItemClicked(item: News) {
}
}
On the above MainActivity.kt of a News app is given.When I try to run the app the app is crashing. It is showing that lateinit property mAdapter has not been initialized. Please help me to figure out the problem.Please try to explain the simplest way as I am a beginner to Android so it is quite difficult to me to understand it quickly.
Error
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.newstoday, PID: 10633
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.newstoday/com.example.newstoday.MainActivity}: kotlin.UninitializedPropertyAccessException: lateinit property mAdapter has not been initialized
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property mAdapter has not been initialized
It seems like you're instantiating your adapter and store the reference in a local variable (adapter) instead of the global one (mAdapter) which you specifically created to update its data once you receive the response from the network request.
If you change your code to:
mAdapter = NewsListAdapter(this)
the crash would be solved.

Custom Lazy<Deferred<T>> fun leads to frame skipping and crash

I'm following the MVVM using ROOM,Retrofit,Koin DI etc. and inside of my MainFragment class i'm calling my bindUI() function that is responsible for fetching data through the viewModel asynchronously using kotlin coroutines as you can see below. Now, when i run my app it crashes almost immediately.
Here's what i tried: I placed a breakpoint inside of bindUI() and specifically on my first .await() call on val currentWeather and run the debugger. I noticed that as soon as the await call is resolved and the result is returned to the variable, the application crashes saying that Skipped 1501 frames! The application may be doing too much work on its main thread. and then Skipped 359 frames! The application may be doing too much work on its main thread.
Now, why would that be since i'm running those async calls inside of the Dispathcers.IO thread and at the moment of the crash i'm only executing one await() call?
Here's my MainFragment class:
const val UNIT_SYSTEM_KEY = "UNIT_SYSTEM"
class MainFragment(
private val weatherUnitConverter: WeatherUnitConverter
) : ScopedFragment() {
// Lazy inject the view model
private val viewModel: WeatherViewModel by viewModel()
private lateinit var unitSystem:String
private val TAG = MainFragment::class.java.simpleName
// View declarations
private lateinit var lcHourlyForecasts: LineChart
private lateinit var weeklyForecastRCV: RecyclerView
private lateinit var scrollView: NestedScrollView
private lateinit var detailsExpandedArrow:ImageView
private lateinit var detailsExpandedLayout: LinearLayout
private lateinit var dailyWeatherDetailsHeader:LinearLayout
private lateinit var settingsBtnImageView:ImageView
private lateinit var unitSystemImgView:ImageView
private lateinit var locationTxtView:TextView
// Current weather view declarations
private lateinit var currentWeatherDate:TextView
private lateinit var currentWeatherTemp:TextView
private lateinit var currentWeatherSummaryText:TextView
private lateinit var currentWeatherSummaryIcon:ImageView
private lateinit var currentWeatherPrecipProb:TextView
// Today/Details weather view declarations
private lateinit var todayHighLowTemp:TextView
private lateinit var todayWindSpeed:TextView
private lateinit var todayFeelsLike:TextView
private lateinit var todayUvIndex:TextView
private lateinit var todayPrecipProb:TextView
private lateinit var todayCloudCover:TextView
private lateinit var todayHumidity:TextView
private lateinit var todayPressure:TextView
private lateinit var todaySunriseTime:TextView
private lateinit var todaySunsetTime:TextView
// OnClickListener to handle the current weather's "Details" layout expansion/collapse
private val onCurrentWeatherDetailsClicked:View.OnClickListener = View.OnClickListener {
if(detailsExpandedLayout.visibility == View.GONE) {
detailsExpandedLayout.visibility = View.VISIBLE
detailsExpandedArrow.setImageResource(R.drawable.ic_arrow_up_black)
}
else {
detailsExpandedLayout.visibility = View.GONE
detailsExpandedArrow.setImageResource(R.drawable.ic_down_arrow)
}
}
// OnClickListener to allow navigating from this fragment to the settings one
private val onSettingsButtonClicked:View.OnClickListener = View.OnClickListener {
(activity as MainActivity).openSettingsPage()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
val view = inflater.inflate(R.layout.main_fragment, container, false)
// View initializations
scrollView = view.findViewById(R.id.nsv_main)
lcHourlyForecasts = view.findViewById(R.id.lc_hourly_forecasts)
detailsExpandedLayout = view.findViewById(R.id.ll_expandable)
detailsExpandedArrow = view.findViewById(R.id.iv_arrow)
dailyWeatherDetailsHeader = view.findViewById(R.id.current_weather_details_header)
dailyWeatherDetailsHeader.setOnClickListener(onCurrentWeatherDetailsClicked)
settingsBtnImageView = view.findViewById(R.id.settings)
settingsBtnImageView.setOnClickListener(onSettingsButtonClicked)
unitSystemImgView = view.findViewById(R.id.unitSystemImg)
locationTxtView = view.findViewById(R.id.location)
initCurrentWeatherViews(view)
initTodayWeatherViews(view)
// RCV initialization
weeklyForecastRCV = view.findViewById(R.id.weekly_forecast_rcv)
weeklyForecastRCV.adapter = WeeklyWeatherAdapter(listOf(),viewModel.preferences, this,weatherUnitConverter) // init the adapter with empty data
weeklyForecastRCV.setHasFixedSize(true)
// Disable nested scrolling to control the RCV scrolling via the parent NestedScrollView
weeklyForecastRCV.isNestedScrollingEnabled = false
return view
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
initLineChart()
bindUI()
}
private fun SharedPreferences.stringLiveData(key: String, defValue: String): SharedPreferenceLiveData<String> {
return SharedPreferenceStringLiveData(this, key, defValue)
}
private fun bindUI() = launch(Dispatchers.Main) {
//TODO:sp get the coordinates dynamically
viewModel.setLocCoordinates(37.8267,-122.4233)
// fetch current weather
val currentWeather = viewModel.currentWeatherData.await()
// fetch weekly weather
val weeklyWeather = viewModel.weeklyWeatherEntries.await()
// fetch the location
val weatherLocation = viewModel.weatherLocation.await()
// Observe the location for changes
weatherLocation.observe(viewLifecycleOwner, Observer { location ->
if(location == null) return#Observer
launch {
updateLocation(location)
}
})
// Observe the current weather live data
currentWeather.observe(viewLifecycleOwner, Observer {currently ->
if(currently == null) return#Observer
setCurrentWeatherDate(currently.time.toDouble())
// Observe the unit system sharedPrefs live data for changes
viewModel.preferences.stringLiveData(UNIT_SYSTEM_KEY, UnitSystem.SI.name.toLowerCase(Locale.ROOT))
.observe(viewLifecycleOwner, Observer {unitSystem ->
when(unitSystem) {
UnitSystem.SI.name.toLowerCase(Locale.ROOT) -> {
setCurrentWeatherTemp(currently.temperature)
setUnitSystemImgView(unitSystem)
}
UnitSystem.US.name.toLowerCase(Locale.ROOT) -> {
setCurrentWeatherTemp(weatherUnitConverter.convertToFahrenheit(
currently.temperature
))
setUnitSystemImgView(unitSystem)
}
}
})
setCurrentWeatherSummaryText(currently.summary)
setCurrentWeatherSummaryIcon(currently.icon)
setCurrentWeatherPrecipProb(currently.precipProbability)
})
// observe the weekly weather live data
weeklyWeather.observe(viewLifecycleOwner, Observer {weatherEntries ->
if(weatherEntries == null) return#Observer
// update the recyclerView with the new data
(weeklyForecastRCV.adapter as WeeklyWeatherAdapter).updateWeeklyWeatherData(weatherEntries)
initTodayData(weatherEntries[0])
})
}
/**
* Uses the location param's lat & longt values
* to determine the selected location and updates
* the view.
*/
private suspend fun updateLocation(location: WeatherLocation) {
withContext(Dispatchers.IO) {
val geocoder = Geocoder(activity,Locale.getDefault())
try {
val addr = geocoder.getFromLocation(location.latitude,location.longitude,1)
val adobj = addr[0]
locationTxtView.text = adobj.countryName
} catch (e:IOException) {
Log.d(TAG, e.printStackTrace().toString())
}
}
}
/**
* Initializes the views for the current weather.
*/
private fun initCurrentWeatherViews(view: View) {
currentWeatherDate = view.findViewById(R.id.current_weather_date)
currentWeatherTemp = view.findViewById(R.id.current_temp_main)
currentWeatherSummaryText = view.findViewById(R.id.current_weather_summary_text)
currentWeatherSummaryIcon = view.findViewById(R.id.current_weather_summary_icon)
currentWeatherPrecipProb = view.findViewById(R.id.current_weather_precip_text)
}
/**
* Initializes the views for the Detailed Today weather view.
*/
private fun initTodayWeatherViews(view: View?) {
if(view == null) return
todayHighLowTemp = view.findViewById(R.id.today_lowHighTemp)
todayWindSpeed = view.findViewById(R.id.today_windSpeed)
todayFeelsLike = view.findViewById(R.id.today_feelsLike)
todayUvIndex = view.findViewById(R.id.today_uvIndex)
todayPrecipProb = view.findViewById(R.id.today_precipProb)
todayCloudCover = view.findViewById(R.id.today_cloudCover)
todayHumidity = view.findViewById(R.id.today_humidity)
todayPressure = view.findViewById(R.id.today_pressure)
todaySunriseTime = view.findViewById(R.id.today_sunriseTime)
todaySunsetTime = view.findViewById(R.id.today_sunsetTime)
}
private fun setUnitSystemImgView(unitSystem:String) {
val resource = when(unitSystem) {
UnitSystem.SI.name.toLowerCase(Locale.ROOT)
-> R.drawable.ic_celsius
UnitSystem.US.name.toLowerCase(Locale.ROOT)
-> R.drawable.ic_fahrenheit
else -> R.drawable.ic_celsius
}
unitSystemImgView.setImageResource(resource)
}
/**
* Links the data to the view for the Today(Details) Weather View.
*/
private fun initTodayData(weekDayWeatherEntry: WeekDayWeatherEntry) {
// Observe the unit system sharedPrefs live data for changes
viewModel.preferences.stringLiveData(UNIT_SYSTEM_KEY, UnitSystem.SI.name.toLowerCase(Locale.ROOT))
.observe(viewLifecycleOwner, Observer {unitSystem ->
when(unitSystem) {
UnitSystem.SI.name.toLowerCase(Locale.ROOT) -> {
setTodayWeatherLowHighTemp(weekDayWeatherEntry.temperatureLow,weekDayWeatherEntry.temperatureHigh)
setTodayWeatherWindSpeed(weekDayWeatherEntry.windSpeed,unitSystem)
setTodayWeatherFeelsLike(weekDayWeatherEntry.apparentTemperatureLow,weekDayWeatherEntry.apparentTemperatureHigh)
}
UnitSystem.US.name.toLowerCase(Locale.ROOT) -> {
setTodayWeatherLowHighTemp(weatherUnitConverter.convertToFahrenheit(
weekDayWeatherEntry.temperatureLow),
weatherUnitConverter.convertToFahrenheit(
weekDayWeatherEntry.temperatureHigh))
setTodayWeatherWindSpeed(weatherUnitConverter.convertToMiles(weekDayWeatherEntry.windSpeed),unitSystem)
setTodayWeatherFeelsLike(weatherUnitConverter.convertToFahrenheit(
weekDayWeatherEntry.apparentTemperatureLow)
,weatherUnitConverter.convertToFahrenheit(weekDayWeatherEntry.apparentTemperatureHigh))
}
}
})
setTodayWeatherUvIndex(weekDayWeatherEntry.uvIndex)
setTodayWeatherPrecipProb(weekDayWeatherEntry.precipProbability)
setTodayWeatherCloudCover(weekDayWeatherEntry.cloudCover)
setTodayWeatherHumidity(weekDayWeatherEntry.humidity)
setTodayWeatherPressure(weekDayWeatherEntry.pressure)
setTodayWeatherSunriseTime(weekDayWeatherEntry.sunriseTime)
setTodayWeatherSunsetTime(weekDayWeatherEntry.sunsetTime)
}
...
}
WeatherViewModel.kt:
class WeatherViewModel(
private val forecastRepository: ForecastRepository,
context:Context
) : ViewModel() {
private var mLatitude:Double = 0.0
private var mLongitute:Double = 0.0
private val appContext = context.applicationContext
// Retrieve the sharedPrefs
val preferences:SharedPreferences
get() = PreferenceManager.getDefaultSharedPreferences(appContext)
// This will run only when currentWeatherData is called from the View
val currentWeatherData by lazyDeferred {
forecastRepository.getCurrentWeather(mLatitude, mLongitute)
}
val weeklyWeatherEntries by lazyDeferred {
val currentDateEpoch = LocalDate.now().toEpochDay()
forecastRepository.getWeekDayWeatherList(mLatitude, mLongitute, currentDateEpoch)
}
val weatherLocation by lazyDeferred {
forecastRepository.getWeatherLocation()
}
fun setLocCoordinates(latitude:Double,longitude:Double) {
mLatitude = latitude
mLongitute = longitude
}
}
Here's my custom Lazy<Deferred<T>> fun inside of my Delegates.kt file:
fun<T> lazyDeferred(block: suspend CoroutineScope.() -> T) : Lazy<Deferred<T>> {
return lazy {
GlobalScope.async(start = CoroutineStart.LAZY) {
block.invoke(this)
}
}
}
Here's my repository class just in case:
private const val WEEKLY_FORECAST_DAYS_COUNT = 7
/**
* The Repository class responsible
* for caching the downloaded weather data
* and for swapping between different data sources.
*/
class ForecastRepositoryImpl(
private val currentWeatherDao: CurrentWeatherDao,
private val weekDayWeatherDao: WeekDayWeatherDao,
private val weatherLocationDao: WeatherLocationDao,
private val locationProvider: LocationProvider,
private val weatherNetworkDataSource: WeatherNetworkDataSource
) : ForecastRepository {
init {
weatherNetworkDataSource.apply {
// Persist downloaded data
downloadedCurrentWeatherData.observeForever { newCurrentWeather: CurrentWeatherResponse? ->
persistFetchedCurrentWeather(newCurrentWeather!!)
}
downloadedWeeklyWeatherData.observeForever { newWeeklyWeather: WeeklyWeatherResponse? ->
persistFetchedWeeklyWeather(newWeeklyWeather!!)
}
}
}
override suspend fun getCurrentWeather(latitude:Double,longitude:Double): LiveData<CurrentWeatherEntry> {
return withContext(Dispatchers.IO) {
initWeatherData(latitude,longitude)
return#withContext currentWeatherDao.getCurrentWeather()
}
}
override suspend fun getWeekDayWeatherList(latitude: Double,longitude: Double,time:Long): LiveData<out List<WeekDayWeatherEntry>> {
return withContext(Dispatchers.IO) {
initWeatherData(latitude,longitude)
return#withContext weekDayWeatherDao.getFutureWeather(time)
}
}
override suspend fun getWeatherLocation(): LiveData<WeatherLocation> {
return withContext(Dispatchers.IO) {
return#withContext weatherLocationDao.getWeatherLocation()
}
}
private suspend fun initWeatherData(latitude:Double,longitude:Double) {
// retrieve the last weather location from room
val lastWeatherLocation = weatherLocationDao.getWeatherLocation().value
if(lastWeatherLocation == null ||
locationProvider.hasLocationChanged(lastWeatherLocation)) { // then this is the first time we are launching the app
fetchCurrentWeather()
fetchWeeklyWeather()
return
}
if(isFetchCurrentNeeded(lastWeatherLocation.zonedDateTime))
fetchCurrentWeather()
if(isFetchWeeklyNeeded())
fetchWeeklyWeather()
}
/**
* Checks if the current weather data should be re-fetched.
* #param lastFetchedTime The time at which the current weather data were last fetched
* #return True or false respectively
*/
private fun isFetchCurrentNeeded(lastFetchedTime: ZonedDateTime) : Boolean {
val thirtyMinutesAgo = ZonedDateTime.now().minusMinutes(30)
return lastFetchedTime.isBefore(thirtyMinutesAgo)
}
/**
* Fetches the Current Weather data from the WeatherNetworkDataSource.
*/
private suspend fun fetchCurrentWeather() {
weatherNetworkDataSource.fetchCurrentWeather(
locationProvider.getPreferredLocationLat(),
locationProvider.getPreferredLocationLong()
)
}
private fun isFetchWeeklyNeeded(): Boolean {
val todayEpochTime = LocalDate.now().toEpochDay()
val futureWeekDayCount = weekDayWeatherDao.countFutureWeekDays(todayEpochTime)
return futureWeekDayCount < WEEKLY_FORECAST_DAYS_COUNT
}
private suspend fun fetchWeeklyWeather() {
weatherNetworkDataSource.fetchWeeklyWeather(
locationProvider.getPreferredLocationLat(),
locationProvider.getPreferredLocationLong()
)
}
/**
* Caches the downloaded current weather data to the local
* database.
* #param fetchedCurrentWeather The most recently fetched current weather data
*/
private fun persistFetchedCurrentWeather(fetchedCurrentWeather:CurrentWeatherResponse) {
// Using a GlobalScope since a Repository class doesn't have a lifecycle
GlobalScope.launch(Dispatchers.IO) {
// cache the data
currentWeatherDao.upsert(fetchedCurrentWeather.currentWeatherEntry)
weatherLocationDao.upsert(fetchedCurrentWeather.location)
}
}
/**
* Caches the downloaded weekly weather data to the local
* database.
* #param fetchedWeeklyWeather The most recently fetched weekly weather data
*/
private fun persistFetchedWeeklyWeather(fetchedWeeklyWeather: WeeklyWeatherResponse) {
fun deleteOldData() {
val time = LocalDate.now().toEpochDay()
weekDayWeatherDao.deleteOldEntries(time)
}
GlobalScope.launch(Dispatchers.IO) {
deleteOldData()
val weekDayEntriesList = fetchedWeeklyWeather.weeklyWeatherContainer.weekDayEntries
weekDayWeatherDao.insert(weekDayEntriesList)
}
}
}
EDIT: Here's the crash log i found out about earlier today:
2020-04-13 01:43:48.628 26875-26904/com.nesoinode.flogaweather E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-2
Process: com.nesoinode.flogaweather, PID: 26875
java.lang.NullPointerException: Attempt to invoke virtual method 'int com.nesoinode.flogaweather.model.db.entity.WeatherLocation.getId()' on a null object reference
at com.nesoinode.flogaweather.model.db.dao.WeatherLocationDao_Impl$1.bind(WeatherLocationDao_Impl.java:34)
at com.nesoinode.flogaweather.model.db.dao.WeatherLocationDao_Impl$1.bind(WeatherLocationDao_Impl.java:26)
at androidx.room.EntityInsertionAdapter.insert(EntityInsertionAdapter.java:63)
at com.nesoinode.flogaweather.model.db.dao.WeatherLocationDao_Impl.upsert(WeatherLocationDao_Impl.java:52)
at com.nesoinode.flogaweather.model.repository.ForecastRepositoryImpl$persistFetchedCurrentWeather$1.invokeSuspend(ForecastRepositoryImpl.kt:131)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:561)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:727)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:667)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:655)
It's pointing to this part of my repository class as the root cause. I can't tell why though.
private fun persistFetchedCurrentWeather(fetchedCurrentWeather:CurrentWeatherResponse) {
// Using a GlobalScope since a Repository class doesn't have a lifecycle
GlobalScope.launch(Dispatchers.IO) {
// cache the data
currentWeatherDao.upsert(fetchedCurrentWeather.currentWeatherEntry)
weatherLocationDao.upsert(fetchedCurrentWeather.location)
}
}
UPDATE #2:
CurrentWeatherEntry :
const val CURRENT_WEATHER_ID = 0
#Entity(tableName = "current_weather")
data class CurrentWeatherEntry(
val time: Long, // epoch timestamp
val icon: String,
val summary: String,
val precipProbability: Double,
val temperature: Double
) {
#PrimaryKey(autoGenerate = false)
var id:Int = CURRENT_WEATHER_ID
}
WeatherLocation:
const val WEATHER_LOCATION_ID = 0
#Entity(tableName = "weather_location")
data class WeatherLocation(
val latitude: Double,
val longitude: Double,
val timezone: String
) {
#PrimaryKey(autoGenerate = false)
var id:Int = WEATHER_LOCATION_ID
private var epochTimeVal:Long = 0
val zonedDateTime:ZonedDateTime
get() {
val instant = Instant.ofEpochMilli(this.epochTimeVal)
val zoneId = ZoneId.of(timezone)
return ZonedDateTime.ofInstant(instant,zoneId)
}
fun setEpochTimeVal(time:Long) {
this.epochTimeVal = time}
fun getEpochTimeVal() : Long = epochTimeVal
}
and CurrentWeatherResponse:
data class CurrentWeatherResponse(
// Tells GSON that the "currently" field of the JSON returned by the
// API should be tied with our CurrentWeatherEntry data class
#SerializedName("currently")
val currentWeatherEntry: CurrentWeatherEntry,
#Embedded
val location: WeatherLocation
) {
init {
location.setEpochTimeVal(currentWeatherEntry.time)
}
}
According to the further diagnosis you made, the problem is unrelated to lazyDeferred, blocking the main thread, or coroutines in general. Your CurrentWeatherDao sometimes returns a CurrentWeatherResponse with location == null.
You never specified where forecastRepository.getWeatherLocation() should be executed so it gets executed on the dispatcher of your bindUI function which is Dispatchers.Main.
This means the request blocks your UI thread and causes the warning you're seeing in the log.
You need to specify that it executes on a separate dispatcher so the UI can continue updating normally:
lazyDeferred {
withContext(Dispatchers.IO) {
forecastRepository.getWeatherLocation()
}
}
As a separate issue, your lazyDeferred is slightly redundant in that it is "double"-lazy. You could remove the outer Lazy<T> and it would still work exactly the same way, or remove the start = CoroutineStart.LAZY and have the result arrive slightly sooner. (This essentially depends on whether the request starts when the Lazy is resolved, or when Deferred.await is called)

How to start a thread that is defined in a private inner class?

I found this example of how to read from a Bluetooth socket using a separate read thread on the Android developer website. The read thread is defined in the private inner class "ConnectedThread".
class MyBluetoothService(
// handler that gets info from Bluetooth service
private val handler: Handler) {
private inner class ConnectedThread(private val mmSocket: BluetoothSocket) : Thread() {
private val mmInStream: InputStream = mmSocket.inputStream
private val mmOutStream: OutputStream = mmSocket.outputStream
private val mmBuffer: ByteArray = ByteArray(1024) // mmBuffer store for the stream
override fun run() {
var numBytes: Int // bytes returned from read()
// Keep listening to the InputStream until an exception occurs.
while (true) {
// Read from the InputStream.
numBytes = try {
mmInStream.read(mmBuffer)
} catch (e: IOException) {
Log.d(TAG, "Input stream was disconnected", e)
break
}
// Send the obtained bytes to the UI activity.
val readMsg = handler.obtainMessage(
MESSAGE_READ, numBytes, -1,
mmBuffer)
readMsg.sendToTarget()
}
}
//Other functions like write, cancel that I omitted from this example
}
So I added a function in MyBluetoothService to start the read thread:
#JvmStatic
fun read(){
val reader = ConnectedThread(myBluetoothSocket)
Reader.start()
}
But this gives an immediate error:
Constructor of inner class ConnectedThread can be called only with
receiver of containing class
How should I start the thread from the example code?
Your ConnectedThread is an inner class of MyBluetoothService so it can't be instantiated outside an instance of MyBluetoothService.
Change it like this (remove private inner):
class ConnectedThread(private val mmSocket: BluetoothSocket) : Thread() {
You'll have to get access to the service some other way, or alternatively create a factory method in your service that instantiates the thread and returns that.

kotlin.TypeCastException: null cannot be cast to non-null type error

I'm trying to get a info box working with Google Maps API. When trying to run the application, and adding a marker with custom info box it crashes.
Here is my code:
class MainMapsActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var mMap: GoogleMap
private val test: ArrayList<String> = arrayListOf()
private var mLocationPermissionGranted: Boolean = false
private val PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: Int = 1234
// ^Number isnt definitive, as long as its unique inside the application
private val DEFAULT_ZOOM: Float = 15.0F
private var mLastKnownLocation: Location? = null
private val mDefaultLocation = LatLng(60.312491, 24.484248)
private lateinit var mFusedLocationProviderClient: FusedLocationProviderClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_maps)
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
}
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
getDeviceLocation()
//PRESSING WILL ADD MARKER
mMap.setOnMapClickListener(GoogleMap.OnMapClickListener { point ->
val builder: AlertDialog.Builder
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder = AlertDialog.Builder(this, android.R.style.Theme_Material_Dialog_Alert)
} else {
builder = AlertDialog.Builder(this)
}
val marker = MarkerOptions().position(point)
builder.setTitle("Are you sure you want to add a map location here?")
.setMessage("Are you sure you want to add a map location here?")
.setPositiveButton(android.R.string.yes, DialogInterface.OnClickListener { dialog, which ->
mMap.addMarker(marker)
//CUSTOM MARKER
.setIcon(BitmapDescriptorFactory.fromResource(R.mipmap.pinetree_foreground))
})
.setNegativeButton(android.R.string.no, DialogInterface.OnClickListener { dialog, which ->
// do nothing
})
.show()
true
val mapInfoWindowFragment = supportFragmentManager.findFragmentById(R.id.infoWindowMap) as MapInfoWindowFragment
//Set Custom InfoWindow
val infoWindow = InfoWindow(point, InfoWindow.MarkerSpecification(0, 0), mapInfoWindowFragment)
// Shows the InfoWindow or hides it if it is already opened.
mapInfoWindowFragment.infoWindowManager()?.toggle(infoWindow, true);
})
And here is the error Logcat gives me:
kotlin.TypeCastException: null cannot be cast to non-null type com.example.sampo.luontoalueet.MapInfoWindowFragment
at com.example.sampo.luontoalueet.MainMapsActivity$onMapReady$1.onMapClick(MainMapsActivity.kt:123)
My question is how to change change this statement into non-null type?
val mapInfoWindowFragment = supportFragmentManager.findFragmentById(R.id.infoWindowMap) as MapInfoWindowFragment
Your findfragmentById is returning null (which indicates that your fragment does not exist).
If you're absolutely sure that fragment cannot be null at this point, you need to review your code. Maybe your fragment is not attached to supportFragmentManager?
If you want to handle a case where this fragment might not exist, You can use nullable cast as?:
val mapInfoWindowFragment = supportFragmentManager.findFragmentById(R.id.infoWindowMap) as? MapInfoWindowFragment
Then you can check conditionally if(mapInfoWindowFragment == null) and handle the null case.

Debugging Package Manager Console Update-Database Seed Method

I wanted to debug the Seed() method in my Entity Framework database configuration class when I run Update-Database from the Package Manager Console but didn't know how to do it. I wanted to share the solution with others in case they have the same issue.
Here is similar question with a solution that works really well.
It does NOT require Thread.Sleep.
Just Launches the debugger using this code.
Clipped from the answer
if (!System.Diagnostics.Debugger.IsAttached)
System.Diagnostics.Debugger.Launch();
The way I solved this was to open a new instance of Visual Studio and then open the same solution in this new instance of Visual Studio. I then attached the debugger in this new instance to the old instance (devenv.exe) while running the update-database command. This allowed me to debug the Seed method.
Just to make sure I didn't miss the breakpoint by not attaching in time I added a Thread.Sleep before the breakpoint.
I hope this helps someone.
If you need to get a specific variable's value, a quick hack is to throw an exception:
throw new Exception(variable);
A cleaner solution (I guess this requires EF 6) would IMHO be to call update-database from code:
var configuration = new DbMigrationsConfiguration<TContext>();
var databaseMigrator = new DbMigrator(configuration);
databaseMigrator.Update();
This allows you to debug the Seed method.
You may take this one step further and construct a unit test (or, more precisely, an integration test) that creates an empty test database, applies all EF migrations, runs the Seed method, and drops the test database again:
var configuration = new DbMigrationsConfiguration<TContext>();
Database.Delete("TestDatabaseNameOrConnectionString");
var databaseMigrator = new DbMigrator(configuration);
databaseMigrator.Update();
Database.Delete("TestDatabaseNameOrConnectionString");
But be careful not to run this against your development database!
I know this is an old question, but if all you want is messages, and you don't care to include references to WinForms in your project, I made some simple debug window where I can send Trace events.
For more serious and step-by-step debugging, I'll open another Visual Studio instance, but it's not necessary for simple stuff.
This is the whole code:
SeedApplicationContext.cs
using System;
using System.Data.Entity;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace Data.Persistence.Migrations.SeedDebug
{
public class SeedApplicationContext<T> : ApplicationContext
where T : DbContext
{
private class SeedTraceListener : TraceListener
{
private readonly SeedApplicationContext<T> _appContext;
public SeedTraceListener(SeedApplicationContext<T> appContext)
{
_appContext = appContext;
}
public override void Write(string message)
{
_appContext.WriteDebugText(message);
}
public override void WriteLine(string message)
{
_appContext.WriteDebugLine(message);
}
}
private Form _debugForm;
private TextBox _debugTextBox;
private TraceListener _traceListener;
private readonly Action<T> _seedAction;
private readonly T _dbcontext;
public Exception Exception { get; private set; }
public bool WaitBeforeExit { get; private set; }
public SeedApplicationContext(Action<T> seedAction, T dbcontext, bool waitBeforeExit = false)
{
_dbcontext = dbcontext;
_seedAction = seedAction;
WaitBeforeExit = waitBeforeExit;
_traceListener = new SeedTraceListener(this);
CreateDebugForm();
MainForm = _debugForm;
Trace.Listeners.Add(_traceListener);
}
private void CreateDebugForm()
{
var textbox = new TextBox {Multiline = true, Dock = DockStyle.Fill, ScrollBars = ScrollBars.Both, WordWrap = false};
var form = new Form {Font = new Font(#"Lucida Console", 8), Text = "Seed Trace"};
form.Controls.Add(tb);
form.Shown += OnFormShown;
_debugForm = form;
_debugTextBox = textbox;
}
private void OnFormShown(object sender, EventArgs eventArgs)
{
WriteDebugLine("Initializing seed...");
try
{
_seedAction(_dbcontext);
if(!WaitBeforeExit)
_debugForm.Close();
else
WriteDebugLine("Finished seed. Close this window to continue");
}
catch (Exception e)
{
Exception = e;
var einner = e;
while (einner != null)
{
WriteDebugLine(string.Format("[Exception {0}] {1}", einner.GetType(), einner.Message));
WriteDebugLine(einner.StackTrace);
einner = einner.InnerException;
if (einner != null)
WriteDebugLine("------- Inner Exception -------");
}
}
}
protected override void Dispose(bool disposing)
{
if (disposing && _traceListener != null)
{
Trace.Listeners.Remove(_traceListener);
_traceListener.Dispose();
_traceListener = null;
}
base.Dispose(disposing);
}
private void WriteDebugText(string message)
{
_debugTextBox.Text += message;
Application.DoEvents();
}
private void WriteDebugLine(string message)
{
WriteDebugText(message + Environment.NewLine);
}
}
}
And on your standard Configuration.cs
// ...
using System.Windows.Forms;
using Data.Persistence.Migrations.SeedDebug;
// ...
namespace Data.Persistence.Migrations
{
internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
public Configuration()
{
// Migrations configuration here
}
protected override void Seed(MyContext context)
{
// Create our application context which will host our debug window and message loop
var appContext = new SeedApplicationContext<MyContext>(SeedInternal, context, false);
Application.Run(appContext);
var e = appContext.Exception;
Application.Exit();
// Rethrow the exception to the package manager console
if (e != null)
throw e;
}
// Our original Seed method, now with Trace support!
private void SeedInternal(MyContext context)
{
// ...
Trace.WriteLine("I'm seeding!")
// ...
}
}
}
Uh Debugging is one thing but don't forget to call:
context.Update()
Also don't wrap in try catch without a good inner exceptions spill to the console.
https://coderwall.com/p/fbcyaw/debug-into-entity-framework-code-first
with catch (DbEntityValidationException ex)
I have 2 workarounds (without Debugger.Launch() since it doesn't work for me):
To print message in Package Manager Console use exception:
throw new Exception("Your message");
Another way is to print message in file by creating a cmd process:
// Logs to file {solution folder}\seed.log data from Seed method (for DEBUG only)
private void Log(string msg)
{
string echoCmd = $"/C echo {DateTime.Now} - {msg} >> seed.log";
System.Diagnostics.Process.Start("cmd.exe", echoCmd);
}

Resources