Chironex
Chironex

Reputation: 819

How can I bind my Activity to my currently running Service in Android?

I have an activity which I want to use to display data sent from my service. The activity updates a textview every time the service sends a message to the activity. The user is capable of starting and stopping the service using buttons from the activity (The service is started/stopped explicitly by the user). At the moment my activity binds to the service in onResume() and unbinds in onDestroy() which seems to be working for the moment.

The problem occurs when I start the service, press the back button, and then attempt to focus the activity again. I'm confronted with a new activity which is not being updated by the service, but appears to be able to send messages to the service (I've noticed this from Logs in my handlers [Boy that phrase made me laugh!]). I ideally want only one instance of my activity to exist and remain bound to the service, but I definitely don't want to see an activity not keeping up with updates. I've updated my manifest with <activity android:launchMode="singleTop">, but I'm not sure that helps the matter much. What can I do to ensure my activity rebinds to the existing instance of the service when it's later resumed? (I want to achieve this with message passing, I don't want to implement a database or use sharedprefereces for this bit). I'm including my code below- I'm relatively new to services, so go easy on me. Cheers.

Optional Extra: I am aiming towards a service which runs continuously from startService(), and where refocussing the activity is optional, but will immediately show the latest information from the service. I believe what I'm looking for is a foreground service. This is not part of the question, but please let me know if you think your answer may prevent me from achieving this.

MainActivity.java:

public class MainActivity extends Activity 
{
    private static final String debug = "MainActivity";
    private boolean bound = false;
    TextView mTextView;
    protected Messenger outMessenger;

    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i(debug, "onCreate called");
        mTextView = (TextView) findViewById(R.id.textView1);

        Button bind = (Button) findViewById(R.id.bind);
        Button unbind = (Button) findViewById(R.id.unbind);

        bind.setOnClickListener(new OnClickListener()
        {
            public void onClick(View v) 
            {
                Message message = new Message();
                Bundle bundle = new Bundle();
                bundle.putString("ACTION", "START");
                message.setData(bundle);
                try
                {
                    outMessenger.send(message);
                } catch (RemoteException e)
                {
                    Log.d(debug, "She can't reach the service captain!");
                }
            }
        });

        unbind.setOnClickListener(new OnClickListener()
        {
            public void onClick(View v) 
            {
                Message message = new Message();
                Bundle bundle = new Bundle();
                bundle.putString("ACTION", "STOP");
                message.setData(bundle);
                try
                {
                    outMessenger.send(message);
                } catch (RemoteException e)
                {
                    Log.d(debug, "She can't reach the service captain!");
                }
            }
        });
    }

    @Override 
    public void onPause()
    {
        super.onPause();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        doBindService();
    }

    public void onDestroy()
    {
        super.onDestroy();
        Log.i(debug, "onDestroy Called");
        doUnbindService();
    }

    private void doBindService()
    {
        Log.i(debug, "Attempting to bind");
        bound = true;
        Intent i = new Intent(this, MyService.class);
        i.putExtra("MESSENGER", new Messenger(mHandler));
        bindService(i, mConnection, Context.BIND_AUTO_CREATE);
    }

    private void doUnbindService()
    {
        Log.i(debug, "Attempting to unbind");
        if (bound)
        {
            bound = false;
            unbindService(mConnection);
        }
    }

    private Handler mHandler = new Handler()
    {
        public void handleMessage(Message message)
        {
            Log.d(debug, "Got a message!");
            Bundle data = message.getData();
            if (data != null)
            {
                Log.i(debug, "This data ain't null!");
                if (data.containsKey("MESSAGE"))
                {
                    mTextView.setText(data.getString("MESSAGE"));
                }
            }
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() 
    {
        public void onServiceConnected(ComponentName className, IBinder binder) 
        {
            Log.i(debug, "Service connected!");
            outMessenger = new Messenger(binder);
        }

        @Override
        public void onServiceDisconnected(ComponentName className) 
        {
            Log.d(debug, "Service Disconnected!");
        }
    };
}

MyService.java

public class MyService extends Service
{
    private static final String debug = "MyService";
    private Messenger inMessenger = new Messenger(new IncomingHandler());
    Messenger outMessenger;
    private boolean bound = false;
    int secret = 0;

    @Override
    public void onCreate()
    {
        Log.d(debug , "MyService Created");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
    {
        Log.d(debug , "onStartCommand called");
        handleCommand(intent);
        return START_STICKY;
    }

    Handler handler = new Handler();
    public boolean running;
    void handleCommand(Intent i)
    {
        Runnable r = new Runnable()
        {
            public void run()
            {
                updateResultsInUi();
                if (running)
                {
                    handler.postDelayed(this, 500);
                }
            }
        };
        r.run();
    }

    void updateResultsInUi()
    {
        Bundle bundle = new Bundle();
        bundle.putString("MESSAGE", 
                Integer.toString(secret = ++secret % 10));
        Message message = new Message();
        message.setData(bundle);

        if (bound)
        {
            try
            {
                outMessenger.send(message);
            } 
            catch (RemoteException e)
            {
                Log.d(debug, "Messaging failed");
            }
        } 
        else 
        {
            Log.d(debug, "Not connected, so not messaging!");
        }

    }

    @Override
    public IBinder onBind(Intent intent)
    {
        Log.i(debug, "Bound");
        bound = true;
        Bundle extras = intent.getExtras();
        outMessenger = (Messenger) extras.get("MESSENGER");
        return inMessenger.getBinder();
    }

    @Override
    public void onRebind(Intent i)
    {
        bound = true;
        Bundle extras = i.getExtras();
        outMessenger = (Messenger) extras.get("MESSENGER");
    }

    @Override
    public boolean onUnbind(Intent i)
    {
        Log.d(debug, "onUnbind() called.");
        bound = false;
        outMessenger = null;
        return true;
    }

    class IncomingHandler extends Handler 
    {
        @Override
        public void handleMessage(Message msg) 
        {
            Log.d(debug, "Got message");
            Bundle data = msg.getData();
            if (data.containsKey("ACTION"))
            {
                if ("START".equals(data.getString("ACTION")))
                {
                    if (!running)
                    {
                        running = true;
                        Intent i = new Intent(getApplicationContext(), MyService.class);
                        startService(i);
                    } 
                    else 
                    {
                        Log.d(debug, "We're already up and running!");
                    }
                } 
                else if ("STOP".equals(data.getString("ACTION")))
                {
                    running = false;
                    Intent i = new Intent(getApplicationContext(), MyService.class);
                    stopService(i);
                }
            }
        }
    }
}

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tests.servicetest"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.tests.servicetest.MainActivity"
            android:label="@string/title_activity_main" 
            android:launchMode="singleTop" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name="com.tests.servicetest.MyService"
            android:process=":inaneService">
        </service>
    </application>
</manifest>

And for those of you who want to run it, activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:padding="@dimen/padding_medium"
        android:text="@string/hello_world"
        tools:context=".MainActivity" />

    <Button
        android:id="@+id/bind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView1"
        android:layout_below="@+id/textView1"
        android:text="Start" />

    <Button
        android:id="@+id/unbind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView1"
        android:layout_below="@+id/bind"
        android:text="Stop" />

</RelativeLayout>

Upvotes: 3

Views: 7002

Answers (1)

Jonas Gr&#246;ger
Jonas Gr&#246;ger

Reputation: 1628

You might want to have a look at this: https://github.com/JonasGroeger/GPSService

Upvotes: 4

Related Questions