i cant use BuildConfig.DEBUG - android

When I use
BuildConfig.DEBUG
in Kotlin I get this error:
expecting member declaratuon
My code:
class API {
companion object {
private lateinit var instance: Retrofit
private const val baseUrl = baseURL
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
builder.addInterceptor(interceptor);
}
}

You can't use an if statement as a top level declaration like that, you have to declare it inside a function or init block.
So something like this, perhaps:
class API {
companion object {
private lateinit var instance: Retrofit
private const val baseUrl = baseURL
init {
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
builder.addInterceptor(interceptor);
}
}
}
}

You're making a call outside a function or constructor. You cannot have if-statements outside method bodies, which applies to both Kotlin and Java.
objects are classes too, all though they follow the singleton pattern. You still can't put if-statements outside method bodies. The class-level declarations can only contain methods, constructors, and fields, and some blocks (i.e. init), not if-statements and calls to the defined variables.
In addition, you're using Java syntax which won't compile at all. Use Kotlin syntax instead and move it to an init block inside the companion object.
The init block is called like initialization when the companion object is initialized.
companion object{
//Other declarations
init{
if (BuildConfig.DEBUG) {
var interceptor = HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
builder.addInterceptor(interceptor);//I have no clue where you define builder, but I'm assuming you've done it *somewhere* and just left it out of the question
}
}
}

Related

How to make an HTTP call in Android

I noticed that when I create a separate Java file in my android studio project
and run it. It doesn't run like in a normal java project. I wanted to write
code for making HTTP call and read the response. Before integrating into the app
I need to test it out. One way could be to open Intellij Idea and write
a completely different small java project and then put that code inside the android
app.
There are several ways to do this. You can use Retrofit or okhttp.
using volley is one of the easiest ways:
implementation 'com.android.volley:volley:1.2.0'
manifest:
<uses-permission android:name="android.permission.INTERNET" />
in the fragment/activity/viewModel:
private var USGS_REQUEST_URL =
"example.com"
fun getResponse() {
volleySingleton = VolleySingleton.getInstance(application)
}
val stringRequest = StringRequest(
Request.Method.GET, REQUEST_URL,
{ response ->
//Handel your result
},
{
Log.d(TAG, "error")
}).setRetryPolicy(
DefaultRetryPolicy(
DefaultRetryPolicy.DEFAULT_TIMEOUT_MS, 3,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
)
).setShouldCache(true)
volleySingleton!!.addToRequestQueue(stringRequest)
VolleySingleton :
class VolleySingleton constructor(context: Context){
companion object {
/*
#Volatile: meaning that writes to this field are immediately made visible to other threads.
*/
#Volatile
private var instance: VolleySingleton? = null
fun getInstance(context: Context) = instance?: synchronized(this){
instance?: VolleySingleton(context)
}
}
/*
by lazy: requestQueue won't be initialized until this method gets called
*/
val requestQueue: RequestQueue by lazy {
// applicationContext is key, it keeps you from leaking the
// Activity or BroadcastReceiver if someone passes one in.
Volley.newRequestQueue(context.applicationContext)
}
fun <T> addToRequestQueue(req: Request<T>){
requestQueue.add(req)
}}

Kotlin coroutines: Attempt to invoke virtual method 'void androidx.lifecycle.MutableLiveData.postValue(java.lang.Object)' on a null object reference

