When my database is running, everything is okey. But when is not running my mobile app always crash.
Error message:
Caused by: java.net.ConnectException: Failed to connect to /httpURL.
How to fix problem?
here is my code:
AsyncTaskHandleJson().execute(url)
inner class AsyncTaskHandleJson : AsyncTask<String, String, String>() {
override fun doInBackground(vararg url: String?): String {
var text: String
var connection = URL(url[0]).openConnection() as HttpURLConnection
try {
connection.connect()
text = connection.inputStream.use { it.reader().use { reader -> reader.readText() } }
} finally {
connection.disconnect()
}
return text
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
handleJson(result)
}
}
Since there is no catch block in your code, you are not catching any exceptions currently.
If you would like to handle the ConnectException, then you simply have to catch it:
override fun doInBackground(vararg url: String?): String {
var text = ""
var connection: HttpURLConnection? = null
try {
connection = URL(url[0]).openConnection() as HttpURLConnection
connection.connect()
text = connection.inputStream.use {
it.reader().use { reader ->
reader.readText()
}
}
} catch (ce: ConnectException) {
// ConnectionException occurred, do whatever you'd like
ce.printStackTrace()
} catch (e: Exception) {
// some other Exception occurred
e.printStackTrace()
} finally {
connection?.disconnect()
}
return text
}
Check out the Exceptions reference.
Related
I am trying to map the error body from an exception into into a useful model with now luck, using moshi and retrofit 2.9.0
I found numerous posts discussing the same issue but none of the solutions worked for me.
My code is the following
private fun getErrorMessageFromGenericResponse(httpException: HttpException): String? {
var errorMessage: String? = null
try {
val body = httpException.response()!!.errorBody()!!
val errorResponse = body.string().toObject(ErrorResponse::class.java)
errorMessage = errorResponse?.message
} catch (e: IOException) {
e.printStackTrace()
} finally {
return errorMessage
}
}
fun <T> String.toObject(objectClass: Class<T>): T? {
val moshi = Moshi.Builder().build()
val adapter = moshi.adapter(objectClass).lenient()
return adapter.fromJson(this)
}
I tried also using this but it also does not work:
httpException.response()!!.errorBody()!!.source().buffer.snapshot().utf8()
I am probably missing something really simple as I think its a common usecase.
Thanks in advance.
fun handleErrorResponse(e: Throwable): String {
return when (e) {
is HttpException -> {
parseHTTPError(e.response()!!.errorBody())
}
is SocketTimeoutException -> {
ApiConstants.TIME_OUT
}
is IOException -> {
ApiConstants.SERVERERROR
}
else -> ApiConstants.SERVERERROR
}
}
fun parseHTTPError(responseBody: ResponseBody?): String {
try {
val jsonObject=JSONObject(responseBody!!.string())
try {
val error=jsonObject.getJSONArray("message")
return error[0].toString()
}
catch (ex: Exception) {
responseBody!!.close()
return ""
}
responseBody.close()
return ""
}
I have the following setup
Service
// ItunesService
suspend fun searchItunesPodcast(#Query("term") term: String): Response<PodcastResponse>
Repository
// ItunesRepo
override suspend fun searchByTerm(term: String) = withContext(ioDispatcher) {
return#withContext itunesService.searchItunesPodcast(term)
}
ViewModel
fun searchPodcasts(term: String) {
viewModelScope.launch {
_res.value = Result.loading()
try {
val response = itunesRepo.searchByTerm(term)
if (response.isSuccessful) { // Nothing from here when no internet
_res.value = Result.success(response.body())
} else {
_res.value = Result.error(response.errorBody().toString())
}
} catch (e: Exception) {
_res.value = Result.exception(e)
}
}
}
Everything works great until i turn off mobile data/internet on my testing device. _res value stuck on Loading state. I have tried adding break point at if (response.isSuccessful) when there is no internet and it seams like val response = itunesRepo.searchByTerm(term) never returns how can I fix this
I switched to using Flow api on my Repository
override suspend fun searchPodcasts(term: String) = flow {
emit(Result.Loading)
try {
val res = itunesService.searchItunesPodcast(term)
if (res.isSuccessful)
emit(Result.Success(res.body()))
else
emit(Result.Error("Generic error: ${res.code()}"))
} catch (e: Exception) {
emit(Result.Error("Unexpected error", e))
}
}.flowOn(ioDispatcher)
Then collect the results on my ViewModels
I have simple client and server socket application connecting each other based on this code. The problem is i can send and get values but when client getting answer from server continue to send message.
Where is my problem?
here is server : My server socket is in Service.I execute it when service starts
class SocketService: Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
thread { ClientHandler().run() }
return START_STICKY
}
internal inner class ClientHandler() : Runnable{
override fun run() {
val server = ServerSocket(5000)
val client = server.accept()
var reader = BufferedReader(InputStreamReader(client.getInputStream()))
var writer = PrintWriter(client.getOutputStream())
try {
receiveString = reader.readLine()
while(receiveString != "")
{
println(receiveString)
writer.write("hello from server" + "\n")
writer.flush()
}
writer.close();
reader.close();
server.close();
client.close();
} catch (ex: Exception) {
Timber.e("$TAG $formatted $ex")
}
}
}
Here my client :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
connectToPaymentService()
}
fun connectToPaymentService(){
thread { ThreadopenPort().run() }
}
internal inner class ThreadopenPort : Runnable {
override fun run() {
val socket: Socket
try {
socket = Socket(SERVER_IP, SERVER_PORT)
output = PrintWriter(socket.getOutputStream())
input = BufferedReader(InputStreamReader(socket.getInputStream()))
rMessage = input!!.readLine()
println(rMessage)
while(rMessage != ""){
output!!.write("hello from client" + "\n")
output!!.flush()
rMessage = input!!.readLine()
}
output!!.close();
input!!.close();
socket.close();
} catch (e: Exception) {
e.printStackTrace()
}
}
}
#saulyasar -
If I gave you an example, it would be in Java, not Kotlin ;)
Stylistically, you should prefer "use()" to explicit "close()". It won't help your immediate problem - but it's a good habit :)
Your problem is: while(receiveString != ""). You loop ... but "receiveString" is never modified. So the loop never terminates. Whoops!
SUGGESTED ALTERNATIVE:
Assignment not allowed in while expression?
BufferedReader(reader).use { r ->
r.lineSequence().forEach {
println(it)
}
}
I believe this idiom can be successfully applied to your socket "read" loop.
Please post back what you find :)
I'm using retrofit 2.6 with kotlin coroutines to make API call without block the UI thread, I got it work but the app crashes when I switch off the internet connection. The logcat error is: E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
Here is my code:
private fun handleIntent(slug: String) {
val service = UtilityMethods.migrationTimeService()
UtilityMethods.showView(loading_view)
UtilityMethods.hideView(network_error_msg)
CoroutineScope(Dispatchers.IO).launch {
val res = service.getPostBySlug(slug)
try {
withContext(Dispatchers.Main) {
//Do something with response e.g show to the UI.
val post = res.body()!!.first()
UtilityMethods.hideView(loading_view)
val title = post.title?.rendered
val content = post.content?.rendered
val imageUrl = post.jetPackFeaturedMediaUrl
title_txtView.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
Html.fromHtml(title, Html.FROM_HTML_MODE_COMPACT).toString()
else
Html.fromHtml(title).toString()
content_txtView.loadData(content.toString(), "text/html", "UTF-8")
Picasso.get().load(imageUrl).fit().centerCrop().into(thumbnail_imgview)
}
} catch (e: HttpException) {
UtilityMethods.showView(network_error_msg)
} catch (e: Throwable) {
Toast.makeText(this#PostContentActivity, "Ooops: Something else went wrong", Toast.LENGTH_LONG)
}
}
}
I've got the code working, the new code is:
private fun handleIntent(slug: String) = GlobalScope.launch(Dispatchers.Main) {
val service = UtilityMethods.migrationTimeService()
UtilityMethods.showView(loading_view)
UtilityMethods.hideView(network_error_msg)
try {
val res = withContext(Dispatchers.IO) {
service.getPostBySlug(slug)
}
//Do something with response e.g show to the UI.
val post = res.body()!!.first()
UtilityMethods.hideView(loading_view)
val title = post.title?.rendered
val content = post.content?.rendered
val imageUrl = post.jetPackFeaturedMediaUrl
title_txtView.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
Html.fromHtml(title, Html.FROM_HTML_MODE_COMPACT).toString()
else
Html.fromHtml(title).toString()
content_txtView.loadData(content.toString(), "text/html", "UTF-8")
Picasso.get().load(imageUrl).fit().centerCrop().into(thumbnail_imgview)
}
catch (e: HttpException) {
Toast.makeText(this#PostContentActivity, "Exception ${e.message}", Toast.LENGTH_LONG).show()
}catch (e: IOException) {
UtilityMethods.hideView(loading_view)
UtilityMethods.showView(network_error_msg)
} catch (e: Throwable) {
Toast.makeText(this#PostContentActivity, "Ooops: Something else went wrong ${e.message}", Toast.LENGTH_LONG).show()
}
}
So while looking into stacktrace I found that ConnectException is thrown when network is unavailable
And that's how I do it in kotlin and it works for me,
suspend fun<T: Any> safeAPICall(call: suspend () -> Response<T>) : T{
val response = try {
call.invoke()
}
catch (e:java.lang.Exception){
e.printStackTrace()
val message = if( e is ConnectException) "Connection Error" else "Something went wrong. Please try again."
throw IOException(ResponseError(message, 500).convertToJsonString())
}
// When connection is OK
if(response.isSuccessful){
return response.body()!!
}else{
val error = response.errorBody()?.string()
error?.let{
val message = JSONObject(it).optString("message", "Something went wrong")
val responseError = ResponseError(message, response.code())
throw IOException(responseError.convertToJsonString())
}
throw IOException(ResponseError("Something went wrong. Please try again.", 500).convertToJsonString())
}
}
The data class that I use
data class ResponseError(val message:String, val errorCode:Int)
Usage:
try {
val response = safeAPICall {APIClient.planner.viewSites(view.context.authToken)}
}
catch (e:Exception){
view.snack(e.message?.toModel<ResponseError>()?.message?: unspecified_error)
}
Bonus:
inline fun <reified T> JSONObject.toModel(): T? = this.run {
try {
Gson().fromJson<T>(this.toString(), T::class.java)
}
catch (e:java.lang.Exception){ e.printStackTrace(); null }
}
inline fun <reified T> String.toModel(): T? = this.run {
try {
JSONObject(this).toModel<T>()
}
catch (e:java.lang.Exception){ null }
}
Instead of this:
CoroutineScope(Dispatchers.IO).launch {
val res = service.getPostBySlug(slug)
try {
withContext(Dispatchers.Main) {
Try this one:
CoroutineScope(Dispatchers.Main).launch {
val res = service.getPostBySlug(slug)
withContext(Dispatchers.IO) {
try {
wrap your 'try and catch' block code within Dispatchers.IO instead of wraping your Dispatchers.IO with in yout try block
Before loading a website in WebView, I want to check the URL and make sure that it loads. If it does, show the WebView; if not, show another View with a message.
The idea is to check if the website can be loaded and depending on the response show either the "Website cannot be loaded" screen or show the WebView with URL loaded.
I have already checked if the connection is available, so no need to worry about that.
Need to support API 25+.
My solution below is trying to do two things:
Using AsyncTask "ping" a website and then
By passing context from MainActivity, call a function to show WebView (using WeakReference here to achieve that)
class MainActivity : AppCompatActivity() {
private var websiteURL = "https://www.google.com"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// ...
webView.webViewClient = WebViewClient()
}
// I called this function after checking that there is Internet connection
private fun connectionResponseFunction(): String {
return if (isConnected) {
// *** is connected
// pass in "this" context from MainActivity
val downloadData = CheckLink(this)
downloadData.execute(websiteURL)
} else {
// *** not connected
}
}
private fun showWebView() {
webView.loadUrl(websiteURL)
}
companion object {
class CheckLink internal constructor(context: MainActivity) : AsyncTask<String, Void, String>() {
// Needed if you want to use webView or anything else from MainActivity
private val activityReference: WeakReference<MainActivity> = WeakReference(context)
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
val activity = activityReference.get()
if (activity == null || activity.isFinishing) return
if (result == "success") {
// URL loaded, show webView
activity.showWebView()
} else {
// URL didn't load
}
}
override fun doInBackground(vararg url: String?): String {
val linkLoaded = loadLink(url[0])
if (!linkLoaded) {
return "failure"
}
return "success"
}
private fun loadLink(urlPath: String?): Boolean {
try {
val url = URL(urlPath)
val connection: HttpURLConnection = url.openConnection() as HttpURLConnection
connection.setRequestProperty("Connection", "close")
connection.connectTimeout = 3000
connection.connect()
val response = connection.responseCode
// 200 for success
return if (response == 200) {
true
} else {
false
}
} catch (e: Exception) {
// Handle exceptions
when (e) {
is MalformedURLException -> "loadLink: Invalid URL ${e.message}"
is IOException -> "loadLink: IO Exception reading data: ${e.message}"
is SecurityException -> { e.printStackTrace()
"loadLink: Security Exception. Needs permission? ${e.message}"
}
else -> "Unknown error: ${e.message}"
}
}
return false // Error
}
}
}
}
I'm quite new to Android and Kotlin, so I'm open to any suggestions to make it better.
I could not find any recent code that works for API 25+.
Another (shorter) alternative to AsyncTask would it be using Thread:
Thread {
val result = isHostAvailable(BuildConfig.BASE_URL)
runOnUiThread {
if (result) {
// do something ...
}
else showToast("no connection to server")
}
}.start()
and
fun isHostAvailable(urlPath: String): Boolean {
try {
val url = URL(urlPath)
val connection: HttpURLConnection = url.openConnection() as HttpURLConnection
connection.setRequestProperty("Connection", "close")
connection.connectTimeout = 3000
connection.connect()
return when (connection.responseCode) {
200, 403 -> true
else -> false
}
} catch (e: Exception) {
when (e) {
is MalformedURLException -> "loadLink: Invalid URL ${e.message}"
is IOException -> "loadLink: IO Exception reading data: ${e.message}"
is SecurityException -> {
e.printStackTrace()
"loadLink: Security Exception. Needs permission? ${e.message}"
}
else -> "Unknown error: ${e.message}"
}
}
return false
}
Looks like ping doesn't work on emulators (How to Ping External IP from Java Android)...instead, try this on a real device, it'll work:
val process = Runtime.getRuntime().exec("/system/bin/ping -c 1 $SERVIDOR")
val pingResult = process .waitFor()
return pingResult == 0
By the way, the answer given by #quietbits is pretty obsolete...AsyncTask class was the way to go like 5 years ago...Since Android Studio supports kotlin, you should use coroutines!! the code line difference is huge...check this code labs for more info (https://codelabs.developers.google.com/codelabs/kotlin-coroutines/#0)