안녕하세요. 로그 토스터는 안드로이드 폰 상에서, 바로 로그캣에 출력되는 로그 내용을 확인하고, 해당 내용을 파일로 저장하거나 이메일로 보낼 수 있는 무료;; 로그 뷰어입니다. 로그 토스터를 이용하면,

  1. 안드로이드 폰 상에서 전체 로그를 확인하거나,
  2. 여러분의 어플리케이션을 테스트 하며, 토스트 형식으로 로그를 확인 할 수 있으며,
  3. 3. 에러가 일어난 로그를 쉽게 확인하거나,
  4. 4. 원하는 로그를 파일로 저장하거나 메일로 전송할 수 있습니다.
  5. 5. 또한, 현재 로그 토스터의 동작 상황을 상태바를 통해 확인하고 제어 할 수 있습니다.


 에...어플리케이션 광고는 여기까지 하고, 하여간 개인적으로 최초로 만들어본 나름대로 뜻깊은... 로그 토스터는 소스가 공개된 오픈소스 프로젝트입니다;;; (참여자는 저 혼자이지만... 아파치 라이센스를 적용하고 있음으로, 소스에 포함된 카피라이트 정보만 유지해주신다면, 자유롭게 수정하거나 사용하실 수 있습니다.)  코드 구글사이트를 통해 호스팅되고 있는데, 다음의 경로로 접근하시면 코드를 확인 하실 수 있으며, Subclipse 등의 SVN 도구를 사용하시면 간편하게 체크아웃 받으실 수 있습니다.
코드 구글:
SVN : 
 https://weathermusic.googlecode.com/svn/trunk/LogToaster
 로그 토스터는 비교적 간단한 어플리케이션이지만, 안드로이드 어플리케이션을 이제 막 개발하시는 분들이 살펴보신다면 도움이 될만한 내용이 조금... 포함되어 있습니다.

 우선, 레이아웃 관련하여... 가장 기본이라고 할 수 있는, 커스텀한 ArrayAdapter 가 사용되었고, TextView 에 표시되는 값들을 알록달록하게 꾸미는 방법이 활용되었습니다. 또한, 커스텀한 위치(화면 하단...)에 TabWidget 을 배치하였고, 로그가 표시되는 Toast 의 경우에는, 최신 글이 가장 아래쪽으로 정렬이 되도록 나름의 꽁수를 마련하기도 하였지요. (최신 로그만 Toast 로 표시하기 위해서, 여러가지로 고민이 많은 문제였는데, margin 값을 이용하여 해결하였답니다.) 화면 상에 Toast 를 계속 유지시키기 위하여 주기적으로 Toast 의 show 메소드를 호출한 것은 애교로 봐주세요...

public class LogView extends TextView{
 
 public LogView(Context context){
  super(context);
 }

 public LogView(Context context, AttributeSet attrs) {
  super(context, attrs);
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
  //automatically align to bottom. (last log should be placed at bottom.)
  
  int offset = getLineCount() * getLineHeight();
  int height = getHeight();  
  int padding = getTotalPaddingTop();
  
  if(padding != height - offset)
   setPadding(10, height-offset, 10, 0);

  super.onDraw(canvas);
 }
}//end of class
<가장 마지막 글이 TextView 하단에 정렬되는 LogView>

 어플리케이션 컴포넌트와 각 컴포넌트간의 생명주기에 관련해서도 기본적인 내용이 구현되어있습니다. 로그 토스터는 Activity 와 Service 그리고 Notification 을 모두 사용하고 있는 어플리케이션이기 때문에, ServiceConnection 객체를 bind 하고 unbind 하는 타이밍이라던가, 개별 컴포넌트들이 작동 중일때와 그렇지 않은 경우를 잘 처리하는 방법. 그리고 Notification 에서 PendingIntent 형식으로 호출되는 Activity 가 별개의 Task 로 실행되어 깔끔하게 Dialog 형태로 표시되도록 메니페스트에 launchMode 를 설정한 부분도 관심있게 살펴보시면 좋겠습니다. (그렇지 않으면, 로그 토스터 전체 어플리케이션이 첫화면으로 올라와 버리거든요...)

