How to access an already-running Application Context from a Sync Adapter service in Android? - service

I have an app that consists of several activities, and I use the Application Context (entended from the Application Class, and I made it persistent) to share data and objects between all the activities. I use the Application Class instead of a background service for several good reasons, which I won't go into here.
I also recently added an custom contact sync adapter to my app. It's under the same package, in the same APK. So, I set it up to access the Application Context just like everything else in my app to give it access to all the shared data and objects. However, even though it works (mostly), it creates a new instance of the Application Context. So there are basically 2 separate instances of my application running, and the data isn't shared between them.
I think that the problem is that my Applicattion never starts the sync service, the OS does. All my other activities are either started by the application, or the main activity accesses the Application Context when it launches, and then the App Context controls everything else. Is there a way to have the sync service access the existing Application Context, instead of creating the new instance of it?
Here's the basic structure of my app:
The application
package com.mycomany.myapp;
public class MyApp extends Application{
...
}
Activity1
package com.mycomany.myapp;
public class MyActivity1 extends Activity{
MyApp a;
#Override
public void onCreate(Bundle savedInstanceState){
a = (MyApp) getApplicationContext();
...
}
}
SyncAdapterService
package com.mycomany.myapp;
public class SyncAdapterService extends Service {
private static SyncAdapterImpl sSyncAdapter = null;
private static final Object sSyncAdapterLock = new Object();
private static ContentResolver mContentResolver = null;
private static MyApp a;
public SyncAdapterService() {
super();
}
private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
private Context mContext;
public SyncAdapterImpl(Context context) {
super(context, true);
mContext = context;
}
#Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
try {
SyncAdapterService.performSync(mContext, account, extras, authority, provider, syncResult);
} catch (OperationCanceledException e) {}
}
}
#Override
public void onCreate() {
super.onCreate();
synchronized (sSyncAdapterLock) {
if(a == null){
a = (MyApp) getApplicationContext();
}
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapterImpl(getApplicationContext());
}
}
}
#Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
private static void performSync(Context context, Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult)
throws OperationCanceledException {
...
}
}

Have you copy&pasted this training for the SyncAdapter http://developer.android.com/training/sync-adapters/creating-sync-adapter.html?
At the end there is this XML Snippet:
<service
android:name="com.example.android.datasync.SyncService"
android:exported="true"
android:process=":sync">
<intent-filter>com.example.android.datasync.provider
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="#xml/syncadapter" />
</service>
With the attribute android:process=":sync" meaning you create a separate sync process. Remove it and you're good to go.

You might want to look into binding the service to your Application context. That way, if your application context does not exist, the service won't exist, as it runs in the same process (that of the Application) . See bindSerivce()
If your service is a remote one try using callbacks

Are you still having this problem?
If the service is declared in your manifest file without specifying a different android:process, isn't it supposed to run in the default process defined by your task?
Can't you in that case just use getApplicationContext to get what you need?
I have my sync adapter implemented in this way and it is working

Related

How to create a background service in .NET Maui

