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

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.

Related

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

Kotlin Coroutines: Make a thread executing a code while the other waiting the first to complete

I have find the following code from https://github.com/blink22/react-native-html-to-pdf/blob/master/android/src/main/java/android/print/PdfConverter.java converted in Kotlin:
import android.content.Context
import android.os.Build
import android.os.Handler
import android.os.ParcelFileDescriptor
import android.print.PrintAttributes.Resolution
import android.print.PrintDocumentAdapter.LayoutResultCallback
import android.print.PrintDocumentAdapter.WriteResultCallback
import android.util.Log
import android.webkit.WebView
import android.webkit.WebViewClient
import java.io.File
/**
* Converts HTML to PDF.
*
*
* Can convert only one task at a time, any requests to do more conversions before
* ending the current task are ignored.
*/
class PdfConverter private constructor() : Runnable {
private var mContext: Context? = null
private var mHtmlString: String? = null
private var mPdfFile: File? = null
private var mPdfPrintAttrs: PrintAttributes? = null
private var mIsCurrentlyConverting = false
private var mWebView: WebView? = null
private var done: Boolean = false
override fun run() {
mWebView = WebView(mContext as Context)
mWebView!!.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) throw RuntimeException(
"call requires API level 19"
) else {
val documentAdapter =
mWebView!!.createPrintDocumentAdapter()
documentAdapter.onLayout(
null,
pdfPrintAttrs,
null,
object : LayoutResultCallback() {},
null
)
documentAdapter.onWrite(
arrayOf(PageRange.ALL_PAGES),
outputFileDescriptor,
null,
object : WriteResultCallback() {
override fun onWriteFinished(pages: Array<PageRange>) {
destroy()
done = true
}
})
}
Log.d("end of onpagefinished()", "end of onpagefinished()")
}
}
mWebView!!.loadData(mHtmlString, "text/HTML", "UTF-8")
Log.d("end of run()", "end of run()")
}
var pdfPrintAttrs: PrintAttributes?
get() = if (mPdfPrintAttrs != null) mPdfPrintAttrs else defaultPrintAttrs
set(printAttrs) {
mPdfPrintAttrs = printAttrs
}
fun convert(
context: Context?,
htmlString: String?,
file: File?
) {
requireNotNull(context) { "context can't be null" }
requireNotNull(htmlString) { "htmlString can't be null" }
requireNotNull(file) { "file can't be null" }
if (mIsCurrentlyConverting) return
mContext = context
mHtmlString = htmlString
mPdfFile = file
mIsCurrentlyConverting = true
runOnUiThread(this)
Log.d("end of convert()","end of convert()")
}
private val outputFileDescriptor: ParcelFileDescriptor?
private get() {
try {
mPdfFile!!.createNewFile()
Log.d("outputfiledescriptor","the file has been created")
return ParcelFileDescriptor.open(
mPdfFile,
ParcelFileDescriptor.MODE_TRUNCATE or ParcelFileDescriptor.MODE_READ_WRITE
)
} catch (e: Exception) {
Log.d(TAG, "Failed to open ParcelFileDescriptor", e)
}
return null
}
private val defaultPrintAttrs: PrintAttributes?
private get() = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) null else PrintAttributes.Builder()
.setMediaSize(PrintAttributes.MediaSize.NA_GOVT_LETTER)
.setResolution(Resolution("RESOLUTION_ID", "RESOLUTION_ID", 600, 600))
.setMinMargins(PrintAttributes.Margins.NO_MARGINS)
.build()
private fun runOnUiThread(runnable: Runnable) {
val handler = Handler(mContext!!.mainLooper)
handler.post(this)
}
private fun destroy() {
mContext = null
mHtmlString = null
mPdfFile = null
mPdfPrintAttrs = null
mIsCurrentlyConverting = false
mWebView = null
Log.d("end of destroy()","end of destroy()")
}
companion object {
private const val TAG = "PdfConverter"
private var sInstance: PdfConverter? = null
#get:Synchronized
val instance: PdfConverter?
get() {
if (sInstance == null) sInstance =
PdfConverter()
return sInstance
}
}
}
I want the execution to wait for the onWriteFinished before go back to runOnUiThread. Also I want the main Thread to execute run. So I tried to make it happen with the following code using coroutines :
package android.print
import kotlinx.coroutines.runBlocking
import android.content.Context
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.ParcelFileDescriptor
import android.print.PrintAttributes.Resolution
import android.print.PrintDocumentAdapter.LayoutResultCallback
import android.print.PrintDocumentAdapter.WriteResultCallback
import android.util.Log
import android.webkit.WebView
import android.webkit.WebViewClient
import java.io.File
/**
* Converts HTML to PDF.
*
*
* Can convert only one task at a time, any requests to do more conversions before
* ending the current task are ignored.
*/
class PdfConverter2 {
private var mContext: Context? = null
private var mHtmlString: String? = null
private var mPdfFile: File? = null
private var mPdfPrintAttrs: PrintAttributes? = null
private var mIsCurrentlyConverting = false
private var mWebView: WebView? = null
private var done: Boolean = false
suspend fun run() {
Log.d("run()","is this the main thread :"+(Looper.myLooper() == Looper.getMainLooper()))
mWebView = WebView(mContext as Context)
mWebView!!.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) throw RuntimeException(
"call requires API level 19"
) else {
val documentAdapter =
mWebView!!.createPrintDocumentAdapter()
documentAdapter.onLayout(
null,
pdfPrintAttrs,
null,
object : LayoutResultCallback() {},
null
)
documentAdapter.onWrite(
arrayOf(PageRange.ALL_PAGES),
outputFileDescriptor,
null,
object : WriteResultCallback() {
override fun onWriteFinished(pages: Array<PageRange>) {
destroy()
done = true
}
})
}
Log.d("end of onpagefinished()", "end of onpagefinished()")
}
}
mWebView!!.loadData(mHtmlString, "text/HTML", "UTF-8")
Log.d("end of run()", "end of run()")
}
var pdfPrintAttrs: PrintAttributes?
get() = if (mPdfPrintAttrs != null) mPdfPrintAttrs else defaultPrintAttrs
set(printAttrs) {
mPdfPrintAttrs = printAttrs
}
fun convert(
context: Context?,
htmlString: String?,
file: File?
) {
requireNotNull(context) { "context can't be null" }
requireNotNull(htmlString) { "htmlString can't be null" }
requireNotNull(file) { "file can't be null" }
if (mIsCurrentlyConverting) return
mContext = context
mHtmlString = htmlString
mPdfFile = file
mIsCurrentlyConverting = true
runOnUiThread()
Log.d("end of convert()","end of convert()")
}
private val outputFileDescriptor: ParcelFileDescriptor?
private get() {
try {
mPdfFile!!.createNewFile()
Log.d("outputfiledescriptor","the file has been created")
return ParcelFileDescriptor.open(
mPdfFile,
ParcelFileDescriptor.MODE_TRUNCATE or ParcelFileDescriptor.MODE_READ_WRITE
)
} catch (e: Exception) {
Log.d(TAG, "Failed to open ParcelFileDescriptor", e)
}
return null
}
private val defaultPrintAttrs: PrintAttributes?
private get() = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) null else PrintAttributes.Builder()
.setMediaSize(PrintAttributes.MediaSize.NA_GOVT_LETTER)
.setResolution(Resolution("RESOLUTION_ID", "RESOLUTION_ID", 600, 600))
.setMinMargins(PrintAttributes.Margins.NO_MARGINS)
.build()
private fun runOnUiThread() {
runBlocking {
run()
while(!done){
}
}
}
private fun destroy() {
mContext = null
mHtmlString = null
mPdfFile = null
mPdfPrintAttrs = null
mIsCurrentlyConverting = false
mWebView = null
Log.d("end of destroy()","end of destroy()")
}
companion object {
private const val TAG = "PdfConverter2"
private var sInstance: PdfConverter2? = null
#get:Synchronized
val instance: PdfConverter2?
get() {
if (sInstance == null) sInstance =
PdfConverter2()
return sInstance
}
}
}
Also there is a function in another file that calls the PdfConverter and it calls the PdfConverter.
fun createPdfFromHtml(htmlstring: String) {
val converter: PdfConverter? = PdfConverter.instance
val file = File(
Environment.getExternalStorageDirectory().getPath().toString() + "/" + name_of_directory_of_pdfs + "/",
nameofpdf
)
converter?.convert(m_context, htmlstring, file)
mFilepdf = file
}
What I want is the code execution stops at the 'converter?.convert(m_context, htmlstring, file)' and wait for 'onWriteFinished' of PdfConverter to be executed and then continue. The other senario I thought was that the execution stops at 'runonUiThread' and wait for ''onWriteFinished' again to be executed.
After the answer of #m0skit0 I change the last code :
fun createPdfFromHtml(htmlstring: String) {
val file = File(
Environment.getExternalStorageDirectory().path.toString() + "/" + name_of_directory_of_pdfs + "/",
nameofpdf
)
var converter = PdfConverter3.from(m_context)
GlobalScope.launch(Dispatchers.IO) {// I TRY ALSO Dispatchers.Main
converter.convert(htmlstring, file)
}
mFilepdf = file
Log.d("mich/createPDfFromHtml", "at the end of createPdfFromHtml, is this the main thread ? "+ (Looper.myLooper() == Looper.getMainLooper()))
}
But the thing is existing again.
Here's my take on translating that class to Kotlin using coroutines
package org.m0skit0.android.testapp
import android.annotation.TargetApi
import android.content.Context
import android.os.ParcelFileDescriptor
import android.print.PageRange
import android.print.PrintAttributes
import android.print.PrintDocumentAdapter
import android.webkit.WebView
import android.webkit.WebViewClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
#TargetApi(19)
class PdfConverter private constructor(private val context: Context) {
private val defaultPrintAttributes: PrintAttributes by lazy {
PrintAttributes.Builder()
.setMediaSize(PrintAttributes.MediaSize.NA_GOVT_LETTER)
.setResolution(PrintAttributes.Resolution("RESOLUTION_ID", "RESOLUTION_ID", 600, 600))
.setMinMargins(PrintAttributes.Margins.NO_MARGINS)
.build()
}
private var printAttributes: PrintAttributes? = null
fun printAttributes(printAttributes: PrintAttributes): PdfConverter = apply {
this.printAttributes = printAttributes
}
suspend fun convert(htmlString: String, pdfFile: File) {
withContext(Dispatchers.Main) {
suspendCoroutine<Unit> { continuation ->
WebView(context).apply {
webViewClient = WebViewClientImpl(pdfFile, continuation)
}.loadData(htmlString, "text/html", "UTF-8")
}
}
}
private fun File.outputFileDescriptor(): ParcelFileDescriptor? =
try {
createNewFile()
ParcelFileDescriptor.open(this, ParcelFileDescriptor.MODE_TRUNCATE or ParcelFileDescriptor.MODE_READ_WRITE)
} catch (e: Exception) {
null
}
companion object {
fun from(context: Context): PdfConverter = PdfConverter(context)
}
private inner class WebViewClientImpl(private val file: File, private val continuation: Continuation<Unit>) : WebViewClient() {
override fun onPageFinished(webView: WebView, url: String) {
webView.createPrintDocumentAdapter()?.run {
onLayout(
null,
printAttributes ?: defaultPrintAttributes,
null,
object : PrintDocumentAdapter.LayoutResultCallback() {},
null
)
onWrite(
arrayOf(PageRange.ALL_PAGES),
file.outputFileDescriptor(),
null,
object : PrintDocumentAdapter.WriteResultCallback() {
override fun onWriteCancelled() {
super.onWriteCancelled()
continuation.resume(Unit)
}
override fun onWriteFailed(error: CharSequence?) {
super.onWriteFailed(error)
continuation.resumeWithException(Exception(error.toString()))
}
override fun onWriteFinished(pages: Array<out PageRange>?) {
super.onWriteFinished(pages)
continuation.resume(Unit)
}
}
)
}
}
}
}
if I understood you correctly, you want run to be executed on the main thread. In order to achieve this, instead of making run a suspend function, you could let the function launch a coroutine with the Dispatchers.Main scope.