android:name="com.huewu.example.logreader.LogRemocon" android:launchMode="singleTask" android:theme="@android:style/Theme.Translucent.NoTitleBar" android:label="@string/app_name" />
 그리고, 성능 향상을 위해서는 두 가지 요소가 고려되었습니다. 우선, 로그가 범람하는 경우, 빈번한 GUI 업데이트로 인해 성능 문제가 발생하는 것을 방지하고자, 로그가 올라올 경우, 해당 로그를 매번 처리하는 대신, 약간의 버퍼와 최소 업데이트 시간 간격을 설정해서, 아무리 로그가 범람하는 상황에서도 정해진 업데이트 주기에 따라 GUI 가 업데이트 되도록 구현되었습니다. 일반적인 플랫폼에서는 나름 구현하기 까다로운 기법인데, 안드로이드에서는 Handler 의 delayed 계열 메서드를 사용해서 비교적 간단하게 구현할 수 있습니다. 알아두시면 여러가지로 쓸모가 있는 작은 팁입니다. 그리고, 너무 빈번하게 GC 가 호출되지 않도록, 캐시 역할을 하는 버퍼를 사용하는 부분도 안드로이드의 특성상 꼭 한번쯤은 고려해볼만한 방법이라고 생각됩니다.

//Small cache for performance and avoiding frequent GC calls.
static LinkedHashMap mTokenCache = new LinkedHashMap(MAX_ENTRIES + 1){
 private static final long serialVersionUID = 1L;
 protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
  return size() > MAX_ENTRIES;    
 };
};


public static String[] parseTokens(String log){

 String[] result = null;
 result = mTokenCache.get(log);

 if( result != null )
  return result; //hit!!

 int index1 = log.indexOf("/");
 int index2 = log.indexOf(":");
 if(index1 < 0 || index2 < 0){
  result = new String[]{log};
  mTokenCache.put(log, result);
  return result;
 } else {
  result = new String[]{
    log.substring(0, index1),
    log.substring(index1+1, index2+1),
    log.substring(index2+1).trim()};
  mTokenCache.put(log, result);
  return result;
 }
}
<파싱 결과를 캐쉬에 저장한 후, 동일한 문자열은 은 파싱하지 않고 캐싱된 값을 바로 반환>
 
  마지막으로, 서로 다른 어플리케이션과 연동하는 부분이 있습니다. 로그 토스터는 출력된 특정 로그를 이메일이나 문자메세지등으로 전달하거나, 전체 로그를 파일로 저장하여 이메일로 전달하는 기능을 담고있습니다. 바로 Intent 를 이용해서 말이지요. 몇 가지 주의 사항만 조심하신다면, ACTION_SEND 나 ACTION_SENDTO 를 이용하여 비교적 쉽게 이런 기능을 구현하실 수 있습니다. ACTION_SEND 를 사용하는 경우, EXTRA_TEXT 를 통해 전달하고자 하는 문자열을 담아야 하며, EXTRA_STREAM 을 추가하여 전달하고자 하는 파일의 URI 경로를 담게 되어있습니다. 또한, ACTION_SENDTO 인텐트를 사용하는 경우에는, Intent 의 Data 로 직접 "mailto:" 형식의 URI 값을 지정해 주어야 합니다.


switch(mSaveLogMode){
case SAVE_FULL_LOG:
 mTempLogFile = LogUtils.createTempLogFile();
 if(mTempLogFile != null && LogUtils.saveLogFile(mTempLogFile, mLogAdapter) == true){
  Uri uri = Uri.fromFile(mTempLogFile);
  intent.setType("application/Octet-Stream");
  intent.putExtra(Intent.EXTRA_STREAM, uri);
 }
 break;
case SAVE_PARTIAL_LOG:
 intent.setType("text/plain");
 intent.putExtra(Intent.EXTRA_TEXT, mSelectedLog);
 break;
}
startActivity(intent);


 아주, 간략하게 로그 토스터에 적용된 기능들에 어떤 것들이 있으며, 어떤식으로 구현되어있는지 설명드렸습니다. 뭐;;; 별로 특별한 것은 없습니다만, 처음 어플리케이션을 개발하시는 분들이라면 하나의 샘플로서 활용하실 수 있을 듯 합니다. 출처 확실하고;;; 소스와 관련되어 물어보면 친절(?)하게 답해줄지도 모를 개발자도 있고 말이지요;;;;;

 마지막으로, 한 가지 로그 토스터 어플리케이션은 아래의 QR 코드를 통해 다운로드 받으실 수 있습니다. 어플리케이션 개발할 때 나름;;; 유용한 툴이니 많이 애용해 주시고, 어플리케이션에 관련된 궁금증이나 소스에 관련된 궁금점은 이 게시물이나 어플리케이션 소개 포스트에 답글로 남겨주세요. 그럼 즐거운 코딩 되시기 바랍니다~~~