+ All Categories
Home > Documents > Ipc: aidl sexy, not a curse

Ipc: aidl sexy, not a curse

Date post: 16-Apr-2017
Category:
Upload: yonatan-levin
View: 213 times
Download: 4 times
Share this document with a friend
55
Build a sustainable app with an IPC mechanism IPC: AIDL is Sexy, not a curse
Transcript
Page 1: Ipc: aidl sexy, not a curse

Build a sustainable app with an IPC mechanism

IPC: AIDL is Sexy, not a curse

Page 2: Ipc: aidl sexy, not a curse

Yonatan Levin

Page 3: Ipc: aidl sexy, not a curse

> 10M users

Ruby, Golang, Python, Android,

iOS

52 cities

Page 4: Ipc: aidl sexy, not a curse

> 1000 members

Largest StudyJam

in the World

AndroidAcademy

Page 5: Ipc: aidl sexy, not a curse

What are we going to do?

Page 6: Ipc: aidl sexy, not a curse

Learn how to talk

➔ Became best fellows with IPC➔ Best friends with Binder➔ Fall in love with AIDL

Page 7: Ipc: aidl sexy, not a curse

Goal:

Make our app run smooth in the background while performing multiple long tasks (like

playing music) unrelated to the UI

Page 8: Ipc: aidl sexy, not a curse
Page 9: Ipc: aidl sexy, not a curse

DarthVaderActivity

@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.darth_vader_activity); findViewById(R.id.iv_dva_build_death_star).setOnClickListener(this); findViewById(R.id.iv_dva_interact_with_luke).setOnClickListener(this);}

Page 10: Ipc: aidl sexy, not a curse

EmpireService<service android:name=".EmpireService" android:enabled="true" android:process=":remote"></service>

Page 11: Ipc: aidl sexy, not a curse

Why separate process?● GC not affecting your app

● Crashing not affecting UI

● Less chance to be killed:

○Process serving others has at least the ranking of those it serve

○Process with Service > Process with Background activities

● New Heap Space

● Smaller cold start + resource efficiency

● Because we can! :)

Page 12: Ipc: aidl sexy, not a curse

Why and when not?●If you don’t have driver licenses●Memory leaks●Two process●Keep it simple, stupid

Page 13: Ipc: aidl sexy, not a curse

https://github.com/parahall/AIDLServiceExample.git

Try this at home

Page 14: Ipc: aidl sexy, not a curse

IPC?

Inter-process communication (IPC) is a framework for the exchange of signals and data across multiple processes

Page 15: Ipc: aidl sexy, not a curse

Linux legacy

Page 16: Ipc: aidl sexy, not a curse

Android

Page 17: Ipc: aidl sexy, not a curse

Why Binder?

Performance

Security Stability Memory

Page 18: Ipc: aidl sexy, not a curse

Benefit for free●Thread migration●Identifying senders to receivers●Unique object-mapping across process●AIDL!!!!●Bult-in support for marshalling●Local execution mode (no IPC) if same process

Page 19: Ipc: aidl sexy, not a curse

We are already using binder today

●LifeCycle callbacks (onResume, OnDestroy) invoked by ActivityManagerService

●Intents

●Content Provider

Page 20: Ipc: aidl sexy, not a curse

1

IPC using Intentgit checkout IPCwithIntent

Page 21: Ipc: aidl sexy, not a curse

DarthVaderActivity@Overridepublic void onClick(View v) { Intent intent = new Intent(this, EmpireService.class); switch (v.getId()) { case R.id.iv_dva_build_death_star: intent.putExtra(COMMAND_TYPE, EmpireService.EmpireServiceCommands.BUILD_DEATH_STAR); break; case R.id.iv_dva_interact_with_luke: intent.putExtra(COMMAND_TYPE, EmpireService.EmpireServiceCommands.FIND_LUKE); break; } mStartedCommandTime = System.currentTimeMillis(); startService(intent);}

Page 22: Ipc: aidl sexy, not a curse

DarthVaderActivity

public class EmpireServiceReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) {

Log.d(TAG,"Time took: " +String.valueOf(System.currentTimeMillis()- mStartedCommandTime));

Toast.makeText(DarthVaderActivity.this, "Death Star deployed and ready for your command, my lord", Toast.LENGTH_LONG).show(); }}

Page 23: Ipc: aidl sexy, not a curse

