Xamarin NullReferenceException when calling 'Finish()' on activity - xamarin

I created a biometric authentication service that starts an activity and registers a callback to a static EventHandler for the result.
The handler:
public class BiometricHandler : IBiometricHandler
{
private TaskCompletionSource<byte[]> taskCompletionSource;
public Task<byte[]> StartBiometricAuth()
{
Intent intent = new Intent(Android.App.Application.Context, typeof(BiometricActivity));
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
taskCompletionSource = new TaskCompletionSource<byte[]>();
BiometricActivity.BiometricEventHandler += BiometricCompleted;
return taskCompletionSource.Task;
}
private void BiometricCompleted(object sender, BiometricEventArgs e)
{
taskCompletionSource.SetResult(e.Success ? e.Payload : new byte[] { });
BiometricActivity.BiometricEventHandler -= BiometricCompleted;
}
}
And the activity (not the actual code obviously):
public class BiometricActivity : Activity
{
public static event EventHandler<BiometricEventArgs> BiometricEventHandler;
private readonly int BIOMETRIC_REQUEST = 1;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Intent intent = new Intent(Application.Context, typeof(BiometricAuth));
StartActivityForResult(intent, BIOMETRIC_REQUEST);
}
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
BiometricEventHandler?.Invoke(
this,
new BiometricEventArgs(
true, new byte[] {1, 2, 3}
);
Finish();
}
}
The above code throws a NullReferenceException:
0xFFFFFFFFFFFFFFFF in System.Diagnostics.Debugger.Mono_UnhandledException_internal
0x1 in System.Diagnostics.Debugger.Mono_UnhandledException at /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/mcs/class/corlib/System.Diagnostics/Debugger.cs:125,4
0x20 in Android.Runtime.DynamicMethodNameCounter.57
0x6 in Xamarin.Forms.Platform.Android.PlatformConfigurationExtensions.OnThisPlatform<Xamarin.Forms.Application> at D:\a\_work\1\s\Xamarin.Forms.Platform.Android\PlatformConfigurationExtensions.cs:8,4
0xC in Xamarin.Forms.Platform.Android.AppCompat.FragmentContainer.OnResume at D:\a\_work\1\s\Xamarin.Forms.Platform.Android\AppCompat\FragmentContainer.cs:126,4
0x8 in AndroidX.Fragment.App.Fragment.n_OnResume at C:\a\_work\1\s\generated\androidx.fragment.fragment\obj\Release\monoandroid12.0\generated\src\AndroidX.Fragment.App.Fragment.cs:2570,4
0x11 in Android.Runtime.DynamicMethodNameCounter.57
And the console:
**System.NullReferenceException:** 'Object reference not set to an instance of an object.'
I narrowed it down to the Finish() method.
There is no exception when it is not called.
I tried calling Finish() from the handler using BroadcastReceiver, same result.
Why does Finish() throw this exception?

Well, it seems like the issue is an asynchronous race condition.
Finish() takes a bit longer to complete than the service to return the data.
Then the content page tries to perform a UI event, and since Finish() hasn't finished yet, the activity is still "visible" and the content page is not yet responsible for the UI thread. So the UI event fails with the exception.
Since there doesn't seem to be an easy way to await the Finish() call, I used Task.Await to delay the UI event for a bit and to Finish() complete.
Not ideal, but at least NullReferenceException is gone

Related

How can I correctly run a timed process in my app the will start on stop when the application is in use or in the background?

I have an application that starts up and runs a background check of the database every minute. Below is the code for this.
I'm getting what I think is a memory leak and am looking at all areas of the code that loop.
Is there any possibility that this code could be left in a looping state and contribute to a memory leak or is the way the onSleep and onResume coded a 100% sure way to correctly stop and start the timer loop?
Note that I only want the timed part of the code to run once a minute when the application is being used and in the foreground.
namespace Japanese
{
public partial class App : Application
{
private static Stopwatch stopWatch = new Stopwatch();
public App()
{
InitializeComponent();
MainPage = new Japanese.MainPage();
}
protected override void OnStart()
{
App.DB.InitData();
if (!stopWatch.IsRunning)
stopWatch.Start();
Device.StartTimer(new TimeSpan(0, 0, 1), () =>
{
if (stopWatch.IsRunning && stopWatch.Elapsed.Minutes >= defaultTimespan)
{
Debug.WriteLine("Checking database");
PointChecker.CheckScore();
stopWatch.Restart();
}
return true;
});
}
protected override void OnSleep()
{
stopWatch.Reset();
}
protected override void OnResume()
{
stopWatch.Start();
}
}
}
The App class is the class that represents the cross-platform mobile application, it is running even your "MainPage" was not, so i think you need to use OnAppearing and OnDisappearing methods in your main page (a :ContentPage).
Maybe something like :
protected override void OnAppearing()
{
stopWatch.Start();
base.OnAppearing();
}
and,
protected override void OnDisappearing()
{
stopWatch.Reset();
base.OnDisappearing();
}
I hope that helps,
Mabrouk.