I'm new to mobile app development and am learning .NET Maui. The app I'm creating needs to listen for Accelerometer events, and send a notification to a web service if the events meet certain criteria. The bit I'm struggling with is how to have the app run in the background, i.e. with no UI visible, without going to sleep, as I'd want the user to close the UI completely. So I'm thinking the app needs to run as some kind of service, with the option to show a UI when needed - how can this be done?
i know it's beign a while but will post an answer for future users!
First we need to understand that background services depends on which platform we use.(thanks Jason) And i will focus on ANDROID, based on Xamarin Documentation (thanks Eli), adapted to Maui.
Since we are working with ANDROID, on MauiProgram we will add the following:
/// Add dependecy injection to main page
builder.Services.AddSingleton<MainPage>();
#if ANDROID
builder.Services.AddTransient<IServiceTest, DemoServices>();
#endif
And we create our Interface for DI which provides us the methods to start and stop the foreground service
public interface IServiceTest
{
void Start();
void Stop();
}
Then, before platform code we need to add Android Permissions on AndroidManifest.xml:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Android Main Activity
public class MainActivity : MauiAppCompatActivity
{
//set an activity on main application to get the reference on the service
public static MainActivity ActivityCurrent { get; set; }
public MainActivity()
{
ActivityCurrent = this;
}
}
And Finally we create our Android foreground service. Check Comments Below. Also on xamarin docs, they show the different properties for notification Builder.
[Service]
public class DemoServices : Service, IServiceTest //we implement our service (IServiceTest) and use Android Native Service Class
{
public override IBinder OnBind(Intent intent)
{
throw new NotImplementedException();
}
[return: GeneratedEnum]//we catch the actions intents to know the state of the foreground service
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
if (intent.Action == "START_SERVICE")
{
RegisterNotification();//Proceed to notify
}
else if (intent.Action == "STOP_SERVICE")
{
StopForeground(true);//Stop the service
StopSelfResult(startId);
}
return StartCommandResult.NotSticky;
}
//Start and Stop Intents, set the actions for the MainActivity to get the state of the foreground service
//Setting one action to start and one action to stop the foreground service
public void Start()
{
Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(DemoServices));
startService.SetAction("START_SERVICE");
MainActivity.ActivityCurrent.StartService(startService);
}
public void Stop()
{
Intent stopIntent = new Intent(MainActivity.ActivityCurrent, this.Class);
stopIntent.SetAction("STOP_SERVICE");
MainActivity.ActivityCurrent.StartService(stopIntent);
}
private void RegisterNotification()
{
NotificationChannel channel = new NotificationChannel("ServiceChannel", "ServiceDemo", NotificationImportance.Max);
NotificationManager manager = (NotificationManager)MainActivity.ActivityCurrent.GetSystemService(Context.NotificationService);
manager.CreateNotificationChannel(channel);
Notification notification = new Notification.Builder(this, "ServiceChannel")
.SetContentTitle("Service Working")
.SetSmallIcon(Resource.Drawable.abc_ab_share_pack_mtrl_alpha)
.SetOngoing(true)
.Build();
StartForeground(100, notification);
}
}
Now we have our foreground Service working on Android, that show a notification ("Service Working"). Every time it starts. I make a show message foreground service to see it better while testing, in your case it suppose to close the app if that's what you want, but the functioning it's the same.
So having our background service working only left a way to call it so on our main page (as example) i will do the following:
MainPage.xaml
<VerticalStackLayout>
<Label
Text="Welcome to .NET Multi-platform App UI"
FontSize="18"
HorizontalOptions="Center" />
<Button
x:Name="CounterBtn"
Text="start Services"
Clicked="OnServiceStartClicked"
HorizontalOptions="Center" />
<Button Text="Stop Service" Clicked="Button_Clicked"></Button>
</VerticalStackLayout>
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
IServiceTest Services;
public MainPage(IServiceTest Services_)
{
InitializeComponent();
ToggleAccelerometer();
Services = Services_;
}
//method to start manually foreground service
private void OnServiceStartClicked(object sender, EventArgs e)
{
Services.Start();
}
//method to stop manually foreground service
private void Button_Clicked(object sender, EventArgs e)
{
Services.Stop();
}
//method to work with accelerometer
public void ToggleAccelerometer()
{
if (Accelerometer.Default.IsSupported)
{
if (!Accelerometer.Default.IsMonitoring)
{
Accelerometer.Default.ReadingChanged += Accelerometer_ReadingChanged;
Accelerometer.Default.Start(SensorSpeed.UI);
}
else
{
Accelerometer.Default.Stop();
Accelerometer.Default.ReadingChanged -= Accelerometer_ReadingChanged;
}
}
}
//on accelerometer property change we call our service and it would send a message
private void Accelerometer_ReadingChanged(object sender, AccelerometerChangedEventArgs e)
{
Services.Start(); //this will never stop until we made some logic here
}
}
It's a long Answer and it would be great to have more official documentation about this! Hope it helps! If anyone can provide more info about IOS, Windows, MacCatalyst would be awesome!

How do I pass an Activity via MethodChannel in Flutter?

