Fetching a URL in Android Kotlin asynchronously - android

So I'm trying to write a very simple Android app that fetches a response from a URL when a button gets pressed. The kotlin Android extensions have been advertised as a drop-in replacement for the boilerplate necessary in Java, so I tried my hand. Here's what I tried so far:
package com.example.susemihl.myapplication
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.runBlocking
import java.net.URL
suspend fun fetch_url(url: String): String {
return URL(url).readText()
}
fun fetch_async(url: String, view: TextView) = runBlocking {
val result = async(CommonPool) { fetch_url(url) }
view.setText(result.await())
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainTextView.setText("Hello there.")
mainButton.setOnClickListener {
mainButton.setText("Check again.")
fetch_async("https://random-app.appspot.com/",
mainTextView)
}
}
}
This worked intermittently, but is now completely broken. There is no response to the button click. Print-debugging shows me that the thread gets executed, but seems to hang on the readText() call. Anything stupid I'm doing wrong here?

You have to switch to main thread in order to update the UI from a suspend function. I would do the networking logic in a ViewModel and expose the result as LiveData to your Activity:
class MainViewModel : ViewModel() {
val urlLiveData = MutableLiveData<String>()
fun fetchUrl(url: String): String {
viewModelScope.launch {
// Dispatchers.IO (main-safety block)
withContext(Dispatchers.IO) {
fetchAsync(url)
}
}
}
private suspend fun fetchAsync(url: String) {
urlLiveData.postValue(URL(url).readText())
}
}
class MainActivity : AppCompatActivity() {
private val mainViewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainViewModel.urlLiveData.observe(
viewLifecycleOwner,
Observer { urlText ->
mainTextView.setText(urlText)
}
)
}
mainViewModel.fetchUrl(""https://random-app.appspot.com/")
}

I know your case, it's because of you are using runBlocking, although await isn't block the thread, but it will suspend the coroutine, and because of the current coroutine did not complete yet, the runBlocking thread will be blocked waiting for it.
So just using launc(UI) instead of runBlocking to solve this problem:
package com.example.susemihl.myapplication
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.launch
import java.net.URL
fun fetch_url(url: String): String {
return URL(url).readText()
}
fun fetch_async(url: String, view: TextView) = launch(UI) {
val result = async(CommonPool) { fetch_url(url) }
view.text = result.await()
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainTextView.text = "Hello there."
mainButton.setOnClickListener {
mainButton.text = "Check again."
fetch_async("https://jacksgong.com", mainTextView)
}
}
}

Here is a async sample that can be used with kotlin which is working perfect for me
val result = URL("<api call>").readText()
try {
URL url = new URL("<api call>");
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null) {
// Nothing to do.
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
return null;
}
result = buffer.toString();
} catch (IOException e) {
Log.e("Request", "Error ", e);
return null;
} finally{
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e("Request", "Error closing stream", e);
}
}
}
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
task = new AsyncTask<Void, Void, String>() {
#Override protected String doInBackground(Void... params) {
return requestFromServer("<api call>");
}
#Override protected void onPostExecute(String s) {
if (!isFinishing() && !isCancelled()) {
Log.d("Request", s);
Toast.makeText(ExampleActivity.this, "Request performed", Toast.LENGTH_LONG).show();
}
}
};
}
#Override protected void onDestroy() {
super.onDestroy();
if (task != null) {
task.cancel(true);
task = null;
}
}
Referenced from - antonioleiva

Related

Using BroadcastReceiver and registerReceiver in android kotlin to get an mms and pass values

