Xamarin Shiny Periodic Background Job Stops After 2 Days - Return Value for IJob.Run() Missing - xamarin

I need a periodic background job in Xamarin that runs once per day. I've landed on the Shiny library to handle this functionality. The tutorial I found by Allan Ritchie says that Run() needs to return true if network calls were made. Today there is no return value for Run(); only Task. Is this not still a necessity?
In my project I setup a Shiny job exactly as defined in the sample from Github. It runs great for 2 days and just stops running any more after that. This is on iOS. I basically copy/pasta'd the sample and the job kicks off fine for a few times and then just stops the next day.
What could I be missing or doing wrong?
Source for IJob: https://github.com/shinyorg/shiny/blob/v2.7.0/src/Shiny.Jobs/IJob.cs
Sample of Shiny Jobs: https://github.com/shinyorg/samples/blob/main/Jobs/Sample/SampleJob.cs
Code from the tutorial by Allan Ritchie: https://allanritchie.com/posts/shinyjobs
public class YourFirstJob : Shiny.Jobs.IJob
{
readonly IYourDepdendency depdency;
public YourFirstJob(IYourDependency dependency)
{
this.dependency = dependency;
}
public async Task<bool> Run(JobInfo jobInfo, CancellationToken cancelToken)
{
var id = jobInfo.GetValue("Id", 25); // we'll cover this in a minute
await this.dependency.SomeAsyncMethod(id);
return true; // this is for iOS - try not to lie about this - return true when you actually do receive new data from the remote method
}
}

Related

Running a non-blocking, high-performance activity in nativescript/javascript

This question is about running a non-blocking, high-performance activity in nativescript that is needed for the simple task of reading and saving raw audio from the microphone by directly accessing the hardware through the native Android API. I believe I have brought the nativescript framework to the edge of its capabilities, and I need experts' help.
I'm building a WAV audio recorder in Nativescript Android. Native implementation is described here (relevant code below).
In short, this can be done by reading audio steam from an android.media.AudioRecord buffer, and then writing the buffer to a file in a separate thread, as described:
Native Android implementation
startRecording() is triggered by a button press, and starts a new Thread that runs writeAudioDataToFile():
private void startRecording() {
// ... init Recorder
recorder.startRecording();
isRecording = true;
recordingThread = new Thread(new Runnable() {
#Override
public void run() {
writeAudioDataToFile();
}
}, "AudioRecorder Thread");
recordingThread.start();
}
Recording is stopped by setting isRecording to false (stopRecording() is triggered by a button press):
private void stopRecording() {
isRecording = false;
recorder.stop();
recorder.release();
recordingThread = null;
}
Reading and saving buffer is stopped if isRecording = false:
private void writeAudioDataToFile() {
// ... init file and buffer
ByteArrayOutputStream recData = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(recData);
int read = 0;
while(isRecording) {
read = recorder.read(data, 0, bufferSize);
for(int i = 0; i < bufferReadResult; i++) {
dos.writeShort(buffer[i]);
}
}
}
My Nativescript javascript implementation:
I wrote a nativescript typescript code that does the same as the native Android code above. The problem #1 I faced was that I can't run while(isRecording) because the javascript thread would be busy running inside the while loop, and would never be able to catch button clicks to run stopRecording().
I tried to solve problem #1 by using setInterval for asynchronous execution, like this:
startRecording() is triggered by a button press, and sets a time interval of 10ms that executes writeAudioDataToFile():
startRecording() {
this.audioRecord.startRecording();
this.audioBufferSavingTimer = setInterval(() => this.writeAudioDataToFile(), 10);
}
writeAudioDataToFile() callbacks are queued up every 10ms:
writeAudioDataToFile() {
let bufferReadResult = this.audioRecord.read(
this.buffer,
0,
this.minBufferSize / 4
);
for (let i = 0; i < bufferReadResult; i++) {
dos.writeShort(buffer[i]);
}
}
Recording is stopped by clearing the time interval (stopRecording() is triggered by button press):
stopRecording() {
clearInterval(this.audioBufferSavingTimer);
this.audioRecord.stop();
this.audioRecord.release();
}
Problem #2: While this works well, in many cases it makes the UI freeze for 1-10 seconds (for example after clicking a button to stop recording).
I tried to change the time interval that executes writeAudioDataToFile() from 10ms to 0ms and up to 1000ms (while having a very big buffer), but then the UI freezes were longer and, and I experienced loss in the saved data (buffered data that was not saved to the file).
I tried to offload this operation to a separate Thread by using a nativescript worker thread as described here, where startRecording() and stopRecording() are called by messages sent to the thread like this:
global.onmessage = function(msg) {
if (msg.data === 'startRecording') {
startRecording();
} else if (msg.data === 'stopRecording') {
stopRecording();
}
}
This solved the UI problem, but created problem #3: The recorder stop was not executed on time (i.e. recording stops 10 to 50 seconds after the 'stopRecording' msg.data is received by the worker thread). I tried to use different time intervals in the setInterval inside the worker thread (0ms to 1000ms) but that didn't solve the problem and even made stopRecording() be executed with greater delays.
Does anyone have an idea of how to perform such a non-blocking high-performance recording activity in nativescript/javascript?
Is there a better approach to solve problem #1 (javascript asynchronous execution) that I described above?
Thanks
I would keep the complete Java implementation in actual Java, you can do this by creating a java file in your plugin folder:
platforms/android/java, so maybe something like:
platforms/android/java/org/nativescript/AudioRecord.java
In there you can do everything threaded, so you won't be troubled by the UI being blocked. You can call the Java methods directly from NativeScript for starting and stopping the recording. When you build your project, the Java file will automatically be compiled and included.
You can generate typings from your Java class by grabbing classes.jar from the generated .aar file of your plugin ({plugin_name}.aar) and generate type declarations for it: https://docs.nativescript.org/core-concepts/android-runtime/metadata/generating-typescript-declarations
This way you have all the method/class/type information available in your editor.