How to monitor the changes in the Bluetooth Connectivity state changes from Xamarin.Forms

Need to check the bluetooth connection to a remote device exists or got disconnected. Its basically a Forms which mainly targets Android and UWP.
I tried with the Dependency services and made the implementation in Android as below,
_[assembly: Xamarin.Forms.Dependency(typeof(BluetoothListenerActivity))]
namespace demotool.Droid
{
public class BluetoothListenerActivity : Activity,IBluetoothListener
{
public event EventHandler OnDeviceDisconnected;
public static BluetoothListenerActivity mySelf;
//string device;
public void start()
{
mySelf = this;
BluetoothStatusBroadCast mreceiver = new BluetoothStatusBroadCast();
IntentFilter mfilter = new IntentFilter(BluetoothDevice.ActionAclDisconnected);
Forms.Context.RegisterReceiver(mreceiver,mfilter);
}
public void receivedstatuschangd(string devicename,string state)
{
OnDeviceDisconnected(this, new DeviceDisconnectedEventArgs(name: devicename,status: state));
}
}
}_
BroadcastReceiver:
namespace Demo.Droid
{
[BroadcastReceiver]
class BluetoothStatusBroadCast : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
BluetoothDevice device =(BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice);
BluetoothListenerActivity.mySelf.receivedstatuschangd(device.Name, intent.Action);
}
}
}
Xamarin Forms Part:
_ protected override void OnStart()
{
IBluetoothListener bluetoothlistener = DependencyService.Get();
bluetoothlistener.start();
bluetoothlistener.OnDeviceDisconnected += Bluetoothlistener_OnDeviceDisconnected;
}
private void Bluetoothlistener_OnDeviceDisconnected(object sender, DeviceDisconnectedEventArgs e)
{
Page page1 = new Page();
page1.DisplayAlert(e.Name+ " " +e.Status, "Alert", "OK");
}_
The Intent Action that I have registered- BluetoothDevice.ActionAclDisconnected, is getting triggered once the Pairing is completed or a connection request is made, which I assume is not the actual Disconnection of the devices
Is there any common plugin which monitors the Bluetooth Connectivity Changes to a remote device. Or could you please tell me the actual Intent Action that I should listen for.
Thanks in Advance !

AsyncTaskCodeActivity and lost context after await

AsyncTaskCodeActivity fails when the context parameter is accessed after the first await is performed. For example:
public class TestAsyncTaskCodeActivity : AsyncTaskCodeActivity<int>
{
protected async override Task<int> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken)
{
await Task.Delay(50);
// context has already been disposed and the next line throws
// ObjectDisposedException with the message:
// An ActivityContext can only be accessed within the scope of the function it was passed into.
context.Track(new CustomTrackingRecord("test"));
// more awaits can happen here
return 3;
}
}
Is there any simple way to preserve the context so it can be used also after awaiting something?
Ah.
When I wrote AsyncTaskCodeActivity<T>, I assumed that the AsyncCodeActivityContext was in fact going to be the same instance at the beginning and end of the asynchronous method, and be available all the way through. This is not the case (which is a bit odd - not sure why the WF team made that decision).
Instead, the AsyncCodeActivityContext can only be accessed at the beginning and end of the activity. Awkward, indeed.
The updated code below will allow you to access the context at the beginning (e.g., reading In variables) and then access the context again at the end. I also introduce an optional TState, which can be used for storing activity state (which the activity can access throughout its execution). Let me know if this fits your needs; I haven't tested it.
public abstract class AsyncTaskCodeActivity<T, TState> : AsyncCodeActivity<T>
{
protected sealed override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
{
TState activityState = PreExecute(context);
context.UserState = activityState;
var task = ExecuteAsync(activityState);
return AsyncFactory<T>.ToBegin(task, callback, state);
}
protected sealed override T EndExecute(AsyncCodeActivityContext context, IAsyncResult asyncResult)
{
var result = AsyncFactory<T>.ToEnd(asyncResult);
return PostExecute(context, (TState)context.UserState, result);
}
protected virtual TState PreExecute(AsyncCodeActivityContext context)
{
return default(TState);
}
protected abstract Task<T> ExecuteAsync(TState activityState);
protected virtual T PostExecute(AsyncCodeActivityContext context, TState activityState, T result)
{
return result;
}
}
public abstract class AsyncTaskCodeActivity<T> : AsyncTaskCodeActivity<T, object>
{
}

Slow loading of layout