I have some errors in Retrofit2 and Kotlin Coroutines technologies. I need to dynamically query an info in my service.
For example, the URL is "https://exampleapidomain.com/api/sub_categories/read.php?id=2" I want to change id parameter dynamically.
My service:
interface AltKategoriService {
#GET("alt_kategoriler/" + Const.READ_URL_PIECE)
fun getId(#Query("id") id: String?): Call<Resource<AltKategorilerResponse>>
companion object{
fun build(): AltKategoriService {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(interceptor)
.build()
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(Const.BASE_URL)
.client(okHttpClient)
.build()
return retrofit.create(AltKategoriService::class.java)
}
}
}
My DataSource file:
class RemoteAltKategorilerDataSource : AltKategorilerDataSource {
override fun getSubCategories(): Flow<Resource<AltKategorilerResponse>> = flow {
try {
emit(Resource.Loading())
val call = AltKategoriService.build().getId("2").execute()
if (call.isSuccessful) {
call.body()?.let {
emit(it)
}
}
} catch (ex: Exception) {
emit(Resource.Error(ex))
ex.printStackTrace()
}
}
}
I get the following error:
Attempt to invoke virtual method 'void androidx.lifecycle.MutableLiveData.postValue(java.lang.Object)' on a null object reference" and then, app crashes.
I'm waiting for your answers and code examples. Thank you!
Edited. My ViewModel:
class SubCategoryViewModel: ViewModel() {
private val altKategoriRepository = AltKategoriRepository()
init {
getUsers()
}
var loading: MutableLiveData<Boolean>? = MutableLiveData()
var altKategoriLiveData = MutableLiveData<AltKategorilerResponse>()
var error = MutableLiveData<Throwable>()
fun getUsers() = viewModelScope.launch {
altKategoriRepository.getSubCategories()
.asLiveData(viewModelScope.coroutineContext).observeForever {
when (it.status) {
ResourceStatus.LOADING -> {
loading?.postValue(true)
}
ResourceStatus.SUCCESS -> {
Log.e("Message", it.data.toString())
altKategoriLiveData.postValue(it.data!!)
loading?.postValue(false)
}
ResourceStatus.ERROR -> {
error.postValue(it.throwable!!)
loading?.postValue(false)
}
}
}
}
}
Kotlin class initialisation takes place in the following order:
primary constructor -> init block -> secondary constructor
As no initialisation is done neither for var loading, var altKategoriLiveData nor var error class members of SubCategoryViewModel by the time getUsers() is called in the init { } block, you get the exception resulting in the app crash.
Regarding your implementation of the MVVM pattern, it contradicts to that of the official Android documentation, where a View is supposed to call a corresponding method of ViewModel explicitly or implicitly.
It should also work by just moving the init { } after the variable declarations or if you declare your loading state directly as LOADING. Also i think it's fine to declare it inside the init block, the documentation doesn't refer to that as being at fault if i'm reading correctly. Also it helps doing the call in init to avoid multiple times loading in Activities or Fragments if you only need to do the call once when the viewmodel is created and avoiding multiple calls by e.g: orientation change or other lifecycle dependent things. but please correct me if i'm wrong.

Moshi ArrayOutOfBoundsException when adding factory

I'm finding this exception related with Moshi sometimes when opening the app:
Caused by java.lang.ArrayIndexOutOfBoundsException: length=33; index=33
at java.util.ArrayList.add(ArrayList.java:468)
at com.squareup.moshi.Moshi$Builder.add(Moshi.java:231)
We initialise a repository in the BaseApplication which, sometimes, results in the mentioned crash when initialising Moshi. I'm finding this error in the app reports but I'm not able to reproduce it. Let's jump to the what we have and see if you might have a clue on it.
This factory is used to create Moshi instances, getting the crash when adding KotlinJsonAdapterFactory:
object MyMoshiConverterFactory {
fun create(setup: (Moshi.Builder.() -> Unit)? = null): Converter.Factory {
val moshi = MoshiUtil.createMoshi()
setup?.let { moshi.it() }
moshi.add(KotlinJsonAdapterFactory()) // Here is the crash!
return MoshiConverterFactory.create(moshi.build())
}
}
Here we have a class where we have all the converters we use. It really has a lot more of converters, but I've removed a few of them for simplicity:
object MoshiUtil {
private val lazyMoshi by lazy {
Moshi.Builder().apply {
add(DateAdapter())
add(DefaultOnDataMismatchAdapter.newFactory(FeedItem::class.java, null))
add(SkipListNullValuesAdapter.createFactory(Element::class.java))
add(SkipListNullValuesAdapter.createFactory(Post::class.java))
add(SkipListNullValuesAdapter.createFactory(MetadataItem::class.java))
add(GeoGeometry::class.java, GeometryAdapter())
}
}
fun createMoshi() = lazyMoshi
}
And finally, in our BaseApplication, we have something like this:
class BaseApplication {
#Override
public void onCreate() {
super.onCreate();
val myService = getMyService(applicationContext)
}
private fun getMyService(appContext: Context): MyService {
val converterFactory = MyMoshiConverterFactory.create()
return Retrofit.Builder().baseUrl(baseUrl).apply {
addConverterFactory(converterFactory)
client(okHttpClientBuilder.build())
}.build().create(MyService::class.java)
}
}
}
So, do you see anything that could be causing it? Do you think it might be a concurrency issue happening at startup when the several places in the app are creating the MoshiUtils object at the same time?. Looking forward to hear from you guys, thanks!
Moshi.Builder is mutable and not thread-safe, so this error you're getting sometimes is a race condition as a result of that. You should call .build() on that base MoshiUtil instance to get an immutable Moshi instance, then make the return value of MoshiUtil.createMoshi be moshi.newBuilder() (creates a Moshi.Builder already configured like the existing Moshi instance), like so:
object MoshiUtil {
private val baseMoshi: Moshi = Moshi.Builder().apply {
// ...
}.build()
fun createMoshi(): Moshi.Builder = baseMoshi.newBuilder()
}
Since every person that calls createMoshi now gets their own instance of Moshi.Builder, there shouldn't be any concurrency problems anymore.