I'm making a Flutter app that needs to call a third party library (jar file provided).
The Java documentation for the third-party API object is created by calling something
public class MainActivity extends Activity {
private ThirdPartyAPI mAPI;
private ThirdPartyAPICallbacks mCallbacks = new ThirdPartyAPICallbacks(){
#Override
public void Connected() {
}
};
protected void onCreate(Bundle savedInstanceState) {
mAPI = new ThirdPartyAPI(this, mCallbacks); // how do we do the equivalent in Flutter?
}
}
How do I do this in Flutter?
I tried MethodChannel, but I don't know what to pass as the Activity instance to the ThirdPartyAPI constructor.
MethodChannel doesn't and will never allow to send something like an Activity.
The only types allowed are the following from the official DOCS:
If you really need to send something to Flutter, you'd need to create a method on Flutter which will call Java/Kotlin side, then get anything important you need from that 3rd party API/Library/Etc and send that info back to Flutter using a MethodChannel.
You don't transfer to Activity across the method channel. It only ever exists at the Java end. Instead it gets 'attached' to the Java end of the channel.
When creating a plugin (this is probably preferred as you can re-use it in different projects), make sure the plugin class also implements ActivityAware. Create stub implementation for the four methods required, in particular onAttachedToActivity.
For example:
public class ThirdPartyApiPlugin implements FlutterPlugin, ActivityAware {
#Override
public void onAttachedToActivity(ActivityPluginBinding binding) {
Activity activity = binding.getActivity();
mAPI = new ThirdPartyAPI(activity, mCallbacks);
}
If not using a plugin, modify the MainActivity class as described here.
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "someChannelName";
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
mAPI = new ThirdPartyAPI(this, mCallbacks);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler(
(call, result) -> {
// Note: this method is invoked on the main thread.
// TODO
}
);
}
}

log4net: How to distinguish between different forms on the same UI thread?

is there a way (NDC, Properties, ...?) to have a name/id per form that is included in all log4net messages, so I can distinguish between the forms in all log messages?
I have many service methods etc. that are used in all my forms, and I'd like to see e.g. that a service was called as a result of user input in what form (think multiple nonmodal similar forms (same class), running in the same UI thread, containing a button, and in the button's Click-Event, a service method is called. Inside the service method, there are logging calls. In the log messages, I'd like to have a property containing the information of in exactly which form instance the button was clicked in).
I don't want to modify ALL logging calls. The examples in the web for log contexts / NDC all only talk about multiple clients / asp.net requests / etc., not multiple forms in 1 thread.
Thanks,
Tim
To do this, set the properties in the form's Activated event to what you want to log:
private void Form1_Activated(object sender, System.EventArgs e)
{
// for example
log4net.GlobalContext.Properties["Name"] = this.GetType().Name;
log4net.GlobalContext.Properties["Id"] = this.Id;
}
The in your logging configuration, you can reference the properties in the PatternLayout for each appender:
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{Name} : %property{Id} : [%level]- %message%newline" />
</layout>
Edit: to preserve multiple values, use a stack, as in this unit test which outputs:
Now in TestClass1 Now in TestClass2
using log4net.Appender;
using log4net.Config;
using log4net.Core;
using log4net.Layout;
using NUnit.Framework;
namespace log4net.Tests
{
[TestFixture] // A NUnit test
public class log4net_Stacks
{
[SetUp]
public void Setup()
{
ConsoleAppender ca = new ConsoleAppender
{
Layout = new PatternLayout("%property{demo}"),
Threshold = Level.All
};
ca.ActivateOptions();
BasicConfigurator.Configure(ca);
}
[Test]
public void Stacks_Demo()
{
new TestClass1().Method1();
LogManager.GetLogger("logger").Debug("");
ThreadContext.Stacks["demo"].Clear();
}
private abstract class BaseTestClass
{
protected static void AddToStack(string message)
{
ThreadContext.Stacks["demo"].Push(message);
}
}
private class TestClass1 : BaseTestClass
{
public void Method1()
{
AddToStack("Now in " + GetType().Name);
var tc2 = new TestClass2();
tc2.Method2();
}
}
private class TestClass2 : BaseTestClass
{
public void Method2()
{
AddToStack("Now in " + GetType().Name);
}
}
}
}

Proper Hub dependency lifetime management for SignalR and Castle Windsor

