개발 업무를 몇년째 하고 있는데, 일부 개발자분들이 thread를 new Thread().start() 와 같이 단편적인 worker로만 사용하는 경우가 많이 있고, 백그라운드로 동작시키면서 queue 처리가 필요한 작업을 하려고 할때 thread pool을 만들고 pool size를 1로 해서 구현하는 경우가 있는가 하면, 내부에 lock, unlock, semaphore, flag 등을 동원하여 쓰레드를 제어하는 경우도 있다.


쓰레드는 잘 쓰면 참 좋은 도구이지만, 잘못 쓰면 디버깅도 힘들고 이슈가 발생하여도 재현도 잘 되지 않으며, 그리고 결정적으로 고치기는 매우 힘들다.


나는 플래그 사용하는 것도 싫어하고, 멤버 변수에 thread 변수를 생성하여 if thread가 생성되었나? 되었으면 blocking list에 insert 하는 구조도 좋아하지 않는다.


최근 업무로 구조를 새로 잡는 프로젝트가 있는데, 진짜 지옥 of 지옥이다.


개인적으로정리도 해둘겸해서 백그라운드 작업용도로 사용할 쓰레드를 Handler, Looper와 함께 사용하는 방법으로 정리해둔다.


두가지 방법이 있는데, 첫번째는 안드로이드의 Thread와 Handler 그리고 Looper를 직접 사용하는 방법이다.



Thread 를 상속받아 run 내부에 Looper 와 hanlder를 직접 사용하여 queued 백그라운드 처리를 할수 있다.


작업을 요청할때는 아래와 같이 요청하면 된다.



Hander에 메시지를 보내는것으로 작업요청은 끝이고, Handler에 의해서 작업요청이 queue에 의해서 하나씩 순차적으로 처리가된다.


데이터 정합성을 보장해야하는 경우 요청이 들어오는 순서대로 처리를 해야 정합성을 그마나 보장할수 있다. (반드시 해당 쓰레드 한군데에서만 데이터 베이스에 접속을 하는것도 빠지면 안된다.)



두번째 방법은 위와 같은 구현을 Android에서 제공하는 것이 있는데, 바로 HandlerThread이다.



작업을 요청하는 방법은 첫번째 방법과 다르지 않다.



Thread 관리를 위해서 별도로 구현한 ThreadMonitor와 ClosableThread인터페이스가 있는데, Looper.loop() 통한 무한루프를 가지고 있는 쓰레드이기 때문에 때문에 작업이 모두 끝나면 getLooper.quit() 등으로 looper를 종료시켜주어야 한다.


위에서 request시  thread 의 state를 관리하여 CountdDownLatch로 동시성 제어를 하는 이유는, 쓰레드가 생성되고 running 상태가 되고나서야 Handler를 통해서 메시지를 보내기 위해서이다.


실행결과는 다음과 같이 요청에 대해서 순차적으로 처리가 된다.



쓰레드 id를 보면 첫번째 구현으로 만든 SimpleThread는 32164 이고, 두번째 구현으로 만든 SimpleHandlerThread는 32163 이다.


이 둘의 쓰레드를 각각 모니터링하여 작업이 모두 끝나면 쓰레드를 종료시켜주기 위한 ThreadMonitor를 각각 생성하여 추가하였는데, 작업이 끝날때마다 onProcessed 를 전달받아 작업 요청사항들을 관리하고 종료가되면 쓰레드들을 종료시키는 기능을 한다.


플래그 사용하는 것을 싫어하기 때문에 사용하는 방법이기 때문에 아래 첨부한 소스를 참고하여 다른 방법으로 응용하여 사용하여도 좋을듯하다.


ClosableThread.java

MainActivity.java

Monitor.java

SimpleHandlerThread.java

SimpleThread.java

ThreadMonitor.java



블로그 이미지

rekun,ekun 커뉴

이 세상에서 꿈 이상으로 확실한 것을, 인간은 가지고 있는 것일까?

오늘 아침 눈뜨자 마자 갑자기 코딩이 하고 싶어서 일요일인데도 불구하고 6시 10분에 눈이 번쩍 하고 떠졌다.


꿈을 꾼것인지, 뭔지 ... 


어제는 계속 낚시하는 공부만 하다가 잤는데, 눈뜨자마자 그간 미뤄왔던 안드로이드 앱 수정과 그에 맞는 웹호스팅중인 php 코드도 좀 수정을 해야 겠다 마음을 먹고 바로 컴퓨터를 켰다.


