2011년 6월 16일 목요일

Android Inter-App Communication

이번 튜토리얼에서는 두 개의 Process 사이에서  하나가 다른 Process의 Service을 호출하는 방법에 대하여 설명하고 있습니다.

본 문서에서는 다음 내용을 다룹니다.
  • Invocation Handler을 작성하는 방법
  • Remote Service을 Bind 하는 방법
  • Invocation을 호출한 Process에게 Value Return 하는 방법


Step 1: Create the Service
Invocation될 Service을 만듭니다. Service에는반드시 IBinder 객체를 Return하는 onBind Method을 Override해야 합니다.
Service에서는 Messenger 컴포넌트를 사용합니다. 이 Messenger 컴포넌트는 내부 Process 간 Communication 이 가능하게 해줍니다.
public class RemoteService extends Service
{
private Messenger messenger; //receives remote invocations

@Override
public IBinder onBind(Intent intent)
{
if(this.messenger == null)
{
synchronized(RemoteService.class)
{
if(this.messenger == null)
{
this.messenger = new Messenger(new IncomingHandler());
}
}
}
//Return the proper IBinder instance
return this.messenger.getBinder();
}


Step 2: Implement the Handler
Handler는 Messenger을 등록할수 있는 컴포넌트 입니다. 이 컴포넌트는 Remote Invocation을 받을 수 있는 컴포너트 중 하나 입니다.
Handler는 들어온 요청의 Messenger에 따라 요청을 처리하고 응답을 보내게 되어있습니다.
private class IncomingHandler extends Handler
{
@Override
               public void handleMessage(Message msg)
{
System.out.println("*****************************************");
System.out.println("Remote Service successfully invoked!!!!!!");
System.out.println("*****************************************");

int what = msg.what;

Toast.makeText(RemoteService.this.getApplicationContext(), "Remote Service invoked-("+what+")", Toast.LENGTH_LONG).show();

//Setup the reply message
Message message = Message.obtain(null, 2, 0, 0);
try
{
//make the RPC invocation
Messenger replyTo = msg.replyTo;
replyTo.send(message);
}
catch(RemoteException rme)
{
//Show an Error Message
Toast.makeText(RemoteService.this, "Invocation Failed!!", Toast.LENGTH_LONG).show();
}
   }
}


아래는 RemoterService Application의 Full Source입니다.

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="org.openmobster.remote.service.android.app"
     android:versionCode="1"
     android:versionName="1.0">
   <application android:label="@string/app_name" android:icon="@drawable/icon">
       <activity android:name="org.openmobster.app.MainActivity"
                 android:label="@string/app_name">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
           <intent-filter>
               <action android:name="android.intent.action.VIEW" />
           </intent-filter>
       </activity>
       
