AIDL을 이용한 RPC 서비스를 구현해보자!

Mobile Programming/Android 2011. 1. 19. 12:07

앞선 포스팅에서 로컬 서비스를 구현해 보았다.
RPC 서비스는 Remote Procedure Call 의 Abbreviation(?) 로서 , 다른 프로세스들이 소비할 수 있는 서비스임을 뜻한다.

다른 플랫폼들과 마찬가지로 클라이언트에 공개될 인터페이스 정의를 위해 IDL (interface definition language) 을 사용한다.

안드로이드에서는 이를 Android IDL , 즉 AIDL 이라고 지칭한다 .

서론은 이쯤하고 , RPC 서비스를 구현해보자.

1. AIDL 파일 만들기. 인터페이스를 만들자. (구현할 필요는 없다.)
2.이 파일을 src 폴더안에 넣기.
3.컴파일하기.
4.res아래에 웬 자바파일이 생겼다!
5.service를 상속한 액티비티를 하나 만들자.
6.그 안에 1번의 인터페이스를 구현할 클래스를 만들자. 상속하는 클래스는 ~.Stub
7.6에 메서드들을 구현한다.
8.로컬서비스에서 바인더클래스를 만드는대신에 6의 클래스를 만들어준것이다. 이 클래스를 onBind()에서 리턴해주자.
9.나머지는 로컬과 같다!

소스는 다음과 같다.


public class LocalService extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        ServiceConnection tserviceConnection = new ServiceConnection(){

			public void onServiceConnected(ComponentName name, IBinder service) {
				// TODO Auto-generated method stub
				Log.d("aidl","connected");

				try {
					((Service2.Service2impl)service).sayHello2();
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					Log.d("aidl",e.getStackTrace().toString());
				}
				
				
			}

			public void onServiceDisconnected(ComponentName name) {
				// TODO Auto-generated method stub
				
        	
			}
        };
        
        startService(new Intent(LocalService.this, Service2.class));
        
        bindService(new Intent(LocalService.this, Service2.class),tserviceConnection,0);
        
        Button btn_dis = (Button)findViewById(R.id.discon);
        
        btn_dis.setOnClickListener(new OnClickListener(){

			public void onClick(View v) {
				// TODO Auto-generated method stub
				stopService(new Intent(LocalService.this,Service2.class));
			}
        	
        });
        
    }
}
다음은 구현부이다.
public class Service2 extends Service {
	
	public class Service2impl extends aidlService.Stub{

		public void sayHello2() throws RemoteException {
			// TODO Auto-generated method stub
			Log.d("aidl","service2, sayHello2!");
		}
		
	}
	
	Service2impl tb = new Service2impl();
	
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return tb;
	}

}


여기까지 적고 메니페스트에 서비스에 대한 정보 한줄만을 적어도 로컬에서는 잘 돌아가지만, 이 서비스를 외부에서 이용할 수 있도록 인텐트 필터를 더 넣어줄 필요가 있다.


        	
        		
        	



여기까지하면 이제 외부에서 com.android.servicelocal 패키지안에 있는 aidlService를 접근할 수 있게된다 .

이제 새로운 프로젝트를 만들어서 이 서비스에 접근해보도록 하자 . 
역시 이름 명명법이 좀 헷갈리긴 하지만..  양해바랍니다.

TestService가 외부 액티비티로서 이 클래스는 com.android.testService 패키지 안에 존재한다.
앞서 aidl 파일을 이미 만들어 주었지만, 서비스에 접근할 이를테면, Client 프로세스에게도 일종의 계약서 같은 것이 필요하다.
따라서 생성해두었던 aidl 파일을 서비스가 존재하는 패키지경로로 패키지를 만들고 그안에 복사해 두도록 하자.


외부 서비스를 호출할때는 startService 대신에 bindService를 이용한다.
bindService의 첫번째 인자 intent는 이전 로컬에서 생성할때와는 다르게 인텐트 필터를 통해 서비스를 찾아갈수 있도록 
getName() 을 호출해주어야한다 .  (액션을 다른 스트링으로 주고 , 그 액션 인텐트를 넘겨주어도 OK!)

호출하는 외부 액티비티는 논거할 다른 기술은 없고 , 
bind할때 인텐트에 주의하고 , 그 서비스를 조작하기위해 서비스를 얻어올때 로컬과 같이 캐스팅을 통하지않고 
자동으로 생성된 java 파일의 클래스 객체.Stub.asInterface(IBinder) 임을 확인하자.



public class TestService extends Activity {

	public void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		
		setContentView(R.layout.main);
		