Unable to read data via bluetooth successfully

I'm trying to create an App which can receive data and send data to the microcontroller (ESP32). But for some reason, I'm unable to receive data from microcontroller successfully.
The app is written in Kotlin, and I already tried some examples mentioned on StackOverflow, but none of them actually works on my code.
I can successfully send data to the microcontroller via Bluetooth, but I can't receive data from Bluetooth. (The method I used in the microcontroller is just simply "ESP_BT.println("Check");"
In the code snippet, the function relates to my receiving data is called "receiveBluetooth"
class ControlActivity: AppCompatActivity() {
companion object {
val myUUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
var myBluetoothSocket: BluetoothSocket? = null
lateinit var myProgress: ProgressDialog
lateinit var myBluetoothAdapter: BluetoothAdapter
var myIsConnected: Boolean = false
lateinit var myAddress: String
val mmInStream: InputStream? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.control_layout)
myAddress = intent.getStringExtra(SelectDeviceActivity.EXTRA_ADDRESS)
ConnectToDevice(this).execute()
val btnShow = findViewById<Button>(R.id.btnShow)
var inputRPM: String
//Read in value and store it as String
btnShow.setOnClickListener{
inputRPM = receiveInput()
sendCommand(inputRPM)
}
//Read RPM from microcontroller (bluetooth)
val showCountTextView = findViewById<TextView>(R.id.textView)
btnRefresh.setOnClickListener {
//showCountTextView.text = receiveBluetooth()
receiveBluetooth(showCountTextView)
}
control_disconnect.setOnClickListener{
disconnect()
}
}
private fun receiveInput(): String {
val input = findViewById<EditText>(R.id.editText)
return input.text.toString()
}
private fun sendCommand(input: String) {
if (myBluetoothSocket != null) {
try{
myBluetoothSocket!!.outputStream.write(input.toByteArray())
} catch (e: IOException) {
e.printStackTrace()
}
}
}
private fun receiveBluetooth(input: TextView) {
val buffer = ByteArray(256)
val bytes:Int
var tmpIn: InputStream? = null
if (myBluetoothSocket != null) {
try {
tmpIn = myBluetoothSocket!!.inputStream
val mmInStream = DataInputStream(tmpIn)
bytes = mmInStream.read(buffer)
val readMessage = String(buffer, 0, bytes)
input.text = readMessage
//input.text="123"
} catch (e:IOException) {
e.printStackTrace()
}
}
}
private fun disconnect() {
if (myBluetoothSocket != null) {
try {
myBluetoothSocket!!.close()
myBluetoothSocket = null
myIsConnected = 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()
myProgress = ProgressDialog.show(context, "Connecting", "Please wait")
}
override fun doInBackground(vararg params: Void?): String? {
try {
if (myBluetoothSocket == null || !myIsConnected) {
myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
val device: BluetoothDevice = myBluetoothAdapter.getRemoteDevice(myAddress)
myBluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(myUUID)
BluetoothAdapter.getDefaultAdapter().cancelDiscovery()
myBluetoothSocket!!.connect()
}
} catch (e: IOException) {
connectSuccess = false
e.printStackTrace()
}
//Needs be fixed
return null
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
if (!connectSuccess) {
Log.i("data", "couldn't connect")
} else {
myIsConnected = true
}
myProgress.dismiss()
}
}
}
I expect the text will show exactly "Check", but instead, my text will only show the initial value that I assigned.
Maybe you should use a library. For me works fine RxAndroidBle library:
Gradle:
implementation "com.polidea.rxandroidble2:rxandroidble:1.8.1"
Implementation:
In my project with Android Java and ESP32 too, I read some characteristics or values with simple implementations, for example:
public void setupNotification() {
if (isConnected()) {
final Disposable disposable = connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(charactSensorDataUuid))
.doOnNext(notificationObservable -> { notificationHasBeenSetUp(); })
.flatMap(notificationObservable -> notificationObservable)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onNotificationReceived, this::onNotificationSetupFailure);
compositeDisposable.add(disposable);
}
}
public void readSensorConfig(){
if (isConnected()) {
final Disposable disposable = connectionObservable
.firstOrError()
.flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(charactConfigUuid))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSensorConfigRead, this::onReadFailure);
compositeDisposable.add(disposable);
}
}
public void readSensorData(){
if (isConnected()) {
final Disposable disposable = connectionObservable
.firstOrError()
.flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(charactSensorDataUuid))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSensorDataRead, this::onReadFailure);
compositeDisposable.add(disposable);
}
}
The complete Java implementation is here:
https://github.com/kike-canaries/android-hpma115s0/blob/master/app/src/main/java/hpsaturn/pollutionreporter/common/BLEHandler.java
The migration to Kotlin should be simple, also on this library the main target is Bluetooth BLE, and they have many samples on Kotlin

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))

Fetching a URL in Android Kotlin asynchronously

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

Categories

Resources