Have some trouble with thread work - java

My event emmiter class has code:
private val socketListeners: ArrayList<SocketContentListener> = ArrayList()
//add listener here
override fun subscribe(socketListener: SocketContentListener) {
socketListeners.add(socketListener)
}
private fun getSocketConnectListener()
: SocketContentListener {
/**
* Post received messages to listeners via Handler
* because handler helps to set all messages in order on main thread.
*/
return object : SocketContentListener {
override fun onUdpServerListenerCreated(inetAddress: InetAddress?, port: Int) {
val subscribers = ArrayList<SocketContentListener>(socketListeners)
for (listener in subscribers) {
Handler(Looper.getMainLooper()).post({ listener.onUdpServerListenerCreated(inetAddress, port) })
}
}
}
I try to crete Observable:
val udpObservable = Observable.create<Int> { emitter ->
val listener = object : SocketListener() {
override fun onUdpServerListenerCreated(inetAddress: InetAddress, port: Int) {
emitter.onNext(port)
emitter.onComplete()
}
}
//add listener here
socketSource.subscribe(listener)
emitter.setCancellable { socketSource.unSubscribe(listener) }
}.subscribeOn(Schedulers.io())
.doOnNext { Log.d("123-thread", "current is: " + Thread.currentThread().name) }
.onErrorReturn { throw ConnectionException(it) }
.subscribe()
But during test instead of the expected RxCachedThreadScheduler-1 thread work i saw
D/123-thread: current is:-> main
So can You help me? please. Where is my mistake? How do I achieve the desired RxCachedThreadScheduler thread for rx chains?

The code for creating Observable will be executed on the scheduler, there is no implicit context change anywhere.
  Your events arrive from the listener on the main thread. Then you send them to the emitter on the same thread.
So, subscription go on Schedulers io Thread, but emitters go on Main Thread
 
So the solution is to add observerOn(Schedulers.newThread()) after Observable create. Like this
val udpObservable = Observable.create<Int> { emitter ->
val listener = object : SocketListener() {
override fun onUdpServerListenerCreated(inetAddress: InetAddress, port: Int) {
emitter.onNext(port)
emitter.onComplete()
}
}
//add listener here
socketSource.subscribe(listener)
emitter.setCancellable { socketSource.unSubscribe(listener) }
}.subscribeOn(Schedulers.io())
//need add this for work with emmit data on background
.observerOn(Schedulers.newThread())
.doOnNext { Log.d("123-thread", "current is: " + Thread.currentThread().name) }
.onErrorReturn { throw ConnectionException(it) }
.subscribe()

Related

Kotlin multi platform flow block convert to custom wrapper flow use for ios

I have a kotlin multi platform project which contains apollo graphql api
in this project i have BaseRepository Class and in this class there is a method to execute query or mutations
suspend fun <D : Query.Data> executeQuery(query: Query<D>): ApolloResponse<D> {
val response = getApolloClient().query(query).execute()
checkOperation(response)
return response
}
suspend fun <D : Mutation.Data> executeMutation(mutation: Mutation<D>): ApolloResponse<D> {
val response = getApolloClient().mutation(mutation).execute()
checkOperation(response)
return response
}
For example i want to use this method in Some repository like this
class HelpRepository : BaseRepository() {
fun test(request: AddFeedBackRequest) = flow {
val feedBackType = if (request.type == AddFeedBackType.Bug) {
FeedbackType.BUG
} else {
FeedbackType.FEEDBACK
}
val input = AddFeedbackInput(request.note, Optional.presentIfNotNull(feedBackType))
emit(true)
val mutation = AddFeedbackMutation(input)
val response = executeMutation(mutation)
emit(false)
}
}
when i add the flow scope i shouldn't be had to convert this method to a suspend function
i dont want to use suspend function because of ios application. When i use suspend function its convert "Kotlinx_coroutines_coreFlowCollector" in xcode
so i found a wrapper function like this
fun <T> Flow<T>.asCommonFlow(): CommonFlow<T> = CommonFlow(this)
class CommonFlow<T>(private val origin: Flow<T>) : Flow<T> by origin {
fun listen(block: (T) -> Unit): Closeable {
val job = Job()
onEach {
block(it)
}.launchIn(CoroutineScope(Dispatchers.Main + job))
return object : Closeable {
override fun close() {
job.cancel()
}
}
}
}
when i use this wrapper with single variable it works exactly what i want in xcode.
but in functions i couldn't find a proper way to do this
i need a wrapper like
= commonFlow {
}
instead of this
= flow {
}
to use this method as a commonFlow wrapper
Can you help me ?
We have pretty much the same thing in one of our projects. We have a extension function that converts the regular flow to a "common" flow so it can be used in both Android and iOS.
You can created flow like always, and wrap it at the end.
fun <T> Flow<T>.wrap(): CommonFlow<T> = CommonFlow(this)
class HelpRepository : BaseRepository() {
fun test(request: AddFeedBackRequest) = flow {
val feedBackType = if (request.type == AddFeedBackType.Bug) {
FeedbackType.BUG
} else {
FeedbackType.FEEDBACK
}
val input = AddFeedbackInput(request.note, Optional.presentIfNotNull(feedBackType))
emit(true)
val mutation = AddFeedbackMutation(input)
val response = executeMutation(mutation)
emit(false)
}
}.wrap()

Wait for a event result in kotlin

I'm using a bluetooth printer SDK,
which has a call back interface method to be trigger on search completed.
fun method1(){
val callBack = HoinPrinterCallBack(callBack = fun (): Int{
//Triggered after search completed
return 0;
})
mHoinPrinter = HoinPrinter.getInstance(this, 1, callBack)
}
Callback interface implementation
typealias CallBack = () -> Int
class HoinPrinterCallBack () : PrinterCallback {
override fun onEvent(p0: PrinterEvent?) {
print(p0)
if(p0?.event == Constant.EVENT_FIND_BT_DEVICE || p0?.event == Constant.EVENT_DISCOVERY_BT_FINISHED){
callBack()
}
}
}
}
I pass a callBack method to be triggered on search completed event in above code.
Then there is a separate method API for search of bluetooth device.
fun methhod2(){
mHoinPrinter.startBtDiscovery();
}
My requirement is to wait for the completed event callback after startBTDiscovery. Any suggestions on how I can proceed further?

