Android/App개발2011. 6. 2. 13:40


JNI, jstring type example
Java의 String 을 JNI에서 다루는 예

JNI Functions Reference : http://download.oracle.com/javase/1.4.2/docs/guide/jni/spec/functions.html#wp17314

JNITest.java

public class  JNITest
{
 static{
  System.loadLibrary("my_dll");
 }

 public native String greeting(String name);

 public static void main(String[] args) 
 {
  JNITest test = new JNITest();
  String result = test.greeting("Smith");
  System.out.println("C 함수 리턴값:  "+result);
 }
}



JNITest.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JNITest */

#ifndef _Included_JNITest
#define _Included_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JNITest
 * Method:    greeting
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */

JNIEXPORT jstring JNICALL Java_JNITest_greeting
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif





my_dll.c

#include <stdio.h>
#include "JNITest.h"
#include <string.h>

JNIEXPORT jstring JNICALL Java_JNITest_greeting
  (JNIEnv *env, jobject obj, jstring jstr) {

 const char *name = (*env)->GetStringUTFChars(env, jstr, NULL);//Java String to C Style string
 char msg[60] = "Hello ";
 jstring result;

 strcat(msg, name);
 (*env)->ReleaseStringUTFChars(env, jstrname);
 puts(msg);

 result = (*env)->NewStringUTF(env, msg); // C style string to Java String
 return result;

}



Posted by 삼스
Android/App개발2011. 5. 11. 11:02

Intent.ACTION_SEND를 이용하여 Email, MMS 앱에 파라메터를 번들형태로 넘겨서 해당 액티비티를 호출하는 것이 가능하다.

Email의 경우 아래와 같이 하면 된다.


Intent i=new Intent(Intent.ACTION_SEND); 

i.addCategory(Intent.CATEGORY_DEFAULT); 

    

// for EMail
// mimetype

i.setType("text/plain");
i.putExtra(Intent.EXTRA_SUBJECT, "제목입니다."); 

i.putExtra(Intent.EXTRA_TEXT, "본문이구요 ....");

i.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{"user1@website.com"}); 

