Kotlin variable getting initialized twice - android

This is the function that gets the object in MyHttpsClient.java.
public static MyHttpsClient getClient(Context context, OnHttpsResult resultListener) throws Exception {
Log.i("TAG","---------------------------" + context.getClass().getName() + " getClient!--------------------------------");
for(String c : contextNames){
if(c == context.getClass().getName()){
throw new Exception("Can not be initialized twice in the same class");
}
}
contextNames.add(context.getClass().getName());
return new MyHttpsClient(context, resultListener);
}
I initialize it in Login.kt like this:
object MyHttpsListener : MyHttpsClient.OnHttpsResult{
override fun OnResult(jsonObject: JSONObject?) {
when(workid){
0 -> {
var status = jsonObject?.getInt("status")
if(status == 1){
instance.startActivity(Intent(instance, MainActivity::class.java))
}
else{
Toast.makeText(instance, "Incorrect email or password!", Toast.LENGTH_LONG).show()
}
instance.pb_login.visibility = View.GONE
}
}
}
}
var client = MyHttpsClient.getClient(this, MyHttpsListener)
After running app, I found that the "client" called
"getClient()" twice, so the app crashed.
Finally, I solved the problem in this way:
lateinit var client: MyHttpsClient
And the "client" initialized in onCreate
override fun onCreate(savedInstanceState: Bundle?) {
...
client = MyHttpsClient.getClient(this, MyHttpsListener)
...
}
But I don't know why this happened. I'm looking forward to someone who can help me.

If you need to create a conection (MyHttpsClient) and you don't look to the connection you've already made, you can create multiple instance for the same conection and, in fact, for the same object.
The best way is to look for the singleton pattern which is a good patern to restrict number of conection and so the number of instance.

Thanks a lot for taking the time to check my question. I am visited stackoverflow first time. Finally I found out my problem, I mistakenly initialized it at the top. And I will show the complete code next time..
private var instance : Login = Login()
class Login : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {

Related

Android Health Connect freezing when making a request after 15 seconds

I am using Health Connect to read records, like steps and exercises. I use Health Connect in a few different places in Kotlin, and the code generally looks something like:
suspend fun fetchStepData(
healthConnectClient: HealthConnectClient,
viewModel: StepViewViewModel,
): StepViewViewModel {
kotlin.runCatching {
val todayStart = Instant.now().atZone(ZoneId.systemDefault()).toLocalDate().atStartOfDay();
val response: ReadRecordsResponse<StepsRecord>
try {
response = healthConnectClient.readRecords(
ReadRecordsRequest(
StepsRecord::class,
timeRangeFilter = TimeRangeFilter.after(todayStart)
)
)
var steps: Long = 0;
if (response.records.isNotEmpty()) {
for (stepRecord in response.records) {
steps += stepRecord.count
}
}
return viewModel
} catch (e: Exception) {
Log.e("StepUtil", "Unhandled exception, ", e)
}
}
return viewModel
}
I have an update function that is run when focus changes to ensure that the app is in the foreground.
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
binding.root.invalidate()
val fragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment_activity_main)?.childFragmentManager?.primaryNavigationFragment
if (fragment is MyFragment) {
displayLoadingIndicator(true)
runBlocking {
if (fragment.fetchStepData(this#MainActivity.healthConnectClient, fragment.getViewModel()) != null) {
displayLoadingIndicator(false)
}
}
}
I have a loading indicator present when I am fetching the data.
I use a drawer, and if I wait about 15 seconds and press the drawer button corresponding with MyFragment, the application hangs on the loading indicator, never successfully dismissing it.
I've tried stepping through the application in debug mode, and as I do, I always hang on
response = healthConnectClient.readRecords(
ReadRecordsRequest(
StepsRecord::class,
timeRangeFilter = TimeRangeFilter.after(todayStart)
)
)
in fetchStepData. I did at one point have my application making multiple requests for HealthConnectClient.getOrCreate(context), but I have since consolidated them to one instantiation call. I'm thinking I may be reading the data wrong and maybe I need to use getChanges, or maybe I'm being rate limited. Does anyone have any insight? Thanks in advance!

How to fix android run time error in Stripe Terminal discover device code build using kotlin

I am trying to integrate stripe terminal code with my android app build using kotlin, unfortunately I am getting the following run time error which I could not able to fix
java.lang.IllegalStateException: initTerminal must be called before attempting to get the instance
The code I have added is used below
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pay_screen)
onDiscoverReaders()
}
fun onDiscoverReaders() {
val config = DiscoveryConfiguration(
timeout = 0,
discoveryMethod = DiscoveryMethod.LOCAL_MOBILE,
isSimulated = false,
location = "xxxxxxxxxxxxxxx"
)
// Save this cancelable to an instance variable
discoveryCancelable = Terminal.getInstance().discoverReaders(config,
discoveryListener = object : DiscoveryListener {
override fun onUpdateDiscoveredReaders(readers: List<Reader>) {
}
}
, object : Callback {
override fun onSuccess() {
println("Finished discovering readers")
}
override fun onFailure(e: TerminalException) {
e.printStackTrace()
}
})
}
I have added this to one of my activity and my intention is to check if my phone is supporting stripe tap on mobile
I guess the issue could be calling onDiscoverReaders() from a wrong place, someone please help me to fix this issue
Thanks in advance
In stripe docs you can check
// Create your listener object. Override any methods that you want to be notified about
val listener = object : TerminalListener {
}
// Choose the level of messages that should be logged to your console
val logLevel = LogLevel.VERBOSE
// Create your token provider.
val tokenProvider = TokenProvider()
// Pass in the current application context, your desired logging level, your token provider, and the listener you created
if (!Terminal.isInitialized()) {
Terminal.initTerminal(applicationContext, logLevel, tokenProvider, listener)
}
// Since the Terminal is a singleton, you can call getInstance whenever you need it
Terminal.getInstance()
might be you missed to initialise terminal before getting Instance so try add above code before onDiscoverReaders()
The error speaks for itself - first you need to initialize the api terminal, and then call the terminal instance.
Based on the documentation, we follow the following steps to get started with the api terminal:
Initialize the terminal application in the application class of the
application
class App : Application() {
override fun onCreate() {
super.onCreate()
TerminalApplicationDelegate.onCreate(this)
}
}
We request the necessary permissions for correct work with the
terminal search (bluetooth, geolocation), if everything is provided,
we call the init terminal with parameters like that:
Terminal.initTerminal(
context = context,
logLevel = LogLevel.VERBOSE,
tokenProvider = TokenProvider(),
listener = object : TerminalListener {
override fun onUnexpectedReaderDisconnect(reader: Reader) {
Log.d("log", "onUnexpectedReaderDisconnect")
}
override fun onConnectionStatusChange(status: ConnectionStatus) {
super.onConnectionStatusChange(status)
Log.d("log", "onConnectionStatusChange")
}
override fun onPaymentStatusChange(status: PaymentStatus) {
super.onPaymentStatusChange(status)
Log.d("log", "onPaymentStatusChange")
}
}
)
After this initialization, you can call the terminal instance and
work with it.

