views:

84

answers:

1

Based on a suggestion in a previous question I asked on here, I'm trying to push my socket connection for an application that I've written into a service. I spent the better part of the day yesterday researching services and actually mocked up a few (one remote, one local).

My question is in two parts:

1) after having played with both a local service and a remote service, I'm still not sure as to which one would be best for my situation. This is due in large part to the fact that I guess I still don't quite understand what advantages running in another 'process' is going to give me. I'm spawning a new thread for the socket connection no matter what so I won't have any thread contention with the UI. So what does putting the service in another process enable me to do? Will I potentially see better performance that way? My limited understanding is that by putting it in a different process, the service will run independently of whatever activity I have running on my app. I do have a few different activities, but only one of them requires the socket connection which I will rebuild everytime that activity is opened anyway. So would a local service be the way to go for me?

2) I'm going to have my socket "listener" (DataInputStream().readLine() inside a while loop) inside my service for any new data that gets passed down from the server. After the playing I did yesterday, I could not figure out how to pass the data that it reads to the actual "client" (either bound client by remote service, or local client itself) in "realtime".

Would greatly appreciate some suggestions for part 1, and some help with part 2 (code examples? :))

TIA

Edit: added code of my service - going with local service

Service Class:

   public class SocketService extends Service {

    Socket s;
    PrintStream os;

    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return myBinder;
    }

    private final IBinder myBinder = new LocalBinder();

    public class LocalBinder extends Binder {
        public SocketService getService() {
            return SocketService.this;
        }
    }


    @Override
    public void onCreate() {
        super.onCreate();
        s = new Socket();
    }

    public void IsBoundable(){
        Toast.makeText(this,"I bind like butter", Toast.LENGTH_LONG).show();
    }

    public void onStart(Intent intent, int startId){
        super.onStart(intent, startId);
        Toast.makeText(this,"Service created ...", Toast.LENGTH_LONG).show();
        Runnable connect = new connectSocket();
        new Thread(connect).start();
    }

    class connectSocket implements Runnable {

        @Override
        public void run() {
            SocketAddress socketAddress = new InetSocketAddress("192.168.1.104", 4505);
            try {               
                s.connect(socketAddress);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        try {
            s.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        s = null;
    }
}

Activity that calls service:

public class SocketServiceController extends Activity {

        private SocketService mBoundService;
        private Boolean mIsBound;
        public SocketServiceController ssc;
       @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ssc = this;
            setContentView(R.layout.main);

            Button start = (Button)findViewById(R.id.serviceButton);
            Button stop = (Button)findViewById(R.id.cancelButton);

            start.setOnClickListener(startListener);
            stop.setOnClickListener(stopListener);

       }

       private ServiceConnection mConnection = new ServiceConnection() {
            public void onServiceConnected(ComponentName className, IBinder service) {
                mBoundService = ((SocketService.LocalBinder)service).getService();

            }
            public void onServiceDisconnected(ComponentName className) {
                mBoundService = null;
            }
        };

        private void doBindService() {
            bindService(new Intent(SocketServiceController.this, SocketService.class), mConnection, Context.BIND_AUTO_CREATE);
            mIsBound = true;
            mBoundService.IsBoundable();
        }


        private void doUnbindService() {
            if (mIsBound) {
                // Detach our existing connection.
                unbindService(mConnection);
                mIsBound = false;
            }
        }

        @Override
        protected void onDestroy() {
            super.onDestroy();
            doUnbindService();
        }


       private OnClickListener startListener = new OnClickListener() {
        public void onClick(View v){
            startService(new Intent(SocketServiceController.this,SocketService.class));
            doBindService(); 

        }               
       };

       private OnClickListener stopListener = new OnClickListener() {
            public void onClick(View v){
                stopService(new Intent(SocketServiceController.this,SocketService.class));
            }               
          };
}
A: 

This is due in large part to the fact that I guess I still don't quite understand what advantages running in another 'process' is going to give me.

Generally, none. You create a remote service if you are expecting other applications to communicate with the service. If it will only be used by your own application, use a local service.

Also, a remote service has nothing to do with creating a separate process within your application.

Will I potentially see better performance that way?

You will see worse performance that way, due to extra memory consumption.

My limited understanding is that by putting it in a different process, the service will run independently of whatever activity I have running on my app.

Services have a lifecycle independent from activities regardless of whether it is local or remote.

So would a local service be the way to go for me?

Sounds likely.

After the playing I did yesterday, I could not figure out how to pass the data that it reads to the actual "client" (either bound client by remote service, or local client itself) in "realtime".

Use the local binding pattern, and have the activity call an API on the service to register (and unregister) an event listener. Have the service pass the data to the activity via the listener.

CommonsWare
@CommonsWare - Thank you so much for the very considered response. I have to admit, I'm a bit lost by your last section (adding the listener). Would the listener go on the client or within the service itself. I've posted some code in my OP of how my service looks (I will add the threading later...just wanted a POC for you to see). I generally like to figure out code things myself, but any examples you can provide or point me to would be wonderful with regards to setting up the listener. Thanks again.
Kyle
@Kyle: "Would the listener go on the client or within the service itself" -- the client would create the listener and pass it to the service, using some common Java interface or something. However, this only works if your client is binding to the service (e.g., `bindService()` and `onBind()`), which is not how your service is presently structured.
CommonsWare
@CommonsWare Thanks again for the response. I thought I had that nailed down after my toying around yesterday...but apparently not. I've updated my OP again with code from the service class as well as code from the activity that calls the service. I'm trying to use the instance "mBoundService" to call a method on the service "IsBoundable" which should just toast something to me. I figure this is the way I"d need to pass the listener to the service, however, I get a force close when I call that method. If I don't call it, it appears to be binding correctly as best I can tell. Any ideas?
Kyle
I'm still stuck here. I read your feedback here: http://stackoverflow.com/questions/2440548/how-to-stop-an-android-service-only-when-there-are-no-other-activities-in-my-app to a similar issue and you say that the bindService() method should start my service with the BIND_AUTO_CREATE flag set. It's not starting my service. I realize that's sort of off topic with regards to my original question, but it makes me wonder if my mBoundService is actually getting bound. I know that the service isn't starting because it's not connecting to my socket server whereas when I use startservice() it does.
Kyle
@Kyle: Here is an example of a project using a local service and the local binding pattern: http://github.com/commonsguy/cw-android/tree/master/Service/WeatherPlus/
CommonsWare
Thanks for that. I was doing essentially the same thing as he/she was and my mBoundService was not working. When I set the bindService() method call into my onCreate() method of the activity, it fixed it...why would that not work on the button click? I'll try working on the listener now and report back with any issues I have. Thanks again.
Kyle
@Kyle: FWIW, `bindService()` is asynchronous. You will not have a bound connection until `onServiceConnected()` is called on your `ServiceConnection`.
CommonsWare
right, and that was being called, I was toasting inside the onServiceConnected() and verifying that my mBoundService was no longer null...it wasn't. Is the reason that the button click wasn't doing the job is that bindService() is asynchronous? Why would it then work in the onCreate() of the activity? Sorry for my newbness...just tryign to wrap my head around all of this.
Kyle
ok I'm nearing the home stretch here. Thanks again for all your help. I assume by 'listener' you just mean an object that both the client and service share. I created such an object that has one method in it to update a textview on the client. If I call that method from the client it works fine, however, when I pass in the instance of the object to the service and try calling that method from the service, I get a force close? Did I mis-understand your reference to "listener".
Kyle
@Kyle: "I assume by 'listener' you just mean an object that both the client and service share." -- I mean a callback object, akin to `OnClickListener`. "I get a force close" -- use `adb logcat`, DDMS, or the DDMS perspective in Eclipse to examine LogCat and look at the stack trace associated with your "force close".
CommonsWare
I'm there! Thanks so much again for all your advice/assistance!!!
Kyle
I know I said I"m done with this....and I am (got it working), but I still would like to know why the bindService() method doesn't work inside an onclick of a button...does that have to do with the asynchronous thign?
Kyle