I sucessfully setted a event channel between Kotlin and Flutter. However, I need another Kotlin thread to be able to use this Event Channel. I only found tutorials on how to launch things from the Event Channel inside the onListen method, like making a counter that sends things every second.
How can I make another thread call event.sucess(something)?
Here's what I done:
class MainActivity: FlutterActivity(){
companion object {
//this is called by another thread
#JvmStatic
private fun onEventChannel(b: ByteArray):Int {
//How do I make this call events.success(b) on onListen?
}
//...
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
EventChannel(flutterEngine.dartExecutor.binaryMessenger, EVENT_CHANNEL).setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(args: Any, events: EventChannel.EventSink) {
Log.d(TAG, "adding listener for $EVENT_CHANNEL")
events.success(true)
}
override fun onCancel(args: Any) {
Log.d(TAG, "cancelling listener for $EVENT_CHANNEL")
}
})
Related
I'm developing an android app that is a bitcoin wallet using Jetpack Compose.
I have Wallet.kt file with:
fun sync() {
Log.i(TAG, "Wallet is syncing")
wallet.sync(blockchain, LogProgress)
}
fun getBalance(): ULong = wallet.getBalance().total
then in HomeScreen.kt I have
internal class WalletViewModel() : ViewModel() {
private var _balance: MutableLiveData<ULong> = MutableLiveData(0u)
val balance: LiveData<ULong>
get() = _balance
fun updateBalance() {
Wallet.sync()
_balance.value = Wallet.getBalance()
}
then outside of this is composable function HomeScreen
internal fun HomeScreen(
navController: NavController,
walletViewModel: WalletViewModel = viewModel()
) {
val balance by walletViewModel.balance.observeAsState()
Image(Modifier.clickable{ walletViewModel.updateBalance() }
}
My problem being that when I click on that Image which has clickable, the whole app freezes, until the updateBalance() is completed.
I learned that this is because the sync() function inside Wallet.kt file is performing network task on the Main Thread and the app is in Main Thread, so the whole app has to wait until sync is done.
Can you suggest how should I implement coroutines or different way, so that the sync happens inside background thread and then updates _balance to/in the Main ?
I've tried lots of things, including suspend before sync() and async in the viewModelScope, but nothing seems to work how I want to.
Thanks
You can solve it in two ways, one is to attach the async call into the viewModelScope and mark the async call as suspend, the other one is creating a coroutine in your repository with Context and execute it in another thread.
Solution 1
fun updateBalance() {
viewModelScope.launch {
Wallet.sync()
_balance.value = Wallet.getBalance()
}
}
suspend fun sync() {
Log.i(TAG, "Wallet is syncing")
wallet.sync(blockchain, LogProgress)
}
Solution 2
suspend fun sync() {
Log.i(TAG, "Wallet is syncing")
withContext(Dispatcher.IO) {
wallet.sync(blockchain, LogProgress)
}
}
Hello I have a command bus, a query bus, which basically has a keypair with the name of the command or query and the handler and then I execute the command that should publish my event.
But I have some doubts about how I could do my event-bus.
is the command-bus part of an event-bus?
how could I do an event-bus with the handlers
command-bus:
export interface ICommand {
}
export interface ICommandHandler<
TCommand extends ICommand = any,
TResult = any
> {
execute(command: TCommand): Promise<TResult>
}
export interface ICommandBus<CommandBase extends ICommand = ICommand> {
execute<T extends CommandBase>(command: T): Promise<any>
register(data:{commandHandler: ICommandHandler, command: ICommand}[]): void
}
command-bus implementation:
export class CommandBus<Command extends ICommand = ICommand>
implements ICommandBus<Command> {
private handlers = new Map<string, ICommandHandler<Command>>()
public execute<T extends Command>(command: T): Promise<any> {
const commandName = this.getCommandName(command as any)
const handler = this.handlers.get(commandName)
if (!handler) throw new Error(``)
return handler.execute(command)
}
public register(
data: { commandHandler: ICommandHandler; command: ICommand }[],
): void {
data.forEach(({command,commandHandler}) => {
this.bind(commandHandler, this.getCommandName(command as any))
})
}
private bind<T extends Command>(handler: ICommandHandler<T>, name: string) {
this.handlers.set(name, handler)
}
private getCommandName(command: Function): string {
const { constructor } = Object.getPrototypeOf(command)
return constructor.name as string
}
}
Here another question arose, who should have the responsibility to publish the events in my event db or read a stream of my event db is my class event-store?
event-store class:
export class EventStoreClient {
[x: string]: any;
/**
* #constructor
*/
constructor(private readonly config: TCPConfig) {
this.type = 'event-store';
this.eventFactory = new EventFactory();
this.connect();
}
connect() {
this.client = new TCPClient(this.config);
return this;
}
getClient() {
return this.client;
}
newEvent(name: any, payload: any) {
return this.eventFactory.newEvent(name, payload);
}
close() {
this.client.close();
return this;
}
}
And then I have doubts about how to implement my event-bus, with my event handlers and my events.
I would be happy if someone could help me ..
event-interface:
export interface IEvent {
readonly aggregrateVersion: number
readonly aggregateId: string
}
export interface IEventHandler<T extends IEvent = any> {
handle(event: T): any
}
maybe usage:
commandBus.execute(new Command())
class commandHandler {
constructor(repository: IRepository, eventBus ????){}
execute(){
//how i can publish an event with after command handler logic with event bus her
}
}
I see there's some confusion between the various Buses and the Event Store. Before attempting to implement an Event Bus, you need to answer one important question that lies at the foundation of any Event Sourcing implementation:
How to preserve the Event Store as the Single Source of Truth?
That is, your Event Store contains the complete state of the domain. This also means that the consumers of the Event Bus (whatever it ends up being - a message queue, a streaming platform, Redis, etc.) should only get the events that are persisted. Therefore, the goals become:
Only deliver events on the Bus that are persisted to the Store (so if you get an error writing to the Store, or maybe a Concurrency Exception, do not deliver via bus!)
Deliver all events to all interested consumers, without losing any events
These two goals intuitively translate to "I want atomic commit between the Event Store and the Event Bus". This is simplest to achieve when they're the same thing!
So, instead of thinking about how to connect an "Event Bus" to command handlers and send events back and forth, think about how to retrieve already persisted events from the Event Store and subscribe to that. This also removes any dependency between command handlers and event subscribers - they live on different sides of the Event Store (writer vs. reader), and could be in different processes, on different machines.
I would like to control events of Load Families and Create Type with revit api. Someone can give me a direction ? I don't understand very well the documentation that I read.
First you need to subscribe to an event by creating an event listener in the IExternalApplication OnStartup method.
public class AppCommand : IExternalApplication
{
public Result OnStartup(UIControlledApplication application)
{
application.ControlledApplication.FamilyLoadedIntoDocument += OnFamilyLoaded;
return Result.Succeeded;
}
}
Next you need a handler for that event:
private void OnFamilyLoaded(object sender, FamilyLoadedIntoDocumentEventArgs args)
{
// do work here
}
When finished you need to unregister the event handler:
public Result OnShutdown(UIControlledApplication application)
{
application.FamilyLoadedIntoDocument -= OnFamilyLoaded;
return Result.Succeeded;
}
The other events available that you can subscribe to are these:
http://www.revitapidocs.com/2018/b69e9d33-3c49-e895-3267-7daabab85fdf.htm
Cheers!
I have a MainForm class (as you'd expect, it is a form) that has a text box on it. I also have another class called 'Application_Server' That does a load of other stuff (not just form-background related, quite a lot of network based stuff etc.).
The Application_Server class runs in it's own thread, but needs to be able to update the controls on the form, for this question, we will stick with just the textbox.
The problem is that even though I am executing the command to set the text of the textBox control via 'Invoke' I am still getting the following exception during runtime:
Additional information: Cross-thread operation not valid: Control
'DebugTextBox' accessed from a thread other than the thread it was
created on.
What could be causing this? I am definitely invoking a delegate within MainForm.
Here are the relevant code segments (cut down for readability):
MainForm.h:
public ref class MainForm : public System::Windows::Forms::Form {
delegate void del_updateDebugText(String^ msg);
del_updateDebugText^ updateDebugText = gcnew del_updateDebugText(this, &MainForm::postDebugMessage);
private: void postDebugMessage(String^ message);
};
MainForm.cpp:
void EagleEye_Server::MainForm::postDebugMessage(String^ message)
{
Monitor::Enter(DebugTextBox);
if (this->DebugTextBox->InvokeRequired)
{
this->Invoke(updateDebugText, gcnew array<Object^> { message });
}
else
{
this->DebugTextBox->AppendText(message);
}
Monitor::Exit(DebugTextBox);
}
And finally, the code calling it:
void ServerAppManager::postDebugMessage(System::String^ message)
{
mainFormHandle->updateDebugText(message);
}
void ServerAppManager::applicationStep()
{
postDebugMessage("Starting\n");
// This is Run in seperate thread in MainForm.cpp
while (s_appState == ApplicationState::RUN)
{
postDebugMessage("Testing\n");
}
}
Thanks!
From background worker called bwSearch we do the call as following from the DoWork event handler:
private: System::Void bwSearch_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
//... logic
UpdateTxtOutput("Some message");
//... more logic
}
I have a RitchTextBox called txtOutput, also the windows form control containing this code is called frmMain, the UpdateTxtOutput is defined in three parts as follows:
delegate void UpdateTxtOutputDelegate(String^ text);
void UpdateTxtOutput(String^ text)
{
UpdateTxtOutputDelegate^ action = gcnew UpdateTxtOutputDelegate(this, &frmMain::Worker);
this->BeginInvoke(action, text);
}
void Worker(String^ text)
{
txtOutput->AppendText("\t" + text + "\n");
}
I managed to get it working by simplifying the method within the 'MainForm' class to:
void EagleEye_Server::MainForm::postDebugMessage(String^ message)
{
Monitor::Enter(DebugTextBox);
DebugTextBox->AppendText(message);
Monitor::Exit(DebugTextBox);
}
And then moving the 'Invoke' call to the method calling the delegate, not pretty but it works for now. I think the issue may have been caused by the form getting stuck inside an Invoke loop. I say this as I noticed that the form would lock up and stop responding after it hit the recursive Invoke statement.
As my user changes the CurrentItem of a dataForm, I need to go the server to get addtional data. It's quite likely that the user could scroll through several items before finding the desired one. I would like to sleep for 500ms before going to get the data.
Is there a component already in the SDK or toolkit like a background worker that would assist in getting back to the UI thread to make my WCF async call once the 500ms sleep is done? It seems that if I don't do that, and try instead to call the WCF async method on the sleeper thread then the Completed event fires on the sleeper thread and not the UI thread, which of course is not good.
I think you might be a little off-track in your thinking. I'm not sure why you feel you need to get back to the UI thread in order to make the asych call. Generally you do as much work as you can on a BG thread and only marshal back to the UI thread when you have the results (by way of the Dispatcher).
I typically use a System.Threading.Timer for this purpose:
public class MyViewModel
{
private readonly Timer refreshTimer;
public MyViewModel()
{
this.refreshTimer = new Timer(this.DoRefresh);
}
public object CurrentItem
{
get { ... }
set
{
...
Invalidate();
}
}
// anything that should invalidate the data should wind up calling this, such as when the user selects a different item
private void Invalidate()
{
// 1 second delay
this.refreshTimer.Change(1000, Timeout.Infinite);
}
private void DoRefresh()
{
// make the async call here, with a callback of DoRefreshComplete
}
private void DoRefreshComplete()
{
// update the UI here by way of the Dispatcher
}
}