Getting a SyncAdapter to work

PeterDavisPeterDavis USMember ✭✭✭
edited May 2014 in Xamarin.Android

I've been trying to get a SyncAdapter to work. Man, what a ridiculously complicated mess. I don't even know where to begin, so I guess I'll just dump all the related code. If I put breakpoints pretty much everywhere in the code below, the following stuff actually gets called at startup:

1: StubContentProvider.OnCreate()

2: AndroidApp.InitSyncService() (called explicitly when my app starts up).

And that's it. Nothing else ever gets called.

AndroidApp.cs

public static class AndroidApp
{

    public static string ACCOUNT_TYPE = "georgesmobile.android.backgroundservice";
    public static string ACCOUNT = "mobile";
    public static string AUTHORITY = "georgesmobile.android.provider";

    public static Account SyncAccount { get; private set; }

    public static void InitSyncService(Context context)
    {
        if (SyncAccount == null)
        {
            CreateSyncAccount(context);
            ContentResolver.SetIsSyncable(AndroidApp.SyncAccount, AndroidApp.AUTHORITY, 1);
            ContentResolver.SetSyncAutomatically(AndroidApp.SyncAccount, AndroidApp.AUTHORITY, true);
            Bundle bund = new Bundle();
            ContentResolver.AddPeriodicSync(AndroidApp.SyncAccount, AndroidApp.AUTHORITY, bund, 2);
        }
    }
    private static Account CreateSyncAccount(Context context)
    {
        SyncAccount = new Account(ACCOUNT, ACCOUNT_TYPE);
        AccountManager accountManager =  (AccountManager)context.GetSystemService(Context.AccountService);
        return SyncAccount;
    }
}

StubContentProvider.cs

[ContentProvider(new[] { "georgesmobile.android.provider" },
    Name = "georgesmobile.android.backgroundservice.StubContentProvider",
    Exported = false,
    Syncable = true)]
public class StubContentProvider : ContentProvider
{
    public override int Delete(global::Android.Net.Uri uri, string selection, string[] selectionArgs)
    {
        return 0;
    }

    public override string GetType(global::Android.Net.Uri uri)
    {
        return "";
    }

    public override global::Android.Net.Uri Insert(global::Android.Net.Uri uri, ContentValues values)
    {
        return null;
    }

    public override bool OnCreate()
    {
        return true;
    }

    public override global::Android.Database.ICursor Query(global::Android.Net.Uri uri, string[] projection, string selection, string[] selectionArgs, string sortOrder)
    {
        return null;
    }

    public override int Update(global::Android.Net.Uri uri, ContentValues values, string selection, string[] selectionArgs)
    {
        return 0;
    }
}

GenericAccountService.cs

[Service(Name = "georgesmobile.android.backgroundservice.GenericAccountService")]
[IntentFilter(new string[] { "android.accounts.AccountAuthenticator" })]
[MetaData("android.accounts.AccountAuthenticator", Resource = "@xml/authenticator")]
public class GenericAccountService : Service
{
    private Authenticator _authenticator;

    public static Account GetAccount(string accountType)
    {
        return new Account(AndroidApp.ACCOUNT, AndroidApp.ACCOUNT_TYPE);
    }

    public override void OnCreate()
    {
        base.OnCreate();
        _authenticator = new Authenticator(this);
    }

    public override IBinder OnBind(Intent intent)
    {
        return _authenticator.IBinder;
    }
}

SyncAdapter.cs

public class SyncAdapter : AbstractThreadedSyncAdapter 
{

    public override void OnPerformSync(global::Android.Accounts.Account account, Bundle extras, string authority, ContentProviderClient provider, SyncResult syncResult)
    {
        BackgroundDataSync.Synchronize();
    }
}

SyncService.cs

[Service(Name = "georgesmobile.android.backgroundservice.SyncService", Exported = true)]
[MetaData("android.content.SyncAdapter", Resource = "@xml/syncadapter")]
public class SyncService : Service
{
    private static SyncAdapter _syncAdapter;
    private static object _syncLock = new object();