i.putExtra(Intent.EXTRA_CC, new String[]{"user_cc1@website.com"user_cc2@website.com"}); 

i.putExtra(Intent.EXTRA_BCC, new String[]{"user_bcc1@website.com"}); 

// 파일 첨부
i.putExtra(Intent.EXTRA_STREAM, Uri.parse("file:///sdcard/mic_rec3.wav"));

// 앱선택 박스를 띄움.

startActivity(Intent.createChooser(i, "How do you want to send message?"));


MMS의 경우는 아래와 같이 Extra정보를 설정한다.    

// for MMS

i.putExtra("address", "07011111111;01022222222");

i.putExtra("exit_on_sent", true);

i.putExtra("sms_body", "MMS 테스트입니다."); 

startActivity(Intent.createChooser(i, "How do you want to send message?"));


앱선택박스를 띄우지 않고 바로 특정 앱(MMS, GMail)으로 바로 연결되도록 하려면 해당하는 액티비티 Component를 Intent에 등록하면 된다.

// com.google.android.gm 패키지의 ComposeActivityGmail 액티비티를 명시적으로 기입해도 될듯~!

i.setComponent(new ComponentName("com.google.android.gm","com.google.android.gm.ComposeActivity"));


그러나 위와 같이 하면 permission에러가 나면서 해당 액티비티가 호출되지 않는다.
권한문제를 피해가기 위해 Intent.createChooser()를 이용하여 해당 액티비티로 연결하도록 되어 있다.

이를 피하기 위해서는 권한 문제가 없는 자체적인 Mail client나 MMS client를 직접 작성해야 할것으로 보인다.
그러나 이 문제도 권한문제로 인해 구현이 불가할 수 있다.



Posted by 삼스
Android/App개발2011. 5. 8. 18:03

MIME-Type Description File Extension
application/acad AutoCAD drawing files dwg
application/clariscad ClarisCAD files ccad
application/dxf DXF (AutoCAD) dxf
application/msaccess Microsoft Access file mdb
application/msword Microsoft Word file doc
application/octet-stream Uninterpreted binary bin
application/pdf PDF (Adobe Acrobat) pdf
application/postscript Postscript, encapsulated Postscript, ai, ps, eps
Adobe Illustrator
application/rtf Rich Text Format file rtf rtf
application/vnd.ms-excel Microsoft Excel file xls
application/vnd.ms-powerpoint Microsoft PowerPoint file ppt
application/x-cdf Channel Definition Format file cdf
application/x-csh C-shell script csh csh
application/x-dvi TeX dvi dvi dvi
application/x-javascript Javascript source file js
application/x-latex LaTeX source file latex
application/x-mif FrameMaker MIF format mif
application/x-msexcel Microsoft Excel file xls
application/x-mspowerpoint Microsoft PowerPoint file ppt
application/x-tcl TCL script tcl
application/x-tex TeX source file tex
application/x-texinfo Texinfo (emacs) texinfo, texi
application/x-troff troff file t, tr, roff t, tr, roff
application/x-troff-man troff with MAN macros man
application/x-troff-me troff with ME macros me
application/x-troff-ms troff with MS macros ms
application/x-wais-source WAIS source file src
application/zip ZIP archive zip
audio/basic Basic audio (usually m-law) au, snd
audio/x-aiff AIFF audio aif, aiff, aifc
audio/x-wav Windows WAVE audio wav
image/gif GIF image gif
image/ief Image Exchange Format file ief
image/jpeg JPEG image jpeg, jpg jpe
image/tiff TIFF image tiff, tif
image/x-cmu-raster CMU Raster image ras
image/x-portable-anymap PBM Anymap image format pnm
image/x-portable-bitmap PBM Bitmap image format pbm
image/x-portable-graymap PBM Graymap image format pgm
image/x-portable-pixmap PBM Pixmap image format ppm
image/x-rgb RGB image format rgb
image/x-xbitmap X Bitmap image xbm
image/x-xpixmap X Pixmap image xpm
image/x-xwindowdump X Windows Dump (xwd) xwd
multipart/x-gzip GNU ZIP archive gzip
multipart/x-zip PKZIP archive zip
text/css Cascading style sheet css
text/html HTML file html, htm
text/plain Plain text txt
text/richtext MIME Rich Text rtx
text/tab-separated- values Text with tab-separated values tsv
text/xml XML document xml
text/x-setext Struct-Enhanced text etx
text/xsl XSL style sheet xsl
video/mpeg MPEG video mpeg, mpg, mpe
video/quicktime QuickTime video qt, mov
video/x-msvideo Microsoft Windows video avi
video/x-sgi-movie SGI movie player format movie


MIME 확장명

파일 확장명 

 application/x-silverlight-app  .xap
 application/manifest  .manifest 
 application/x-ms-application  .application 
 application/x-ms-xbap  .xbap
 application/octet-stream  .deploy
 application/vnd.ms-xpsdocument  .xps 
 application/xaml+xml  .xaml
 application/vnd.ms-cab-compressed  .cab
 application/vnd.openxmlformats-officedocument.wordprocessingml.document  .docx
 application/vnd.openxmlformats-officedocument.wordprocessingml.document  .docm
 application/vnd.openxmlformats-officedocument.presentationml.presentation  .pptx
 application/vnd.openxmlformats-officedocument.presentationml.presentation  .pptm
 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet  .xlsx
 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet  .xlsm
 application/msaccess  .accdb
 application/x-mspublisher  .pub
 image/svg+xml  .svg
 application/xhtml+xml  .xht
 application/xhtml+xml  .xhtml

Posted by 삼스
Android/App개발2011. 3. 31. 18:55

JNI function을 테스트하기 위해 ndk-gdb를 사용하려고 하는데 에뮬레이터가 아닌 상용단말에서 안되는 경우가 있다.
내 경우 HTC Desire에서 동작하지 않았다.

이유를 찾아보니 shell 명령중에 run-as가 정상동작하지 않도록 해놓았단다.

이런 젝일...
갤럭시S도 그렇다는데 되는 디바이스가 있을지 모르겠다.. 
넥서스시리즈는 될까???

혹시 이글 읽고 다른 디바이스에서 되는 분 있으면 댓글 좀...

원문 출처: http://groups.google.com/group/android-ndk/tree/browse_frm/month/2011-03/1ce4f2052e3378db?rnum=41&_done=%2Fgroup%2Fandroid-ndk%2Fbrowse_frm%2Fmonth%2F2011-03%3F

It's a known Samsung-specific issue: native debugging doesn't work because 
they made platform customizations without updating the run-as source 
appropriately. The problem is that "run-as" is looking for for 
/data/system/packages.list, which doesn't exist on the Samsung builds, 
because they use /dbdata instead of /data 

One solution is to root your system, then create a symlink from 
/data/system/ to /dbdata/system/. There is a previous post on the 
android-ndk forum about this. 

The same issue happens on Galaxy S builds, btw. 

 
Posted by 삼스
Android/App개발2011. 2. 22. 21:42


open_src/system/core/adb가 윈도우용으로 빌드될 때 아래의 소스들이 컴파일된다.
 usb_windows.c
 get_my_path_windows.c
 adb.c 
 console.c 
 transport.c 
 transport_local.c 
 transport_usb.c 
 commandline.c 
 adb_client.c 
 sockets.c 
 services.c 
 file_sync_client.c 
 shlist.c 
 utils.c 
 usb_vendors.c

아래 위치의 헤더파일들이 참조된다.
development/host/windows/usb/api/

프로그램의 시작지점은 아래와 같다.

int main(int argc, char **argv)
{
    adb_trace_init();
#if ADB_HOST
    adb_sysdeps_init();
    return adb_commandline(argc - 1, argv + 1);
#else
    if((argc > 1) && (!strcmp(argv[1],"recovery"))) {
        adb_device_banner = "recovery";
        recovery_mode = 1;
    }

    start_device_log();
    return adb_main(0);
#endif
}

commandline.c의 아래 함수에 커맨드라인 명령어의 처리 코드를 찾아볼 수 있다.

int adb_commandline(int argc, char **argv)
{
...
}

Posted by 삼스
Android/App개발2011. 2. 22. 21:15


adb윈도우용은 AdbWinApi.dll과 AdbWinUsbApi.dll과 함께 연동된다.
주요 API는 AdbWinApi.dll에 정의되어 있으며 AdbWinUsbApi.dll은 실제 Usb device와 연동부분을 책임진다.

오픈소스상의 아래 위치에 소스가 위치한다.

open_src/system/core/adb -> adb server, adb demon, adb client based on command-line의 소스가 OS별(linux, window, OSX)로 구현되어 있다.
open_src/developments/hosts/windows/usb -> 윈도우용 AdbWinApi.dll과 AdbWinUsbApi.dll의 소스와 드라이버등이 있다.

developments/hosts/windows/usb폴더구조는 다음과 같다.
 adb_winapi_test : test app
 api                   : AdbWinApi의 소스, adb.exe가 직접적으로 사용한다.
 legacy              : 드라이버
 winusb             : AdbWinUsbApi의 소스, Usb 연동부분에 대한 소스로 AdbWinApi가 로드하여 사용한다.

adb와 유사한 클라이언트 앱을 만들고자 한다면 AdbWinApi.dll이 노출하는 API들에 대한 스펙을 숙지하는것이 중요하다.
아래에 몇가지 API들을 소개한다.

adb_api.h
ADBWIN_API ADBAPIHANDLE __cdecl AdbEnumInterfaces(GUID class_id,
                                          bool exclude_not_present,
                                          bool exclude_removed,
                                          bool active_only);

ADBWIN_API bool __cdecl AdbNextInterface(ADBAPIHANDLE adb_handle,
                                 AdbInterfaceInfo* info,
                                 unsigned long* size);

ADBWIN_API bool __cdecl AdbResetInterfaceEnum(ADBAPIHANDLE adb_handle);

ADBWIN_API ADBAPIHANDLE __cdecl AdbCreateInterfaceByName(const wchar_t* interface_name);

ADBWIN_API ADBAPIHANDLE __cdecl AdbCreateInterface(GUID class_id,
                                           unsigned short vendor_id,
                                           unsigned short product_id,
                                           unsigned char interface_id);

ADBWIN_API bool __cdecl AdbGetInterfaceName(ADBAPIHANDLE adb_interface,
                                    void* buffer,
                                    unsigned long* buffer_char_size,
                                    bool ansi);

ADBWIN_API bool __cdecl AdbGetSerialNumber(ADBAPIHANDLE adb_interface,
                                   void* buffer,
                                   unsigned long* buffer_char_size,
                                   bool ansi);

ADBWIN_API bool __cdecl AdbGetUsbDeviceDescriptor(ADBAPIHANDLE adb_interface,
                                          USB_DEVICE_DESCRIPTOR* desc);

ADBWIN_API bool __cdecl AdbGetUsbConfigurationDescriptor(
                    ADBAPIHANDLE adb_interface,
                    USB_CONFIGURATION_DESCRIPTOR* desc);

ADBWIN_API bool __cdecl AdbGetUsbInterfaceDescriptor(ADBAPIHANDLE adb_interface,
                                             USB_INTERFACE_DESCRIPTOR* desc);

ADBWIN_API bool __cdecl AdbGetEndpointInformation(ADBAPIHANDLE adb_interface,
                                          unsigned char endpoint_index,
                                          AdbEndpointInformation* info);

ADBWIN_API bool __cdecl AdbGetDefaultBulkReadEndpointInformation(
                    ADBAPIHANDLE adb_interface,
                    AdbEndpointInformation* info);

ADBWIN_API bool __cdecl AdbGetDefaultBulkWriteEndpointInformation(
                    ADBAPIHANDLE adb_interface,
                    AdbEndpointInformation* info);

ADBWIN_API ADBAPIHANDLE __cdecl AdbOpenEndpoint(ADBAPIHANDLE adb_interface,
                                        unsigned char endpoint_index,
                                        AdbOpenAccessType access_type,
                                        AdbOpenSharingMode sharing_mode);

ADBWIN_API ADBAPIHANDLE __cdecl AdbOpenDefaultBulkReadEndpoint(
                            ADBAPIHANDLE adb_interface,
                            AdbOpenAccessType access_type,
                            AdbOpenSharingMode sharing_mode);

ADBWIN_API ADBAPIHANDLE __cdecl AdbOpenDefaultBulkWriteEndpoint(
                            ADBAPIHANDLE adb_interface,
                            AdbOpenAccessType access_type,
                            AdbOpenSharingMode sharing_mode);

ADBWIN_API ADBAPIHANDLE __cdecl AdbGetEndpointInterface(ADBAPIHANDLE adb_endpoint);

ADBWIN_API bool __cdecl AdbQueryInformationEndpoint(ADBAPIHANDLE adb_endpoint,
                                            AdbEndpointInformation* info);

ADBWIN_API ADBAPIHANDLE __cdecl AdbReadEndpointAsync(ADBAPIHANDLE adb_endpoint,
                                             void* buffer,
                                             unsigned long bytes_to_read,
                                             unsigned long* bytes_read,
                                             unsigned long time_out,
                                             HANDLE event_handle);

ADBWIN_API ADBAPIHANDLE __cdecl AdbWriteEndpointAsync(ADBAPIHANDLE adb_endpoint,
                                              void* buffer,
                                              unsigned long bytes_to_write,
                                              unsigned long* bytes_written,
                                              unsigned long time_out,
                                              HANDLE event_handle);

ADBWIN_API bool __cdecl AdbReadEndpointSync(ADBAPIHANDLE adb_endpoint,
                                    void* buffer,
                                    unsigned long bytes_to_read,
                                    unsigned long* bytes_read,
                                    unsigned long time_out);

ADBWIN_API bool __cdecl AdbWriteEndpointSync(ADBAPIHANDLE adb_endpoint,
                                     void* buffer,
                                     unsigned long bytes_to_write,
                                     unsigned long* bytes_written,
                                     unsigned long time_out);

ADBWIN_API bool __cdecl AdbGetOvelappedIoResult(ADBAPIHANDLE adb_io_completion,
                                        LPOVERLAPPED overlapped,
                                        unsigned long* bytes_transferred,
                                        bool wait);

ADBWIN_API bool __cdecl AdbHasOvelappedIoComplated(ADBAPIHANDLE adb_io_completion);


ADBWIN_API bool __cdecl AdbCloseHandle(ADBAPIHANDLE adb_handle);

Posted by 삼스
Android/App개발2011. 2. 11. 17:39

원문 : http://www.wakaleo.com/blog/302-android-development-with-maven

당신이 자바개발자고 모바일디바이스용 앱을 작성하고자 한다면 안드로이드는 의심할 여지 없는 최적의 플랫폼이다. 당신이 알고있는 자바기술과 툴등 이미 익숙한 환경에서 접근이 가능하다. 이런 이유로 iPhone이나 iPad용 앱개발보다 접근이 쉽다. 또한 훌룡한 많은 툴들과 기술들을 사용할 수 있다(유닛테스트, 자동빌드, 연속빌드등등)

이 문서는 안드로이드 앱개발에 관한것이 아니다. 자동화된 빌드인프라에 안드로이드프로세스를 통합하는것에 대한 내용을 기술한다. 두개의 시리즈로 나뉘며 첫번째는 안드로이드 빌드프로세스를 메이븐을 통해 자동화하는 법에 대한 내용이고 두번째는 자동화된 테스트와 연속빌드에 대한 내용이다.

안드로이드 개발자는 이클립스를 많이 사용한다. 안드로이드는 훌룡한 이클립스플러그인을 제공하는데 안드로이드개발관련 많은 항목들을 아주 쉽게 사용할 수 있게 해준다. 실시간 안드로이드의 에러체킹과 빌드와 에뮬레이터와 연결하여 테스트하는것까지 가능하다.

그러나 멋진 이클립스플러그인도 빌드프로세스를 자동화하지는 못하고 있고 많은 메이븐의 기능들이 빠져있다(의존성을 선언만으로 관리하는것과 배포프로세스를 클린하는것). 다행히 메이븐플러그인이 존재한다. 다만 이 두가지를 잘 사용하려면 조심해서 써야 한다. 이 문서에서 안드로이드에서 메이븐을 다루는 기본적인 사항에 대한것과 몇가지 팁 그리고 방법에 대해 안내할것이다.

시작하기
먼저 안드로이드 SDK와 이클립스플러그인이 설치되었음을 가정한다. 설치가 안되었다면 먼저 설치후 진행하기 바란다. 정상적으로 환경이 갖추어졌다면 File->New->Android Project를 선택하면 아래 화면이 나타날것이다.

Android and Maven
이클립스는 좋지만 중요한 자바개발프로젝트는 하나의 자동화된 빌드스크립트가 필요하다. 안드로이드가 메이븐과 함께 작업되게 하려면 몇가지 단계의 작업을 해야 한다. 먼저 ANDROID_HOME환경변수를 안드로이드SDK가 설치된 경로로 설정한다.
export ANDROID_HOME=/opt/android-sdk-linux
메이븐 안드로이드 플러그인은 이 환경변수가 필요하다.
또한 settings.xml파일에 안드로이드 플로그인 그룹을 반드시 추가해야 한다. 그러면 커맨드라인에서 안드로이드플러그인을 쉽게 사용할 수 있다.
<pluginGroups>
  <pluginGroup>com.jayway.maven.plugins.android.generation2</pluginGroup> 
</pluginGroups>
안드로이드 AVD Manager에서 emulator를 생성하라. 여기서 입력한 AVD name은 프로젝트설정파일(pom.xml)에서 참조된다.
이제 당신의 프로젝트에서 pom.xml파일을 편집할 차례이다. 간편한 방법은 프로젝트 디렉토리에서 바로 pom.xml파일을 생성하는 것이다. pom.xml파일의 예제를 아래 나타내었다.
<project xmlns="http://maven.apache.org/POM/4.0.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.wakaleo.training.android.qotd</groupId>
  <artifactId>QuoteOfTheDay</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>apk</packaging>
  <name>QuoteOfTheDay</name>
  <dependencies>
    <dependency>
      <groupId>com.google.android</groupId>
      <artifactId>android</artifactId>
      <version>2.2.1</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>com.google.android</groupId>
      <artifactId>android-test</artifactId>
      <version>2.2.1</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.8.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-all</artifactId>
      <version>1.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-all</artifactId>
      <version>1.8.5</version>
      <scope>test</scope>
    </dependency>

  </dependencies>
  <build>
    <sourceDirectory>src</sourceDirectory>
    <testSourceDirectory>test</testSourceDirectory>
    <plugins>
      <plugin>
        <groupId>com.jayway.maven.plugins.android.generation2</groupId>
        <artifactId>maven-android-plugin</artifactId>
        <version>2.6.0</version>
        <configuration>
          <sdk>
            <platform>8</platform>
          </sdk>
          <emulator>
            <avd>em22</avd>
          </emulator>
          <deleteConflictingFiles>true</deleteConflictingFiles>
          <undeployBeforeDeploy>true</undeployBeforeDeploy>
        </configuration>
        <extensions>true</extensions>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
      </plugin>
    </plugins>
  </build>
</project>

위 pom.xml에서 중요한 항목이 두가지가 있다. 먼저 packaging타입이 apk인것이다. 이것은 안드로이드애플리케이션임을 나타낸다. dependencies에서 android와 android-test library는 provided로 설정되어 있다. 이 라이브러리는 디바이스에 존재하기 때문에 package에 포함되지 않도록 하게된다. 그리고 default source directory와 test directory를 를 변경하여 안드로이드 이클립스프로젝트 설정과 함께 사용가능해졌다.

이 pom파일은 메이블을 통해 커맨드라인명령으로 안드로이드의 공통된 작업을 할 수 있게 해준다. 예를 들어 애플리케이션을 빌드하는것이 단순해진다. 그냥 mvn clean install을 실행하면 된다. emulator에 deploy하고자 한다면 에뮬레이터를 먼저 띄워야 한다. 커맨드라인에서 띄우려면 mvn android:emulator-start를 실행하면 된다. 에뮬레이터는 1~2분 정도후에 뜨게 된다.
$ mvn android:emulator-start
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building QuoteOfTheDay 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-android-plugin:2.6.0:emulator-start (default-cli) @ qotd ---
[INFO] Android emulator command: /opt/android/android-sdk-mac_x86/tools/emulator -avd em22 
unknown
[INFO] Starting android emulator with script: /var/folders/y+/y+a+wZ-jG6WKHEm9KwnSvE+++TI/-
Tmp-//maven-android-plugin-emulator-start.sh
[INFO] Waiting for emulator start:5000
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.836s
[INFO] Finished at: Thu Oct 28 14:34:40 NZDT 2010
[INFO] Final Memory: 8M/81M
[INFO] ------------------------------------------------------------------------

그리고 에뮬레이터에 애플리케이션을 deploy하고 싶다면 mvn android:deploy를 실행하면 된다.
$ mvn android:deploy
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building QuoteOfTheDay 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-android-plugin:2.6.0:deploy (default-cli) @ qotd ---
[INFO] /opt/android/android-sdk-mac_x86/tools/adb [install, -r, /Users/johnsmart/Projects
/Training/android/android-quote-of-the-day/quoteoftheday/target/qotd-0.0.1-SNAPSHOT.apk]
[INFO] 1068 KB/s (13169 bytes in 0.012s)
        pkg: /data/local/tmp/qotd-0.0.1-SNAPSHOT.apk
Success
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.537s
[INFO] Finished at: Thu Oct 28 14:35:25 NZDT 2010
[INFO] Final Memory: 8M/81M
[INFO] ------------------------------------------------------------------------

이클립스로 돌아가서
이제 메이븐으로 당신을 안드로이드 앱을 빌드하고 배치할수 있게 되었다. 당신은 이제 이클립스를 이 파티에 초대할 필요가 있다. 프로젝트에 메이븐 기능을 활성화하는 일반적인 방법은 'Maven -> Enable Dependency Management'메뉴를 통해 가능하다. 불행히도 이작업이 불가능할것이다. 이작업을 하려면 m2eclipse Android Integration plugin 를 설치해야 한다. 이 플러그인은 m2eclipse와 Android Eclipse plugin를 연결해주는 역할을 한다. 이 플러그인을 설치하면 'Enable Dependency Management'메뉴가 나타날것이다.
메이븐이 활성화된 안드로이드 프로젝트를 'Run As-> Android Application'메뉴를 통해 실행이 가능해야 한다. 이는 에뮬레이터를 실행하고 애플리케이션을 배치할것이다. 또는 활성화된 애뮬레이터에 바로 배치할것이다.

이제 빌드하고, 배치하고 실행하는 것을 메이븐을 통한것과 이클립스를 통해 모두 가능해졌다. 이제 자동화된 빌드를 위한 문이 열렸다. 그리고 로컬 CI server를 셋업할수 있게 되었다.

다음 시리즈에서는 자동화된 유닌과 테스트를 통합하는것과 허드슨을 사용하여 당신의 안드로이드 프로젝트에 Continuous Integration을 셋업하는 방법에 대해 안내할것이다. 

Trouble shooting...
메이븐을 사용하는 가장 큰 이유는 참조라이브러리들의 관리가 쉽다는 것인데 위 절차대로 수행 후 센트럴레포지트리가 아닌 로컬레포지트리의 라이브러리를 가져오지 못하는 경우가 있을 수 있다.
콘솔에서는 참조를 잘하는데 이클립스에서는 잘 안된다면 바로 메이븐의 설정파일을 이클립스에서 참조하지 않고 있어서 발생하는것으로 생각하면 된다. 이것때문에 하루정도 삽질을 ...

eclipse 환경설정에서 Maven -> Installations에서 Add선택 후 설치한 메이븐 폴더를 지정해주면 된다.


Posted by 삼스
Android/App개발2011. 1. 6. 13:36
펌출처 : http://javafreak.tistory.com/232#comment4925306


자바 언어가 1.x 에서 2.x 대를 넘나들 시절에 thread 를 다룰때 뻔질나게 자주 쓰였던 thread 메소드가 resume, suspend , stop 인데 아쉽게도 deprecated (앞으로 쓰지 말라는 뜻) 되어서 별 수 없이 쓰레드의 상태를 관리하는 방식으로 구현을 해야 한다.

구현은 아래와 같은 간단한 코드에서 시작한다.
public class ThreadHandle implements Runnable {
    @Override    public void run() {
        // TODO Auto-generated method stub
    }
}
Runnable을 구현한 것을 볼 수 있는데, 꼭 저렇게 할 필요는 없으나 Runnable을 implement 해주지 않으면 별도의 구현체를 클래스로 정의해야하기 때문에 쓰레드를 이용해서 단위 작업을 수행하는 ThreadHandle과 같은 클래스가 자체적으로 Runnable을 구현하면 편리하다.

이제 저 상태에서 start, resume, suspend, stop 메소드를 정의해준다.
public class ThreadHandle implements Runnable {
    public void start(){
        ;
    }
    public void resume() {
        ;
    }
    public void suspend() {
        ;
    }
    public void stop() {
        ;
    }
    public void join() {
        ;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
    }
}
ThreadHandle을 사용하는 쪽에서는 아래와같이 초기화하는데 쓰레드를 사용하는 것과 별 차이가 없어보이게 된다.
    public void test_thread_handle() throws Exception {
        ThreadHandle threadHandle = new ThreadHandle();
        threadHandle.start();
    }
그리고 start() 메소드가 제대로 동작하도록 하려면 아래와 같이 ThreadHandle이 thread를 생성하고 실행하게 만들어준다.
    private Thread thisThread ;
    private String threadName ;
    public ThreadHandle(){}
    public ThreadHandle(String threadName){
        this.threadName = threadName;
    }
    public void start(){
        thisThread = new Thread(this);
        if ( threadName != null) thisThread.setName(threadName);
        thisThread.start();
    }
ThreadHandle.start() 안에서 쓰레드 인스턴스를 생성하고 start()를 호출함으로써 새로운 쓰레드가 실행된다. threadHandle.start()를 호출이 <<main>> 쓰레드에서 실행되었다면 아래와 같이 새로운 쓰레드가 분기하게 된다.

두 개의 쓰레드가 함께 실행된다.

"t-0"라고 이름을 붙인 새로운 쓰레드는 run() 메소드를 시작으로 작업을 수행하는데 쓰레드에 일시정지, 재시작, 종료 기능을 추가하기 위해서 run() 메소드 안에서 반복문을 집어넣는다.
public class ThreadHandle implements Runnable {
    ....
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while ( true ){
            processTask();
        }
    }
}
반복문에 별다른 종료 조건을 주지 않았기 때문에 위와같은 상태에서는 정신없이 작업이 계속될 것이다. while 안에 적절한 종료 조건을 넣어서 적절한 시점에 쓰레드가 종료하게 해야 한다.

종료 조건은 두가지로 나눌 수 있다.

1. run() 메소드의 반복문 안에서 더이상 작업을 계속할 필요가 없어서 끝내는 경우
2. 외부에서 ThreadHandler.stop() 메소드를 호출했을 때.

1번은 쓰레드가 스스로 종료 조건을 판단하는 것이고 2번은 외부에서 강제로 종료시키는 경우에 해당한다.

1번의 전형적인 예로 소켓 연결을 통해서 데이터를 주고받은 뒤 연결을 끊는 경우를 들 수 있다.
    public void run() {
        while ( true ) {
            int nRead = in.read(buf, 0, buf.length);
            if ( nRead < 0 ) break;
            ....
        }
    }
더이상 읽을 데이터가 없다면 while 문을 끝내고 run() 메소드가 종료하면서 쓰레드도 생을 마감하게 된다.

2번처럼 외부에서 강제로 종료시키는 경우(정확하게 말하면 종료하라고 신호만 보낸다. 자바 스레드에서는 한 스레드가 다른 스레드를 강제로 종료시킬 방법이 없다.)가 바로 여기서 구현할 내용인데 다음과 같이 thread의 상태를 나타내는 static 변수를 정의하고 이를 참조하는 stateCode 프로퍼티를 도입해서 상태를 관리한다.
public class ThreadHandle implements Runnable {
    final private static int STATE_INIT = 0x1;
    final private static int STATE_STARTED = 0x1 << 1;
    final private static int STATE_SUSPENDED = 0x1 << 2;
    final private static int STATE_STOPPED = 0x1 << 3;

    private int stateCode = STATE_INIT;
    public void start(){
        if ( stateCode != STATE_INIT) // INIT 상태가 아니면 이미 실행되었다.
            throw new IllegalStateException("already started");
        
        thisThread = new Thread(this);
        if ( threadName != null) thisThread.setName(threadName);
        thisThread.start();
        stateCode = STATE_STARTED;
    }
    ....
    public void stop() {
        if ( stateCode == STATE_STOPPED) // 이미 멈췄다면 또 호출할 필요가 없음.
            throw new IllegalStateException("already stopped");
        thisThread.interrupt();
        stateCode = STATE_STOPPED ;
    }
    ....
}
STATE_INIT은 쓰레드 인스턴스가 생성되었지만 실행되지는 않은 상태를 의미하고, STATE_STARTED 는 Thread의 start() 메소드가 호출되어서 실행된 상태를 의미한다. 마찬가지로 STATE_STOPPED 는 쓰레드가 끝났거나 끝나는 중임을(while 루프를 빠져나와서 run() 메소드가 끝나가는 단계) 나타낸다.

각 상태는 다음 상태의 조건이 되는데, start() 메소드에서 현재 상태를 확인해서 STATE_INIT이 아니라면 이미 시작되었다는 뜻이므로 예외를 던지게 한다. 마찬가지로 stop() 메소드에서도 상태가 STATE_STOPPED 라면 이미 종료가 되었거나 종료 중이므로 예외를 던진다.

위 구현은 멀티쓰레드 환경에서는 오작동을 할 가능성이 있는데, 하나의 ThreadHandle 인스턴스를 여러개의 스레드들이 공유하는 상황이라면 스레드들이 동시에 start(), stop() 메소드를 호출할 때 stateCode 프로퍼티의 일관성이 깨질 수가 있다.

스레드 A와 스레드 B가 동시에 start()를 호출할 때 A가 현재 stateCode가 STATE_INIT임을 보고 if 조건절을 무사히 통과해서 thisThread.start();를 호출할 것이다. 그리고 스레드 A가 stateCode를 STATE_STARTED로 갱신하기 전에 스레드 B가 if 조건절에 진입하면 stateCode가 STATE_INIT 이기 때문에 무사히 통과해서 또다시 스레드 인스턴스를 생성하고 실행시키게 된다.

스레드 A가 stateCode를 갱신한 후에 스레드 B가 if 조건절에 진입하더라도 stateCode가 volatile이 아니기 때문에 스레드 B는 스레드 A가 갱신한 최신의 stateCode 값을 보지 못할 수도 있다.

따라서 ThreadHandle을 멀티쓰레드에서 안전하게 하려면 아래와 같이 critical section을 잘 막아줘야 한다.
    public void start(){
        synchronized ( this ){
            if ( stateCode != STATE_INIT)
                throw new IllegalStateException("already started");
            
            thisThread = new Thread(this);
            if ( threadName != null) thisThread.setName(threadName);
            thisThread.start();
            stateCode = STATE_STARTED;
        }
    }
    ....
    public void stop() {
        synchronized ( this ){
            if ( stateCode == STATE_STOPPED)
                throw new IllegalStateException("already stopped");
            this.stateCode = STATE_STOPPED;
            thisThread.interrupt();
        }
    }
suspend(), resume() 도 위와같이 구현하면 오직 하나의 스레드만이 threadHandle의 상태를 변경할 수 있고 상태를 변경하는동안 다른 스레드들이 진입해서 일관성이 깨지는 것을 막을 수 있다.

stop() 메소드가 호출된 후 상태가 변경되었으므로(stateCode가 변경되었으므로) run() 메소드의 while 문 안에서 이 변경을 확인해서 반복문을 빠져나오는 코드를 삽입한다.
    @Override
    public void run() {
        while ( true ){            
            if ( stateCode == STATE_STOPPED)
                break//종료하라는 신호이므로 루프를 끝낸다.
            processComplexJob();
        }
    }
위에서는 processComplexJob(); 메소드를 실행하기 전에 상태를 확인했는데, 수행할 작업을 끝내는데 꽤 많은 시간이 걸린다면 그 작업을 시작하기 전에 확인해주면 응답성을 높일 수가 있다. 

하지만 if 조건절을 판단한 직후에(종료조건이 아님을 확인한 직후에) 다른 스레드가 stop() 을 호출해서 스레드의 상태를 STATE_STOPPED로 바꾸주었다면 간발의 차이로 오랜 시간이 걸리는 작업은 실행될 수 밖에 없다.

이럴 경우 processComplexJob() 메소드 안에서 별도로 stateCode를 다시 한 번 확인하지 않는 한, 일단 시작된 작업이 끝나고 나서 while문을 한 번 반복한 후 다시 if 조건절에 도착해서 STAE_STOPPED 값을 보고 루프를 빠져나올 것이다.

suspend()를 구현한 코드는 아래와 같은데 stateCode 값을 변경하기 전에 현재의 상태를 미리 확인하는 과정을 거친다.
    public void suspend() {
        synchronized ( this ){
            if ( stateCode == STATE_SUSPENDED) return;
            if ( stateCode == STATE_INIT )
                throw new IllegalStateException("not started yet");
            if ( stateCode == STATE_STOPPED)
                throw new IllegalStateException("already stopped");
            stateCode = STATE_SUSPENDED;
        }    
    }
현재의 상태가 STATE_STARTED 가 아니라면 일시 정지가 별 의미가 없으므로 위처럼 모든 상태를 체크해서 적절히 예외를 던지거나 더이상 진행하지 말아야 하는데, 모든 상태를 체크하다보니 실제 상태를 변경하는 코드보다 조건을 검증하는 코드(if 구문들)가 더 많아진다. 

여기서는 STARTED, SUSPENDED, STOPPED 의 조건만을 판단하기 때문에 코딩량을 감당할 수 있으나 구현 조건에 따라서 정지중, 정지, 종료중, 종료, 재시작중, 재시작처럼 쓰레드가 거치는 상태가 많다면 state pattern을 도입하는 것을 고려해볼만 하다. 

일시 정지 기능을 구현했으니 stateCode의 변경을 보고 스레드를 잠시 정지하는 기능을 run 메소드의 while 반복문 안에 삽입해준다.
    public void run() {
        while ( true ){
            // 상태 코드가 일시 정지라면 while문에서 계속 대기하도록 한다.
            while ( stateCode == STATE_SUSPENDED){
                try {
                    System.out.println("[handle] suspending...");
                    Thread.sleep(24 * 60 * 60 * 1000);
                } catch (InterruptedException e) {
                    if ( stateCode != STATE_SUSPENDED){
                        System.out.println("[handle] resuming...");
                        break;
                    }
                }
            }
            if ( stateCode == STATE_STOPPED){
                System.out.println("[handle] stopping...");
                break;
            }
            processComplexJob();
        }
여기서는 아주 오랜시간동안 쓰레드를 재우는 식으로 구현했는데 저렇게 오랫동안 스레드를 정지시켰을때에는 반드시 누군가가 thisThread.interrupt(); 를 호출해주어야 한다. 따라서 suspend()에서 잠재운 스레드를 다시 실행시키는 resume() 메소드에서는 반드시 thisThread.interrupt(); 를 호출해 준다.
    public void resume() {
        synchronized ( this ){
            if ( stateCode == STATE_STARTED || stateCode == STATE_INIT) return;
            if ( stateCode == STATE_STOPPED)
                throw new IllegalStateException("already stopped");
            stateCode = STATE_STARTED;
            thisThread.interrupt(); // 꼭 해줘야 한다.
        }
    }
stop() 메소드 구현에서도 interrupte() 를 호출하는데, stop() 메소드는 이미 멈춘 상태가 아니라면 언제든 호출될 수 있기 때문에 resume()과 마찬가지로 상태를 변경한 후에 스레드를 깨워주어야 한다. 

마지막으로 stateCode 를 volatile 로 바꿔주어서 run() 메소드를 실행중인 스레드가 공유 변수의 복사본만 바라보느라 외부 스레드가 갱신한 최신의 stateCode 의 값을 놓치는 일이 없게 해준다.
public class ThreadHandle implements Runnable {
    ....
    private volatile int stateCode = STATE_INIT;
이렇게 대강 start, resume, suspend, stop 기능을 구현했는데, 이런 메소드가 호출될때마다 ThreadHandle 내에서 관리하는 스레드는 아래와 같이 상태 전이가 반복된다.
스레드의 상태 전이를 관리할 때 위와같이 state diagram을 그려놓으면 구현할 때 도움이 많이 된다.  현재 상태에 따라서 호출할 수 있는 메소드의 종류가 나뉘는 것을 한눈에 알 수 있기 때문에 상태 관리 및 전이를 코드로 옮기기에 용이하다.

어떤 코드들을 보면 스레드에 걸리는 인터럽트 신호를 상태 전이의 조건으로 사용하는 경우도 있다. 

위에서는 stop()이 호출된 후 run() 메소드의 while 문 안에서 STATE_STOPPED 값을 확인하고 break; 하도록 했는데 아래처럼 while문의 조건절에서 인터럽트 신호 여부를 종료 조건으로 판단할 수도 있다.
    while ( ! thisThread.isInterrupted() ){
        .....
    }
이럴 경우 스레드 내에서 함부로 인터럽트 신호를 먹어버리지 않도록 유의해야한다. ( 관련글 : [Java] 자바 Thread의 interrupt 이해하기 ) 실행 상태가 아닌 BLOCK 상태에서 인터럽트를 걸면 스레드가 깨어나면서 인터럽트 신호가 해제되므로 catch 절에서 다시 한 번 인터럽트를 걸어서 인터럽스 신호를 복구시켜야 한다.
    try {
        Thread.sleep( sleepTime );
    catch (InteruptedException e){
        thisThread.interrupt(); // 다시 한 번 신호를 걸어준다.
    }
인터럽트 신호를 상태 전이의 힌트로 사용하는 방식도 나쁘진 않지만 스레드가 java nio 를 이용해서 스트립 입출력을 다룬다면 스레드에 인터럽트를 걸 때 신중해야 한다.

NIO 이전의 입출력 메소드들은 읽기, 쓰기 도중에 BLOCK 상태에 있으면 인터럽트에도 반응하지 않았지만 NIO 에서는 읽고 쓰는 도중에 BLOCK 상태에 있을때 인터럽트를 걸면 ClosedByInterruptException 예외가 던져지면서 스트림이 닫혀버린다. ( BLOCK 상태가 아니더라도 현재 스레드에 인터럽트가 걸려있는걸 확인하면 NIO 관련 스트림 클래스들은 스트림을 닫아버린다.)

위의 구현에는 해당되지 않으나 단지 스레드를 깨울 목적으로 인터럽트를 걸었는데 NIO 의 read() 에서 여전히 살아있는 인터럽트 신호를 보고 스트림을 닫아버리는 일이 발생한다. 이럴 경우 NIO의 read, write 실행 직전에 반드시 인터럽트 신호를 해제해줘야하고 read, write 실행 중에 인터럽트가 걸리지 않게 해줘야 하는데 이렇게되면 "입출력중" 이라는 별도의 상태를 정의해야 할 수도 있다. ( 복잡도 증가 )

Posted by 삼스
Android/App개발2010. 12. 18. 23:45


안드로이드 Strings 리소스로 format string작업을 하고자 할경우가 있다.


한국어와 영어로 '나는 안드로이드이다'를 resource로 처리하고자 할 경우를 예를 들면..


한국어 = '나는 android 이다'

영어 = 'I am an android'


가 된다...

이를 다국어 작업을 하기 위해서는 C code의 printf의 입력문처럼 아래와 같이 처리하고자 할것이다.


한국어 printf("나는 %s 이다", "android")

영어 printf("I am an %s", "android")


위와 같이 하기 위해서는 


1. strings.xml에resources태그에  xmlns:xliff 속성을 아래와 같이 추가하고...


<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">


2. string item을 아래의 형식으로 정의한다.

한국어 <string name="im_android">"나는 <xliff:g id="NAME">%s</xliff:g> 이다."</string>

영어 <string name="im_android">"I am an <xliff:g id="NAME">%s</xliff:g>"</string>


3. 코드상에서는 getString(..)함수를 사용해서 다국어처리가 된 문자열을 얻을 수 있다.

getString(R.string.im_android, "android");



Posted by 삼스
Android/App개발2010. 12. 9. 19:04


간혹 암호화가 필요할 때가 있다. 이 경우 사용가능한 대칭키 암호화에 사용되는 자바 유틸이다.

javax패키지를 사용하며 안드로이드에서 그대로 사용이 가능하다.

Base64는 안드로이드 오픈소스에서 가져다가 사용하였다.


import java.io.UnsupportedEncodingException;


import javax.crypto.Cipher;

import javax.crypto.IllegalBlockSizeException;

import javax.crypto.SecretKey;


public class CryptoUtil {

Cipher ecipher;

    Cipher dcipher;


    public CryptoUtil(SecretKey key, String algorithm) {

        try {

            ecipher = Cipher.getInstance(algorithm);

            dcipher = Cipher.getInstance(algorithm);

            ecipher.init(Cipher.ENCRYPT_MODE, key);

            dcipher.init(Cipher.DECRYPT_MODE, key);


        } catch (javax.crypto.NoSuchPaddingException e) {

        } catch (java.security.NoSuchAlgorithmException e) {

        } catch (java.security.InvalidKeyException e) {

        }

    }


public String encrypt(String str) {

try {

// Encode the string into bytes using utf-8

byte[] utf8 = str.getBytes("UTF8");

// Encrypt

byte[] enc = ecipher.doFinal(utf8);

// Encode bytes to base64 to get a string

return Base64.encodeToString(enc, Base64.URL_SAFE|Base64.NO_WRAP);

//return String.valueOf(enc);

} catch (javax.crypto.BadPaddingException e) {

e.printStackTrace();

} catch (IllegalBlockSizeException e) {

e.printStackTrace();

} catch (UnsupportedEncodingException e) {

e.printStackTrace();

} catch (java.io.IOException e) {

e.printStackTrace();

}

return null;

}


public String decrypt(String str) {

try {

    // Decode base64 to get bytes

byte[] dec = Base64.decode(str, Base64.URL_SAFE|Base64.NO_WRAP);

// Decrypt

byte[] utf8 = dcipher.doFinal(dec);

// Decode using utf-8

return new String(utf8, "UTF8");

} catch (javax.crypto.BadPaddingException e) {

e.printStackTrace();

} catch (IllegalBlockSizeException e) {

e.printStackTrace();

} catch (UnsupportedEncodingException e) {

e.printStackTrace();

} catch (java.io.IOException e) {

e.printStackTrace();

}

return null;

}

    public static void main(String[] args) throws Exception {

        // Generate a temporary key. In practice, you would save this key.

        SecretKey key = KeyGenerator.getInstance("DES").generateKey();

        

        // Create encrypter/decrypter class

        CryptoUtil c = new CryptoUtil(key, "DES");

        

        // Encrypt

        String encrypted = c.encrypt("안녕하세요 이요삼입니다...");

        System.out.println(encrypted);

       

        // Decrypt

        String decrypted = c.decrypt(encrypted);

        System.out.println(decrypted);       

    }


}

Posted by 삼스