Java8에 들어간 stream, parallelStream이 좋다는 이야기만 듣고 대충 개념만 이해한 상태에서...
"그냥 좋겠지"란생각으로 쓰려다...

간단하게 테스트 해보고 정리 ^^ 참고로 로컬 PC에서 돌렸으며, 로컬 PC의 물리 cpu 코어 개수는 4개이다.




10만건, 100만건 테스트를 그냥 간단하게 2, 3회 정도 실시해 본 결과는 아래와 같다.


10만

##  1 소요시간(초.0f) : 0.005초

##  2 소요시간(초.0f) : 0.058초

##  3 소요시간(초.0f) : 0.013초


##  1 소요시간(초.0f) : 0.004초

##  2 소요시간(초.0f) : 0.042초

##  3 소요시간(초.0f) : 0.007초


100만

##  1 소요시간(초.0f) : 0.015초

##  2 소요시간(초.0f) : 0.063초

##  3 소요시간(초.0f) : 0.056초


##  1 소요시간(초.0f) : 0.015초

##  2 소요시간(초.0f) : 0.062초

##  3 소요시간(초.0f) : 0.058초


결과 1이 단순 for loop, 결과 2가 stream, 결과 3이 parallelStream 이다.

10만건이 되었든 100만건이 되었든... 단순 for loop가 빠르다.

이로 인해 내릴 수 있는 결론은 loop문 안에서 처리되는 비즈니스 로직에 block이나 delay 요소가 없다면 단순 for loop로 돌리는게 더 빠를 수 있다. stream이나 parallelStream은 list를 stream으로 바꾸고 내부적으로 라이브러리를 사용하는 비용이 소모 되므로 단순 작업에서는 더 느릴 수 있다고 어디선가 본 것 같다 ^^;;


그럼 loop문 안에 인위적으로 sleep을 넣는다면? 소스 코드 안의 sleep을 주석을 풀고 실행하게 되면 결과는 아래와 같다.


10만

##  1 소요시간(초.0f) : 1.001초

##  2 소요시간(초.0f) : 1.037초

##  3 소요시간(초.0f) : 0.144초


##  1 소요시간(초.0f) : 1.0초

##  2 소요시간(초.0f) : 1.077초

##  3 소요시간(초.0f) : 0.161초


100만

##  1 소요시간(초.0f) : 10.012초

##  2 소요시간(초.0f) : 10.09초

##  3 소요시간(초.0f) : 1.289초


##  1 소요시간(초.0f) : 10.004초

##  2 소요시간(초.0f) : 10.105초

##  3 소요시간(초.0f) : 1.291초


단순 for loop와 stream은 거의 차이가 없으며 parallelStream이 압도적으로 빠르다.

list를 parallelStream 으로 변환하고 라이브러리를 로드하고 사용하는 비용을 쓰더라도 loop문 안에서 지연이 발생해서 loop를 도는 속도가 현저히 떨어지게 된다면 병렬로 나눠 처리하는 것이 좋다는 결론을 얻게 된다.


하지만 loop 안에서 지연이 발생한다고 해서 무조건 parallelStream 을 쓰는 것이 좋을까?

경험 상 병렬로 작업을 처리한다 하더라도 loop 내부에 DB Insert, Update, Delete와 같은 것이 있다면 DB에 크나큰 부담으로 시스템 장애를 일으킬 수 있으니 조심해야 한다.

또한 parallelStream 이 CPU를 점유할 경우 다른 parallelStream 작업에도 영향을 미칠 수 있으므로 조심 ^^


덧. for loop보다 stream이 이론 상으로 더 빨라야 하는 것 같은데... 오히려 stream이 느리네..

stream이 더 빠른 경우가 무엇인지에 대한 고민이 필요 ^^;;



저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

보통 개발 시에 파일 업로드를 하려 하면 html form에서 multipart/form-data로 파일을 선택해서 업로드 하고, 이를 서버 단에서 받아 처리를 하게 된다.

하지만 이런 방법이 아니라 원격지의 이미지 파일을 읽어온 후 필요 시 리사이지, 그리고 나서 다시 다른 곳에 있는 서버로 파일을 업로드 하는 기능이 필요해 개발을 하다 보니, 많이 사용되는 방법이 아니기에 정리해 둔다.


<원격지에서 파일을 읽어 들여 파일 객체로 만든 후 리사이징, 업로드하기>


<이미지 리사이징>



저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

구매후기 이미지 업로드에서 오류가 나고 있어서 해당 내용을 검토 중에 알게 된 내용을 정리합니다.

기본적으로 아래와 같은 방식으로 업로드가 되고 있습니다.


브라우저 -> php -> Java


그런데 문제는 어느 한쪽의 문제가 아니라 php, Java 둘다 문제가 발생...

그리고 문제의 원인은 둘다 Version Up이였습니다.


1. 먼저 Java

Error Message가 "Required MultipartFile parameter 'file' is not present" 라고 발생.

구글링 하니 http://stackoverflow.com/questions/25830503/spring-mvc-http-status-400-required-multipartfile-parameter-file-is-not-pre 가 검색 됨.


아래와 같은 방식으로 수정


AS-IS : <bean id="multipartResolver" class="**org.springframework.web.multipart.commons.CommonsMultipartResolver**" />

TO-BE : <bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver" />


2. 다음으로 PHP