I have some SignalR hubs which may need to access some transient and singleton dependencies. Hooking the creation of the Hub is easy and works just fine however SignalR does its own Dispose() call on the created Hub rather than notifying the dependency resolver and letting it get involved in the disposal.
This isn't such a big deal if the dependencies are registered singletons, but if they're registered as transients then they'll never get disposed (if that was required) and Windsor will keep them alive until the Windsor container is collected (when the web server is shutting down anyway).
I see several possible ways of handling this...
a) Someone here points out a way to subclass SignalR's HubDispatcher class so that it can do proper disposal. It's not part of SignalR's standard DependencyResolver so this might be difficult / impossible
b) Some other class in SignalR, elsewhere in the pipeline, can be overridden or easily replaced so that we could subclass HubDispatcher and ensure that subclass is used. From what I can tell this would have to be the Owin middleware class HubDispatcherMiddleware. Is there some way to force Owin to not register this class and instead register my own version of this (which in turn uses my own HubDispatcher)?
c) There's some way of intercepting the Dispose() call made by SignalR on my Hub classes so that a call could be made back to Windsor to ensure any dependencies are properly disposed and released from the container
d) Studiously avoid using transient lifestyle dependencies and instead pass in typed factories so that we can resolve and release each dependency via the typed factory within the Hub
At the moment (d) is the only one I know how to do. (a) or (b) would be great. (c) is mostly covered by this post http://kozmic.net/2010/01/27/transparently-releasing-components-in-windsor/, however, the interceptor requires that Dispose() be called via IDisposable. SignalR's HubDispather class' implementation of hub disposal is
private static void DisposeHubs(IEnumerable<IHub> hubs)
{
foreach (var hub in hubs)
{
hub.Dispose();
}
}
No casting to IDisposable there... Also Dispose() on the Hub class is virtual and that blog post implies that a virtual Dispose() could add some complexity (I'm not quite sure how much and I don't know enough about Castle's interceptors and whether or not that missing cast to IDisposable can be worked around anyway).
I appreciate I've written this question for a fairly narrow audience - those who have used Windsor AND SignalR and care about more than just resolving dependencies. Every example I've found, including those on StackOverflow, seems to just ignore the release of dependencies.
Thanks!
I've had a bit similar problem but with Unity instead of Castle Windsor.
My requirements:
I wanted to avoid singleton registrations on the container.
All objects are resolved in Hub and should be disposed on Hub destruction.
Registrations reused across Web Api and SignalR.
Object lifetime is managed by HierarchicalLifetimeManager - child containers resolve and manage separate object instances. Registered like this:
container.RegisterType<IMessageService, MessageService>(new HierarchicalLifetimeManager());
This is my solution:
[HubName("exampleHub")]
public class ExampleHub : Hub
{
IUnityContainer _container;
public CarrierApiHub(IUnityContainer container) // container itself injected in hub
{
_container = container.CreateChildContainer(); // child container derived from the main container.
}
public async Task<int> UnreadMessagesCount()
{
// Here i'm resolving instance of IMessageService which depends on
// other registrations specified on the container. Full object graph
// is constructed and destroyed on hub disposal.
var messageSvc = _container.Resolve<IMessageService>();
return await messageSvc.CountUnreadOf(UserId);
}
protected override void Dispose(bool disposing)
{
_container.Dispose(); // child container destroyed. all resolved objects disposed.
base.Dispose(disposing);
}
private int UserId
{
get
{
// only an example
var claim = ((ClaimsPrincipal)Context.User).GetClaim("user_id");
return int.Parse(claim.Value);
}
}
}
SignalR and dependency resolver configuration:
public static class ConfigureSignalR
{
public static void Initialize(UnityContainer unityContainer, IAppBuilder app)
{
app.Map("/signalr", map =>
{
var resolver = new AppSignalRDependencyResolver(unityContainer);
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableJavaScriptProxies = false,
EnableJSONP = true, // Required for IE 9 (supports only polling)
Resolver = resolver
};
map.RunSignalR(hubConfiguration);
});
}
}
Dependency resolver implementation:
public class AppSignalRDependencyResolver : DefaultDependencyResolver
{
protected IUnityContainer _container;
public AppSignalRDependencyResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this._container = container.CreateChildContainer();
}
public override object GetService(Type serviceType)
{
try
{
return _container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return base.GetService(serviceType);
}
}
public override IEnumerable<object> GetServices(Type serviceType)
{
try
{
return _container.ResolveAll(serviceType).Concat(base.GetServices(serviceType));
}
catch (ResolutionFailedException)
{
return base.GetServices(serviceType);
}
}
protected override void Dispose(bool disposing)
{
_container.Dispose();
base.Dispose(disposing);
}
}

Android Activity and Service communication: how to keep the UI up-to-date