HttpLoggingInterceptor Not Logging

I am running across weird behaviors with HttpLoggingInterceptor. I have noticed that if I use newBuilder() the logging does not work.
// instantiate object (in app)
val okHttpRequestManager: HttpRequestManager = OkHttpRequestManager(OkHttpClient(), null)
// execute request (in app)
okHttpRequestManager.execute(request, callback)
// another class (in request module)
open class OkHttpRequestManager(private val client: OkHttpClient,
private val httpLoggingInterceptor: HttpLoggingInterceptor?) : HttpRequestExecutor {
override fun execute(httpRequest: HttpRequest, callback: HttpResponseCallback?) {
if (httpLoggingInterceptor != null) {
client.newBuilder().addInterceptor(httpLoggingInterceptor).build()
}
// perform request below
...
}
}
The above code snippet does not work. However, if I make my parameter a builder, everything works fine. Is using newBuilder() the incorrect way to do this?
// the below works
// another class (in request module)
open class OkHttpRequestManager(private val client: OkHttpClient.Builder,
private val httpLoggingInterceptor: HttpLoggingInterceptor?) : HttpRequestExecutor {
override fun execute(httpRequest: HttpRequest, callback: HttpResponseCallback?) {
if (httpLoggingInterceptor != null) {
// no newBuilder() or build() and works like a charm
client.addInterceptor(httpLoggingInterceptor)
}
// perform request below
...
}
}
Anyone have an idea as to why this is?
That's because the method newBuilder() as the name implies, returns the new builder object and when you call build() on it, new instance of OkHttpClient will be returned created from the new builder.
Here is the source code:
/** Prepares the [request] to be executed at some point in the future. */
override fun newCall(request: Request): Call {
return RealCall.newRealCall(this, request, forWebSocket = false)
}
build() method
fun build(): OkHttpClient = OkHttpClient(this)
The newBuilder adds to the attributes of the existing client so you
will have a new client with both the old and new attributes.
If you want to use newBuilder() method then you need to make use of the newly created OkHttpClient.
// another class (in request module)
open class OkHttpRequestManager(private val client: OkHttpClient,
private val httpLoggingInterceptor: HttpLoggingInterceptor?) : HttpRequestExecutor {
override fun execute(httpRequest: HttpRequest, callback: HttpResponseCallback?) {
if (httpLoggingInterceptor != null) {
val newClient = client.newBuilder().addInterceptor(httpLoggingInterceptor).build()
}
// perform request below using newClient
...
}
}

Kotlin inner class accessing outer class?

How do I call a method in outer class from the inner class?
I can pass it as context, but I can't call methods on it
loginButton.setOnClickListener {
ssoManager.login(emailEditText.text.toString(), passwordEditText.text.toString())
.subscribe(object: Consumer<SSOToken> {
val intent = Intent(this#LoginActiviy, PasscodeActivity::class.java)
this#LoginActiviy.startActivity(intent)
})
I'm not sure what APIs you're using here, I'm gonna assume that your Consumer is java.util.function.Consumer for the sake of the answer.
You are writing code directly in the body of your object, and not inside a function. The first line of creating the Intent only works because you're declaring a property (and not a local variable!).
What you should do instead is implement the appropriate methods of Consumer, and write the code you want to execute inside there:
loginButton.setOnClickListener {
ssoManager.login()
.subscribe(
object : Consumer<SSOToken> {
val foo = "bar" // this is a property of the object
override fun accept(t: SSOToken) {
val intent = Intent(this#LoginActiviy, PasscodeActivity::class.java)
this#LoginActiviy.startActivity(intent)
}
}
)
}

Categories

Resources