1

I'm writing my first Android app and it's supposed to count steps (via accelerometer, source code from http://www.gadgetsaint.com/android/create-pedometer-step-counter-android/)

So what do I have for now:

  1. MainActivity with three TextView fields (steps, bonuses, speed).
  2. Service where I detect steps and calculate distanse, bonus and speed.

This service is a Bound Service and it consist of three classes: MyService, MyServiceBinder and MyServiceConnection. I've done it as explained in Xamarin.Android documentation and example: https://github.com/xamarin/monodroid-samples/tree/master/ApplicationFundamentals/ServiceSamples/BoundServiceDemo.

The point is every time I have a step, I want to show this on MainActivity (update those textviews) in real time. And I don't understand how to do this in a proper way. Events? Broadcast reciever? I get messed with binding/connection, it's a new thing to me. Please help me to get it work! Any examples (Java is ok) would be very helpful.

Here is the source code (I've cleaned it up from extra code).

MainActivity.cs

public class MainActivity : Activity
{
    //BOUND SERVICE: STEPSERVICE 
    MyServiceConnection myServiceConnection;

    //UI
    internal TextView textBonus, textSteps, textSpeed;

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        SetContentView(Resource.Layout.Main);

        textBonus = FindViewById<TextView>(Resource.Id.textViewBonus);
        textStep = FindViewById<TextView>(Resource.Id.textViewDistance);
        textSpeed = FindViewById<TextView>(Resource.Id.textViewSpeed);
    }

    protected override void OnStart()
    {
        base.OnStart();
        if (myServiceConnection == null)
        {
            myServiceConnection = new MyServiceConnection(this);
        }
        DoBindMyService();
    }

    private void DoBindMyService()
    {
        Intent serviceIntent = new Intent(this, typeof(MyService));
        BindService(serviceIntent, myServiceConnection, Bind.AutoCreate);
    }


    protected override void OnDestroy()
    {
        //stop services!
        base.OnDestroy();
    }

    private void UpdateUI()
    {
        RunOnUiThread(() =>
        {
            textBonus.Text = myServiceConnection.Binder.Service.Bonus;              
            textSteps.Text = myServiceConnection.Binder.Service.Steps;
            textSpeed.Text = myServiceConnection.Binder.Service.Speed;
        });
    }
}

MyService.cs

[Service]
public class MyService : Service, IStepListener, ISensorEventListener
{
    //counters
    private int bonus;
    public int Bonus
    {
        get { return bonus; }
        set { bonus = value; }
    }

    private int steps = 0;
    public int StepsT
    {
        get { return steps; }
        set
        {
            steps = value;
        }
    }

    private float speed = 0.0F;
    public float Speed
    {
        get { return speed; }
        set
        {
            speed = value;
        }
    }

    public IBinder Binder { get; private set; }

    public override IBinder OnBind(Intent intent)
    {
        this.Binder = new MyServiceBinder(this);
        return this.Binder;
    }

    public override void OnCreate()
    {
        base.OnCreate();
    }

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

    public void Step(long timeNs)
    {
        Steps++;
        Bonus++;
        Speed++;
    }

    public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy)
    {
        // We don't want to do anything here.
    }

    public void OnSensorChanged(SensorEvent e)
    {
        simpleStepDetector.UpdateAccel(e.Timestamp, e.Values[0], e.Values[1], e.Values[2]);
    }
}

MyServiceConnection.cs

public class MyServiceConnection : Java.Lang.Object, IServiceConnection
{
    MainActivity mainActivity;

    public MyServiceConnection(MainActivity activity)
    {
        IsConnected = false;
        Binder = null;
        mainActivity = activity;
    }

    public bool IsConnected { get; private set; }
    public MyServiceBinder Binder { get; private set; }

    public void OnServiceConnected(ComponentName name, IBinder service)
    {
        Binder = service as MyServiceBinder;
        IsConnected = this.Binder != null;

        string message = "onSecondServiceConnected - ";

        if (IsConnected)
        {
            message = message + " bound to service " + name.ClassName;
        }
        else
        {
            message = message + " not bound to service " + name.ClassName;
        }

        mainActivity.textSpeed.Text = message;
    }

