I am trying to understand bound services. I have an xamarin app using mqtt and the mqtt is getting disrupted during the activity lifecycle. Currently I have a connection class that handles the connection to the mqtt that is called from an activity.
I need the connection class to run without interruption regardless if app is in foreground background etc. (if anyone is curious the keepalive routine gets interrupted when the app goes into the background) The app is 100% stable if I don't let the phone go to sleep (developer option stay awake while charging). If I let the phone go to sleep after a while the mqtt gets disconnected.
So from what I am understanding I need a boundService, boundServiceBinder, and boundServiceConnection classes.
It looks like I want the heavy lifting to happen in the boundServiceConnection class.
Example
class BoundServiceConnection : Java.Lang.Object, IServiceConnection
{
Connection connect;
public void OnServiceConnected(ComponentName name, IBinder service)
{
connect = new connection();
}
public void OnServiceDisconnected(ComponentName name)
{
}
}
Now will this work, will I be able to access the properties of the connection class as I am able to when I instantiate in an activity?
Thanks all
Mark
Yes you can,
what you want to do is just create a method in you boundServiceConnection class that instantiates the object. and call it after you have bound to the service
Related
I'm trying to host my Blazor application on my server.
I spent all the summer on it and I just realized every time I open my website on new device it doesn't create a new session restarting from zero, but continues where I left it. The worst part is there is a login system behind it, so I feel super dumb at the moment.
I really need a big hint on how to fix this "not little" issue.
Is there a way to make server create new session every time someone open the website (without making it loose to other users)?
The solution should be use a Client Template instead, but the performance are really to slow.
UPDATE:
Accounts "user password" are:
- user user
- test test
Download project sample (requires Net Core 3.0)
[SOLUTION] itminus found the solution to my issue.
You have also to add in ConfigureServices in Startup.cs this services.AddScoped<Storage>();
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddScoped<Storage>();
}
every time I open my website on new device it doesn't create a new session restarting from zero, but continues where I left it.
I checkout your code and find that you're using Singleton Pattern to initialize the Storage. If I understand it correctly, this Storage singleton instance will be shared across different users (also across different devices). As this instance will be used to render the Main.razor page, there will be concurrency problems that you're experiencing now .
To fix that issue, the Storage instance should be limited within some specific connection. As you're using Blazor Server Side, you could register the Storage as a Scoped Service:
In Blazor Server apps, a scoped service registration is scoped to the connection. For this reason, using scoped services is preferred for services that should be scoped to the current user, even if the current intent is to run client-side in the browser.
Firstly, remove the static singleton instance :
public class Storage
{
private static Storage instance;
private Storage()
{
}
public static Storage GetInstance()
{
if (Storage.instance == null)
Storage.instance = new Storage();
return Storage.instance;
}
public List<Items>list {get;set;} = new List<Items>();
public string password {get;set;}
}
Register this Class as a scoped service:
services.AddScoped<Storage>();
And then inject this service in your Login.razor and Main.razor :
#inject project.Storage Storage
Finally, you need change all the Storage.GetInstance(). to Storage.:
Storage.list = Order;
...
Storage.password = password;
I notice that you're also creating the Importer/Additional instance using the Singleton Pattern. I would suggest you should refactor them to use Service Injection in a similar way.
First some background:
My setup uses a Service, which implements BeaconConsumer and binds to the BeaconManager. I have additional handling so when my app leaves the foreground, I move my Service to run in foreground, and when my app enters the foreground, I move my Service to run in background. That way, the persistent notification should display if and only if the app isn't displaying. In accordance, I am using the pattern here to tell BeaconManager I'm running this Service in the foreground, to allow for more frequent scanning. The link above isn't quite clear about this, but I believe this pattern should work without alterations needed on both pre-Android 8 as well as Android 8+. It shouldn't be strictly necessary on pre-Android 8, since the OS is more lenient. However, using this setup across all versions has the collateral benefit of ensuring that the OS does not kill the Service. If the app is in the foreground, the Service is background but has priority by virtue of the app, and if the app is not in the foreground, the Service is, and therefore has priority.
Now the problem: On pre-Android 8 devices, my Service is not seeing didEnterRegion called when the app is not in the foreground (but the Service is). It works fine on Android 8+.
some code snippets:
In my Service, set up the BeaconManager, set scan intervals
_beaconManager = BeaconManager.getInstanceForApplication(this);
_beaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout(IBEACON_PATTERN_1));
_beaconManager.setEnableScheduledScanJobs(false);
_beaconManager.setBackgroundBetweenScanPeriod(0);
_beaconManager.setBackgroundScanPeriod(1100);
Function in my Service I invoke to send the service to the foreground, and background:
private void sendServiceToForeground() {
this.startForeground(NOTIFICATION_ID, _notification);
if (_beaconManager != null) {
if (_beaconManager.isBound(this)) {
_beaconManager.unbind(this);
}
_beaconManager.enableForegroundServiceScanning(_notification, NOTIFICATION_ID);
_beaconManager.bind(this);
}
}
private void sendServiceToBackground() {
if (_beaconManager != null) {
if (_beaconManager.isBound(this)) {
_beaconManager.unbind(this);
}
_beaconManager.disableForegroundServiceScanning();
_beaconManager.bind(this);
}
this.stopForeground(true);
}
I can provide more code as requested. Not sure what all is relevant.
Calls to unbind() and bind() are asynchronous, so calling them one after another will be a problem unless you first wait for the unbind() operation to complete. This is tricky, because the library's BeaconManager does not provide a callback to tell you when unbind is complete (indeed, this is because the underlying Android service APIs also do not provide such a callback. You essentially don't know when the library's scanning service has stopped so you can safely restart it again in a different mode.)
It's a bit of a hack, but you might try adding a delay between unbind() and bind() to see if that makes a difference.
Specifically what I am doing is in my AppServiceProvider->boot() method I am creating a singleton class like below:
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
$this->app->singleton('App\Support\PushNotificationHelper', function ($app) {
return new PushNotificationHelper();
});
}
}
The helper class is needed for a Queue worker job I use for Pushing Notifications to mobile apps. When the mobile device is an Apple device I need to establish a curl connection and have the connection persist beyond the life of the queue worker job. This is why I am using the singleton to hold the connection like:
class PushNotificationHelper {
protected $http2Connection;
protected $http2Expire ;
public function getConnection($options) {
$this->http2Connection = curl_init();
curl_setopt_array($this->http2Connection, $options);
return $this->http2Connection;
}
Apple claims if I connect and disconnect repeatedly then they will issue a Denial of Service (DOS). My app literally sends 1000s of notifications per hour. When ever I use the connection I check for errors and will close/reopen the connection when needed like:
curl_close($http2Connection);
However I would like to know how I can detect when the app will close for good so I can gracefully close the connection. If there is no way to do this will it harm my server over time by leaving open connections hanging, lets say months of running if the app was to start/stop several times a day ?
Another option could be is there a curl option to tell the connection to auto disconnect after so much time. (I force a close and reopen every 4 hours) so if I could tell connection to auto-close after 5 hours at least then maybe it would be self-cleaning?
IMHO you can try to add a terminating callback to your app instance, for example in the AppServiceProvider, i.e.:
public function boot()
{
$this->app->terminating(function () {
// your terminating code here
});
}
You can use the boot method for anything.
From laravel docs:
This method is called after all other service providers have been
registered, meaning you have access to all other services that have
been registered by the framework
The key is that boot method run when all services are registered, so, you can inject services in the boot method definition.
public function boot(SomeService $someService, OtherService $otherService)
{
$someService->doSomething();
$otherService->doSomething();
}
In my opinion, you must to use this method to run code required by your app in all contexts: user logged in, user logged out, post, get, put, etc., etc.
Apps handle GeoFence needs to receive PROVIDERS_CHANGED broadcast since:
Registered GeoFences will be removed when both 2 location providers
(network and GPS) are turned off.
When one of 2 location providers is turned on, app needs to register
GeoFences to work. This should be performed w/o asking user to run
my app again.
So my app has been registering its broadcast receiver in manifest. But it does not work any more in Android Oreo since PROVIDERS_CHANGED is not one we can make it work as before.
I can register broadcast receiver for that in app's activity or in service but it will quit (end its life cycle) sooner or later, then I need to unregister it. My app starts working by some events like GeoFence transition, but receiving PROVIDERS_CHANGED is critical to make it work.
I verified PROVIDERS_CHANGED can't be received by receiver registered in manifest in Android Oreo. Is there any solution for it?
#Tomcat and #Virat18 - I've come across a solution to the fact that you can no longer register a Broadcast Receiver in your Manifest to receive the PROVIDERS_CHANGED action Intent-filter in Android-OREO..
The solution? Simply register your BroadcastReceiver dynamically (from within your code), instead of from the Manifest.. Also, instead of checking for the hard-coded regex android.location.PROVIDERS_CHANGED, you should use LocationManager.PROVIDERS_CHANGED_ACTION (and of course import the LocationManager).
Here is an example of the Code I used to get this to work! (ex: from a Button Click):
public void buttonClick(View view) {
IntentFilter filter = new IntentFilter();
filter.addAction("android.location.PROVIDERS_CHANGED");
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().matches(LocationManager.PROVIDERS_CHANGED_ACTION)) {
Log.i(TAG, "Action MATCHES LocationManager.PROVIDERS_CHANGED_ACTION!");
}
}
};
this.getApplicationContext().registerReceiver(receiver, filter);
Log.i(TAG, "RECEIVER HAS BEEN REGISTERED");
}
Also, don't forget to unregister the receiver in your code appropriately.
If you find this to be a good solution, please accept it as the Answer.
Happy Coding!
PS. This will continue to receive the broadcast from the background, even once the User leaves your Activity (presses the back-button, home-button, etc).. However, if the user closes your App from the Multitask button, it will no longer receive, so take note of that.
I have a SignalR hub and two clients (Windows and PCL for Android and iOS). Neither of the clients is able to call some methods on the server. This behaviour is quite odd, since the methods look very similar. Moreover, a colleague of mine is able to call methods I cannot call, and vice versa, does not invoke methods that I invoke with no problems.
Here is an example of a method, which works for me and does not work for my colleague:
public override async Task<bool> RefreshArray(User user, int waitMilis)
{
var cts = new CancellationTokenSource();
try
{
cts.CancelAfter(waitMilis);
await Proxy.Invoke("RefreshArray", user);
return true;
}
catch (Exception ex)
{
OnExceptionOccured(ex);
return false;
}
}
And a method which does not work for me, but works for my colleague:
public override async Task<bool> RequestInformation(User user, Product product, int waitMilis)
{
var cts = new CancellationTokenSource();
try
{
cts.CancelAfter(waitMilis);
await Proxy.Invoke("RequestInformation", user, product);
return true;
}
catch (Exception ex)
{
OnExceptionOccured(ex);
return false;
}
}
Yes, me and my colleague have exactly the same code. And no, there are no typos or different arguments. I have tried to get as much data from the client connection as possible, by setting _connection.TraceLevel = TraceLevels.All; However, I did not get any information on the invoked methods, just on the replies from the hub. When calling RefreshArray, I got exactly the data I requested. When calling RequestInformation, the debugger never even hit the breakpoint in the hub method and the _connection.Trace displayed only this: 11:22:45.6169660 - 7bc57897-489b-49a2-8459-3fcdb8fcf974 - SSE: OnMessage(Data: {})
Has anybody solved a similar issue? Is there a solution?
UPDATE 1
I just realized that I have encountered almost the same issue about a year ago (Possible SignalR bug in Xamarin Android). StackOverflow has also pointed me to a question with almost the same issue (SignalR on Xamarin.iOS - randomly not able to call Hub method), just related to iOS and Azure. However, I got the same proble even outside Xamarin, on Windows Phone 8.1 and and Windows 10 Universal App. Moreover, I am running the server just locally, so it is not an issue od Azure. Is it really possible, that a 2 years old bug has no solution?
UPDATE 2
I have just created a simple console application with SignalR.Client. In the console application every method worked just fine. Amazingly, also the Windows 10 Universal Application started to behave as expected - every hub method was invoked correctly. Windows Phone 8.1 also improved its behaviour (all hub methods invoked). However, every now and then the connection tried to reconnect periodically (for no apparent reason), leading to Connection started reconnecting before invocation result was received. error. The Android application still behaved as before.
So I tried to replicate my previous steps and created another console application, but this time with SignalR.Client.Portable library. To my dissapointment, there was no change in the Android application behaviour.
Next week we will start to test our application on iOS, so I really wonder what new oddities will we encounter.
I have managed to solve the problem (at least so it seems). As it turned out, there is some weird stuff going around, when an application receives an answer from SignalR hub. It seems as if the HubProxy was blocked for a certain period of time on Android, while it drops the connection and starts to reconnect periodically on Windows Phone, not waiting for an asnwer from the hub.
The implementation of RefreshArray on the hub was something like this:
public async Task RefreshArray(User user)
{
await Clients.Caller.SendArray(_globalArray);
await Clients.Caller.SendMoreInformation(_additionalInfo);
}
Because the method sent two methods as an answer, the client Proxy got stuck and each platform handled it in its own unexpected way. The reason why some methods were called on my computer and not on colleagues was, simply, because we had different position of breakpoints, which enabled the application to resolve at least some requests and responses.
The ultimate solution was to add some synchronization into the invokation of methods. Now my hub calls only await Clients.Caller.SendArray(_globalArray);. This is then handled on the client with a ArraySent(string[] array) event, which then subsequently invokes the SendMoreInformation() method on the hub.