I can't seem to use BroadcastReceiver() to get mms data without Cursor() so I tried to use a workaround with ComponentActivity() and registerReceiver(). Does anyone know how to do this with Kotlin? I really need to pass parts of the mms data by passing values to another function. I can't use this to register the class in the manifest. Is that the problem?
package radwil.sms.routing
import android.annotation.SuppressLint
import android.content.*
import android.database.Cursor
import android.net.Uri
import android.os.Bundle
import android.provider.Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION
import android.text.TextUtils
import androidx.activity.ComponentActivity
import radwil.sms.utils.log
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
/**
* This class used to monitor SMS
*/
class MmsReceiverK: ComponentActivity() {
// this is the second problem's solution for now
#SuppressLint("Range")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val receiver = object: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
log( "+++++inside receiver")
}
}
val filterMMS = IntentFilter.create("android.provider.Telephony.WAP_PUSH_RECEIVED", "application/vnd.wap.mms-message")
registerReceiver(receiver, filterMMS)
filterMMS.priority = IntentFilter.SYSTEM_HIGH_PRIORITY
filterMMS.addAction(WAP_PUSH_RECEIVED_ACTION) // MMS
if (TextUtils.equals(intent.action, WAP_PUSH_RECEIVED_ACTION)) {
//handle the WAP_PUSH_RECEIVED_ACTION
val contentResolver: ContentResolver = this.contentResolver
val projection = arrayOf("_id", "ct_t", "Address")
val uri: Uri = Uri.parse("content://mms-sms/conversations/")
val query: Cursor? = contentResolver.query(uri, projection, null, null, null)
if (query != null) {
if (query.moveToFirst()) {
do {
// my second problem is an empty query.getColumnIndex?
val string: String = query.getString(query.getColumnIndex("ct_t"))
if ("application/vnd.wap.multipart.related" == string) {
// mms
val mmsId = query.getColumnIndex("_id")
val mmsAddress = query.getColumnIndex("Address")
val body = getMmsText(mmsId)
log(
"received SMS from: $mmsAddress" +
"with message: $body"
)
} else {
// sms
// return?
}
} while (query.moveToNext())
}
query.close()
}
}
}
// parsing "part" for text of message
private fun getMmsText(id: Int): String {
val partURI = Uri.parse("content://mms/part/$id")
var `is`: InputStream? = null
val sb = StringBuilder()
try {
`is` = this.contentResolver.openInputStream(partURI)
if (`is` != null) {
val isr = InputStreamReader(`is`, "UTF-8")
val reader = BufferedReader(isr)
var temp: String = reader.readLine()
while (true) {
sb.append(temp)
temp = reader.readLine()
}
}
} catch (e: IOException) {
} finally {
if (`is` != null) {
try {
`is`.close()
} catch (e: IOException) {
}
}
}
return sb.toString()
}
}

My activity crashes when making object of json