EmpireServicepublic int onStartCommand(Intent intent, int flags, int startId) { EmpireServiceCommands command = (EmpireServiceCommands) intent.getExtras() .get(COMMAND_TYPE);

switch (command) { case BUILD_DEATH_STAR:

..do some hard work... Intent jobDoneIntent = new Intent(EMPIRE_SERVICE_ACTION); jobDoneIntent.putExtra("result", new DeathStar(270000, 270000, "THIS IS THE BIG GUN")); sendBroadcast(jobDoneIntent); break; case FIND_LUKE: break; } stopSelf(); return START_NOT_STICKY;}

Page 24: Ipc: aidl sexy, not a curse

Pain?

Not really OOPAsync onlyLoosely-definedNot so fastHigh Level Abstraction of Binder

Page 25: Ipc: aidl sexy, not a curse

2

IPC using Messengergit checkout IPCwithMessenger

Page 26: Ipc: aidl sexy, not a curse

A reference to a Handler that can be sent to a remote process via an Intent

Messages sent by the remote process via the messenger are delivered to the local handler

Relatively fast

Overview

Page 27: Ipc: aidl sexy, not a curse

DarthVaderActivitypublic void onClick(View v) { Intent intent = new Intent(this, EmpireService.class); Messenger messenger = new Messenger(new DarthVaderHandler(this)); intent.putExtra("ImperialMessenger", messenger); switch (v.getId()) { case R.id.iv_dva_build_death_star: intent.putExtra("Command type", EmpireService.EmpireServiceCommands.BUILD_DEATH_STAR); break; case R.id.iv_dva_interact_with_luke: intent.putExtra("Command type", EmpireService.EmpireServiceCommands.FIND_LUKE); break; } startService(intent);}

Page 28: Ipc: aidl sexy, not a curse