Threads in Azure Cloud Service Worker Role not responding after Task.Delay

We have our application hosted on Azure Cloud Service containing a Web Role and a Worker Role. The worker role is used for running workflows using Windows Workflow Foundation and other processing works.
We are facing an issue where on encountering a Task.Delay, the thread stops responding in the Worker Role. Initially we were using the Task.Delay in a Custom Code Activity inside Workflow Foundation and on encountering the Task.Delay, the subsequent activities in the workflow would never get triggered. We tried to modify the logic by using a delay activity but faced the same issue.
Recently we changed the logic where are executing a method from the activity (in a fire-and-forget fashion) and calling Task.Delay in that method. But even in this mechanism we are still facing the same problem where the thread stops responding as soon as it hits Task.Delay.
public class CommandStatusCheckerActivity : BaseCodeActivity<Job> //Last Activity in the Workflow
{
protected override Job Execute(CodeActivityContext context)
{
var job = JobContext.Get(context);
var statusUpdater = new JobStatusUpdaterAsync();
//Fire and Forget
Task.Run(
() => statusUpdater.Update(job));
return job;
}
}
public class JobStatusUpdaterAsync
{
public JobStatusUpdaterAsync()
{
//Initialization
}
public void Update(Job job)
{
var iteration = 1;
do
{
_tableLogger.LogMessage(“Sleep Started”);
Task.Delay(backoffDelay).Wait();
_tableLogger.LogMessage(“Active Again”); //This Line is never executed
//Actual Polling Logic
iteration++;
} while (ellapsedTime < timeout && inprogressItems.Any());
}
}
I would like to call out that at the same time there are multiple operations which are happening in the worker role and all those threads are getting completed successfully. But none of the other threads have a Task.Delay in them.
Surprisingly we cannot reproduce the same situation in Local machine, it's happening only in our Cloud Service Worker Role.
Out Worker Role Size in Standard_D2.
Any guidance would be appreciated.

Why use Device.BeginInvokeOnMainThread() in a Xamarin application?