    public override void OnCreate()
    {
        base.OnCreate();
        lock (_syncLock)
        {
            _syncAdapter = new SyncAdapter(ApplicationContext, true);
        }
    }

    public override IBinder OnBind(Intent intent)
    {
        return _syncAdapter.SyncAdapterBinder;
    }

    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {
        return base.OnStartCommand(intent, flags, startId);
    }
}

syncadapter.xml

<?xml version="1.0" encoding="utf-8" ?>
<sync-adapter
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:contentAuthority="georgesmobile.android.provider"
        android:accountType="georgesmobile.android.backgroundservice"
        android:userVisible="true"
        android:supportsUploading="true"
        android:allowParallelSyncs="true"
        android:isAlwaysSyncable="true"/>

authenticator.xml

<?xml version="1.0" encoding="utf-8" ?>
<account-authenticator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:icon="@drawable/georgeslogo_icon"
    android:smallIcon="@drawable/georgeslogo_icon"
    android:label="@string/AppName"        
    android:accountType="georgesmobile.android.backgroundservice"/>

relevant AndroidManifest.xml (as generated by the compiler)

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto" package="georgesmobile.android" android:versionCode="1" android:versionName="Crackerjack">
      <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" />
      <uses-permission android:name="android.permission.READ_SYNC_STATS" />
      <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
      <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
      <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
      <uses-permission android:name="android.permission.ACCOUNT_MANAGER" />
      <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
      <application android:label="GeorgesMobile" android:icon="@drawable/georgeslogo_icon" android:theme="@android:style/Theme.Light" android:name="mono.android.app.Application" android:debuggable="true">
        <receiver android:name="georgesmobile.android.backgroundservice.BootupReceiver">
          <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
          </intent-filter>
        </receiver>
        <service android:name="georgesmobile.android.backgroundservice.GenericAccountService">
          <meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator" />
          <intent-filter>
            <action android:name="android.accounts.AccountAuthenticator" />
          </intent-filter>
        </service>
        <provider android:authorities="georgesmobile.android.provider" android:exported="false" android:name="georgesmobile.android.backgroundservice.StubContentProvider" android:syncable="true" />
        <service android:exported="true" android:name="georgesmobile.android.backgroundservice.SyncService">
          <meta-data android:name="android.content.SyncAdapter" android:resource="@xml/syncadapter" />
        </service>
        <activity android:icon="@drawable/georgeslogo_icon" android:label="George's Inc" android:name="georgesmobile.android.views.HomeActivity">
          <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
        </activity>
        <activity android:icon="@drawable/georgeslogo_icon" android:label="George's Login" android:name="georgesmobile.android.views.LoginActivity" />
        <provider android:name="mono.MonoRuntimeProvider" android:exported="false" android:initOrder="2147483647" android:authorities="georgesmobile.android.mono.MonoRuntimeProvider.__mono_init__" />
        <receiver android:name="mono.android.Seppuku">
          <intent-filter>
            <action android:name="mono.android.intent.action.SEPPUKU" />
            <category android:name="mono.android.intent.category.SEPPUKU.georgesmobile.android" />
          </intent-filter>
        </receiver>
      </application>
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    </manifest>

I love android, but Google's documentation on how this stuff works is a pathetic joke.