When ever i made a object of JSONObject my crashes
when ever i enter this line in my onPostExecute override fun
val jsonObject=JSONObject(result)
my activity damn crashes after starting the android emulator
my whole code is below
package shubham.lists.simpleapicalldemo
import android.app.Dialog
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.loader.content.AsyncTaskLoader
import org.json.JSONObject
import java.io.BufferedReader
import java.io.DataOutputStream
import java.io.IOException
import java.io.InputStreamReader
import java.lang.StringBuilder
import java.net.HttpURLConnection
import java.net.SocketTimeoutException
import java.net.URL
import java.sql.Connection
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
CallAPILoginAsyncTask("SHUBHAM","123456").execute()
}
private inner class CallAPILoginAsyncTask(val username:String,val password:String): AsyncTask<Any, Void, String>() {
private lateinit var customProgressDialog:Dialog
override fun onPreExecute() {
super.onPreExecute()
showProgressDialog()
}
override fun doInBackground(vararg params: Any?): String? {
var result:String
var connection:HttpURLConnection?=null
try{ //this link is getting from the mocky.JSON
val url=URL("https://run.mocky.io/v3/3fb2c711-55ea-4de3-aee4-2d7c6848c9d3")
connection=url.openConnection() as HttpURLConnection
connection.doInput=true
connection.doOutput=true
connection.instanceFollowRedirects=false
connection.requestMethod="POST"
connection.setRequestProperty("Content-Type","application/json")
connection.setRequestProperty("charset","utf-8")
connection.setRequestProperty("Accept","application/json")
connection.useCaches=false
val writeDataOutputStream= DataOutputStream(connection.outputStream)
val jsonRequest=JSONObject()
jsonRequest.put("username",username)
jsonRequest.put("password",password)
writeDataOutputStream.writeBytes(jsonRequest.toString())
writeDataOutputStream.flush()
writeDataOutputStream.close()
val httpResult:Int=connection.responseCode
if(httpResult==HttpURLConnection.HTTP_OK){
val inputStream=connection.inputStream
val reader=BufferedReader(InputStreamReader(inputStream))
val stringBuilder=StringBuilder()
var line:String?
try {
while(reader.readLine().also { line=it }!=null){
stringBuilder.append(line + "\n")
}
} catch (e:IOException){
e.printStackTrace()
}
finally {
try{
inputStream.close()
} catch (e:IOException){
e.printStackTrace()
}
}
result=stringBuilder.toString()
}
else{
result=connection.responseMessage
}
}
catch (e:SocketTimeoutException){
result="Connection timed out"
}
catch (e:Exception){
result="Error "+ e.message
}
finally {
connection?.disconnect()
}
return result
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
cancelProgressDialog()
Log.i("JSON RESPONSE RESULT","$result")
val jsonObject=JSONObject(result) //deconstructing json object
/*val message=jsonObject.optString("message")
Log.i("message",message)
val user_id=jsonObject.optInt("USER_ID")
Log.i("user_id","$user_id")
val profiledetails=jsonObject.optJSONObject("profile_details") //deconstructing json object inside json object
val is_profile_completed= profiledetails?.optString("IS_PROFILE_COMPLETED")
Log.i("is_profile_completed","$is_profile_completed")
val dataList=jsonObject.optJSONArray("data-list")
val dataListArray=jsonObject.optJSONArray()
jsonObject=JSONObject(result)
Log.i(“Data list size”,”${datalistArray.size()}”)
for(item in 0 until datalistArray.length()){
Log.i(“value $item”,”${datalistArray[item]}”)
Val dataItemObject:JSONObject=datalistArray[item] as JSONObject
val id=dataitemObject.optInt(“id”)
Log.i(“ID”,$”id”)
val value=dataitemObject.optString(“value”)
Log.i(“value”,$”value”)
*/
}
private fun showProgressDialog(){
customProgressDialog=Dialog(this#MainActivity)
customProgressDialog.setContentView(R.layout.dialog_custom_progress)
customProgressDialog.show()
}
private fun cancelProgressDialog(){
customProgressDialog.dismiss()
}
}
}

Android Studio Kotlin : App freezes when trying to read from inputStream (Bluetooth)