I have an Activity A (not the main Activity) that launches a Service S that does some stuff in the background and, in the meanwhile, should made some changes to the UI of A.
Let's just say that S count from 0 to 100 and A should display this count in Real-Time. Since the real job of S is quite more complicated and CPU-consuming, I do not want to use AsyncTask for it (indeed "AsyncTasks should ideally be used for short operations (a few seconds at the most.) [...]") but just a regular Service started in a new Thread (an IntentService would be fine as well).
This is my Activity A:
public class A extends Activity {
private static final String TAG = "Activity";
private TextView countTextView; // TextView that shows the number
Button startButton; // Button to start the count
BResultReceiver resultReceiver;
/**
* Receive the result from the Service B.
*/
class BResultReceiver extends ResultReceiver {
public BResultReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
switch ( resultCode ) {
case B.RESULT_CODE_COUNT:
String curCount = resultData.getString(B.RESULT_KEY_COUNT);
Log.d(TAG, "ResultReceived: " + curCount + "\n");
runOnUiThread( new UpdateUI(curCount) ); // NOT WORKING AFTER onResume()!!!
break;
}
}
}
/**
* Runnable class to update the UI.
*/
class UpdateUI implements Runnable {
String updateString;
public UpdateUI(String updateString) {
this.updateString = updateString;
}
public void run() {
countTextView.setText(updateString);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.counter);
countTextView = (TextView) findViewById(R.id.countTextView);
startButton = (Button) findViewById(R.id.startButton);
resultReceiver = new BResultReceiver(null);
}
public void startCounting(View view) {
startButton.setEnabled(false);
//Start the B Service:
Intent intent = new Intent(this, B.class);
intent.putExtra("receiver", resultReceiver);
startService(intent);
}
}
And this is my Service B:
public class B extends Service {
private static final String TAG = "Service";
private Looper serviceLooper;
private ServiceHandler serviceHandler;
private ResultReceiver resultReceiver;
private Integer count;
static final int RESULT_CODE_COUNT = 100;
static final String RESULT_KEY_COUNT = "Count";
/**
* Handler that receives messages from the thread.
*/
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
while ( count < 100 ) {
count++;
//Sleep...
sendMessageToActivity(RESULT_CODE_COUNT, RESULT_KEY_COUNT, count.toString());
}
//Stop the service (using the startId to avoid stopping the service in the middle of handling another job...):
stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
//Start up the thread running the service:
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
this.count = 0;
//Get the HandlerThread's Looper and use it for our Handler
serviceLooper = thread.getLooper();
serviceHandler = new ServiceHandler(serviceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.resultReceiver = intent.getParcelableExtra("receiver");
//For each start request, send a message to start a job and deliver the start ID so we know which request we're stopping when we finish the job:
Message msg = serviceHandler.obtainMessage();
msg.arg1 = startId;
serviceHandler.sendMessage(msg);
//If we get killed, after returning from here, restart:
return START_REDELIVER_INTENT;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* Send a message from to the activity.
*/
protected void sendMessageToActivity(Integer code, String name, String text) {
Bundle bundle = new Bundle();
bundle.putString(name, text);
//Send the message:
resultReceiver.send(code, bundle);
}
}
Everything works fine but if I click the back button (or the home button) and then I re-open the Activity A then the UI of A is not updated anymore (it just shows the initial configuration of A - i.e. the "startButton" is still clickable and the count is not showed - it seems runOnUiThread(...) is not working anymore). However, the Service B is still running in the background and the I can see the correct count is passed to the Activity A in the Log.d(...). Finally, if I click on the "startButton" again, the counting does not start from the beginning (0) but from where B has been arrived (I've double checked it by showing it in the notification bar).
How can I fix this behaviour? I would like that, when I re-open the Activity A, it automatically continues to receive and update the data from the Service B. Or, in other words, that the Service keeps the UI of the Activity A up-to-date.
Please give me some hints, links or piece of code. Thanks!
When you click back button your Activity is destroyed. When you start the Activity again you get a new Activity. That also happen when you rotate the device. This is Android lifecycle event
The Activity is not good for heavy Business Logic only to show stuff/control stuf.
What you have to do is create a simple MVC, Model View Controller. The view (Activity) should only be used for showing results and controlling the eventflow.
The Service can hold an Array of the count and when your Activity start it will onBind() your Service that is running (or if not running will start the Service since you bind to it) Let the Activity(View) get the Array of results and show it. This simple setup exclude the (M)Model Business Logic.
Update
Following up a bit read this it's Android official docs and perfect start since it do kind of what you asking. As you see in the example in the onStart() the Activity establish a connection with the service and in the onStop() the connection is removed. There's no point having a connection after on onStop(). Just like you asking for. I would go with this setup and not let the Service continuously sending data because that would drain resources and the Activity is not always listening because it will stop when in the background.
Here's an activity that binds to LocalService and calls getRandomNumber() when a button is clicked: