Communication with Services

Illya Rodin

 

"A Service is an application component that can perform long-running operations in the background and does not provide a user interface."

Tools for C&C:

  • Binder
  • HaMeR
  • AIDL
  • PendingIntent
  • BroadcastReceiver
  • ResultReceiver
  • Event Bus (EventBus, Otto, e.t.c)
  • LocalBroadcastReceiver

Binder

"This is the preferred technique when your service is merely a background worker for your own application." 
public class BoundService extends Service {

   public class LocalBinder extends Binder{

        public int getA(){
            return -1;
        };


        public void setB(int b){
           //---TODO something ---
        }

   }


    @Override
    public IBinder onBind(Intent intent) {
        return new LocalBinder();
    }


}
<?xml version="1.0" encoding="utf-8"?>
<manifest  ... >

  <application ... > 

        <service
            android:name=".BoundService"/>

    </application>

</manifest>
public class MainActivity extends AppCompatActivity {

   private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinder = (BoundService.LocalBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
           mBinder = null;
        }
    };


    //TODO something----


    @Override
    protected void onStop() {
        super.onStop();
        unbindService(mServiceConnection);

    }


    @Override
    protected void onStart() {
        super.onStart();
        bindService(new Intent(this, BoundService.class), mServiceConnection
                , Service.BIND_AUTO_CREATE);

    }
}

Pros:

  • simple implementation
  • does not require of extra-efforts for IPC

Cons:

  • not usable for IPC
  • require more complicated mechanisms for control

Handlers

Messangers

Runnables

Programming Mobile Services for Android Handheld Systems: Concurrency

https://www.coursera.org/learn/posaconcurrency

"This is the simplest way to perform interprocess communication (IPC), because the Messenger queues all requests into a single thread so that you don't have to design your service to be thread-safe."

public class SimpleService extends Service {

    private Messenger mServiceMessnger;

    private HandlerThread mThread;

    @Override
    public IBinder onBind(Intent intent) {
        return mServiceMessnger.getBinder();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mThread = new HandlerThread("BackgroundThread");
        mThread.start();
        Handler backgroundHandler = 
                       new Handler(mThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                Message newMessage = Message.obtain();
                //TODO something
                try {
                    msg.replyTo.send(newMessage);
                } catch (RemoteException e) {
                    Log.e(TAG,null,e);
                }
            }
        };
        mServiceMessnger = new Messenger(backgroundHandler);
    }

    @Override
    public boolean onUnbind(Intent intent) {
        mThread.quit();
        return super.onUnbind(intent);
    }

}
<?xml version="1.0" encoding="utf-8"?>
<manifest  ... >

  <application ... > 

        <service
            android:name=".SimpleService" />

        //-----OR-----------

         <service
            android:process=":background" 
            android:name=".SimpleService" />


    </application>

</manifest>
public class MainActivity extends Activity {

    private Messenger mServiceMessenger;

    private Messenger mClientMessenger = new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg) {
           //TODO something
        }
    });

   private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mServiceMessenger = new Messenger(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mServiceMessenger = null;
        }
    };

    public void onClick(View view){
        Message message = Message.obtain();
        message.replyTo = mClientMessenger;
     
        message.setData(mBundle); //  OR message.obj = mBundle;

        try {
            mServiceMessenger.send(message);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent();
        intent.setPackage(getApplicationContext().getPackageName());
        intent.setClass(this, SimpleService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(mConnection);
    }

}

Pros:

  • simple implementation
  • support of IPC

Cons:

  • data type limitation in case of IPC (only Parcelable)
  • required additional code for addressing/processing messages

AIDL

 "Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service."

Data Types:​

- Java Primitives (intlongcharboolean, e.t.c)

- String, CharSequence,List,Map

- Parcelable

ParceExample.aidl

package com.bionic.model;

parcelable ParceExample;


ParceExample.java

import android.os.Parcel;
import android.os.Parcelable;

public final class ParceExample implements Parcelable {

    //TODO parcelable implementation

}

- Interface Reference

ParceRefExample.aidl

package com.bionic.model;

import com.bionic.model.ParceExample;

interface ParceRefExample {

    void send(in ParceExample obj);

}

Direction:​

- input  (in, out, inout)

- return (oneway)


    void copyArray(in byte[] source, out byte[] dest);


    oneway void copyArray(in byte[] source, out byte[] dest);

package com.bionic.model;

interface IAidlService {

    int add(in int value1, in int value2);

    oneway  void increase(in int value);

}

public class AidlService extends Service {
  
  @Override
  public IBinder onBind(Intent intent) {

    return new IAidlService.Stub() {

      public int add(int value1, int value2) throws RemoteException {
        return value1 + value2;
      }

      public void increase(int value) throws RemoteException {
        //TODO something
      }

    };
  }

}
package com.bionic.model;

interface IAidlService {

    int add(in int value1, in int value2);

    oneway  void increase(in int value);

}

public class AidlService extends Service {
  
  @Override
  public IBinder onBind(Intent intent) {

    return new IAidlService.Stub() {

      public int add(int value1, int value2) throws RemoteException {
        return value1 + value2;
      }

      public void increase(int value) throws RemoteException {
        //TODO something
      }

    };
  }

}
public class BindingActivity extends Activity {
    
IAidlService mService ;

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(this, 
              LocalService.class);
        bindService(intent, mConnection,
              Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(mConnection);
        mBound = false;
    }

    private ServiceConnection mServiceConnection = 
         new ServiceConnection() {

	@Override
	public void onServiceDisconnected(ComponentName name) {
	 mService = null;
	}

	@Override
	public void onServiceConnected(ComponentName name,
                       IBinder service)
	{
	   mService = IAidlService.Stub
                      .asInterface((IBinder)service);
	}
   };
   //---------------------------------------------------
    {
    int result = mService.add(2,2);
    }
}

Pros:

  • more "clean" interface for IPC
  • support async/sync modes

Cons:

  • data type limitation in case of IPC
  • require additional resources for marshalling/unmarshalling

PendingIntent

public class MainActivity extends Activity {

  
  public void onClickStart(View v) {
    PendingIntent pIntent = createPendingResult(REQUEST_CODE, null, 0);
    Intent intent = new Intent(this, PiService.class);
    intent.putExtra(PARAM_PINTENT, pIntent);
    //TODO add extra params to intent
    startService(intent);
  }


  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode  == REQUEST_CODE){
       //TODO  results processing
    }
  }

}

//----------------------------------------------------------------------------------------

public class PiService extends Service {

 public int onStartCommand(Intent intent, int flags, int startId) {
      final PendingIntent pIntent = intent.getParcelableExtra(MainActivity.PARAM_PINTENT);
     //TODO start new thread
     { 
        public void run(){
             pIntent.send(MainActivity.STATUS_START);
              // ---- or -----------
             Intent resultIntent = new Intent().putExtra(MainActivity.PARAM_RESULT, 9);
             pIntent.send(PiService.this, MainActivity.STATUS_FINISH, resultIntent);
             stopSelf();
        }
     }
     //------------------------
     return super.onStartCommand(intent, flags, startId);
 }

}

BroadcastReceiver

public class MainActivity extends Activity {

    public final static String BROADCAST_ACTION = "com.bionic.broadcast.ACTION";

    private BroadcastReceiver mReceiver =  BroadcastReceiver() {
     
    public void onReceive(Context context, Intent intent) {
           //TOD  broadcast processing
    }

    //TODO add subscribe/unsubscribe from broadcast

}

public class BroadcastService extends Service {

 public int onStartCommand(Intent intent, int flags, int startId) {
     //TODO start new thread
     { 
        public void run(){
             Intent intent = new Intent(MainActivity.BROADCAST_ACTION);
             //TODO fill data into intent
             sendBroadcast(intent);
             stopSelf();
        }
     }
     //------------------------
     return super.onStartCommand(intent, flags, startId);
 }

}

ResultReceiver

public class MainActivity extends Activity {

     public ResultReceiver mReceiver;

     @Override
     public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mReceiver = new ResultReceiver(new Handler()){
            
           @Override
           protected void onReceiveResult(int resultCode, Bundle resultData) {
              //TODO result processing
           }
           
        };
        Intent intent = new Intent(this, MyIntentService.class);
        intent.putExtra(PARAM_RECEIVER, mReceiver);
         //TODO fill data into intent
        startService(intent);
        
     }
}

//-------------------------------------------------------------------------------------

public class MyIntentService extends IntentService {

    @Override
    protected void onHandleIntent(Intent arg0) {
        final ResultReceiver receiver = arg0.getParcelableExtra(PARAM_RECEIVER);
        //TODO  request processing
        Bundle bundle = new Bundle();
        //TODO fill data into bundle
        receiver.send(STATUS_RESULT, bundle);
    }

}

Event Bus (e.g. EventBus :-) ) 

  • simplifies the communication between components
    • decouples event senders and receivers
    • performs well with Activities, Fragments, and background threads
    • avoids complex and error-prone dependencies and life cycle issues
  • makes your code simpler
  • is fast
  • is tiny (<50k jar)
  • is proven in practice by apps with 100,000,000+ installs
  • has advanced features like delivery threads, subscriber priorities, etc.

public class MainActivity extends Activity {

    private EventBus mBus = EventBus.getDefault();
     @Override
     public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBus.register(this);
     }

    @Override
    protected void onDestroy() {
        mBus.unregister(this);
        super.onDestroy();
    }

    public void onEvent(Event event){
    }
    
}

//-------------------------------------------------------------------------------------

public class MyIntentService extends IntentService {

    
    private EventBus mBus = EventBus.getDefault(); 


    @Override
    protected void onHandleIntent(Intent arg0) {
         //TODO result processing
         Event event = new Event();
         mBus.post(event);
    }

}

public class EventBus {

    /** Posts the given event to the event bus. */
    public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }

    //----------------------------------------------

    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (eventInheritance) {
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                Log.d(TAG, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

    //To be continued
}

Pros:

  • very simple in implementation
  • a lot of possible scenarios for cooperation

Cons:

  • no support of IPC
  • provoke to apply "non-architectural" solutions

LocalBroadcastManager

Helper to register for and send broadcasts of Intents to local objects within your process. This is has a number of advantages over sending global broadcasts with sendBroadcast(Intent):

  • You know that the data you are broadcasting won't leave your app, so don't need to worry about leaking private data.
  • It is not possible for other applications to send these broadcasts to your app, so you don't need to worry about having security holes they can exploit.
  • It is more efficient than sending a global broadcast through the system.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
 LocalBroadcastManager.getInstance(this).registerReceiver(
                mReceiver,
                mIntentFilter);
 LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);

Pros:

  • possible to reuse code for processing "non-local" broadcasts
  • fastest event bus ever (maybe :-) )

Cons:

  • no support of IPC
  • required additional code

Q&A

Thank you for attention!

rodin.illya@gmail.com

https://ua.linkedin.com/in/illya-rodin-54a37419

Made with Slides.com