오전내내 만들고 수정한것은 사람들이 대체 무엇을 검색하고 왜 검색하는지 다음,네이버 등을 들어가서 일일이 클릭해서 보는게 귀찮아서 내가 보고 싶을때, 사람들이 많이 본 검색어와 뉴스들을 한방에 긁어오는 웹앱을 만들었다. 근데 웹앱으로 만들어두니까, 가독성도 떨어지고, 나중에 저장해뒀다가 보는것이 불편해서 아예 이슈만 저장하는 블로그를 하나 팠다.


한번씩 긁 읽어볼 필요가 있을때, 즐겨찾기 해둔 웹앱을 실행해서 긁어다가 블로그에 넣고 앉아서 클릭 또는 넣어놓고 출퇴근중에 하나씩 보면 꿀같은 휴식을 취할수가 있다.


이런 웹앱 만드는것은 너무나 간단해서 아주 아주 오래전에 만들어뒀던 php 코드를 살짝 수정해서 완성했다.

처음에는 물론 버그가 있어서, 제일처음 글 올린것은 링크가 다 ....깨졌다.


오늘 자기전에 긁어서 저장해놨다가 내일 출근할때 봐야지.


이런 웹앱을 만드는데는 다른 것을 많이 사용하는 것보다 php로 curlsimplehtmldom 라이브러리만 있으면 만들수 있다.


예제 코드를 하나 올려보면 아래와 같이 함수로 하나 만들어두면 글을 긁어올수 있고, simplehtmldom을 사용하여 요소별로 분리할수 있다.





이것을 하고 나니, 오후가 되었는데, 왠지 오늘 코딩빨이 좀 받는 느낌이 들어서, 그간 귀찮아서 미뤄두었던 kpop app 의 업데이트를 위한 서버 작업, 앱 수정 배포도 했다.




생각보다 많은 외국인들이 사용해주고 메일도 주고 해서 업데이트를 빨리 해야지 하고 있었는데, 지난 몇년간 시달림이 있다보니, 이런 여유시간도 많이 없었고, 여유시간에 코딩을 한다는 것 자체가 정말 오랫만이되어버려서 2년인가 4년 만에 코딩을 다시 한 느낌이 든다.


지금 업데이트 올려보니, 그간 구글에서 많은 정책이 바뀌어서 그것 하나 하나 맞춰주느라.. 진땀 뺐다.


그리고 그간 사용하던 도메인하고, 웹호스팅도 다음달부로 만료된다고 해서 그것들도 다 연장하고 결재하고나니 일요일이 휘리릭 다갔네~


그래도 뭔가 그간 화장실 가고 싶은데 못갔던 느낌으로 몇년간 지내다가 이참에 정리해버리니 뭔가 정리된 느낌도 들고, 이제 앱 리뷰 오는거랑 해서 잘 업데이트 하면서 관리좀 해야지~~ 하며 마음을 다 잡았다.



블로그 이미지

rekun,ekun 커뉴

이 세상에서 꿈 이상으로 확실한 것을, 인간은 가지고 있는 것일까?

안드로이드는 추가된 어카운트에 동기화 기능을 추가하기 아주 쉬운 Sync Adapter 인터페이스를 제공하고 있다.


그 구현하는 방법도 그렇게 어렵지 않게, 서비스 1개, AndroidManifest meta 정의 1개, xml 정의 1개로 기본적인 동기화 서비스를 제공할수 있도록 되어있다.


물론 해당 계정에 대한 인증같은 부분은 따로 처리를 해주어야 하지만, 해당 계정의 특정 authority(동기화 항목)에 대한 동기화는 아래 와 같은 세가지를 구현하면 기본적인 준비가 되게 된다.


1.Sync Adapter 정의 xml 추가

<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
    android:contentAuthority="com.android.contacts"
    android:accountType="[계정타입,예를 들면 com.google 같은것]"
    android:supportsUploading="[업로드지원여부] true"
/>


2.Sync Adapter Service 구현

public class SyncService extends Service {

	@Override
	public void onCreate() {			
		
	}

	@Override
	public IBinder onBind(Intent intent) {
		return new SyncAdapter(getApplicationContext(), true).getSyncAdapterBinder();
	}
	
        // Sync Adapter
	public static class SyncAdapter extends AbstractThreadedSyncAdapter {	
	
		public SyncAdapter(Context context, boolean autoInitialize) {
			super(context, autoInitialize);			
		}	
	
		@Override
		public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {				
			// Sync 가 실행될때 할일들
		}
	