    public void OnServiceDisconnected(ComponentName name)
    {
        IsConnected = false;
        Binder = null;
    }
}

MyServiceBinder.cs

public class MyServiceBinder : Binder
{
    public MyServiceBinder(MyService service)
    {
        this.Service = service;
    }

    public MyService Service { get; private set; }
}

PS. This is my first question here. Excuse me if I've made something wrong.

  • In `OnServiceConnected` method, you can get `MyService` instance by `Binder.Service`, and then you can get the `steps`, `bonuses`, `speed` fields which have been calculated by your `MyService`. – Robbit Dec 13 '17 at 02:46
  • Use [Messenger](https://developer.android.com/guide/components/bound-services.html#Messenger). – Robbit Dec 13 '17 at 05:41
  • Or you can use interface, I think interface is the best way. The `Messenger` is a little complex even though it can be worked with `BindService`. So I suggest you use `StartService` with interface, it is easy to update your UI. – Robbit Dec 13 '17 at 08:28
  • Joe, thanks for your replies! Though I din't get it about `StartService` with interface - what's that? Any example? –  Dec 13 '17 at 09:03
  • Sorry, I mean that you can use `StartService`, and in the service, you can use interface to communicate with activity. – Robbit Dec 13 '17 at 09:19

1 Answers1

0

There are three ways to complete it.

  • Broadcast
  • Interface
  • BindService

BindService

  • In your MyServiceConnection add this:

    public MyService myService;
    

    and in your OnServiceConnected method add this under Binder = service as MyServiceBinder;:

    myService=Binder.Service;
    myService.SetActivity(mainActivity);
    
  • In your MyService class add this (so you can get MainActivity instance in your service):

    MainActivity mainActivity;
    public void SetActivity(MainActivity activity) {
    this.mainActivity = activity;
    }
    

    and add mainActivity.UpdateUI(); in your Step(long timeNs) method, so you can update your UI.

  • In your MainActivity replace private void UpdateUI() with public void UpdateUI().


Interface

  • Add IUpdate interface

    public interface IUpdate
    {
        void Update();
    }
    
  • In your MyService add this:

    IUpdate update;
    public void SetIUpdate(IUpdate update) {
        this.update = update;
    }
    

    and add this.update.Update(); in your Step(long timeNs) method.

  • Implement the IUpdate interface in your MyServiceConnection class, so it will like this:

    public class MyServiceConnection : Java.Lang.Object, IServiceConnection,IUpdate
    {
         MainActivity mainAtivity;
    
        public MyServiceConnection(MainActivity activity)
    {
        IsConnected = false;
        Binder = null;
        mainActivity = activity;
    }
    
    public bool IsConnected { get; private set; }
    public MyServiceBinder Binder { get; private set; }
    public MyService myService;
    
    public void OnServiceConnected(ComponentName name, IBinder service)
    {
        Binder = service as MyServiceBinder;
        myService=Binder.Service;
        myService.SetIUpdate(this);
        //myService.SetActivity(mainActivity);
        IsConnected = this.Binder != null;
    
        string message = "onSecondServiceConnected - ";
    
        if (IsConnected)
        {
            message = message + " bound to service " + name.ClassName;
        }
        else
        {
            message = message + " not bound to service " + name.ClassName;
        }
    
        mainActivity.textSpeed.Text = message;
    }
    
    public void OnServiceDisconnected(ComponentName name)
    {
        IsConnected = false;
        Binder = null;
    }
    
    public void Update()
    {
        mainActivity.UpdateUI();
    }
    }
    

Broadcast

This is what you have already known.

Robbit
  • 4,300
  • 1
  • 13
  • 29
  • Joe, you're amazing! Thank you very much for code examples. I'll try both methods and leave a comment about the result. –  Dec 15 '17 at 10:06
  • So, as I've promised I've tried your suggestions and decided to use an interface. Everything works fine and thank you. –  Jan 10 '18 at 11:54
  • Good luck, I am always here. – Robbit Jan 10 '18 at 12:03