PHP에서 Java 쪽으로 파일을 올릴 때는 CURL을 사용했는데 기존에 /tmp에 올라온 임시 파일을 읽어 들일 때는 @를 사용해서 리소스를 읽어 들였음.

php 5.5.0 기준 Deprecated 된 기능이라서 다른 방법으로 수정



저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

Spring Batch를 그닦 많이 써본 경험이 없지만, 특정 요구 사항이 있어서 개발하게 되었습니다.

작업을 하면서 겪은 오류를 어떻게 해결 했는지 History 및 Backup을 위해 적어 둡니다.


보통 배치는 Reader와 Writer로 구분합니다.

특정 정보를 Reader에서 읽어 들여서 필요한 내용을 가공하고 Writer에서 필요한 행동을 합니다.


그런데 Reader에서 정보를 읽어 들이고 가공하는 시간이 오래 걸리면서 Writer에서 DB Connection을 못 찾게 되고, Writer 작업을 수행하지 못하게 되는 경우가 발생했습니다.


구글링을 한 결과 "autoReconnect 값을 true로 해라", "validationQuery를 날려라" 등등이 나왔지만 유효하지 않은 해결책이였습니다.


결국 찾아낸 해결 방법은 applicationContext.xml에서 database 관련 다음의 옵션을 수정하는 거였습니다.


testOnBorrow : true


이 옵션은 커넥션 풀에서 커넥션을 가져올 경우 유효한지 검사하는 것인데

일반적인 웹서비스에서는 커넥션이 자주 맺고 끊어져서 이 옵션과 상관없이 잘 동작 하는데

배치와 같이 특별한 경우에는 기존 커넥션을 사용하려다가 연결 해제된 커넥션을 검증 안하고 쓰면서 발생한 문제였습니다.

즉 testOnBorrow가 false인 경우에는 유효하지 않은 커넥션을 사용할 수 있으므로, Writer에서 에러가 종종 발생하게 된 것입니다.


저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

List에서 불필요한 item을 제거하기 위해 loop를 돌리게 되면 제거 후 size가 맞지 않는 일로 인해 null point exception을 만나기가 쉽다.

이럴 경우 제대로 된 방법이 무엇인지는 모르지만...

실제로 돌려보고 Exception이 나지 않은 방법을 찾아 정리 차원에서 남겨 둔다.


검색해서 참고한 URL은 https://stackoverflow.com/questions/17279519/removing-items-from-list-in-java/17279565#17279565?newreg=0dee1db24aaa4154a0cebdc173ed5aeb


List<Object> toRemove = new ArrayList<Object>();

for(Object a: list){

if(a.getXXX().equalsIgnoreCase("AAA")){

toRemove.add(a);

}

}

list.removeAll(toRemove);


다른 방법들은 다 해 봤지만 null point exception 발생 함.


저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