Flow provides null as data from room database

When I use Flow ( kotlinx.coroutines.flow.Flow) I get null as response. Dao code below :
#Query("SELECT * FROM connect")
fun getProfiles(): Flow<List<ConnectModel>>
But when I use List<ConnectModel> and remove flow I get expected result.
My connectImpl code:
override fun getProfilesFromDB(): LiveData<List<ConnectModel>>
{
val result = connectDAO.getProfiles()
return result.asLiveData()
}
In viewmodel
private val _users = MutableLiveData<List<ConnectModel>>()
val users: LiveData<List<ConnectModel>>
get() = _users
fun getConnectUsers() {
viewModelScope.launch {
val response = getConnectUsersFromDbUseCase.invoke()
_users.postValue(response.value)
}
}
If I make the _users lateinit then it works fine but not when it is MutableLiveData.
In Fragment
viewModel.users.observe(viewLifecycleOwner,{list ->
if (list != null) {
for(profile in list)
Timber.i("Observer Active ${profile.name}")
}
})
Database
Any kind of help is appreciated. Thanks in advance.
Use collect or first like the code below
connectViewModel.getProfilesFromDB().collect {
//do stuff
}
There is nothing wrong in your code, I've tried it and it's working perfectly fine as it should. It may be how you are observing this live data after getting it. I've this method in my view model and this is how I'm observing it inside fragment.
connectViewModel.getProfilesFromDB().observe(viewLifecycleOwner){
//do stuff
}

android - kotlin - mvvm - posting data to webservice