So im completely new to Android Studio and Kotlin. I have been following videos as well as looking over the Bluetooth overview from developer.android.com. I am really lost in trying to continuously read data from the inputBuffer, and have no idea where to start. I am able to send data successfully through the bluetooth, but whenever I try to continuously listen for data, the app freezes. Could anyone help elaborate or help step through the process for doing this?
package com.example.airboard
import android.app.ProgressDialog
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import android.content.Context
import android.os.AsyncTask
import android.os.Bundle
import android.os.Handler
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.control_layout.*
import org.jetbrains.anko.toast
import java.io.IOException
import java.util.*
class ControlActivity: AppCompatActivity() {
companion object {
var m_myUUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
var m_bluetoothSocket: BluetoothSocket? = null
lateinit var m_progress: ProgressDialog
lateinit var m_bluetoothAdapater: BluetoothAdapter
var m_isConnected: Boolean = false
lateinit var m_address: String
private val mmBuffer: ByteArray = ByteArray(1024)
private const val TAG = "MY_APP_DEBUG_TAG"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.control_layout)
m_address = intent.getStringExtra(SettingsActivity.EXTRA_ADDRESS)!!
ConnectToDevice(this).execute()
control_led_on.setOnClickListener { sendCommand("1") }
control_led_off.setOnClickListener { sendCommand("2") }
control_led_disconnect.setOnClickListener { disconnect() }
listen.setOnClickListener { listen() }
}
private fun listen() {
var numBytes: Int // bytes returned from read()
// Keep listening to the InputStream until an exception occurs.
//while (true) {
// Read from the InputStream.
numBytes = try {
m_bluetoothSocket!!.inputStream.read(mmBuffer)
} catch (e: IOException) {
Log.d(TAG, "Input stream was disconnected", e)
//break
}
toast(numBytes)
// Send the obtained bytes to the UI activity.
// }
}
private fun sendCommand(input: String){
if (m_bluetoothSocket != null){
try {
m_bluetoothSocket!!.outputStream.write(input.toByteArray())
Log.i("data", "sending..")
} catch (e: IOException) {
e.printStackTrace()
Log.i("data", "couldn't send")
}
return
}
}
private fun disconnect(){
if (m_bluetoothSocket != null){
try {
m_bluetoothSocket!!.close()
m_bluetoothSocket = null
m_isConnected = false
} catch (e: IOException) {
e.printStackTrace()
}
}
finish()
}
private class ConnectToDevice(c: Context) : AsyncTask<Void, Void, String>(){
private var connectSuccess: Boolean = true
private val context: Context
init {
this.context = c
}
override fun onPreExecute() {
super.onPreExecute()
m_progress = ProgressDialog.show(context, "Connecting...", "please wait")
}
override fun doInBackground(vararg p0: Void?) : String? {
try {
if (m_bluetoothSocket == null || !m_isConnected){
m_bluetoothAdapater = BluetoothAdapter.getDefaultAdapter()
val device: BluetoothDevice = m_bluetoothAdapater.getRemoteDevice(m_address)
m_bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(m_myUUID)
BluetoothAdapter.getDefaultAdapter().cancelDiscovery()
m_bluetoothSocket!!.connect()
}
} catch (e: IOException){
connectSuccess = false
e.printStackTrace()
}
return null
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
if(!connectSuccess){
Log.i("data", "couldn't connect")
} else {
m_isConnected = true
Log.i("data", "connected")
}
m_progress.dismiss()
}
}
}
Digging through some other questions, maybe ASyncTask is not a good option for doing this?
Android runs code in the UI thread by default. So you cant use while(true) in the main thread or the UI will freeze. Instead, do this task in a separate thread. You can extend the Thread class and check for incoming messages inside the run() method of the class. Check the documentation for more info.

Android studio app suddenly changes of activity

Im making a bluetooth app where you can turn on and off an arduino light. If i comment the setContentView, this does not happen. However, when I turn it on, it happens one of two things:
the app stops
it changes of activity
here's my code
package com.ainimei.remotemouse
import android.app.ProgressDialog
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import android.content.Context
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AlertDialog
import kotlinx.android.synthetic.main.activity_control_bluetooth_connection.*
import java.io.IOException
import java.util.*
class ControlBluetoothConnection : AppCompatActivity() {
companion object {
var m_myUUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
var m_bluetoothSocket: BluetoothSocket? = null
lateinit var m_progress: ProgressDialog
lateinit var m_bluetoothAdapter: BluetoothAdapter
var m_isConnected: Boolean = false
lateinit var m_address: String
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_keyboard)
onCreateExtra()
}
private fun onCreateExtra() {
m_address = intent.getStringExtra(Connection.EXTRA_ADDRESS).toString()
ConnectToDevice(this).execute()
control_led_on.setOnClickListener { sendCommand("a") }
control_led_off.setOnClickListener { sendCommand("b") }
control_led_disconnect.setOnClickListener { disconnect() }
}
private fun sendCommand(input: String) {
if (m_bluetoothSocket != null) {
try{
m_bluetoothSocket!!.outputStream.write(input.toByteArray())
} catch(e: IOException) {
e.printStackTrace()
}
}
}
private fun disconnect() {
if (m_bluetoothSocket != null) {
try {
m_bluetoothSocket!!.close()
m_bluetoothSocket = null
m_isConnected = false
} catch (e: IOException) {
e.printStackTrace()
}
}
finish()
alertMessage("Disconnected Successfully", "Bluetooth")
}
private class ConnectToDevice(c: Context) : AsyncTask<Void, Void, String>() {
private var connectSuccess: Boolean = true
private val context: Context
init {
this.context = c
}
override fun onPreExecute() {
super.onPreExecute()
m_progress = ProgressDialog.show(context, "Connecting...", "please wait")
}
override fun doInBackground(vararg p0: Void?): String? {
try {
if (m_bluetoothSocket == null || !m_isConnected) {
m_bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
val device: BluetoothDevice = m_bluetoothAdapter.getRemoteDevice(m_address)
m_bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(m_myUUID)
BluetoothAdapter.getDefaultAdapter().cancelDiscovery()
m_bluetoothSocket!!.connect()
}
} catch (e: IOException) {
connectSuccess = false
e.printStackTrace()
}
return null
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
if (!connectSuccess) {
//Log.i("data", "couldn't connect")
} else {
m_isConnected = true
}
m_progress.dismiss()
}
}
open fun alertMessage(message: String, title: String) {
var builder = AlertDialog.Builder(this)
builder.setTitle(title)
builder.setMessage(message)
val dialog = builder.create()
dialog.show()
}
}
i tried commenting some functions, and I saw that when i commented the onCreateExtra() function, this made the layout dont appear. need help

