I'm trying to subscribe to a topic via my Android application. Even though it connects successfully subscription fails. As soon as I perform subscription call, IOT connectivity fails and gives an error log as stated below. Want to Know where have I done any wrong coding ?
All the resources of IOT policy is given due to testing purposes to find a clue.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:*",
"Resource": "*"
}
]
}
I referred AWS-amplify for my development.
https://aws-amplify.github.io/docs/android/pubsub
1 .Connecting to IOT
private var mAWSMobileClient : AWSMobileClient = AWSMobileClient.getInstance()
private lateinit var mIotDataManager: AWSIotMqttManager
private lateinit var mAttachedPolicyReq: AttachPolicyRequest
private lateinit var mAwsIOTClient: AWSIotClient
private lateinit var mAwsCredentials: AWSCredentials
private fun connectToIOT() {
Thread(Runnable {
var mDeviceIdentity = Settings.Secure.getString(this.contentResolver, Settings.Secure.ANDROID_ID)
mIotDataManager = AWSIotMqttManager(mDeviceIdentity, <iot endpoint>)
mIotDataManager.keepAlive = 50
mIotDataManager.isAutoReconnect = false
mIotDataManager.connectionStabilityTime = 100
mAwsCredentials = mAWSMobileClient.awsCredentials
mAwsIOTClient = AWSIotClient(mAWSMobileClient)
mAwsIOTClient.setRegion(Region.getRegion(Regions.AP_SOUTHEAST_2))
mAttachedPolicyReq = AttachPolicyRequest()
mAttachedPolicyReq.policyName = "test_policy"
mAttachedPolicyReq.target = mAWSMobileClient.identityId
mAwsIOTClient.attachPolicy(mAttachedPolicyReq)
try {
mIotDataManager.connect(mAWSMobileClient, object : AWSIotMqttClientStatusCallback {
override fun onStatusChanged(
status: AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus?,
throwable: Throwable?
) {
when (status) {
AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus.ConnectionLost -> {}
AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus.Connected -> {}
AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus.Connecting -> {}
AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus.Reconnecting -> {}
else -> {
}
}
}
})
} catch (e: Exception) {
Log.d("IOT Data Manager Connection Errror : $e")
}
}).start()
}
Subscription Method
fun subscribeToThing() {
if(mConnected) {
Thread(Runnable {
mThingsName = "$mThingsName/shadow/get/accepted"
var message: String? = null
try {
mIotDataManager.subscribeToTopic(
mThingsName,
AWSIotMqttQos.QOS1,
object : AWSIotMqttNewMessageCallback {
override fun onMessageArrived(topic: String?, data: ByteArray?) {
try {
message = String(data!!, Charsets.UTF_8)
} catch (e: UnsupportedEncodingException) {
Log.d("Unsupported Encoding error :$e")
}
}
})
} catch (e: Exception) {
Log.d("Subscription error :$e")
}
}).start()
} else {
Log.d("IOT Not Connected")
}
}
Result Log :
W/AWSIotMqttManager: connection is Lost
Subscription error :com.amazonaws.AmazonClientException: Client error
when subscribing.
I see that you are using AWSMobileClient. Perhaps you did not initialize the mobile client instance prior to using the IOT client? The details are outlined in the following documentation.
Related
So I am using the Google's API and for some reason, I'm getting a generic error:
E/Network: searchBooks: Failed Getting books
When it initially loads up, the hard coded query "android" shows up with a list of books associated with the book topic. But when I search up a different topic like "shoes" for example, the error shows up. Even when you hard code a different topic other than "android", it still shows the error. I have checked the API and it is working properly with the different query searches.
Here's the Retrofit Interface:
#Singleton
interface BooksApi {
#GET(BOOK_EP)
suspend fun getAllBooks(
//don't initialize the query, so that the whole api is available to the user
#Query("q") query: String
): Book
#GET("$BOOK_EP/{bookId}")
suspend fun getBookInfo(
#Path("bookId") bookId: String
): Item
}
The Repo
class BookRepository #Inject constructor(private val api: BooksApi) {
suspend fun getBooks(searchQuery: String): Resource<List<Item>> {
return try {
Resource.Loading(data = true)
val itemList = api.getAllBooks(searchQuery).items
if(itemList.isNotEmpty()) Resource.Loading(data = false)
Resource.Success(data = itemList)
}catch (exception: Exception){
Resource.Error(message = exception.message.toString())
}
}
suspend fun getBookInfo(bookId: String): Resource<Item>{
val response = try {
Resource.Loading(data = true)
api.getBookInfo(bookId)
}catch (exception: Exception){
return Resource.Error(message = "An error occurred ${exception.message.toString()}")
}
Resource.Loading(data = false)
return Resource.Success(data = response)
}
The ViewModel:
class SearchViewModel #Inject constructor(private val repository: BookRepository): ViewModel(){
var list: List<Item> by mutableStateOf(listOf())
var isLoading: Boolean by mutableStateOf(true)
init {
loadBooks()
}
private fun loadBooks() {
searchBooks("android")
}
fun searchBooks(query: String) {
viewModelScope.launch(Dispatchers.Default) {
if (query.isEmpty()){
return#launch
}
try {
when(val response = repository.getBooks(query)){
is Resource.Success -> {
list = response.data!!
if (list.isNotEmpty()) isLoading = false
}
is Resource.Error -> {
isLoading = false
Log.e("Network", "searchBooks: Failed Getting books", )
}
else -> {isLoading = false}
}
}catch (exception: Exception){
isLoading = false
Log.d("Network", "searchBooks: ${exception.message.toString()}")
}
}
}
}
I'll leave the project public so you guys can check it out for more of an understanding
Github Link: https://github.com/OEThe11/ReadersApp
P.S. you would have to create a login (takes 30 sec), but once you do, you'll have access to the app immediately.
This issue is occurring because of JsonSyntaxException java.lang.NumberFormatException while the JSON response is getting parsed from the API. This is because the averageRating field in the VolumeInfo data class is declared as Int but the response can contain floating point values.
If you change averageRating field type from Int to Double in the VolumeInfo data class, the exception would no longer occur.
I suggest you to debug your code in such cases.
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 am developing an Android app where all my users who logged inside my application should be remembered. More specifically, get their Device Key register in their user profile. The problem here is my currSignedDevice shows “null” as shown below and when I try to remember it, It is not being remembered.
I am using AWS Cognito and followed their documentation here https://aws.amazon.com/blogs/mobile/tracking-and-remembering-devices-using-amazon-cognito-your-user-pools/. I set my device settings in Cognito and implemented with code below.
fun getSignInUserDeviceDetails(user: String?):Boolean {
var currSignedDevice = userPool.getUser (user).thisDevice();
println("device is "+ currSignedDevice +"\t")
var changeDeviceSettingsHandler: GenericHandler = object : GenericHandler {
override fun onSuccess() {
// Device status successfully changed
println("device remembered successfully")
}
override fun onFailure(exception: java.lang.Exception) {
// Probe exception for the cause of the failure
println("failure in remember device")
}
}
currSignedDevice?.rememberThisDevice(changeDeviceSettingsHandler)
return true
}
Called this function in my loginfragment after the valid authentication.
login.setOnClickListener {
val email = email.text.toString()
val password = password.text.toString()
if (email.trim().isEmpty()) {
toastError("Enter an email.")
return#setOnClickListener
}
if (password.trim().isEmpty()) {
toastError("Enter a password.")
return#setOnClickListener
}
emailVal = email.toLowerCase()
passwordVal = password
signInDialog?.show()
activity?.let {
(it as MainActivity).setDialog50PercentWidth(signInDialog)
}
viewModel.authenticate(email, password, confirmUserHandler)
AWSClient.instance.getSignInUserDeviceDetails(emailVal)
}
The problem here is my currSignedDevice shows “null” as shown below in the figure and when I try to remember it, It is not being remembered. This is also not going inside of success or failure. It’s directly jumping out from that block.
fun getSignInUserDeviceDetails(user: String?):Boolean {
var currSignedDevice = userPool.getUser (user).thisDevice();
println("device is "+ currSignedDevice +"\t") // gives my currSignedDevice as NULL when I debug
var changeDeviceSettingsHandler: GenericHandler = object : GenericHandler {
override fun onSuccess() {
println("device remembered successfully")
}
override fun onFailure(exception: java.lang.Exception) {
// Probe exception for the cause of the failure
println("failure in remeber device")
}
}
currSignedDevice.rememberThisDevice(changeDeviceSettingsHandler)
return true
}
I made app where user can add server (recycler row) to favorites. It only saves the IP and Port. Than, when user open FavoriteFragment Retrofit makes calls for each server
#GET("v0/server/{ip}/{port}")
suspend fun getServer(
#Path("ip") ip: String,
#Path("port") port: Int
): Server
So in repository I mix the sources and make multiple calls:
suspend fun getFavoriteServersToRecyclerView(): Flow<DataState<List<Server>>> = flow {
emit(DataState.Loading)
try {
val getFavoritesServersNotLiveData = favoritesDao.getFavoritesServersNotLiveData()
val list: MutableList<Server> = mutableListOf()
getFavoritesServersNotLiveData.forEach { fav ->
val server = soldatApiService.getServer(fav.ip, fav.port)
list.add(server)
}
emit(DataState.Success(list))
} catch (e: Exception) {
emit(DataState.Error(e))
}
}
and then in ViewModel I create LiveData object
fun getFavoriteServers() {
viewModelScope.launch {
repository.getFavoriteServersToRecyclerView()
.onEach { dataState ->
_favoriteServers.value = dataState
}.launchIn(viewModelScope)
}
}
And everything works fine till the Favorite server is not more available in the Lobby and the Retrofit call failure.
My question is: how to skip the failed call in the loop without crashing whole function.
Emit another flow in catch with emitAll if you wish to continue flow like onResumeNext with RxJava
catch { cause ->
emitAll(flow { emit(DataState.Errorcause)})
}
Ok, I found the solution:
suspend fun getFavoriteServersToRecyclerView(): Flow<DataState<List<Server>>> = flow {
emit(DataState.Loading)
val list: MutableList<Server> = mutableListOf()
try {
val getFavoritesServersNotLiveData = favoritesDao.getFavoritesServersNotLiveData()
val job = CoroutineScope(coroutineContext).launch {
getFavoritesServersNotLiveData.forEach { fav ->
val server = getServer(fav.ip, fav.port)
server.collect { dataState ->
when (dataState) {
is DataState.Loading -> Log.d(TAG, "loading")
is DataState.Error -> Log.d(TAG, dataState.exception.message!!)
is DataState.Success -> {
list.add(dataState.data)
Log.d(TAG, dataState.data.toString())
}
}
}
}
}
job.join()
emit(DataState.Success(list))
} catch (e: Exception) {
emit(DataState.Error(e))
}
}
when using retrofit you can wrap response object with Response<T> (import response from retrofit) so that,
#GET("v0/server/{ip}/{port}")
suspend fun getServer(
#Path("ip") ip: String,
#Path("port") port: Int
): Response<Server>
and then in the Repository you can check if network failed without using try-catch
suspend fun getFavoriteServersToRecyclerView(): Flow<DataState<List<Server>>> = flow {
emit(DataState.Loading)
val getFavoritesServersNotLiveData = favoritesDao.getFavoritesServersNotLiveData()
if(getFavoritesServersNotLiveData.isSuccessful) {
val list: MutableList<Server> = mutableListOf()
getFavoritesServersNotLiveData.body().forEach { fav ->
val server = soldatApiService.getServer(fav.ip, fav.port)
// if the above request fails it wont go to the else block
list.add(server)
}
emit(DataState.Success(list))
} else {
val error = getFavoritesServersNotLiveData.errorBody()!!
//do something with error
}
}
following the code, in init function I create a Person object,and have an exception,now I want to stop the progress in catch like java return. How can I do it?
class Person {
val age: String = "10"
private lateinit var person: Person
init {
try {
person = get(2)
} catch (exception: Throwable) {
}
println("----------------do it $person.age")
}
fun get(i: Int): Person {
when (i) {
1 -> {
return Person()
}
else -> {
throw MyException("aaaaaaaaa")
}
}
}
}
If an instance cannot be created due to errors in init, this error shouldn't be suppressed but delegated to the caller. So just do not catch the exception and the init "stopps" automatically.