i'm trying to fix the NetworkOnMainThreadException, here is my class located in a library
class CoinProvider(private val context: Context, isTestnet: Boolean) {
val result = URL("http://mylink.com/file.json").readText()
val result1 = URL("http://mylink.com/file1.json").readText()
private val filename: String = if (!isTestnet) (result) else (result1)
fun defaultCoins(): CoinResponse {
return CoinResponse.parseFile(context, filename)
}
}
when i try to add this to my class
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
i got this " expecting member declaration "
the code above can be added to the MainActivity of the project but it doesn't do anything because i want to fix the NetworkOnMainThreadException on the library not in the project.
how can i fix the issue in this kotlin library?
Oh No. Request network and query database always not calling in Ui thread because it blocks the UI thread (Ui thread always redraws with 16ms time).
Solution thread:
Thread(Runnable {
...// Something Call
}).
This way you need to either implement a callback to handle the outgoing data, or use Handler
Solution RxJava:
Single.fromCallable {
try {
/// Something Doing
} catch (e: IOException) {
false
}
}.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
With RxJava you can easily work with threads and return results in Mainthread, all you need to do is just Observer the output data.
Solution Kotlin-Coroutines
suspend func getJSON(){
//Something Doing....
}
My understanding will probably help you to solve the problem.
Related
I also made another class that implements runnable to make a new thread still it's now working,
Also tried with Aynctask. At last, I made a view model but still same issue.
Anyone, please Help.
Here is what I am trying to do
GlobalScope.launch (Dispatchers.IO){
uploadFileToGDrive("/storage/emulated/0/Download/Ticket.pdf",this#AddPost)
withContext(Dispatchers.Main){
Toast.makeText(this#AddPost,"I have gone Mad",Toast.LENGTH_LONG).show()
}
}
fun createFile (filePath:String):String{
var file:File=File(filePath)
val gfile = com.google.api.services.drive.model.File()
gfile.setName(file.name)
var mediaContent:FileContent= FileContent("application/pdf",file)
try {
var myFile= mDrive?.files()?.create(gfile,mediaContent)?.execute()
var listOfPermission: MutableList<com.google.api.services.drive.model.Permission>? =myFile!!.permissions
if(listOfPermission!=null){
for (per in listOfPermission){
// Toast.makeText(this,per.toString(),Toast.LENGTH_LONG).show()
}
}
// Toast.makeText(this,"Uploaded Files",Toast.LENGTH_SHORT).show()
return myFile!!.id
}
catch (e:Exception){
// Toast.makeText(this,"Some Error Occured in Uploading Files"+e.toString(),Toast.LENGTH_SHORT).show()
}
return "Failed"
}
I am running this function on a button click in an activity tried all the methods from coroutines to Aync and making new thread still getting the same issue.
Don't use GlobalScope, instead use lifeCycleScope.
This might resolve the issue.
I have still a little bit of trouble putting all information together about the thread-safety of using coroutines to launch network requests.
Let's say we have following use-case, there is a list of users we get and for each of those users, I will do some specific check which has to run over a network request to the API, giving me some information back about this user.
The userCheck happens inside a library, which doesn't expose suspend functions but rather still uses a callback.
Inside of this library, I have seen code like this to launch each of the network requests:
internal suspend fun <T> doNetworkRequest(request: suspend () -> Response<T>): NetworkResult<T> {
return withContext(Dispatchers.IO) {
try {
val response = request.invoke()
...
According to the documentation, Dispatchers.IO can use multiple threads for the execution of the code, also the request function is simply a function from a Retrofit API.
So what I did is to launch the request for each user, and use a single resultHandler object, which will add the results to a list and check if the length of the result list equals the length of the user list, if so, then all userChecks are done and I know that I can do something with the results, which need to be returned all together.
val userList: List<String>? = getUsers()
val userCheckResultList = mutableListOf<UserCheckResult>()
val handler = object : UserCheckResultHandler {
override fun onResult(
userCheckResult: UserCheckResult?
) {
userCheckResult?.let {
userCheckResultList.add(
it
)
}
if (userCheckResultList.size == userList?.size) {
doSomethingWithResultList()
print("SUCCESS")
}
}
}
userList?.forEach {
checkUser(it, handler)
}
My question is: Is this implementation thread-safe? As far as I know, Kotlin objects should be thread safe, but I have gotten feedback that this is possibly not the best implementation :D
But in theory, even if the requests get launched asynchronous and multiple at the same time, only one at a time can access the lock of the thread the result handler is running on and there will be no race condition or problems with adding items to the list and comparing the sizes.
Am I wrong about this?
Is there any way to handle this scenario in a better way?
If you are executing multiple request in parallel - it's not. List is not thread safe. But it's simple fix for that. Create a Mutex object and then just wrap your operation on list in lock, like that:
val lock = Mutex()
val userList: List<String>? = getUsers()
val userCheckResultList = mutableListOf<UserCheckResult>()
val handler = object : UserCheckResultHandler {
override fun onResult(
userCheckResult: UserCheckResult?
) {
lock.withLock {
userCheckResult?.let {
userCheckResultList.add(
it
)
}
if (userCheckResultList.size == userList?.size) {
doSomethingWithResultList()
print("SUCCESS")
}
}
}
}
userList?.forEach {
checkUser(it, handler)
}
I have to add that this whole solution seems very hacky. I would go completely other route. Run all of your requests wrapping those in async { // network request } which will return Deferred object. Add this object to some list. After that wait for all of those deferred objects using awaitAll(). Like that:
val jobs = mutableListOf<Job>()
userList?.forEach {
// i assume checkUser is suspendable here
jobs += async { checkUser(it, handler) }
}
// wait for all requests
jobs.awaitAll()
// After that you can access all results like this:
val resultOfJob0 = jobs[0].getCompleted()
First question here, I will do my best.
I have a Data class that retrieve a data object with firestore at the creation.
I have done some code to the setters with coroutines. I am not sure of my solution but it is working. However, for the getters, I am struggling to wait the initialisation.
In the initialisation, I have a callback to retrieve the data. The issue that the callback is always called from the main thread, event if I use it in a coroutine in another thread. I check this with:
Log.d("THREAD", "Execution thread1: "+Thread.currentThread().name)
For the setter I use a coroutine in useTask to not block the main thread. And a mutex to block this coroutine until the initialisation in the init is done. Not sure about waitInitialisationSuspend but it is working.
But for the getter, I just want to block the main thread (even if it is bad design, it is a first solution) until the initialisation is done, and resume the getter to retrieve the value.
But I am not enable to block the main thread without also blocking the callback in the initialisation because there are in the same thread.
I have read many documentation about coroutine, scope, runBlocking, thread etc. but everything gets mixed up in my head.
class Story(val id: String) : BaseObservable() {
private val storyRef = StoryHelper.getStoryRef(id)!!
private var isInitialized = false
private val initMutex = Mutex(true)
#get:Bindable
var dbStory: DbStory? = null
init {
storyRef.get().addOnCompleteListener { task ->
if (task.isSuccessful && task.result != null) {
dbStory = snapshot.toObject(DbStory::class.java)!!
if (!isInitialized) {
initMutex.unlock()
isInitialized = true
}
notifyPropertyChanged(BR.dbStory)
}
}
}
fun interface StoryListener {
fun onEvent()
}
private fun useTask(function: (task: Task) -> Unit): Task {
val task = Task()
GlobalScope.launch {
waitInitialisationSuspend()
function(task)
}
return task
}
private suspend fun waitInitialisationSuspend()
{
initMutex.withLock {
// no op wait for unlock mutex
}
}
fun typicalSetFunction(value: String) : Task {
return useTask { task ->
storyRef.update("fieldName", value).addOnSuccessListener {
task.doEvent()
}
}
}
fun typicalGetFunction(): String
{
var result = ""
// want something to wait the callback in the init.
return result
}
}
RunBlocking seems to block the main tread, so I can not use it if the callback still use the main thread.
It is the same problem if I use a while loop in main thread.
#1
runBlocking {
initMutex.withLock {
result = dbStory!!.value
}
}
#2
while (!isInitialized){
}
result = dbStory!!.value
#3
Because maybe the callback in the init is in the main thread also. I have tried to launch this initialisation in a coroutines with a IO dispatcher but without success. The coroutine is well in a different thread but the callback still called in the main thread.
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
scope.launch() {
reference.get().addOnCompleteListener { task ->
In the getter, I have to work with the main thread. The solution is maybe to put the callback execution in another thread but I do not know how to do this. And maybe there is a better solution.
Another solution will be te be able to wait the callback in the main thread without blocking the callback but I have no solution for this.
Any ideas ?
I have loocked for many solutions and the conclusion is, don't do it.
This design is worse than I thougt. Android does not want you to block the main thread even for a short time. Blocking the main thread is blocking all UI and synchronisation mecanism, it is really bad solution.
Even using another thread for the callback (that you can do with an Executor) is, I think, a bad idea here. The good way to wait the end of the task in the callback is to retrieve the task and use:
Tasks.await(initTask)
But it is not allowed in the main thread. Android prevent you to do bad design here.
We should deal with the asynchronous way to manage firebase data base, it is the best way to do that.
I can still use my cache on the data. Here I was waiting to display a dialog with a text I retrieve in firebase. So, I can just display the dialog asynchronously when the text data is retrieved. If the cache is available, it will use it.
Keep also in mind that firebase seems to have some API to use a cache.
I have an issue with my android app. I'm fairly new with it and have some issues with finding the correct documentation for a asynchronous. I'm using the kohttp library to help me a bit.
The thing is, you can't run this on the main UI thread so I want to make this request Async. I can't find a clear reference in the documentation and I don't really know how to do this in plain Kotlin.
This is what I come up with; in a separate class named LoginCall. I tried other answers, this however didn't result in success. How can I run this on a new thread and still use the response?
class LoginCall {
fun callLoginRequest(a:String, b:String): Any {
val response: Response = httpPost {
host = "XXX"
path = "XXX"
param { }
header { }
body {
form {
"email" to a
"password" to b
}
}
}
return response
}
}
There are many ways to achieve this, if you're using android as the underlying platform, you can use the native component called AsyncTask a good SO post on how to use it.
If you wish to leverage kotlin as a language and the features provided by it, you can try using coroutines ref.
Personally, i would recommend coroutines, it simplifies exception and error handling, also prevents callback hell.
here's a sample of the same code in a coroutine,
// global
private val mainScope = CoroutineScope(Dispatchers.MAIN + SupervisorJob())
// inside a method
mainScope.launch{
withContext(Dispatchers.IO){
// do your async task here, as you can see, you're doing this in an IO thread scope.
}
}
Also, you can create an issue to implement asyncHttpPost.
Since kohttp 0.10.0 you can use async methods for such cases. You can try them.
Code example:
suspend fun callLoginRequest(a:String, b:String): Any {
val response: Differed<Response> = httpPostAsync {
host = "XXX"
path = "XXX"
param { }
header { }
body {
form {
"email" to a
"password" to b
}
}
}
// for further usage in coroutines
return response.await()
}
And call this function from coroutine
Can someone help me find where I am going wrong here. I need to continously observer network data and update the UI whenever there is a data change from the Worker. Please note that this was working before upgrading to androidx.
Here is a Worker class.
class TestWorker(val context: Context, val params: WorkerParameters): Worker(context, params){
override fun doWork(): Result {
Log.d(TAG, "doWork called")
val networkDataSource = Injector.provideNetworkDataSource(context)
networkDataSource.fetchData(false)
return Worker.Result.SUCCESS
}
companion object {
private const val TAG = "MY_WORKER"
}
}
Which is called as follows:
fun scheduleRecurringFetchDataSync() {
Log.d("FETCH_SCHEDULER", "Scheduling started")
val fetchWork = PeriodicWorkRequest.Builder(TestWorker::class.java, 1, TimeUnit.MINUTES)
.setConstraints(constraints())
.build()
WorkManager.getInstance().enqueue(fetchWork)
}
private fun constraints(): Constraints{
return Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
}
I also have a UserDao and UserRepository to fetch and store data. I am observing the network data in the UserRepository as follows:
class UserRepository (
private val userDao: UserDao,
private val networkDataSource: NetworkDataSource,
private val appExecutors: AppExecutors){
init {
val networkData= networkDataSource.downloadedData
networkData.observeForever { newData->
appExecutors.diskIO().execute {
userDao.insert(newData.user)
}
}}
Can someone help me locate where I am going wrong. This is giving me error as follows:
java.lang.IllegalStateException: Cannot invoke observeForever on a background thread
at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:443)
at androidx.lifecycle.LiveData.observeForever(LiveData.java:204)
at com.example.app.data.repo.UserRepository.<init>(UserRepository.kt:17)
at com.example.app.data.repo.UserRepository$Companion.getInstance(UserRepository.kt:79)
Change this:
networkData.observeForever { newData->
appExecutors.diskIO().execute {
userDao.insert(newData.user)
}
}
To:
Variant B (with coroutines):
GlobalScope.launch(Dispatchers.Main) { networkData.observerForever { /*..*/ } }
But be aware, the usage of GlobalScope is not recommended: https://stackoverflow.com/a/54351785/1185087
Variant A (without coroutines):
Handler(Looper.getMainLooper()).post { networkData.observeForever{ /*..*/ } }
Explanation
Normally observe(..) and observeForever(..) should be called from the main thread because their callbacks (Observer<T>.onChanged(T t)) often change the UI which is only possible in the main thread. That's the reason why android checks if the call of the observe functions is done by the main thread.
In your case UserRepository.init{} is called by a background thread, so the exception is thrown. To switch back to the main thread you can use one of the above variants. But be aware the code inside of your observe callback is executed by the main thread, too. Any expensive processing inside this callback will freeze your UI!
In another solution, you can call it from main dispatcher as
GlobalScope.launch(Dispatchers.Main) {
// your code here...
}
Additionally to the nice and detailled answer from #user1185087 here is a solution if you're using RxJava in your project. It's maybe not that short, but if you already use RxJava in your project, it's an elegant way to switch to the required thread (in this case the Android's UI thread via .observeOn(AndroidSchedulers.mainThread())).
Observable.just(workManager.getStatusById(workRequest.getId()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(status -> status.observeForever(workStatus -> {
// Handling result on UI thread
}), err -> Log.e(TAG, err.getMessage(), err));
In my case I was testing liveData and I forgot to add the InstantTaskExecutorRule().
#RunWith(AndroidJUnit4::class)
class UserDaoTest {
#get:Rule // <----
var instantExecutorRule = InstantTaskExecutorRule() // <----
....
}
Don't forget to add the library to the project.
testImplementation"androidx.arch.core:core-testing:2.1.0" // unit tests
androidTestImplementation "androidx.arch.core:core-testing:2.1.0"//instrumentation tests
Here is what I did in my Java code to make it work
// the LiveData query
LiveData<List<Calf>> calfLiveDataList = getCalfDao().getAllCalves();
Handler handler = new Handler(Looper.getMainLooper()); //This is the main thread
handler.post(new Runnable() { //task to run on main thread
#Override
public void run() {
calfLiveDataList.observeForever(observer);
}
}
);
Ignore my naming conventions, my app is for cow farmers.