I want to post some data to webservice and get the result . this is my code :
fab.setOnClickListener {
viewModel.newBimeGozar(name)
.observe(this#BimeGozarAct, Observer {
dialogbimegozarNew?.hidePg()
})
}
this is my viewmodel :
class BimeNewViewModel:ViewModel() {
private val repository=BimeNewRepository()
fun newBimeGozar(name: String): MutableLiveData<StatModel> {
return repository.newBimeGozar(name)
}
this is my repository :
fun newBimeShode(
name: String
): MutableLiveData<StatModel> {
scope.launch {
val request = api.newBimeShode(name)
withContext(Dispatchers.Main) {
try {
val response = request.await()
regBimeshodeLiveData.value = response
} catch (e: HttpException) {
Log.v("this", e.message);
} catch (e: Throwable) {
Log.v("this", e.message);
}
}
}
return regBimeshodeLiveData;
}
it works fine but there is a problem . I think the observer keeps running and if the result's answer is an error and user press fab button again , it creates a new observer and after this , it returns two value , the first value is the first run and the second value is the second run
how can I fix this ? what is the correct way for submitting forms ?
If your problem is because of LiveData, you should use SingleLiveEvent like as follow
// For first article
val _liveData = MutableLiveData<Event<StatModel>>()
// For second article
val _liveData = SingleLiveEvent<StatModel>()
If you do not know SingleLiveEvent, you can find it here and here.
If your problem is because of your ui element, I think the best solution is to disable the submit button after submitting for the first time.

Accessing Twitter REST API only works in mainActivity, but why?

I am really confused and I need your help! This is only my second App and my first time to work with REST API's. I am simply trying to display some User Information like name and profile picture. It is working perfectly fine when I am using the code in the main Activity, but as soon as I am using a different class for it the API call fails and the code is pretty similar, so I do not know any further. Since Twitter uses Retrofit in their own tutorial I am using it as well.
My Class extending TwitterApiClient, the file is also including the Interface for the custom service:
import android.util.Log
import com.twitter.sdk.android.core.*
import com.twitter.sdk.android.core.models.User
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query
class MyTwitterApiClient(session: TwitterSession) : TwitterApiClient(session) {
fun getCustomService() : GetUsersShowAPICustomService {
return getService(GetUsersShowAPICustomService::class.java)
}
}
interface GetUsersShowAPICustomService {
#GET("/1.1/users/show.json")
fun show(#Query("user_id") userId: Long) : Call<User>
}
My Method in the MainActivity looks like this:
private fun loadTwitterAPI(userID: Long) {
MyTwitterApiClient(session).getCustomService().show(userID).enqueue(object : Callback<User>() {
override fun success(result: Result<User>?) {
text.text = (
"Name: "+result!!.data.name
+"\nLocation: "+result!!.data.location
+"\nFriends: "+result!!.data.friendsCount
)
Picasso.with(baseContext).load(result!!.data.profileImageUrl).resize(250, 250).into(imageView)
}
override fun failure(exception: TwitterException?) {
}
})
}
This works perfectly fine, but I do not want to have the call itself in my main activity and I created a companion Object in my Class extending the TwitterApi which should simply get called with the TwitterSession as parameter and it should return an object of the class User which contains all the important data.
The companion Object inside the MyTwitterApiClient class looks like this:
companion object {
fun start(session: TwitterSession): User {
val userID = session.userId
var data: User? = null
MyTwitterApiClient(session).getCustomService().show(userID).enqueue(object : Callback<User>() {
override fun success(result: Result<User>?) {
data = result!!.data
}
override fun failure(exception: TwitterException?) {
throw exception!!
}
})
return data!!
}
}
The new Method in the MainActivity looks like this:
private fun loadTwitterAPI(userID: Long) {
val t = MyTwitterApiClient.start(session)
text.text = (
"Name: "+t.name
+"\nLocation: "+t.location
+"\nFriends: "+t.friendsCount
)
Picasso.with(baseContext).load(t.profileImageUrl).resize(250, 250).into(imageView)
}
Through testing, I found out, that neither the success Method nor the failure Method gets called. And I do not understand at all why it does not call any Method and just fails.
If anyone here already worked with something like this or has a Tip for me it would be super helpful!
Greetings
Btw: The error that crashes my app in the end is a NullPointerException as the Success Method is not called and null gets returned in the end.
Pastebin to my files:
MainActivity: https://pastebin.com/hWByYUFT
MyTwitterApiClient: https://pastebin.com/85xH284K
activity_main.xml: https://pastebin.com/vkzbkL81
depencies in build.gradle: https://pastebin.com/CpX7cwkS
Ok, starting from your code:
fun start(session: TwitterSession): User {
val userID = session.userId
var data: User? = null
MyTwitterApiClient(session).getCustomService().show(userID).enqueue(object : Callback<User>() {
override fun success(result: Result<User>?) {
data = result!!.data
}
override fun failure(exception: TwitterException?) {
throw exception!!
}
})
return data!!
}
Here you are returning data as if it was assigned. You TwitterApiClient does asynchronous task and so the data from data = result!!.data wont be read correctly from
text.text = (
"Name: "+t.name
+"\nLocation: "+t.location
+"\nFriends: "+t.friendsCount
)
Because t is null then. Its data is not yet set. It will be, sometime in the futur, in the asynchronous callback success().
Your main issue seems to be with how to work with asynchronous tasks and how to notify results. Plenty of sources about it. LiveData, RxJava, EventBus might all be leads.
BTW, the reason why your code worked in MainActivity was because you were setting the text after the result came (in success()), so t was good to read.
Good luck and happy learning!

Categories

Resources