Posts

  • joedonahue.orgjoedonahue.org USMember
    edited May 2014

    I'm working on a similar issue and am not finding a lot of resources. Currently I'm stuck on the implementation of AbstractThreadedSyncAdapter. In your code, you show no constructors - how does that even compile for you? I get an error (at the context variable) when trying to build the constructor.

    public CalendarSyncAdapter() : base(Context context, bool autoInitialize) {
        Console.WriteLine("SyncAdapter.Constructor");        
    }     
    

    Error: ") expected"

  • PeterDavisPeterDavis USMember ✭✭✭
    edited May 2014

    @joedonahue.org‌

    public CalendarSyncAdapter() : base(Context context, bool autoInitialize) {
        Console.WriteLine("SyncAdapter.Constructor");        
    }     
    

    This won't work. The base part is to call the base constructor. To set it up properly, you'd want to do it like this:

    public CalendarSyncAdapter(Context context, bool autoInitialize) : base(context, autoInitialize) {
        Console.WriteLine("SyncAdapter.Constructor");        
    }     
    

    I actually do have a constructor. I removed it from the code above because all it did was get an instance of the logger from the IoC container which wasn't really relevant to the question at hand. You are correct that it would normally produce an error otherwise as AbstractThreadedSyncAdapter has no default constructor.

  • PeterDavisPeterDavis USMember ✭✭✭
    edited May 2014

    This is the actual implementation:

    public class SyncAdapter : AbstractThreadedSyncAdapter 
    {
    #pragma warning disable 649
        private ILogger _logger;
    #pragma warning restore 649
    
        public SyncAdapter(Context context, bool autoInitialize) : base(context, autoInitialize)
        {
            _logger = IoC.Container.Resolve<ILogger>();
            _logger.Info("SyncAdapter.SyncAdapter() - Enter");
        }
    
        public override void OnPerformSync(global::Android.Accounts.Account account, Bundle extras, string authority, ContentProviderClient provider, SyncResult syncResult)
        {
            _logger.Info("SyncAdapter.OnPerformSync() - Enter");
    
            BackgroundDataSync.Synchronize();
    
            _logger.Info("SyncAdapter.OnPerformSync() - Exit");
        }
    }
    
  • joedonahue.orgjoedonahue.org USMember

    This is helpful, thanks.
    Do you have the metadata attribute? These statements were necessary for the AccountAuthenticator implementation, I'm guessing they will need to be here as well. Something like

    [MetaData("android.content.SyncAdapter", Resource = "@xml/syncadapter")]
    public class SyncAdapter : AbstractThreadedSyncAdapter 
    { ... }
    
  • PeterDavisPeterDavis USMember ✭✭✭

    @joedonahue.org‌ That didn't fix my problem, but it was a problem. I had the Metadata tag (Just like yours) on my SyncService instead of my SyncAdapter. Thanks for that.

  • joedonahue.orgjoedonahue.org USMember
    edited May 2014

    I similarly cannot seem to get the SyncService fired up. After InitSyncService (and elsewhere) I get:
    05-22 20:32:30.050 I/mono-stdout( 4095): Home.InitSyncService - Start

    05-22 20:32:30.061 D/SyncManager( 2681): setIsSyncable: Account {name=jd, type=com.myapp}, provider com.intrinsic.provider -> 1

    05-22 20:32:30.061 D/SyncManager( 2681): setIsSyncable: already set to 1, doing nothing

    05-22 20:32:30.061 D/SyncManager( 2681): setSyncAutomatically: , provider com.myapp.provider -> true

    05-22 20:32:30.061 D/SyncManager( 2681): setSyncAutomatically: already set to true, doing nothing

    05-22 20:32:30.070 I/mono-stdout( 4095): Home.InitSyncService - End

    05-22 20:32:55.490 I/CalendarProvider2( 2888): Sending notification intent: Intent { act=android.intent.action.PROVIDER_CHANGED dat=content://com.android.calendar }

    05-22 20:32:55.490 W/ContentResolver( 2888): Failed to get type for: content://com.android.calendar (Unknown URL content://com.android.calendar)

  • PeterDavisPeterDavis USMember ✭✭✭

    The lack of information in logcat is truly disappointing. If I request a manual sync and it can't do it for whatever reason, it ought to log the reason.

    I have this method:

        public static void ForceSync()
        {
            Bundle bund = new Bundle();
            bund.PutBoolean(ContentResolver.SyncExtrasManual, true);
            ContentResolver.RequestSync(SyncAccount, AndroidApp.AUTHORITY, bund);
        }
    

    And I call it in response to a button press, just for testing and it doesn't do squat. Nothing gets logged. That's ridiculous.

  • joedonahue.orgjoedonahue.org USMember

    I'm getting closer. I had issues earlier with the AccountAuthenticator stuff where Xamarin really doesn't like the Service clauses in the Manifest, and need to be correctly configured as Attributes in the Service. I think you were originally correct to put the Attributes in the Service class rather than the Adapter class, even though it links to the Adapter XML file.

  • joedonahue.orgjoedonahue.org USMember
    edited May 2014

    Here's the Attribute code from the SyncService. I've confirmed that the SyncService is actually firing now, although I still have more errors to work through.

    [Service(Name = "com.myapp.CalendarSyncAdapter", Exported = true, Process = ":cal")]
    [IntentFilter(new[] { "android.content.SyncAdapter" })]
    [MetaData("android.content.SyncAdapter", Resource = "@xml/syncadapter")]
    
  • PeterDavisPeterDavis USMember ✭✭✭

    @John‌ Thanks so much for the example. Still trying to get mine to work, but this is helping. I have suspected that some of my issues have to do with provider names and authorities and whether or not I'm naming them properly. I'm still pretty sure that's the issue, but I just haven't quite found the combination that makes it work... I'll keep tweaking and comparing against your example.

  • PeterDavisPeterDavis USMember ✭✭✭
    edited June 2014

    @John, I can't get your example to work on my Nexus 7 (4.4.2).
    I put a breakpoint on the ContentResolver.RequestSync() call in the button.Click event and I see that get hit, but I have a breakpoint in SyncAdapter.OnPerformSync() and it's not getting called. There's nothing happening in logcat.

    I'd like to build it in 2.2 for my LG Optimus V to see if that acts any differently, but when I set the "Compile using Android Version" in the project to 2.2 (or anything other than API Level 19) and it reverts to API Level 19 after I save (Ctrl-shift-S) and then re-open the project properties.

    I can't seem to make enough room on my phone for the API 19 framework, so I can't test it on my phone.

  • PeterDavisPeterDavis USMember ✭✭✭

    Tried running it in the API 10 emulator. Didn't work their either.

  • PeterDavisPeterDavis USMember ✭✭✭

    So I kinda threw breakpoints all over the place. When the app starts up, the StubProvider.OnCreate() gets called. Then MainActivity.OnCreate() gets called, which in turn calls CreateSyncAccount() which then subscribes to the click event for the button.

    When you click the button, ContentResolver.RequestSync() gets called, but nothing happens.

    SyncService.OnCreate() and SyncService.OnBind() are never called. Same with the AuthenticatorService's OnCreate() and OnBind()

  • JohnMillerJohnMiller USForum Administrator, Xamarin Team Xamurai

    @PeterDavis‌

    I don't know if breakpoints are the best way to debug this. I can set a breakpoint in OnPerformSync and it will not get hit, but I still see my message in logcat.

    I also noticed that when I deployed from XS a second time, the message was not happening. I went into the Settings app on my Nexus device and looked under the Accounts section. Listed there was SyncAdapterExample. Touch that and you will see the dummyaccount listed. The Menu had a "Cancel Sync" option available. Once touched, it now says Sync Now. Pressing that seemed to work. I am still wrapping my head around SyncAdapters. I am not sure why it had the option to "Cancel Sync", maybe something failed, or the state of the sync was broken. I am still investigating the documents.

  • PeterDavisPeterDavis USMember ✭✭✭

    John,

    I don't think I'm alone when I say, as a developer, I frequently make the judgment that if a breakpoint isn't hit, the code isn't getting called. I would find it very concerning if I can't reliably expect breakpoints to be hit.

    But regardless, I uninstalled the app (your sample) and the account was removed. When I run it after uninstalling it, nothing happens when I click the button. No logcat messages no hit on the breakpoint. I honestly don't think it's getting called.

  • PeterDavisPeterDavis USMember ✭✭✭

    Incidentally, if I go into accounts while the app is running and cancel the sync and then do a force sync, nothing happens there either. No logcat message, no hit on the breakpoint.

  • JohnMillerJohnMiller USForum Administrator, Xamarin Team Xamurai
    edited June 2014

    @PeterDavis‌

    I don't think I'm alone when I say, as a developer, I frequently make the judgment that if a breakpoint isn't hit, the code isn't getting called. I would find it very concerning if I can't reliably expect breakpoints to be hit.

    I agree. I will need to investigate that more to see if it's a bug in Xamarin Studio or if its related to how the SyncAdapter works. What I mean by this is how its called, via all these services and background threads. Normally break points work just fine when you are debugging. Until I learn more, I would not rely on that as an indication if the SyncAdpater is working.

    But regardless, I uninstalled the app (your sample) and the account was removed. When I run it after uninstalling it, nothing happens when I click the button. No logcat messages no hit on the breakpoint. I honestly don't think it's getting called.

    This is similar to what I am seeing, but the code is getting called. I noticed that when I first install the app from XS, the Sync seems to be doing something. Before pressing the button, go to your Sync settings for dummyaccount. You will notice that you can "Cancel Sync" even though the button was never pressed. I am not sure what this means yet. Sometimes it seems to take a while (even when I choose to run it immediately with ContentResolver.SyncExtrasExpedited). If you let it run for a while, does it eventually show the message?

    I am still learning this and trying to understand it. Thanks for your patience!

  • PeterDavisPeterDavis USMember ✭✭✭

    John,

    I appreciate you working on this. I did wait for a few minutes because I know that syncs aren't necessary immediate, but it never came through. I'll continue to work on it and see if I can get anywhere.

    Thanks again for your efforts on this. I really appreciate it.

  • JohnMillerJohnMiller USForum Administrator, Xamarin Team Xamurai

    @PeterDavis‌

    @BrendanZagaeski‌ discovered the reason why the breakpoints are not being hit. It's likely because the SyncService is running on a different process id and the debugger does not know about it. I actually specified this per the documentation in the SyncService class with this:

    [Service(Name = "com.xamarin.example.SyncService", Exported = true, Process = ":sync")]
    

    Remove the Process = ":sync" attribute and SyncService will run on the same process id as the activity. Now the breakpoints will be hit and the Console.Writeline message will appear in the Application Output window in Xamarin Studio. That should make things easier to debug. See this and this for more context.

  • PeterDavisPeterDavis USMember ✭✭✭

    @John‌ That makes sense. I should have put that together. I noticed you used the Process parameter and I knew that that ran it in a separate process, but I didn't think about the implications on the breakpoints. But in retrospect, yeah, that makes perfect sense.

    I wasn't using the Process in mine.

    I've removed it from your sample and the sample seems to be working. Awesome! Thank you so much. Now it's just a matter of figuring out how modify mine to get it working, but that should be relatively easy now that I sort of see the order in which things should take place and it gives me a better idea of how to approach it. I'll keep you posted on my progress.

    Thanks so much, John. I really do appreciate your efforts on this!

  • PeterDavisPeterDavis USMember ✭✭✭

    @John, Thank you again! My SyncAdapter is now working and I now have a much better understanding of why it wasn't working (several reasons). I'm now thinking that maybe it would be helpful to create a component that would encapsulate a lot of the work of creating a SyncAdapter and let the user focus on the actual work they need to get done and not spending 6 weeks just trying to get the very basics of their SyncAdapter to work :D

    Anyway, I'll add that to my to-do list.

    Again, Thank You! Thank You! Thank You!

    There are a few aspects to this that I NEVER would have figured out on my own (like the issue with the StubProvider's attributes and needing to put the info straight into the AndroidManifest.xml). My boss has been walking by all morning calling me a failure (I'm one of two people who forgot to wear a red shirt for a ridiculous thing going on here), so to have a big success on this, this morning is redemption on the red shirt catastrophe. ;-)

  • PeterDavisPeterDavis USMember ✭✭✭
    edited June 2014

    Here's a summary of some of the key points I got from this whole thing for others that are running into issues with SyncAdapters:

    1> ContentAuthority should be package name + ".provider"

    2> Service names should be package name + . + class name

    3> The StubProvider's AndroidManifest stuff needs to be hand-coded and not generated from attributes

    4> The SyncService needs Metadata attributes for the SyncAdapter (I originally, incorrectly, had the it on the SyncAdapter, not the SyncService)

    5> Don't use the Process property of the SyncService's ServiceAttribute if you want breakpoints to work.

  • xceedxceed GBMember ✭✭
    edited July 2014

    Hello, I had posted a question about trying to replicate the Windows ScheduledTaskAgent here, @PeterDavis directed me to this thread and I'm just looking at the sample code that @John posted (thanks!).

    After removing 'Process = ":sync"' it does seem to be working when I hit the "Hello World, Click Me!", I see the "I have sync'd!" in the output.

    My question is, should this happen automatically periodically or am I missing something? I'd like it to run every x hours or so, is there somewhere I need to set this or would I need to code this myself, and if I did are there any obvious places to start?

    I'm new to Xamarin and indeed Android so please bear with me.

    Thanks

  • PeterDavisPeterDavis USMember ✭✭✭

    @xceed If you look above at the InitSyncService() method in one of my posts, I have:

    Bundle bund = new Bundle();
    ContentResolver.AddPeriodicSync(AndroidApp.SyncAccount, AndroidApp.AUTHORITY, bund, 2);
    

    The 2 means 2 seconds, I believe. Google apparently didn't feel it was important to share the units of measure for the sync period in their documentation, but I'm pretty sure it's in seconds.

  • xceedxceed GBMember ✭✭

    Thanks @PeterDavis, so looking at the code @John submitted, he doesn't seem to make use of the AddPeriodicSync call anywhere, unless I'm missing something. In fact there is no call to InitSyncService either. I'll have another look through your code and try and work out what's going on.

    Cheers

  • PeterDavisPeterDavis USMember ✭✭✭

    @xceed In the sample @John did, he's using:

    ContentResolver.RequestSync(_account, AUTHORITY, settingsBundle);
    

    Which basically fires off the sync immediately, which was fine for the demonstration he was preparing. If the immediate sync works, then the timed sync should work. It's merely a different type of trigger.

  • xceedxceed GBMember ✭✭
    edited July 2014

    Is there somewhere in Settings on the device where you can disable the syncing process? Just thinking back to the Windows version of scheduled tasks and you can enable and disable them.

    Does the periodic sync run completely independently from the app itself?

    I'm still worried about concurrent database access, when the user is saving records in-app and then the background sync task runs simultaneously. I remember a previous post where you mentioned using the lock mechanism.

    The only other way I can think of preventing this issue is to disable syncing when app is in use, re-enable when app is closed/deactivated. That's the way I do it with Windows.

    All I need to run in the OnPerformSync method is a couple calls to Azure using data pulled from the SQLite DB.

    Is there a time limit on how long once instance of the sync method can run for?

    Are the StubProvider and Authenticator classes absolutely necessary, I don't really understand their purpose since the methods return null/zero or aren't implemented.

    Thanks

  • xceedxceed GBMember ✭✭
    edited July 2014

    Sorry to continue this thread, but I wondered if either @John or @PeterDavis ever came across the error:

    "Failed to find provider info for com.xamarin.myexample.provider".

    Everything is named accordingly in my android manifest file, my AUTHORITY is set correctly, accountType is set in authenticator, syncadapter etc.

    The "android:name" attribute for the provider as set in AndroidManifest, does this take the form of the package name then the class name or is it the application name then class name (as seen in @John‌ 's code)? Are namespaces used at all?

  • PeterDavisPeterDavis USMember ✭✭✭

    What is your AUTHORITY string and what is your ContentProvider attribute? Can you post your AndroidManifest.xml (the compiled one from the "obj[[Release/Debug]]\android\" folder) and your syncadapter.xml?

    Something is misnamed. I've run into that before, but I can't remember exactly what caused it (I ran into just about every SyncAdapter error there is in my time trying to get it working).

  • xceedxceed GBMember ✭✭

    Thanks @PeterDavis, I had to change the name attribute in my manifest file so it wasn't the package name followed by the provider class, but the namespace first. After changing that and also the service names in SyncService and AuthenticatorService it is now working :)

    Thanks so much for your help, and also @Paul for your sample code.

    I just have one more question (I promise). Is my understanding correct in that the service will fire every x seconds even if the app is closed? So the first time the app runs, the syncservice/account is setup for the first time, then for subsequent launches the OS will know the service already exists so doesn't try to create it again?

  • PeterDavisPeterDavis USMember ✭✭✭

    I believe it does if you've run the app. If you want, you can handle the BOOT_COMPLETED action with a BroadcastReceiver and start the service up there. See this.

  • HardyErlingerHardyErlinger DEMember
    edited August 2014

    Just a quick follow up for anyone stumbling across this thread: I have found that it isn't required to manually write anything into the manifest as long as you let Xamarin studio create the service names.

    My working service classes look like this:

    [Service]
    [IntentFilter(new[] { "android.accounts.AccountAuthenticator" })]
    [MetaData("android.accounts.AccountAuthenticator", Resource = "@xml/authenticator")]
    public class SyncAuthenticatorService : Service
    {
        ...
    }
    
    [Service(Exported = true)]
    [IntentFilter(new[] { "android.content.SyncAdapter" })]
    [MetaData("android.content.SyncAdapter", Resource = "@xml/syncadapter")]
    public class SyncService : Service
    {
        ...
    }
    

    Note that the Name property is missing from the Service attributes.

    The stub content provider has its attributes set as follows:

    [ContentProvider(new [] { "MY_CONTENT_AUTHORITY" }, Exported = true, Syncable = true)]
    public class SyncStubProvider : ContentProvider
    {
        ...
    }
    

    No Name property either. Replace MY_CONTENT_AUTHORITY with your actual content authority value and you should be good to go. No need to edit the manifest.

    When I tried to hardcode the content provider in the manifest as described earlier in this thread I was unable to find the proper value even though I tried all possible variations of package and namespace names I could think of. Omitting all service names from C# attributes and relying on Xamarin Studio's auto-generated values made the difference.

    Thanks to everyone who contributed to this, especially @PeterDavis and @John.

  • GerardoDuronGerardoDuron USMember ✭✭

    @PeterDavis said:
    Tried running it in the API 10 emulator. Didn't work their either.

    Hi, I made the demo work in a huawei just need to change in the compile tab and select configuration manager and select implement

  • rorndorffrorndorff Member

    Hello, I just wanted to drop by and thank everyone on this thread, particularly @PeterDavis . I spent days trying to figure out why my SyncAdapter wasn't working, when in fact all along it was working, but I was using the process property and none of my breakpoints were hitting: "5> Don't use the Process property of the SyncService's ServiceAttribute if you want breakpoints to work." I removed that attribute while debugging and everything was working all along!

    Thank you again!

  • SagarPanwalaSagarPanwala USMember ✭✭✭

    @PeterDavis : Can you please post working code which works fine in all Android versions and in which we don't have to go in Accounts and Sync it manually ?

  • Dev_93Dev_93 Member

    @PeterDavis @JohnMiller
    Thanks for your inputs, the "SyncAdapterExample" it's working as expected, Release mode it is working fine but when I run the app on the top existing app, I am getting this error.

    "Couldn't connect debugger. You can see more details in Xamarin Diagnostic output and the full exception on logs"

Sign In or Register to comment.