앱을 업데이트하고 올렸더니 다음과 같은 메시지가 나왔다.

"zip 정렬되지 않은 APK를 업로드했습니다. APK에 zip 정렬 도구를 실행한 다음 다시 업로드해야 합니다."

stackoverflow를 검색해보니 zipalign을 사용하면 된다고 나와 있었다. zipalign이 무엇인지는 문서에서 확인할 수 있다. 

sdk 폴더의 build-tools로 이동한 다음 빌드에 사용한 gradle plugin 버전으로 들어간다. 여기에 zipalign이 위치해 있다.

zipalign -v 4 foo.apk bar.apk

다음과 같이 입력해주면 되지만… 결과는 fail이었다. 마지막에 다음과 같이 찍혔다.

4413714 resources.arsc (BAD - 2)
Verification FAILED

구글링을 해보니 명확한 원인은 찾을 수 없었고 수동으로 signing을 하면 된다고 하는 글이 있었다.

수동 사인은 다음과 같이 할 수 있다.

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore [keystore file] foo.apk [alias]

여기서 alias가 뭔지 고민했는데 다음 명령어를 통해 확인할 수 있다.

keytool -keystore [keystore file] -list -v

이번에는 다음 메시지가 나온다.

jarsigner: unable to sign jar: java.util.zip.ZipException: invalid entry compressed size

이는 이미 사인되어 있는 apk일 경우에 나오는 메시지라 한다. 사이닝 정보다 담기는 META-INF 폴더를 다음 명령어로 지운다.

zip -d foo.apk META-INF/\*

자 이제 올려볼까? 이젠 디버그 모드로 되어 있다고 하면서 안 된다. 마지막으로 menifest에 다음을 추가한다.

android:debuggable="false"

된다… 힘들었다


'Tech > Android' 카테고리의 다른 글

SyncAdapter는 무엇이죠?  (0) 2016.05.10
UNEXPECTED TOP-LEVEL EXCEPTION Multiple dex files define  (0) 2015.09.09
AsyncTaskLoader는 무엇이죠?  (0) 2015.08.14

SyncAdapter는 무엇이죠?

SyncAdapter는 2010년 구글 IO에서 발표됐습니다. 디바이스와 서버를 자동으로 동기화시켜주는 기능을 합니다. 다음과 같이 Account가 추가됩니다. 어떤 방식으로 sync할 것인지는 사용자가 매니지할 수 있습니다. 

1. 서버의 데이터가 변경됐을 때
2. 디바이스의 데이터가 변경됐을 때
3. 시스템이 네트워크 메시지를 보냈을 때
4. 특정 주기로
5. 필요할 때


SyncAdapter 장점

1 ~ 4번의 sync 시 프레임워크 내 어카운트가 등록되어 별도의 인증없이 가능합니다. 게다가 디바이스의 베터리 상황에 따라 OS가 sync 여부를 판단한다고 합니다. 구글 도큐멘트에선 5번을 권장하진 않습니다. 유저에게 리프레쉬 권한을 주면 데이터가 변경되었다는 아무런 근거 없이 sync를 시도하게 되어 네트워크와 배터리 자원을 효율적으로 이용하지 못하게 된다는 게 설명입니다. (안드로이드 디벨로퍼 문서 참조)


SyncAdapter 단점

SyncAdapter는 Content Provider와 붙어 다닙니다. Content Provider는 다른 어플리케이션과 데이터를 공유하지 않으면 굳이 만들 필요가 없는데 SyncAdapter를 쓰려면 달아야 한다는 거죠. 물론 Dummy로 만들면 CP를 모두 구현하지 않아도 됩니다. SyncAdapter를 Device와 서버 간의 데이터 전송을 전담하는 객체로 만들 수 있는 까닭에 구조적으로 앱을 더 깔끔하게 만들 수 있게도 해줍니다. 

엄밀히 말하면 이 오류는 gradle 오류다.
A, B라는 저장소에서 소스를 가져다 쓴다고 하자.
만약 A와 B 모두가 C를 라이브러리로 사용하고 있다면 C는 중복될 것이고 위 exception이 발생하게 된다. 

해결책은 A, B 둘 중 하나에서 C를 사용하지 않도록 exclude하는 것이다.
compile ('com.zopim.android:sdk:1.0.0') {
    exclude (module: 'jackson-annotations')
    exclude (module: 'jackson-core')
    exclude (module: 'jackson-databind')
}
이렇게 하면 해결된다.


앱이 빠르기 위해서는 DB 처리와 네트워크 처리를 main thread가 아닌 다른 thread에서 처리해야 합니다. 더불어 사용자에게 필요한 정보를 앱이 켜져 있지 않을 때 가져오면 더 좋습니다. 사용자가 앱을 킬 때 이미 로컬에 저장되어 있는 데이터를 가져오면 되니까요. 전자는 AsyncTastLoader로 후자는 SyncAdapter로 해결할 수 있습니다. 


AsyncTaskLoader는 무엇이죠?

비동기 처리를 위한 클래스로 API Level 11에 도입되었습니다. SupportPackage에 포함되어 있기 때문에 어떤 Version에서든 이용이 가능합니다. 다음과 같은 특징을 가졌습니다. (안드로이드 디벨로퍼 문서 참조)

1. 모든 Activity와 Fragment에서 사용할 수 있습니다. 장점입니다.
2. 비동기적으로 데이터를 로딩할 수 있습니다.
3. 데이터를 모니터하고 내용이 변경되면 새로운 결과를 전달합니다.
4. 설정 변경 후, 다시 만들어질 때 마지막 로더의 커서에 접속합니다. 그 데이터를 re-query할 필요가 없습니다


AsyncTask랑은 무엇이 다르죠?

쓰기 쉽습니다. 더 구조적으로 깔끔합니다. 


AsyncTaskLoader를 쓰면 구체적으로 뭐가 좋지요?

main thread가 아닌 thread로 데이터를 비동기적으로 로딩할 수 있습니다. DB 긁어오는 모듈이 Activity에서 분리되어 더 깔끔한 구조를 가진 앱을 만들 수 있습니다.


어떤 방식으로 실행되나요?

Activity 혹은 Fragment에서 Loader를 implement합니다. 그리고 Loader Class를 따로 만들어주면 됩니다. 메소드들은 다음과 같은 순서로 실행됩니다.

Fragment﹕ +++ Calling initLoader()! +++
Fragment﹕ +++ Initializing the new Loader... +++
Fragment﹕ +++ onCreateLoader() called! +++
Loader﹕ +++ onStartLoading() called! +++
Loader﹕ +++ The current data is data is null... so force load! +++
Loader﹕ +++ forceLoad() called! +++
Loader﹕ +++ loadInBackground() called! +++
Loader﹕ +++ Delivering results to the LoaderManager for the ListFragment to display! +++
Fragment﹕ +++ onLoadFinished() called! +++
ListLoader﹕ +++ onStopLoading() called! +++


참조 

http://dev.classmethod.jp/smartphone/asynctaskloader/

+ Recent posts