       <service android:name="org.openmobster.app.RemoteService" android:exported="true">
       </service>
   </application>
   <uses-permission android:name="android.permission.INTERNET" />
</manifest>

“org.openmobster.app.MainActivity”는 Hello world 수준으로 만들어 줍니다.

RemoteService
package org.openmobster.app;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.widget.Toast;

public class RemoteService extends Service
{
private Messenger messenger; //receives remote invocations

@Override
public IBinder onBind(Intent intent)
{
if(this.messenger == null)
{
synchronized(RemoteService.class)
{
if(this.messenger == null)
{
this.messenger = new Messenger(new IncomingHandler());
}
}
}
//Return the proper IBinder instance
return this.messenger.getBinder();
}

private class IncomingHandler extends Handler
{
@Override
       public void handleMessage(Message msg)
{
System.out.println("*****************************************");
System.out.println("Remote Service successfully invoked!!!!!!");
System.out.println("*****************************************");

int what = msg.what;

Toast.makeText(RemoteService.this.getApplicationContext(), "Remote Service invoked-("+what+")", Toast.LENGTH_LONG).show();

//Setup the reply message
Message message = Message.obtain(null, 2, 0, 0);
try
{
//make the RPC invocation
Messenger replyTo = msg.replyTo;
replyTo.send(message);
}
catch(RemoteException rme)
{
//Show an Error Message
Toast.makeText(RemoteService.this, "Invocation Failed!!", Toast.LENGTH_LONG).show();
}
       }
}
}


Step 3: Create a Client ServiceConnection Object
Client가 Service에 바인딩하는 경우 이 호출은 비동기 방식입니다. 시스템은 클라이언트에게 ServiceConnection 같은 Callback 메커니즘을 제공하고 있습니다.
클라이언트는 제공된 IBinder 인스턴스를 사용하여 Messenger 객체를 설정합니다.
private class RemoteServiceConnection implements ServiceConnection
{
@Override
public void onServiceConnected(ComponentName component, IBinder binder)
{
MainActivity.this.messenger = new Messenger(binder);

MainActivity.this.isBound = true;
}

@Override
public void onServiceDisconnected(ComponentName component)
{
MainActivity.this.messenger = null;

MainActivity.this.isBound = false;
}
}


Step 4: Bind to the Remote Service
Intent을 생성하고 Invoke될 Service을 가리키는 ClassName을 지정합니다.
@Override
protected void onStart()
{
super.onStart();

//Bind to the remote service
Intent intent = new Intent();
intent.setClassName("org.openmobster.remote.service.android.app", "org.openmobster.app.RemoteService");

this.bindService(intent, this.connection, Context.BIND_AUTO_CREATE);
}


Step 5: Make the Remote Invocation
Message.obtain을 이용하여 Message 객체를 생성합니다. message.replyTo에 Local Messenger를 담습니다. Messenger에 Message을 담아 Send합니다.
//Setup the message for invocation
Message message = Message.obtain(null, 1, 0, 0);
try
{
//Set the ReplyTo Messenger for processing the invocation response
message.replyTo = MainActivity.this.replyTo;

//Make the invocation
MainActivity.this.messenger.send(message);
}
catch(RemoteException rme)
{
//Show an Error Message
Toast.makeText(MainActivity.this, "Invocation Failed!!", Toast.LENGTH_LONG).show();
}


아래는 RemoteClient Full Source입니다.

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="org.openmobster.app"
     android:versionCode="1"
     android:versionName="1.0">
   <application android:label="@string/app_name" android:icon="@drawable/icon">
       <activity android:name=".MainActivity"
                 android:label="@string/app_name">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
           <intent-filter>
               <action android:name="android.intent.action.VIEW" />
           </intent-filter>
       </activity>
   </application>
   <uses-permission android:name="android.permission.INTERNET" />
</manifest>


/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">

<Button android:id="@+id/invoke"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="Invoke Remote Service" />                                                                           
</LinearLayout>


MainActivity
package org.openmobster.app;

import java.lang.reflect.Field;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity
{
private Messenger messenger = null; //used to make an RPC invocation
private boolean isBound = false;
private ServiceConnection connection;//receives callbacks from bind and unbind invocations
private Messenger replyTo = null; //invocation replies are processed by this Messenger

public MainActivity()
{

}

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.connection = new RemoteServiceConnection();
this.replyTo = new Messenger(new IncomingHandler());
}

@Override
protected void onStart()
{
super.onStart();

//Bind to the remote service
Intent intent = new Intent();
intent.setClassName("org.openmobster.remote.service.android.app", "org.openmobster.app.RemoteService");

this.bindService(intent, this.connection, Context.BIND_AUTO_CREATE);
}

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

//Unbind if it is bound to the service
if(this.isBound)
{
this.unbindService(connection);
this.isBound = false;
}
}

@Override
protected void onResume()
{
try
{
super.onResume();

//render the main screen
String layoutClass = this.getPackageName()+".R$layout";
String main = "main";
Class clazz = Class.forName(layoutClass);
Field field = clazz.getField(main);
int screenId = field.getInt(clazz);
this.setContentView(screenId);

//Invoke Remote button
Button invokeButton = (Button)findViewById(R.id.invoke);
invokeButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View button)
{
if(MainActivity.this.isBound)
{
//Setup the message for invocation
Message message = Message.obtain(null, 1, 0, 0);
try
{
//Set the ReplyTo Messenger for processing the invocation response
message.replyTo = MainActivity.this.replyTo;

//Make the invocation
MainActivity.this.messenger.send(message);
}
catch(RemoteException rme)
{
//Show an Error Message
Toast.makeText(MainActivity.this, "Invocation Failed!!", Toast.LENGTH_LONG).show();
}
}
else
{
Toast.makeText(MainActivity.this, "Service is Not Bound!!", Toast.LENGTH_LONG).show();
}
}
 }
);
}
catch(Exception e)
{
e.printStackTrace(System.out);
}
}

private class RemoteServiceConnection implements ServiceConnection
{
@Override
public void onServiceConnected(ComponentName component, IBinder binder)
{
MainActivity.this.messenger = new Messenger(binder);

MainActivity.this.isBound = true;
}

@Override
public void onServiceDisconnected(ComponentName component)
{
MainActivity.this.messenger = null;

MainActivity.this.isBound = false;
}
}

private class IncomingHandler extends Handler
{
@Override
       public void handleMessage(Message msg)
{
System.out.println("*****************************************");
System.out.println("Return successfully received!!!!!!");
System.out.println("*****************************************");

int what = msg.what;

Toast.makeText(MainActivity.this.getApplicationContext(), "Remote Service replied-("+what+")", Toast.LENGTH_LONG).show();
       }
}
}

댓글 없음:

댓글 쓰기

ETL 솔루션 환경

ETL 솔루션 환경 하둡은 대용량 데이터를 값싸고 빠르게 분석할 수 있는 길을 만들어줬다. 통계분석 엔진인 “R”역시 하둡 못지 않게 관심을 받고 있다. 빅데이터 역시 데이터라는 점을 볼때 분산처리와 분석 그 이전에 데이터 품질 등 데이...