급하게 바코드 인식 관련 프로그램을 만들다 보니 오픈소스를 검색하게 되었고 Google에서 제작한 zxing 이란 것을 발견하게 되었습니다. (GitHub : https://github.com/zxing/zxing)

해당 코드와 관련된 포스팅들이 여럿 나왔지만 다 조금 씩 부족한 부분들이 있어서 정리 차원에서 올립니다.

물론 제가 올린 내용도 부족한 부분이 많습니다.


1. 외부 앱을 이용한 방법


이미 외부 앱을 이용해서 구동하는 방법은 잘 정리된 곳이 있습니다.

관련 포스팅은 http://code.tutsplus.com/tutorials/android-sdk-create-a-barcode-reader--mobile-17162 를 참고하시면 됩니다.

간단하게 정리하자면 Github에서 다운로드 받은 소스 중에 IntentIntegrator.java와 IntentResult.java 두개의 파일만 사용하는 것입니다.

특정 Button의 onClick Event에서 아래 내용을 호출할 수 있도록 하면 됩니다.




그러면 onActivityResult에서 특정 앱에서 Scan한 값을 받게 됩니다.

리턴 받게 된 값은 아래와 같은 형식으로 받으면 됩니다.




이렇게 하는 방법은 매우 쉽지만 외부 앱을 추가로 깔아야 한다는 단점이 있습니다.

자체적으로 카메라를 구동 시키고 바코드나 QR코드를 인식할 수 있다면 좋겠다는 생각에 다른 방법을 찾아 보았습니다.

조금 무식하지만 필요한 소스 코드를 다 포함하고, 자체적으로 구동하는 것이죠.


2. 자체 구현


사실 소스 코드 크기가 좀 되기에 자체 구현하기가 망설여 졌지만 일단 구현하였습니다.

자체 구현 관련되어서 참고한 사이트는 http://fishbear.tistory.com/3 입니다.

그런데 ant로 core.jar 파일을 만들어야 한다는데... build.xml 파일이 없더라는 ㅠㅠ

그래서 더 찾아보니 zxing의 특정 버전부터 maven으로 바뀌었다고 하네요.

그래서 maven으로 jar 파일을 만들었지만... 호출해야 하는 Activity인 CaptureActivity 가 해당 jar 파일 안에 없더군요 ^^;;

어쩔 수 없이 해당 소스코드를 다 lib 프로젝트에 포함 시켰습니다.

이로 인해 생기는 Resource 관련 오류들을 fix하기 위해 zxing 안에 있는 res 파일들을 포함 시켰구요 ㅠㅠ

어째든 에러가 없도록 만든 다음... 다음과 같이 사용을 하면 됩니다.(fq는 AQuery 인스턴스입니다. AQuery를 사용 안하시는 경우는 native로 구현 하세요.)




하지만 이렇게 하게 될 경우 SCAN이란 Activity가 없다는 에러가 발생합니다.

따라서 Manifest 파일에 다음과 같이 정의를 해 줘야 합니다.




이렇게 하게 되면 성공적으로 바코드 인식 프로그램이 내부적으로 뜨게 됩니다.

그리고 바코드 인식 후 onActivityResult에서 다음처럼 받아 사용하면 됩니다.




49374란 숫자는 intent를 띄울 때 넘어가는 request code 같은데 정확하게 저 숫자가 뭘 의미하는지는 모르겠습니다.

다만 구분하기 위한 숫자이기에 특정 constant에 넣어 사용했습니다.


이상 간단하게 정리해본 Bar Code, QR Code 인식 Android 앱 만들기 정리 포스팅이였습니다.






저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글 하나 달렸습니다.
  1. 모바일에선 소스코드가 안 보이네요. PC버젼으로 봐 주세요 ^^
secret

MySQL에 DateTime 필드에 값이 없을 경우 기본 값으로 "0000-00-00 00:00:00" 들어가는 경우가 있다.

이럴 경우 쿼리해 온 값이 Java의 Model 객체에 Setter로 값을 넣는 과정에서 에러가 발생한다.

에러 메시지는 다음과 같다.


"Cannot convert value '0000-00-00 00:00:00' from column 4 to TIMESTAMP"


뭐 '0000-00-00 00:00:00' 값은 timestamp 값으로 바꿀 수 없다는 메시지 같은데...

이럴 경우 어떻게 해결해야 하나 고민하다가 구글링을 통해 알게 된 내용을 정리한다.

보통 Model 객체의 Setter를 아래와 같이 작성하게 된다.



setter에서 Date 객체로 받아야 하는데 값을 넘겨줄 수 없기에 이런 경우에는 DB 접속 옵션을 아래처럼 바꾸어 주면 된다.


jdbc:mysql://localhost/myDatabase?zeroDateTimeBehavior=convertToNull


zeroDateTime일 경우 null을 반환하라는 의미같은데

위와 같이 옵션을 설정한 후 디버깅을 해 보면 zeroDateTime일 경우 setter에 실제로 null 값이 들어오게 된다.


참고 : http://stackoverflow.com/questions/1363527/cannot-convert-0000-00-00-000000-to-timestamp

저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

이클립스 사용하다 보면 jquery.min 파일에서 "Missing semicolon" 이라는 오류가 뜨게 된다.

이럴 경우 다음과 같은 방법으로 예외 처리를 하게 되면 된다.


출처 : http://metalbird.tistory.com/59


=============================================================================================


query min.js 의 경우 이상없이 작동하나 에러가 표시되게 된다.


이게 싫어서 일반 jquery*.js로 쓰기도 했으나 간단하게 해결할수 있는 방법이 있어서 메모한다.




에러 예외처리에 추가하는것이다.


Project > Properties > JavaScript > Include Path > 아래그림 참고


**/jquery*.js


출처: okjsp - http://www.okjsp.pe.kr/seq/185342




저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

다국어 사용을 위해서 그동안 Struts2 기반 MessageUtil만 사용하다가 Spring 기반으로 변경하기 위해 구글링, 작업한 내용을 정리 차원에서 올립니다.


1. applicationContext.xml에 다음 내용을 추가


<!-- for MessageUtil -->

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">

        <property name="basenames">

            <list>

                <value>/WEB-INF/messages/messages</value>

            </list>

        </property>

        <property name="cacheSeconds" value="100000" />

        <property name="defaultEncoding" value="UTF-8" />

    </bean>

    <bean id="messageSourceAccessor" class="org.springframework.context.support.MessageSourceAccessor">

        <constructor-arg ref="messageSource"/>

    </bean>

    <bean id="messageUtil" class="package.path.MessageUtil">

        <property name="messageSourceAccessor" ref="messageSourceAccessor"/>

    </bean>


2. MessageUtil




참고 사이트

http://springsource.tistory.com/113

http://blog.naver.com/pureb612b/10120505318

http://zinlee.tistory.com/204

저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

출처 : http://warmz.tistory.com/727


1. HttpServletRequest, HttpServletResponse


2. HttpSession


3. Local

 - java.util.Locale 지역 정보


4. InputStream, Reader

 - HttpServletRequest의 getInputStream(), Reader


5. OutputStream, Writer

 - HttpServletResponse의 getOutputStream(), Writer - 서블릿 형태로 만들때 사용한다.


6. @PathVariable

 - @RequestMapping의 URL {} 부분의 패스 변수를 받는다.

    만약 타입이 틀린 값이 들어오면 HTTP 400 - Bad Request 가 전달 된다.

1
2
@RequestMapping("/board/{id}")
public void view( @PathVariable("id") int id ) {...}
 

7. @RequestParam

 - 스프링 내장 변환기가 다룰 수 있는 모든 타입을 지원한다.

   해당 파라미터가 없다면 HTTP 400 - Bad Request 가 전달 된다.

 -  file의 경우는 <input type="file" name="file" /> 에 매핑 된다.

1
2
3
public String edit( @RequestParam("id") int id,
            @RequestParam("title") String title,
            @RequestParam("file") MultipartFile file ) {...}
 

 -  맵 형태로 받으면 모든 파라미터 이름은 맵의 키에 파라미터 값은 맵의 값에 담긴다. 

1
public String add( @RequestParam Map<String, String> params ) {...}

-  파라미터가 필수가 아니라면 required = false 로 지정하면 된다. 

   이때 파라미터가 없으면 NULL이 들어간다. default 값을 지정 할 수도 있다. 

1
2
3
public void view( @RequestParam(value = "id",
                        required = false,
                        defaultValue = "0" int id) {..}.
 

8. @CookieValue

 - @RequestParam과 동일 하며 쿠키값을 가져올 때 사용한다.

1
2
public String check( @CookieValue("check") String check,
                required = false, defaultValue = "" ) {...}
 

9. @RequestHeader

 - 헤더 정보를 메소드 파라미터에 넣어 준다. Ajax로 처리할때 $.ajax(...) 에서 head에 특정 값을 넣고 여기서 받아서 

   있으면 ajax이고 없으면 일반페이지라는 식으로 이용하면 된다.

1
public String header( @RrequestHeader("ajax") String ajax ) {...}
 

10. Map, Model, ModelMap

 - view를 String으로 리턴해 주고 Attribute를 Map, Model, ModelMap 에 담을 수 있다.

 

11. @ModelAttribute

 - 파라미터를 Object형태로 받을때 사용된다. 일반적인 파라미터 형태로 쓰인 경우 타입이 일치하지 않으면 객체에 매핑 되지 

   않으며 에러는 발생 시키지 않는다. 자동으로 ModelMap에 담기므로 modelMap.addAttribute를 해 줄 필요가 없다.

1
public void update( @ModelAttribute("board") Board board) {...}

 - 메소드에도 @ModelAttribute를 설정 할 수 있다. 리턴값이 항상 나머지 컨트롤러에 자동 추가 되며 보통 참조용 데이터 등에 

   이용된다.

1
2
@ModelAttribute("emailList")
public Map<String, String> getEmailList() { ... }
 

12. Errors, BindingResult

  - 모델의 값을 검정한다. 이때 BindingResult나 Errors의 파라미터 값의 위치는 반드시 @ModelAttribute 뒤에 위치해야 한다. 

     자신의 바로 앞에 있는 @ModelAttribute 파라미터의 검정 작업만 하기 때문이다.

1
2
@RequestMapping(value = "/board/add", method = RequestMethod.POST)
public String add( @ModelAttribute("board") Board board, BindingResult result ) {...}
 

13. SessionStatus

 - 모델 오브젝트를 세션에 저장하여 계속 사용한다. 더이상 모델 오브젝트를 사용하지 않을 때는 세션에서 제거해 줘야 한다.


14. @RequestBody

 - HTTP body 부분만 전달 한다. XML 이나 JSON 으로 출력 할 경우 사용한다.

   리턴타입의 @ResponseBody 를 참조하자.

 

15. @Value

 - 프로퍼티값이나 값을 파라미터에 적용한다.

1
2
3
4
5
6
7
8
9
public class BoardController {
    @Value("${eng.url}")
    String engUrl;
 
    @RequestMapping(..)
    public String gotoEng() {
        return this.engUrl;
    }
}
 
 - 위는 프로퍼티중 eng.url 의 값을 String engUrl에 매핑 시키고 메소드에서 사용한 케이스다. 파라미터에도 적용 된다.

1
2
3
public String gotoEng( @Value("${eng.url}") String engUrl ) {
    return engUrl;
}
 

16. @Valid

 - JSR - 303 검증기를 이용해서 @ModelAttribute를 검정하도록 한다.

1
public String add( @Valid @ModelAttribute("board") Board board, BindingResult result ) { ...}


저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

스마트폰에서 Pull to Refresh를 사용하는 것은 매우 흔해진 일이고, 그래서 안드로이드에서도 Pull to Refresh를 구현한 오픈소스가 있다.

현재 프로젝트에서는 handmark.pulltorefresh liabrary를 이용하는데 리스트를 당겼을 시 나오는 폰트의 색깔을 변경하는 것이 어떻게 하는 것인지 몰랐다.

해당 라이브러리의 소스를 분석해서 찾아 들어가 보니 아래와 같은 소스를 만날 수 있었다.


// Text Color attrs need to be set after TextAppearance attrs

if (attrs.hasValue(R.styleable.PullToRefresh_ptrHeaderTextColor)) {

ColorStateList colors = attrs.getColorStateList(R.styleable.PullToRefresh_ptrHeaderTextColor);

if (null != colors) {

setTextColor(colors);

}

}


R.styleable에 정의된 PullToRefresh_ptrHeaderTextColor를 이용한다는 것이었다.

R.styleable이 뭐하는 것인지 또 구글링...

요놈은 attrs.xml 파일에 정의된 내용을 사용하는 것이라는 결론을 내렸다.

attrs.xml 파일 안을 뒤져보니 아래와 같이 ptr에 정의된 내용이 나오더라.


<declare-styleable name="PullToRefresh">


        <!-- A drawable to use as the background of the Refreshable View -->

        <attr name="ptrRefreshableViewBackground" format="reference|color" />


        <!-- A drawable to use as the background of the Header and Footer Loading Views -->

        <attr name="ptrHeaderBackground" format="reference|color" />


        <!-- Text Color of the Header and Footer Loading Views -->

        <attr name="ptrHeaderTextColor" format="reference|color" />


        <!-- Text Color of the Header and Footer Loading Views Sub Header -->

        <attr name="ptrHeaderSubTextColor" format="reference|color" />


ptrHeaderTextColor, ptrHeaderSubTextColor라는 속성을 가진 놈을 어떻게 잘 요리하면 색이 바뀔 듯도 한데... 정확히 사용법을 몰라 또 구글링...

결론은 아래와 같이 layout xml에 정의하면 된다.


 <com.handmark.pulltorefresh.library.PullToRefreshGridView

        xmlns:ptr="http://schemas.android.com/apk/res-auto"

......

ptr:ptrHeaderTextColor="#336699"

ptr:ptrHeaderSubTextColor="#336699"/>


ptr이란 namespace를 정의한 후 해당 속성을 정의하면 된다.

바로 빌드하고 테스트 해 보니 제대로 나오는 것을 확인 ^^


오늘 알게 된 내용도 나 혼자 잊어 버리지 않고자 적어둔 내용이므로, 정리가 매우 불성실하다 ^^

저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

보통 android에서 listView를 생성하는 방법은 ArrayAdapter에서 getView를 통해 하나씩 하나씩 각각의 position 정보를 리턴해서 만든다.

만들줄만 알았지, 가져다 쓰는 listView에서 각 개체에 접근하는 법을 몰라 헤메다가 나름 정리해 보았다.


1. Activity가 되었던 Fragment가 되었던 OnScrollListener를 implements 한 후 필요한 method를 구현한다.

2. 필요한 method 중 onScroll과 onScrollStateChanged 에서 필요한 내용에 접근한다.

3. onScroll method에서는 다음과 같은 방법으로 처리


@Override

public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

for(int i = firstVisibleItem; i<(firstVisibleItem + visibleItemCount); i++) {

}

}


위에서 넘겨주는 파라미터 정보를 사용한다.

view는 전체 listView... firstVisibleItem은 현재 스크롤 상태에서 첫번째 보이는 아이템 키, visibleItemCount는 현재 화면상에 보이는 아이템 개수, totalItemCount는 보이지 않아도 현재 listView에 적채되어 있는 모든 아이템 개수이다.

이 파라미터를 사용하여 필요한만큼 loop를 돌린다.


이때 i의 값이 -1일 경우에는 잘못된 위치 정보이므로 방어 코드를 삽입한다.


if(i == AdapterView.INVALID_POSITION) {

break;

}


4. i 번째 View 객체를 다음과 같은 방법으로 가져온다.


ViewGroup child = (ViewGroup) view.getChildAt(i);


5. 가져온 child 객체에 접근하여 원하는 특정 작업을 한다.


=============================================================================================


그냥 내가 만들어 두고 내가 잊어 버리지 않게 할려고 정리하는 것이므로, 매우 불친절한 설명 --;;

죽어도 못 알아 듣겠다고 생각이 든다면 그때 질문해 주시면 답변 드릴께요 ;;;;


저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

일반 EditText에 별도의 submit 버튼을 만들지 말고 Software keyboard에 submit button이 나오도록 하기 위해서는 아래와 같이 작성해야 한다.


<EditText 

..............

       android:imeOptions="actionSearch"

       android:singleLine="true"/>


즉 imeOption에 actionSearch 속성을 주고, 엔터 시 라인이 개행되지 않도록 singleLine을 true로 주어야 한다.

간단하지만, 모르면 헤맬 수 있는 사실. ^^



저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

프로젝트 도중 SharedPreference를 사용해서 데이터를 저장하는 부분이 있었다.

그런데 SharedPreference에는 배열 값이 저장이 안되는 듯 해서 정말 그런가 궁금했고, 궁금 증을 해결하기 위해 /data 디렉토리를 뒤져봐야 했다.

하지만 adb나 DDMS를 통한 접근은 Permission denied만 나오고, 접근이 불가한 상태였다.

역시 삽질과 구글링을 통해 해결법을 알아 냈다.


1. 현재 연결되어 있는 장비 알아 낸기


adb devices 명령어를 통해서 연결 된 device 목록을 알아 온다.


2. 해당 장비에 접속하기


adb -s emulator-5554 shell 와 같은 명령어로 해당 장비에 접근한다.


3. 내가 개발 중인 프로젝트 권한으로 변경하기


가령 내가 개발중인 프로젝트의 package 이름이 com.nhnent.project라면, run-as com.nhnent.project 라고 날리면 실행 권한이 app의 권한이 된다. adb로 접속한 유저 권한이 아니라 앱의 권한이므로 app이 접근 가능한 /data 디렉토리 안으로 바로 들어갈 수 있게 된다.


4. shared_prefs 디렉토리 접근하기


보통 SharedPreference 디렉토리는 /data/data/패키지/shared_prefs 이다. 해당 디렉토리로 들어가면, 현재 사용중인 SharedPreference의 xml 파일들이 보인다. 해당 파일을 cat으로 열어봐서 해결하였다.


일단 결론은 SharedPreference에 배열 값은 저장이 안되고 있었다이다.

PUT을 통한 데이터 저장은 xml로 변환이 되지만, 그외 변수에 직접 대입하는 값은 메모리에만 남아 있고, xml로는 변환이 되지 않는다.


오늘 삽질을 통해 얻은 결론!

삽질을 통해야지만, 내 지식이 된다. -.-;;



저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

View 화면이 상, 하, 좌, 우에서 날라 들어오거나 다시 들어가는 간단한 애니메이션이 필요해서 검색 중 http://developer.android.com/guide/topics/graphics/view-animation.html 를 발견하게 되었다.

이를 응용해서 현재 개발중인 프로젝트에 사용하였다.


관련 소스는 아래와 같다.

자세한 소스 분석은 언제나 그렇듯이 알아서 ^^


res/anim 아래 animation xml 파일을 만든다. 아래와 같이 from x가 100%에서 시작하면 오른쪽에 숨어 있다가 to x인 0의 위치, 즉 원래 화면 안으로 들어오는 부분이다. 따라서 이름도 slide_in_from_right.xml로 만들었다.

이처럼 100%, -100% 그리고 X, Y만 바꿔가면 상, 하, 좌, 우 애니메이션을 만들 수 있다.


<?xml version="1.0" encoding="utf-8"?>

<translate xmlns:android="http://schemas.android.com/apk/res/android"

    android:duration="@android:integer/config_longAnimTime"

    android:fromXDelta="100%p"

    android:toXDelta="0" />


그리고 나서 위 링크에서 볼 수 있는 것처럼 아래와 같이 사용하면 된다.


Animation inFromRightAni = AnimationUtils.loadAnimation(this, R.anim.slide_in_from_right);

view.startAnimation(inFromRightAni);



저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글 하나 달렸습니다.
  1. 요즘은 XE개발 안하시나요 ㅠ
secret

구글링을 통해서 GPS의 좌표를 통한 거리 구하는 공식을 어렵게 찾아 적용했다.

내용은 아래와 같다.


if(oldCoordinate != null) {

// TODO 거리 누적...

double theta = oldCoordinate.getLongitude() - Double.parseDouble(lp.getLongitude());

double dist = Math.sin(Utility.deg2rad(oldCoordinate.getLatitude())) * Math.sin(Utility.deg2rad(Double.parseDouble(lp.getLatitude()))) +

Math.cos(Utility.deg2rad(oldCoordinate.getLatitude())) * Math.cos(Utility.deg2rad(Double.parseDouble(lp.getLatitude()))) * Math.cos(Utility.deg2rad(theta));

if(dist < -1 || dist > 1) {

logger.d("dist : %s", dist);

}

dist = Math.acos(dist);

dist = Utility.rad2deg(dist);

dist = dist * 60 * 1.1515; // statute miles. 단위는 기본 마일.

dist = dist * 1.609344;

if(dist != Double.NaN) {

totalDist += dist*1000;

}

} else {

oldCoordinate = new Coordinate();

}

if(!"".equals(lp.getAltitude())) oldCoordinate.setAltitude(Double.parseDouble(lp.getAltitude()));

if(!"".equals(lp.getLatitude())) oldCoordinate.setLatitude(Double.parseDouble(lp.getLatitude()));

if(!"".equals(lp.getLongitude())) oldCoordinate.setLongitude(Double.parseDouble(lp.getLongitude()));


하지만 dist 값이 -1보다 작거나 1보다 큰 값이 반환되는 경우 Math.acos에 잘못된 파라미터로 들어가게되고, acos는 NaN을 반환하게 된다.

팀 동료와 고민하면서 구글링하게 된 결과 나온 것은 아래와 같다.


float[] distance = new float[3];

if(oldCoordinate != null) {

Location.distanceBetween(oldCoordinate.getLatitude(), oldCoordinate.longitude,

Double.parseDouble(lp.getLatitude()), Double.parseDouble(lp.getLongitude()), distance);

totalDist += distance[0];

} else {

oldCoordinate = new Coordinate();

}

if(!"".equals(lp.getAltitude())) oldCoordinate.setAltitude(Double.parseDouble(lp.getAltitude()));

if(!"".equals(lp.getLatitude())) oldCoordinate.setLatitude(Double.parseDouble(lp.getLatitude()));

if(!"".equals(lp.getLongitude())) oldCoordinate.setLongitude(Double.parseDouble(lp.getLongitude()));


그냥 Location 안에 static으로 선언 된 distanceBetween 메소드를 이용하면 된다.

알기까지 무지막지한 삽질...

알고나면 허무한 ㅠㅠ

저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

eclipse 또는 adt를 통해 android를 개발하다 보면 에뮬레이터가 늦게 떠서 개발에 어려운 경우가 있다.

이럴 경우 다음과 같은 방법으로 에뮬레이터의 속도를 빠르게 할 수 있다.


1. Window > Android SDK Manager를 연다.

2. SDK Manager에서 다음의 package를 설치 한다.



자신의 버전에 맞는 SDK에서 Intel x86 칩셋 이미지를 다운 받는다. 이미 설치 되어 있을 경우에는 설치할 필요가 없다.

가장 하단의 Extras에 있는 Intel x86 Emulator Accelerator (HAXM)도 다운 받는다.

자신의 컴퓨터에 따라 칩셋 이름이 달라질 수 있다.(아마도)


3. Window > Android Virtual Device Manager을 연다.

4. 이미 만들어진 AVD가 있다면 Edit를 누르고, 없다면 New를 눌러 AVD를 설정한다.



위에서 보는 것처럼 CPU/ABI를 Intel Atom(x86)으로 설정한다. 물론 자신의 CPU 칩셋에 따라 옵션이 다를 수 있다. 기본은 ARM으로 설정되어 있다.


이렇게 할 경우 에뮬레이터의 실행 속도가 빨라지게 된다.

다만 내 경우에는 위와 같이 할 경우 아래와 같은 에러 메시지가 떨어졌다.


[2013-05-16 18:08:07 - Emulator] HAX is not working and emulator runs in emulation mode


이와 관련해서 구글링 하니 다음과 같은 내용을 찾을 수 있었다.


http://blog.83rpm.com/archives/1045


HAX를 사용하기 위해 intel에서 프로그램을 다운받아 설치해야 한다는 것이다.

하지만 그래도 에러 발생 ^^; 똑같은 에러 메시지를 검색하니 다음과 같은 내용이 보여진다.


http://answer.beautifultablet.com/android-sdk/hax-is-not-working-and-emulator-runs-in-emulation-mode/


다른 내용은 자세히 보지 않아 모르겠고 "If your computer does not support Intel Virtualization Technology then it will come out this error." 라는 부분을 보면, Intel 가상화 기술을 지원하지 않는다 라는 메시지가 보인다.

즉 지원하지 않는 CPU? 인 것으로 보인다.

이럴 경우에는 그냥 ARM으로 하라는 친절한(?) 설명까지 ^^;;

결국 HAX를 이용한 에뮬레이터 가속은 현재 PC에서는 힘들어 보여서, 기본 셋팅으로 진행할 수 밖에 없었다.




저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글 하나 달렸습니다.
  1. 아.. HAX적용하면 정말정말 빠른데요 ㅜ
secret
Site-mesh의 decorators.xml 파일에서 excludes를 정의해서 사용하려 했지만
여전희 decorator 파일이 적용 되는 상황이 발생했다.
여기 저기 구글링 해 봤지만 내 검색 능력의 한계인가 잘 찾질 못했다.
결국 사내 아는 분에게 문의해서 원인을 알게 되었다.


sitemesh.xml decorators.xml


위의 decorators.xml에서 uninstall 부분만 decorator를 적용하지 않으려 했지만 처음에는 적용 되지 않았고,
이에 sitemesh.xml의 아래 부분이 추가 되면서 문제가 해결 되었다.

<property name="decorators" value="/WEB-INF/decorators.xml" />
<excludes file="${decorators}" />

삽질은 정말 해도 해도 끝이 없는거 같다.
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
실 서비스 도중에 대량의 작업을 동기식으로 작업하려면 부담이 되는 경우가 많다.
이럴 경우에는 비동기식으로 작업을 하게 되는데, 보통 Queue에 작업해야 할 목록을 집어 넣어두고, 나중에 Cosumer가 해당 작업을 하도록 만드는 것이 일반적이다.
이런 작업을 하는데 유용한 도구가 ActiveMQ이며 이번 주소록 프로젝트를 하면서 ActiveMQ를 사용하게 되어 정리차원에서 블로그에 올려본다.

설치하기 이전에 우선 환경을 셋팅해야 한다.

JDK는 1.5.x 버젼 이상 설치되어 있어야 하며, " JAVA_HOME" 환경 변수는 JDK가 설치된 경로로 잡혀 있어야 한다. Maven은 1.0.2 이상 설치되어 있어야 한다.(Maven은 소스 설치 또는 개발자 버젼 설치를 위해 필요한 듯...)
또한 관련 JAR 파일들은 미리 classpath에 있어야 한다.
따라서 Maven을 사용한다면 dependencies 아래 아래와 같이 추가하면 된다.



<!-- For Active MQ -->
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-core</artifactId>
    <version>5.2.0</version>
</dependency>
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-web</artifactId>
    <version>5.2.0</version>
</dependency>
<dependency>
    <groupId>org.apache.xbean</groupId>
    <artifactId>xbean-spring</artifactId>
    <version>3.3</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>2.5.6.SEC01</version>
</dependency>

 환경 셋팅을 다했다면 ActiveMQ를 다운로드 해서 설치해야 한다. 다운로드는 http://activemq.apache.org/download.html 에서 다운 받을 수 있으며, 현재 기준으로 안정 버젼은 5.3.0이다.

설치는 다운로드 받은 파일을 적당한 곳에 풀어주면 된다.

설치 후 ActiveMQ를 실행 시킨다.

cd [activemq_install_dir]
bin\activemq

위와 같이 하던가 아니면 설치 디렉토리의 activemq.bat 파일을 더블클릭 해도 된다.
Unix(Linux) 환경에서는 아래와 같이 하면 된다.

cd [activemq_install_dir]
bin/activemq

OR

bin/activemq > /tmp/smlog  2>&1 &;
Note: /tmp/smlog may be changed to another file name.

ActiveMQ의 자세한 Start 방법은 http://activemq.apache.org/version-5-getting-started.html#Version5GettingStarted-StartingActiveMQ 를 참고하면 된다.

ActiveMQ를 실행 후 제대로 돌고 있는지 확인하기 위해서는 61616 포트를 확인해 보면 된다.

Windows

netstat -an|find "61616"
  TCP    0.0.0.0:61616          0.0.0.0:0              LISTENING

Unix(Linux)

netstat -an|grep 61616
tcp        0      0 :::61616                    :::*                        LISTEN 

위와 같이 61616 포트를 Listen 하고 있다면 제대로 ActiveMQ가 실행 되고 있는 것이다.
또한 위와 같이 ActiveMQ가 실행되고 있는 것이 확인되면 아래의 url로 접근해서 Queue의 상태를 확인해 볼 수 있다.

http://localhost:8161/admin

물론 해당 서버가 로컬이 아니라면 위의 localhost 도메인을 적절히 수정해야 할 것이다.

기본적으로 ActiveMQ를 위와 같은 방식으로 띄운다면 ActiveMQ가 설치된 디렉토리의 conf 디렉토리 밑에 있는 activemq.xml 파일을 가지고 뜬다. 하지만 원하는 디렉토리 밑에 있는 activemq.xml을 참조하도록 하기 위해서는 아래와 같이 띄우면 된다.

activemq설치디렉토리/bin/activemq xbean:file:원하는소스가 있는 디렉토리

activemq.xml에서는 기본적으로 아래와 같은 셋팅을 하면 된다.


policyEntry 부분에 queue 이름만 정확히 넣어 주고 여기서 셋팅한 이름을 applicationContext.xml 파일에서 사용하면 된다.
jetty 부분은 admin으로 모니터링 할 내용을 적어 둔 것이다.
덧. activemq.base는 상단에서 단순히 properties 파일 선언하고 해당 부분에서 ActiveMQ가 설치된 디렉토리를 지정한 것이다.

* applicationContext.xml 파일의 설정


위와 같이 설정이 모두 마쳐 졌다면 프로그램을 만들 차례이다.
우선 가장 기본이 되는 리스너는 다음과 같은 형식으로 만들어 주면 된다.


위 클래스는 주소를 수정할 경우 registerQueue 메소드를 호출해서 Queue에 한개의 아이디를 쌓고, Listener에서 Queue에 데이터가 들어오면 pullFromQueueAndModifyAddress 메소드를 호출해서 수정한 아이디의 주소를 이용하여 친구들 주소들 모두를 수정해 주는 것이다.

우선 applicationContext.xml에서 선언한 jmsTemplate 관련 setter를 선언해 주고 기본이 되는 2가지 메소드를 만들어 주면 된다.
registerQueue 메소드는 Queue에 등록하는 메소드이며 메소드 등록 시 jmsTemplate를 이용하면 된다. 해당 메소드는 별도의 외부 클래스에서 호출해 주는 것이므로 알아서 테스트 해 보면 될 것이다.
Queue에 데이터가 등록되면 Listener가 알아서 감지하고 pullFromQueueAndModifyAddress 메소드가 실행 된다.(applicationContext.xml의 listener list에 선언한 부분)
안의 비즈니스 로직은 알아서 만들면 되는 것이고 이렇게 되면 http://localhost:8161/admin/queues.jsp 에서 해당 내용이 제대로 적용 되었는지 확인해 볼 수 있다.

보통 Queue에 Data가 쌓이게 되면 바로 바로 Consumer가 작동하게 되므로 서버를 Debug 모드로 돌려 놓고 BreakPoint를 찍어 놓았다면 registerQueue를 실행하는 즉시 디버거가 작동하게 될 것이다.

덧. 백업 용도로 정신 없이 작성하다 보니 내가 써 놓고도 내가 무슨 말이지 모르겠다 ^^ 적당히 알아서 들으시면 될 것 같다. 삽질은 프로그램의 기본 ^^
덧2. ActiveMQ의 설정 및 시작 방법 등 자세한 내용은 http://users.handysoft.co.kr/~wsko/articles/activemq-guide/index.html에 더 잘 나온 것 같다.



신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
매일 프로그램 삽질은 하고 있지만 이번 건은 유난히 더 삽질이 길었다.
3일 동안 아무런 일도 못하고 이 일에만 매달렸다.
다른 일을 하긴 해야 하지만, 개발자 자존심에 해결하지 못하고 넘어가기에도 뭐 하고...
결국에는 해결하게 되었지만 아무것도 아닌 원인 때문에 해결하고도 짜증이 난다.

증상은 다음과 같다.




소스는 간단하다.
그냥 특정 쿠키를 생성한 다음 response에 실어 보내는 것이다.

그런데 로컬과 서버의 결과물이 달랐다.

* 로컬의 결과물
cookieKey1=bla...bla...;
cookieKey2=bla...bla...;

* 서버의 결과물
cookieKey1="bla...bla...";
cookieKey2="bla...bla...";

둘 간의 차이점은 value 앞 뒤로 쌍따옴표(")가 있느냐 없느냐이다.
큰 차이가 아닐지 모르지만 인증 관련된 작업을 다루는데 있어서 해당 값으로 인해 로그인이 되기도 하고 안되기도 하는 부분이라서 매우 골치 아픈 부분이었다.

소스를 이리도 뜯어보고 저리도 뜯어 봤지만 해결은 되지 않았고,
Googling을 아무리 해 봐도 답은 나오지 않았다.(내 능력 부족 -.-;;)

결국 회사 내 다른 분의 도움을 얻어 로컬과 서버의 tomcat 버젼의 차이때문에 생기는 것임을 알게 되었고 서버의 tomcat 버젼을 5.5.27로 downgrade해서 문제를 해결하게 되었다

관련된 내용은 http://tomcat.apache.org/tomcat-5.5-doc/changelog.html 의 46587 에서 확인해 볼 수 있다.

신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

트랙백이 하나이고 , 댓글이 없습니다.
secret