		final ServiceConnection serConn = new ServiceConnection(){

			@Override
			public void onServiceConnected(ComponentName name, IBinder service) {
				// TODO Auto-generated method stub
				Log.d("client","serviceConnected!");
				aidlService aservice;
				
				aservice = aidlService.Stub.asInterface(service);
				
				try {
					aservice.sayHello2();
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			@Override
			public void onServiceDisconnected(ComponentName name) {
				// TODO Auto-generated method stub
				Log.d("client","disConnect");
			}
			
		};
		
		Button btn_bind = (Button)findViewById(R.id.btn_bind);
		Button btn_stop = (Button)findViewById(R.id.btn_stop);
		
		btn_bind.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				bindService(new Intent("com.neo" ),serConn,Context.BIND_AUTO_CREATE);
			}
			
		});
		
		btn_stop.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				unbindService(serConn);
			}
			
		});
		
		
	}
	
}

Local Service를 이용해보자!

Mobile Programming/Android 2011. 1. 18. 18:46
서비스는 백그라운드에서 네트워크를 통해 어떤 파일을 다운받는다거나 ,  음악을 플레이 한다거나등의 작업을 하는 컴포넌트이다.

먼저 액티비티와 같이 Manifest 에 정의해주고 , 

1 . 액티비티에서 서비스 클래스를 실행시키고 ( startService )
2 . bindService 의 두번째 인자에 해당하는 ServiceConnection의 객체를 생성하고 onServiceConnected에 필요한 작업을 구현한다.
3 . 바인드 요청을 위해 bindService 메서드를 호출한다.
4 . 바인드 요청을 받을 서비스 클래스는 onBind 를 구현한다.
5 .  onBind의 리턴값은 액티비티와 통신을 할 IBinder를 상속한 클래스의 객체이다 . 따라서 그 객체를 구현해 줄 필요가 있다.
6 . 객체를 리턴한다.

코드로 확인해보기 바란다.

먼저 액티비티이다. (클래스 명이 헷갈리게 명명된 점 양해부탁드립니다.)


 
public class LocalService extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        ServiceConnection tserviceConnection = new ServiceConnection(){

			public void onServiceConnected(ComponentName name, IBinder service) {
				// TODO Auto-generated method stub
				Log.d("localservice","connected");
				((Service.TempBinder)service).sayHello();
			}

			public void onServiceDisconnected(ComponentName name) {
				// TODO Auto-generated method stub
				
			}
        	
        };
        
        startService(new Intent(LocalService.this, Service.class));
        
        bindService(new Intent(LocalService.this, Service.class),tserviceConnection,0);
    }
}
다음은 서비스클래스이다.
public class Service extends android.app.Service {

	class TempBinder extends Binder{
		public void sayHello(){
			Log.d("localservice","hello");
		}
	}
	
	TempBinder tb = new TempBinder();
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return tb;
	}

}

Android 입문자를 위한 Tutorial - 7. 탭뷰의 이용

Mobile Programming/Android 2010. 11. 10. 18:49

프로젝트하면서 이용했던 탭뷴데 , 다시 정리해둬야 할 것 같아서!

탭뷰는 조금 조잡하다 .
XML에서 탭뷰의 구성요소는 크게 3가지.
<TabHost>, <TabWidget> , <Layout>이다.

TabHost는 Tab을 보유한 레이아웃이며 ,
TabWidget은 흔히들 생각하는 탭,
Layout은 탭에따라 전환되는 "뷰"이다.
보통 전환할 수 있도록 FrameLayout안에 Linear등의 일반 레이아웃을 배치하는 것이 일반적이다.

Java에서 탭뷰 연결방법은 다음과 같다.

1. TabHost를 얻어온다 (findViewById)
2. Host의 setup함수 호출 ( 탭들을 add하기 위한 준비작업 )
3. Host의 newTabSpec을 통해 탭메뉴들을 생성.
4. TabSpec.setIndicater로 탭에 표시될 내용 지정
5. setContent로 XML에서 선언한 전환 뷰 지정
6. Host에 add
7. 3~6을 원하는 탭 메뉴 수만큼 추가
8. setCurrentTab으로 처음 탭뷰 진입했을때의 화면 지정.

탭뷰의 높이 또한 지정 가능하다.
모두 추가한뒤(7)에


for(int tab = 0; tab < tab_host.getTabWidget().getChildCount(); ++tab){
			tab_host.getTabWidget().getChildAt(tab).getLayoutParams().height= 50;
		}

와 같이 for문을 작성하면 해당 height가 변경된다.

다음은 위에서 설명한 과정의 Java 소스 예이다.
TabHost tab_host = (TabHost)findViewById(R.id.tab_host);
		tab_host.setup();
		
		
		TabSpec ts1 = tab_host.newTabSpec("TAB_HOME");
		ts1.setIndicator("home");
		ts1.setContent(R.id.home);
		tab_host.addTab(ts1);



다음은 XML코드의 일부분이다. 

여기서 전환뷰의 아이디로 home이라고 지정했기때문에 setcontent에서 home으로 지정이 가능하다.
;; 사진은 우리 캠퍼스의 상징물. 암튼 탭뷰만을 보실때 실행화면은 다음과 같다.