I have a super class which is in a library. This library take care of initializing some basic layout components and other stuff. My problem is that it takes 1.x seconds to load the layout, and shows the default layout for a while, before setting the child-specified layout.
This is the method of my super class:
public void InitializeWindow(Activity act, int layoutResourceId, String windowTitle,
Object menuAdapter, int slideMenuMode) {
super.setContentView(layoutResourceId);
super.setBehindContentView(R.layout.menu_frame);
this.menuAdapter = menuAdapter;
this.slideMenuMode = slideMenuMode;
setWindowTitle(windowTitle);
initializeSlidingMenu();
}
This is called this way:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.InitializeWindow(this, R.layout.activity_home, "\t\tHome",
new MenuAdapter(this, R.menu.slide_menu), SlidingMenu.TOUCHMODE_FULLSCREEN);
}
The application works like a charm, but it takes, as I said around 1.x seconds to load the layout passed from the child-class. Why does this happen?
By request, this is my initializeSlideMenu() method:
public void initializeSlidingMenu() {
this.setSlidingActionBarEnabled(true);
getSlidingMenu().setBehindOffsetRes(R.dimen.actionbar_home_width);
getSlidingMenu().setShadowWidthRes(R.dimen.shadow_width);
getSlidingMenu().setShadowDrawable(R.drawable.shadow);
getSlidingMenu().setTouchModeAbove(slideMenuMode);
getSlidingMenu().setBehindScrollScale(0.25f);
ListView v = new ListView(this);
v.setBackgroundColor(Color.parseColor("#000000"));
v.setAdapter((ListAdapter) menuAdapter);
getSlidingMenu().setMenu(v);
}
To avoid such problems there are three ways in general.
Let your onCreate() finish after setContentView() call as early as possible. You can use postDelayed runnable to delay few initialization which may not be needed at early stages.
Do some task when the view is ready, it causes the Runnable to be added to the message queue of that view.
Snippet
view.post(new Runnable() {
#Override
public void run() {
}
});
If none of the above helps consider "Optimize with stubs" link : http://android-developers.blogspot.in/2009/03/android-layout-tricks-3-optimize-with.html
Hope it helps.
I suspect that the trouble spot for you is with:
v.setAdapter((ListAdapter) menuAdapter);
You should do this as part of an AsyncTask. It will often be very slow to execute the loading by the adapter.
Here is a snippet from a sample AsyncTask implementation:
//before starting the load, I pop up some indicators that I'm doing some loading
progressBar.setVisibility(View.VISIBLE);
loadingText.setVisibility(View.INVISIBLE);
AsyncTask<Void, Void, Void> loadingTask = new AsyncTask<Void, Void, Void>() {
private ArrayList<Thing> thingArray;
#Override
protected Void doInBackground(Void... params) {
//this is a slow sql fetch and calculate for me
thingArray = MyUtility.fetchThings(inputValue);
return null;
}
#Override
public void onPostExecute(Void arg0) {
EfficientAdapter myAdapter = new EfficientAdapter(MyActivity.this, thingArray);
listView.setAdapter(myAdapter);
//after setting up my adapter, I turn off my loading indicators
progressBar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
RelativeLayout layout = (RelativeLayout)MyActivity.this.findViewById(R.id.spacey);
if (layout != null) {
LayoutInflater inflater = LayoutInflater.from(MyActivity.this);
View view = inflater.inflate(R.layout.name_tabled_sub, layout);
NamedTableView tableView = new NamedTableView(MyActivity.this, view);
}
progressBar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
}
};
loadingTask.execute();
You can also do "PreExecute" items with the Async task, as well as update.

Live SDK Windows Phone 7 GetCompleted callback in UI thread?

public class SyncHelper
{
private LiveConnectClient client;
public event EventHandler SyncStarted;
public event EventHandler SyncCompleted;
public SyncHelper(LiveConnectClient client)
{
this.client = client;
}
public void TrySync()
{
Debug.WriteLine("Beginning sync");
OnSyncStarted();
client.GetCompleted += OnGetCompleted;
client.GetAsync("me/skydrive/files");
}
private void OnGetCompleted(object sender, LiveOperationCompletedEventArgs e)
{
Thread.Sleep(10000);
Debug.WriteLine("Get Completed");
client.GetCompleted -= OnGetCompleted;
OnSyncCompleted();
Debug.WriteLine("Sync completed");
}
private void OnSyncStarted()
{
if (SyncStarted != null)
SyncStarted(this, new EventArgs());
}
private void OnSyncCompleted()
{
if (SyncCompleted != null)
SyncCompleted(this, new EventArgs());
}
}
The function OnGetCompleted is being called in the UI thread and the UI is unresponsive. From whatever I know, I thought these callbacks would work in a different thread and we would have to use the displatcher to post it to the UI thread. Any thoughts? Help!
The GetAsync call is likely using a background thread to do the actual fetch, but then it's trying to help you by calling the Completed callback in the original thread context so you don't have to use a Dispatcher.
Why are you putting in a Sleep(10000) anyway? The callback says "hey, I'm done". At that point you should update the UI if you want. If you need to do further processing that takes significant time, spawn a background thread, threadpool task or use another asynchronous call with another callback.

Resources