DarthVaderActivityprivate static class DarthVaderHandler extends Handler {

private final WeakReference<DarthVaderActivity> clientRef;

public DarthVaderHandler(DarthVaderActivity client) { this.clientRef = new WeakReference<>(client); }

...//Handle message}

Page 29: Ipc: aidl sexy, not a curse

DarthVaderActivityprivate static class DarthVaderHandler extends Handler {

@Override public void handleMessage(Message msg) { Bundle data = msg.getData(); DarthVaderActivity client = clientRef.get(); if (client != null && msg.what == EmpireService.CALLBACK_MSG && data != null) {

Toast.makeText(client, "Death Star deployed and ready for your command, my lord", Toast.LENGTH_LONG).show(); } }}

Page 30: Ipc: aidl sexy, not a curse

EmpireService

public int onStartCommand(Intent intent, int flags, int startId) { Messenger messenger = intent.getParcelableExtra("ImperialMessenger"); EmpireServiceCommands command = (EmpireServiceCommands) intent.getExtras() .get("Command type");

… more code coming...

Page 31: Ipc: aidl sexy, not a curse

EmpireServiceswitch (command) { case BUILD_DEATH_STAR:

..do some hard work... if (messenger != null) { Message message = Message.obtain(); message.what = CALLBACK_MSG; Bundle data = new Bundle(1); data.putParcelable("result", new DeathStar(270000, 270000, "THIS IS THE BIG GUN")); message.setData(data); try { messenger.send(message); } catch (RemoteException e) { e.printStackTrace(); } } break; }

Page 32: Ipc: aidl sexy, not a curse

But what if...We want sync calls?

Full OOPBusiness Transparent

Page 33: Ipc: aidl sexy, not a curse

3

IPC using AIDLgit checkout IPCwithAIDL

Page 34: Ipc: aidl sexy, not a curse

Android Interface Definition Language (AIDL)Our business operation on top of Binder objectJava-like interfaceDefined in a separate .aidl fileAIDL Generate code of real java interface

Page 35: Ipc: aidl sexy, not a curse

.AIDL interface

interface IStarWars { String buildDeathStar(out DeathStar deathStar); oneway void findLuke(in IEmpireServiceResponseListener listener);}

Page 36: Ipc: aidl sexy, not a curse

DeathStar.aidlpackage com.academy.android.aidlserviceexample;// Declare DeathStar so AIDL can find it and knows that it implements// the parcelable protocol.parcelable DeathStar;

Page 37: Ipc: aidl sexy, not a curse

IEmpireServiceResponseListener.aidlinterface IEmpireServiceResponseListener { void onResponse(String response);}

Page 38: Ipc: aidl sexy, not a curse

IStarWars.Stub/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.academy.android.aidlserviceexample.IStarWars {

@Overridepublic java.lang.String buildDeathStar( com.academy.android.aidlserviceexample.DeathStar deathStar) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_buildDeathStar, _data, _reply, 0); … not really necessary to dig in

Page 39: Ipc: aidl sexy, not a curse

Interface directional flagin means that the object is transferred from client to

service and only used for inputs. If any changes are made to the bar object in the service then they won’t reflect in the client.

out indicates that the object has no relevant data and will be populated by the service and returned as a response.

inout means that the data gets copied, i.e., if any changes are made to bar in the service then that’ll also reflect in the client’s bar object.

Page 40: Ipc: aidl sexy, not a curse

AIDL Data Types

All primitives and arrays of primitives

FileDescriptorSerializable/

Parcelable****

MapBundleListSparseArray

Page 41: Ipc: aidl sexy, not a curse

Sync Call - Death Star

Let the fun begin!

Page 42: Ipc: aidl sexy, not a curse

IStarWarsImplementationpublic class IStarWarsImplementation extends IStarWars.Stub {

public String buildDeathStar(DeathStar deathStar) throws RemoteException {

...doing hard work..

deathStar.setBFG("BIG GUN IS VERY VERY");deathStar.setHeight(270000);deathStar.setWidth(270000);return "Death Star deployed and ready for your command, my

lord";

}…other interface methods

Page 43: Ipc: aidl sexy, not a curse

EmpireServicepublic class EmpireService extends Service {

private IStarWarsImplementation service;

@Override public void onCreate() { super.onCreate(); service = new IStarWarsImplementation(); }

Page 44: Ipc: aidl sexy, not a curse

EmpireService@Overridepublic IBinder onBind(Intent intent) { return service;}

@Overridepublic boolean onUnbind(Intent intent) { return super.onUnbind(intent);}

@Overridepublic void onDestroy() { this.service = null; super.onDestroy();}

Page 45: Ipc: aidl sexy, not a curse

DarthVaderActivitypublic class DarthVaderActivity extends Activity implements ServiceConnection {

@Overrideprotected void onResume() { super.onResume(); final boolean isServiceBounded = super.bindService(new Intent(this, EmpireService.class), this, BIND_AUTO_CREATE); if (!isServiceBounded) { Log.w(TAG, "Failed to bind to service"); }}

… more methods

Page 46: Ipc: aidl sexy, not a curse

DarthVaderActivity@Overridepublic void onServiceConnected(ComponentName name, IBinder

service) { this.service = IStarWars.Stub.asInterface(service);}

@Overridepublic void onServiceDisconnected(ComponentName name) { this.service = null;}

Page 47: Ipc: aidl sexy, not a curse

DarthVaderActivity@Overridepublic void onClick(View v) { try { switch (v.getId()) { case R.id.iv_dva_build_death_star:

DeathStar deathStar = new DeathStar();final String reply = service.buildDeathStar(deathStar);String buildDeathStar = String .format("%s and it have %s", reply, deathStar.getBFG());Toast.makeText(this, buildDeathStar, Toast.LENGTH_LONG).show();break;

} } catch (RemoteException e) { e.printStackTrace(); }}

Page 48: Ipc: aidl sexy, not a curse

ASync Call - Find a luke

Page 49: Ipc: aidl sexy, not a curse

.AIDL interfaceinterface IStarWars { String buildDeathStar(out DeathStar deathStar); oneway void findLuke(in IEmpireServiceResponseListener listener);}

interface IEmpireServiceResponseListener { void onResponse(String response);}

Page 50: Ipc: aidl sexy, not a curse

IStarWarsImplementation...other AIDL interface implementation methods

@Overridepublic void findLuke(IEmpireServiceResponseListener listener) throws RemoteException {

..Doing very hard job through couple episodes…

listener.onResponse("I'm your father, Luke!");}

Page 51: Ipc: aidl sexy, not a curse

DarthVaderActivityIEmpireServiceResponseListener.Stub mListener = new IEmpireServiceResponseListener.Stub() { @Override public void onResponse(final String response) throws RemoteException { //Other process. We should run on UI Thread in order to interact with UI mHandler.post(new Runnable() { @Override public void run() { Toast.makeText(DarthVaderActivity.this, response, Toast.LENGTH_LONG) .show(); } }); }};

Page 52: Ipc: aidl sexy, not a curse

DarthVaderActivity@Overridepublic void onClick(View v) { try { switch (v.getId()) { case R.id.iv_dva_interact_with_luke: service.findLuke(mListener); break; } } catch (RemoteException e) { e.printStackTrace(); }}

Page 53: Ipc: aidl sexy, not a curse

Binder Overhead:0 ms

Page 54: Ipc: aidl sexy, not a curse

What did we get?

Services running on separate processEasy expandable communicationGreat performanceFun :)


Recommended