How to call Activity class method in inner class method in kotlin android

I have create one progress method in Activity class. then create one another class for AsyncTask.
My requirement is call processProgressBar() method in AsyncTask class doInBackground()
How it possible?
See My Code:
package com.example.bharat.generalknowledge
import android.os.AsyncTask
import android.os.Bundle
import android.os.Handler
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.View
import android.widget.ProgressBar
import com.example.bharat.generalknowledge.dbhandler.DatabaseHandler
import org.xml.sax.InputSource
import java.io.BufferedInputStream
import java.io.FileOutputStream
import java.net.URL
import java.util.zip.GZIPInputStream
class WelcomeActivity : AppCompatActivity() {
init {
println("Init block")
}
companion object {
var dbPath: String = ""
var isDatabaseExist: Boolean = false
private val handler = Handler()
val wActivity = WelcomeActivity()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_welcome)
val dbHandler = DatabaseHandler(this)
isDatabaseExist = dbHandler.checkIfTableExistOrNot()
Log.w("isDatabaseExist: ", isDatabaseExist.toString())
if(!isDatabaseExist){
dbPath = dbHandler.getDatabasePath()
downloadDb().execute()
}
}
fun processProgressBar(pStatus: Int=10){
println("====================processProgressBar==========================")
val res = resources
val drawable = res.getDrawable(R.drawable.circular)
val mProgress = findViewById<View>(R.id.circularProgressbar) as ProgressBar
mProgress.progress = 0 // Main Progress
mProgress.secondaryProgress = 100 // Secondary Progress
mProgress.max = 100 // Maximum Progress
mProgress.progressDrawable = drawable
mProgress.progress = pStatus
}
class downloadDb() : AsyncTask<Void, Void, String>() {
override fun doInBackground(vararg params: Void?): String? {
try {
// download the file
val url = URL("http://192.168.0.105/new-gk-app/web/uploads/db-backup/gk_app.gz")
val connection = url.openConnection()
connection.connect()
// get stream and convert gzip to db original
var stream = connection.getInputStream()
stream = GZIPInputStream(stream)
val `is` = InputSource(stream)
val input = BufferedInputStream(`is`.byteStream, 8192)
val output = FileOutputStream(dbPath)
val data = ByteArray(1024)
while ((input.read(data)) != -1) {
output.write(data);
// processProgressBar(30) /* ****** my requirement is call this method ******* */
}
output.flush()
output.close()
input.close()
} catch (e: Exception) {
Log.e("Error: ", e.message)
}
return null
}
override fun onPreExecute() {
super.onPreExecute()
Log.w("Database Downloded: ", "Start")
// ...
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
Log.w("Database Downloded: ", "Finish")
}
}
}
You can pass the reference of your activity in the constructor of your Async task, wrapped in a weak reference and call your function from there.
class downloadDb(var activity:WeakReference<WelcomeActivity >) : AsyncTask<Void, Void, String>()
and in you doInBackground
activity.get()?.let {
it.runOnUiThread({
})
}
To initialise the AysncTask,downloadDb(WeakReference(this))

Categories

Resources