Implementing MVI Architecture in Android without Mosby

I am trying to implement MVI Architecture in Android, but don't want to use Mosby Library. I want to learn the basics first.
I am building a sample app where when I press a button, text in the textview changes(initially the text is something else). Here is the code for MainActivity and MainPresenter.
class MainActivity : AppCompatActivity(), MainContract.View {
lateinit var mPresenter: MainContract.Presenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mPresenter = MainPresenter()
mPresenter.attachPresenter(this)
bind()
}
#SuppressLint("CheckResult")
private fun bind() {
mPresenter.states().subscribe({ state ->
render(state)
}, {
Log.e("error", "Error is: ", it)
it.printStackTrace()
})
mPresenter.addIntents(intents())
}
override fun intents(): Observable<MainIntent> {
return Observable.merge(
initialIntent(),
clickIntent()
)
}
override fun render(state: MainViewState) {
btn_show.isEnabled = state.isEnabledButton
helloWorldTextView.text = state.message
loadingIndicator.visibility = if (state.isLoading) View.VISIBLE else View.GONE
}
private fun initialIntent(): Observable<MainIntent.InitialIntent> = Observable.just(MainIntent.InitialIntent)
private fun clickIntent(): Observable<MainIntent.ClickIntent> {
return btn_show.clicks().map { MainIntent.ClickIntent("Eureka") }
}
}
class MainPresenter : MainContract.Presenter {
private val intentsSubject: PublishSubject<MainIntent> = PublishSubject.create()
override fun states(): Observable<MainViewState> {
return statesObservable
}
private lateinit var view: MainContract.View
override fun attachPresenter(view: MainContract.View) {
this.view = view
}
#SuppressLint("CheckResult")
override fun addIntents(intents: Observable<MainIntent>) {
intents.subscribe(intentsSubject)
}
private val reducer =
BiFunction { previousState: MainViewState, result: MainResult ->
when (result) {
is MainResult.InitialResult.InFlight -> previousState.copy(
isLoading = true,
message = "Initial Result",
isEnabledButton = false
)
is MainResult.InitialResult.Success -> previousState.copy(
isLoading = true,
message = "Initial Success",
isEnabledButton = true
)
is MainResult.InitialResult.Error -> previousState.copy(
isLoading = false,
message = "Error Initially",
isEnabledButton = true
)
is MainResult.ClickedResult.Success -> previousState.copy(
isLoading = false,
message = System.currentTimeMillis().toString(),
isEnabledButton = true
)
is MainResult.ClickedResult.Error -> previousState.copy(
isLoading = false,
message = "Error Clicked",
isEnabledButton = true
)
is MainResult.ClickedResult.InFlight -> previousState.copy(
isLoading = true,
message = "Clicked In Flight",
isEnabledButton = false
)
}
}
private fun actionFromIntent(intent: MainIntent): MainAction {
if (intent is MainIntent.InitialIntent) {
return MainAction.InitialAction
} else if (intent is MainIntent.ClickIntent) {
return MainAction.ClickedAction("Hello")
} else {
return MainAction.InitialAction
}
}
private var actionProcessor: ObservableTransformer<MainAction, MainResult> = ObservableTransformer { actions ->
actions.publish { shared ->
Observable.merge<MainResult>(
shared.ofType(MainAction.InitialAction::class.java).compose(initialActionProcessor),
shared.ofType(MainAction.ClickedAction::class.java).compose(clickActionProcessor)
)
}
}
private val initialActionProcessor =
ObservableTransformer<MainAction.InitialAction, MainResult.InitialResult> { action: Observable<MainAction.InitialAction> ->
action.switchMap {
Observable.just("hello initially")
.map { MainResult.InitialResult.Success(it) }
.cast(MainResult.InitialResult::class.java)
.onErrorReturn { MainResult.InitialResult.Error(it.message!!) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.startWith { MainResult.InitialResult.InFlight }
}
}
private val clickActionProcessor =
ObservableTransformer<MainAction.ClickedAction, MainResult.ClickedResult> { action: Observable<MainAction.ClickedAction> ->
Observable.just("Click").map { message ->
MainResult.ClickedResult.Success(message)
}.cast(MainResult.ClickedResult::class.java)
.onErrorReturn { MainResult.ClickedResult.Error("Error") }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.startWith { MainResult.ClickedResult.InFlight }
}
private val statesObservable: Observable<MainViewState> = compose()
private fun compose(): Observable<MainViewState> {
return intentsSubject
.map {
actionFromIntent(it)
}
.compose(actionProcessor)
.scan(MainViewState.idle(), reducer)
.distinctUntilChanged()
.replay(1)
.autoConnect(0)
}
}
Problem is that only the Inital event is fired and nothing else. The code doesn't respond to clicks, render is called only initially once.
Also, if I remove the startWith{} from the actionProcessors code responds to clicks, but only once. After that, nothing happens.
Does anyone see issue with the code? I have been trying to get my head around this problem for a while now.
My previous reply:
It's not straight answer to your question. But if you implement what's below, you probably won't have the problem you actually asked about and you'll have easier MVI solution.
You probably try to merge https://speakerdeck.com/jakewharton/the-state-of-managing-state-with-rxjava-devoxx-us-2017, http://hannesdorfmann.com/android/mosby3-mvi-1 and https://medium.com/#oldergod/managing-state-with-rxjava-b0798a6c5757 ideas.
Take a look here: https://proandroiddev.com/taming-state-in-android-with-elm-architecture-and-kotlin-part-1-566caae0f706 - it's simpler. Part 1 and 2 should be enough.
I tried the 1st approach and was repulsed by initial complexity. In 2nd approach you don't have Action, Intent, Result, but Msg instead. It's simpler to reason about.
There's also new MVI course - but haven't checked it yet.
Current approach:
I tried mentioned Elm Architecture, but it is not complete. There are at least 2 problems:
Only one request can get through queue at one moment. Some RxJava
should do the trick (groupBy with 2 streams: ui, background
probably).
parallel requests would update the same state, so you should differentiate DataStates inside your UiState. So different state for different part of UI.
Before writing actual fixes we realised, this is not the way to go ATM: announced Composables could do the MVI trick: smooth and precise data transition to specific parts of UI.
Disclaimer: moderator removed my answer which WAS actual answer. Even more, my answer moved to comment is cut down, which makes it look unfinished. That's why this post emerged once again. After you read it dear moderator, you can remove disclaimer, thanks :)

Java rx nested asychronous calls

I am working with the java rx library now, I am having problems with the internal calls with flatMap and map, for some reason the most internal call is not been done.
Besides, if I replace the flatMap with suscribe() in the internal call, (before the childrenItemsResponse.forEach code), this code is executed, bat the execution is not synchronous, I mean this call is done after the main flatMap execution finish.
This is my code:
override fun getSportList(dCSServiceContext: DCSServiceContext): Single<List<EntityBrowse>> {
return dCSService.get(dCSServiceContext).flatMap { item ->
val entityBrowseList = arrayListOf<EntityBrowse>()
val section = builSection(item?.firstOrNull()!!)
if (item.firstOrNull()?.collections?.get("NavItems")?.size!! > 0) {
dCSServiceContext.contentIds = item.firstOrNull()?.collections?.get("NavItems")
buildNavItems(dCSServiceContext).map { section ->
return#map section
}.map { items ->
section.items = items
return#map entityBrowseList
}
} else {
Single.just(entityBrowseList)
}
}
}
The problem is presented in the buildNavItems, method:
private fun buildNavItems(dCSServiceContext: DCSServiceContext): Single<MutableList<Item>> {
return dCSService.get(dCSServiceContext).map { itemsResponse ->
val items: MutableList<Item> = arrayListOf()
itemsResponse.forEach { item ->
val transformedItem = buildItem(item!!)
if (item?.collections?.get("NavItems") != null) {
dCSServiceContext.contentIds = item?.collections?.get("NavItems")
val childrenItems: MutableList<Item> = arrayListOf()
dCSService.get(dCSServiceContext).flatMap { childrenItemsResponse ->
childrenItemsResponse.forEach { childrenItem ->
val transformedChildrenItem = buildItem(childrenItem!!)
childrenItems.add(transformedChildrenItem)
}
val section = Section("", "", false,childrenItems )
val data = Data(section)
val children = Children(data)
transformedItem.children = children
items.add(transformedItem)
Single.just(items)
}
} else {
val transformedItem = buildItem(item!!)
items.add(transformedItem)
}
}
return#map items
//Single.just(items)
}
}
More specifically, the in the line code: dCSService.get(dCSServiceContext).flatMap { childrenItemsResponse ->
This code is never executed.
I am not sure about what could be the cause of the issue.
Thanks in advance!!
To activate an observer chain, there has to be a subscription. You don't subscribe to the observer chain that starts with dCSService.... flatMap() itself won't subscribe to its interior observables until the outer observable chain has been subscribed to.

Android kotlin - BottomNavigationView use BadgeView inside TimerTask

This is the code which works:
class Stackoverflow : AppCompatActivity() {
private var menuView: BottomNavigationMenuView? = null
private var mBottomNavigationView: BottomNavigationView? = null
private var newmsgTimer: Timer? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
mBottomNavigationView = findViewById<View>(R.id.bottom_navigation) as BottomNavigationView
menuView = mBottomNavigationView!!.getChildAt(0) as BottomNavigationMenuView
mBottomNavigationView!!.setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.nav_voting -> {
}
R.id.nav_search -> {
}
}
return#setOnNavigationItemSelectedListener true
}
newmsg()
}
private fun newmsg(){
QBadgeView(this#Stackoverflow).bindTarget(menuView!!.getChildAt(3)).badgeNumber = 7 // IT WORKS HERE
newmsgTimer = Timer()
val timerTask = object:TimerTask() {
override fun run() {
Log.d("letsSee", "PIKABOO") // it's getting printed
}
}
newmsgTimer!!.schedule(timerTask, 0, 60000)
}
}
But when I try to use QBadgeView(this#Home).bindTarget(menuView!!.getChildAt(3)).badgeNumber = 7 inside TimerTask:
private fun newmsg(){
newmsgTimer = Timer()
val timerTask = object:TimerTask() {
override fun run() {
Log.d("letsSee", "PIKABOO") // it's getting printed
QBadgeView(this#Stackoverflow).bindTarget(menuView!!.getChildAt(3)).badgeNumber = 7 // DOESN'T WORKS HERE
}
}
newmsgTimer!!.schedule(timerTask, 0, 60000)
}
then it doesn't work.
And I don't know what this error is about:
java.lang.NullPointerException: Attempt to invoke virtual method 'int android.view.View.getVisibility()' on a null object reference
at android.support.design.internal.BottomNavigationMenuView.onMeasure(BottomNavigationMenuView.java:145)
Any ideas how to make it work inside TimeTask? Thanks in advance
EDIT:
After another run I also got this error:
FATAL EXCEPTION: Timer-1
Process: com.exmpl.exmpl, PID: 9978
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Ignore this: need to add more words blabla
You can not change view properties on another thread directly, you can only change on main thread.
val timerTask = object : TimerTask() {
override fun run() {
Log.d("letsSee", "PIKABOO") // it's getting printed
menuView.post{
QBadgeView(this#Stackoverflow).bindTarget(menuView!!.getChildAt(3)).badgeNumber = 7
}
}

Categories

Resources