		@Override
		public void onSyncCanceled() {
			super.onSyncCanceled();	
		       // Sync가 취소 될때 할일들
		}
	}
}


3.Android Manifest 수정


            
                
            

            
     


블로그 이미지

rekun,ekun 커뉴

이 세상에서 꿈 이상으로 확실한 것을, 인간은 가지고 있는 것일까?

아주 아주 오래전 간단한 서비스를 하는 어플을 하나 만들었는데, 어플이 웹과 연동도 되어야 하고, 주기적으로 정보도 갱신을 해야되는데 어플에서는 네트워크 비용이 최대한 들어가지 않도록 설계를 해야 했었다.


그래서 고민하고 고안한 방법이, 서비스를 제공하는 어플은 단지 별도로 제작한 웹에서 정보를 읽어만 오고, 해당 웹에 정보를 채워넣기 위해서 웹앱을 하나 만들어서 주기적으로 어플에서 로딩만 하게 하면, 모든 처리는 서버내에서 이루어지게 되므로, 스마트폰에서는 별도의 추가적인 네트워크가 많이 발생하지 않게 되었다.


일단 주기적으로 특정한 웹페이지를 계속해서 로딩하는 방법은 여러가지가 있지만 아주 간단하게, 서비스 하나와 타이머로 스마트폰이 켜져있고 네트워크가 연결되어있는한 계속해서 실행할수 있다.


코드는 아래와 같이 아주 간단하게 만들어주면 된다.



public class Scheduler extends Service {
	private String TAG = "Scheduler";
	private static final String url = "[웹페이지주소]";
	private static final long delta = 30 * 60 * 1000; // 30분마다 한번

	@Override
	public void onCreate() {
		

	}

	@Override
	public int onStartCommand(final Intent intent, int flags, int startId) {
		super.onStartCommand(intent, flags, startId);

		start();
		return START_STICKY;  // 이 서비스는 불멸이다.

	}

	private void start() {

		Timer timer = new Timer();
		
		timer.schedule(new TimerTask() {
			public void run() {
				URL obj;
				try {
					obj = new URL(url);
					HttpURLConnection conn = (HttpURLConnection) obj.openConnection();

					BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
					String inputLine;

					while ((inputLine = in.readLine()) != null) {
						Log.v(TAG, inputLine);
					}
					in.close();
				} catch (MalformedURLException e) {					
					e.printStackTrace();
				} catch (IOException e) {					
					e.printStackTrace();
				}
			}
		}, 0, delta);
	}

	@Override
	public IBinder onBind(Intent arg0) {		
		return null;
	}
}
블로그 이미지

rekun,ekun 커뉴

이 세상에서 꿈 이상으로 확실한 것을, 인간은 가지고 있는 것일까?


HTTP를 그냥쓰기는 뭣하고, 그래도 채널만이라도 암호화해야 되지 않을까 하는 생각에 OpenSSL을 이용한 인증서 만들기로 서버와 앱에 적용시켜 보았다.


크흑... 엄청난 삽질.. 삽질... 결국 인증서를 만든 이유는 오직 한가지, 채널만 암호화 하자는 것에 만족!!!


일단 RSA 암호화 방식으로 개인키를 생성해준다.

$openssl genpkey -out privatekey.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048


만들어진 키로 인증서 생성 요청을 한다. 알고리즘은 X509를 사용하도록 해서, 만료기한은 10000일으로..!!
$openssl req -new -x509 -key privatekey.pem -out cetificate.pem -days 10000


여기까지가 개인키, 생성후 인증서 생성하는 부분으로, 서버의 어플에서 사용할 부분이다.

그리고 앱에서 사용할 인증서를 pkcs12 방식으로 export 한다.단지 앱과 통신을 위한것일뿐이므로.. 정보 넣는것보다는 만드는것에 집중해보자..

$openssl pkcs12 -export -in cetificate.pem -inkey privatekey.pem  > cetificate.pkcs12


이제 모든것이 만들어졌으니, 서버에서 인증서를 이용하여 https 서비스를 돌리고, 단말에서는 해당 인증서로 https 채널을 뚫어서 이제 통신하면 된다..


이거 하나 하느라고 키 만드는 것은 쉬웠으나, 앱에 올리고 디버그 하는데 너무 많은 시간이 걸렸다.





블로그 이미지

rekun,ekun 커뉴

이 세상에서 꿈 이상으로 확실한 것을, 인간은 가지고 있는 것일까?