My code looks like this:
public void Init() {
if (AS.pti == PTI.UserInput)
{
AS.runCardTimer = false;
}
else
{
AS.runCardTimer = true;
Device.BeginInvokeOnMainThread(() => showCards().ContinueWith((arg) => { }));
}
}
The Init method is called from the constructor. Can someone please explain to me why the developer might have added the Device.BeginInvokeOnMainThread() instead of just calling the method showCards?
Also what does the ContinueWith((arg)) do and why would that be included?
The class where this Init() method is might be created on a background thread. I'm assuming showCards() are updating some kind of UI. UI can only be updated on the UI/Main thread. Device.BeginInvokeOnMainThread() ensures that the code inside the lambda is executed on the main thread.
ContinueWith() is a method which can be found on Task. If showCards() returns a task, ContinueWith() makes sure the task will complete before exiting the lambda.
UI actions must be performed on UI thread (different name for main thread). If you try to perform UI changes from non main thread, your application will crash. I think developer wanted to make sure it will work as intended.
The simple answer is: Background thread cannot modify UI elements because most UI operations in iOS and Android are not thread-safe; therefore, you need to invoke UI thread to execute the code that modifies UI such MyLabel.Text="New Text".
The detailed answer can be found in Xamarin document:
For iOS:
IOSPlatformServices.BeginInvokeOnMainThread() Method simply calls NSRunLoop.Main.BeginInvokeOnMainThread
public void BeginInvokeOnMainThread(Action action)
{
NSRunLoop.Main.BeginInvokeOnMainThread(action.Invoke);
}
https://developer.xamarin.com/api/member/Foundation.NSObject.BeginInvokeOnMainThread/p/ObjCRuntime.Selector/Foundation.NSObject/
You use this method from a thread to invoke the code in the specified object that is exposed with the specified selector in the UI thread. This is required for most operations that affect UIKit or AppKit as neither one of those APIs is thread safe.
The code is executed when the main thread goes back to its main loop for processing events.
For Android:
Many People think on Xamarin.Android BeginInvokeOnMainThread() method use Activity.runOnUiThread(), BUT this is NOT the case, and there is a difference between using runOnUiThread() and Handler.Post():
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);//<-- post message delays action until UI thread is scheduled to handle messages
} else {
action.run();//<--action is executed immediately if current running thread is UI thread.
}
}
The actual implementation of Xamarin.Android BeginInvokeOnMainThread() method can be found in AndroidPlatformServices.cs class
public void BeginInvokeOnMainThread(Action action)
{
if (s_handler == null || s_handler.Looper != Looper.MainLooper)
{
s_handler = new Handler(Looper.MainLooper);
}
s_handler.Post(action);
}
https://developer.android.com/reference/android/os/Handler.html#post(java.lang.Runnable)
As you can see, you action code is not executed immediately by Handler.Post(action). It is added to the Looper's message queue, and is handled when the UI thread's scheduled to handle its message.

When is a windows service considered "started"

We have a process that is executed as a windows service,
This process serves as an interface server processing incoming messages, transforms them and sends them out to another interface.
it is a rather heavy process, it needs to load a lot of things into memory and that takes some time (few minutes).
due to its nature, when we start it using its windows service, it remains in "starting" status for a very long time (sometimes more than 20 minutes)
even when we can see the process already works and process messages just fine (going by its logs).
so the question is - when is a service considered "starting" and when is it considered "started"? based on what factors?
Starting status finish when onstart is completed.
You should write starting code after onstart event.
puclic class Service1
{
private Timer timer = new Timer();
protected override void OnStart(string[] args)
{
this.timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
this.timer.Interval = 1 * 1000; // 1 second
this.timer.Enabled = true;
}
private void OnElapsedTime(object source, ElapsedEventArgs e)
{
this.timer.Enabled = false; // OnElapsedTime run only one time
// Write your code
}
}

Struggling to find issue with why AsyncTask is not being stopped by Handler

I am using this example:
Android - Setting a Timeout for an AsyncTask?
in the following way:
al.setOnClickListener(new OnClickListener(){
public void onClick(View w)
{
final AlogLoader loader = new AlogLoader();
loader.execute();
Handler handler = new Handler();
handler.postDelayed(new Runnable()
{
public void run()
{
if(loader.getStatus() == AsyncTask.Status.RUNNING)
{
loader.cancel(true);
}
}
}, 1);
}
});
I set it to "1" because I wanted to see if I can stop it practically before it even starts - to see if my handler is working - in reality I would probably set it to 15000 (15 seconds).
However what happens is confusing:
Running the application, causes my onPreExecute() to draw a loading screen, which doesn't ever exit, so the user just sees a loading screen perpetually.
Running it in the debugger with the breakpoint at loader.cancel(true) -> Causes the debugger to stop at that line, which is expected because its only allowed to run for 1 millisecond. However, when I hit the resume button in the debugger after that - my onPostExecute() is called... How is that possible?
Clearly, I'm very new to timing out asynctasks - after some research, I found the example above and it seemed to make the most sense to me, definitely more then
loader.get(15000, TimeUnit.MILLISECONDS);, since that blocks the UI Thread.
Any help is appreciated...even an explanation on the process..
Okay,
So I essentially solved my problem but I am still confused... Don't know if that's all good.
I was unclear on how loader.cancel(true) actually works. Based on the API level (read this question : AsyncTask.onCancelled() not being called after cancel(true)) you need to have an onCancelled() or onCancelled(params) method, or both in your AsyncTask.
I did not have this, so this explains why I saw a loading screen forever. Okay Good.
Yet, it doesn't explain how in the debugger, I managed to still call the onPostExecute(), because according to Android API's, onPostExecute is never called once cancel(true) has been called...
The answer is:
Add this to your AsyncTask-
#Override
protected void onCancelled()
{
Toast.makeText(FriendsActivity.this,"Blah- reason", Toast.LENGTH_LONG).show();
loadingScreen.dismiss();
}

Resources