구글 플레이 앱 아이콘과 런처아이콘은 달라도 된다.


앱인덱싱 Web to app에서 검색 링크 클릭시 앱 특정 페이지로 

https://firebase.google.com/docs/app-indexing/

블로그 이미지

dev김

안드로이드 개발자로 만 4년이 좀 안되게 근무했었고 그 이상의 공백을 가지고 있다. 다시 현업에 복귀하기 위한 노력의 흔적을 담으려고 한다.

,

알파버전 베타버전 활용 알파->베타->프로덕트

단계적 공개 기능 5%->20%->100%

APK나누기는 비권장

최근에는 앱번들과 다이나믹 딜리버리가 공개되어서 

마켓에서 다운로드시 기기에 맞게 apk의 리소스 선택가능

블로그 이미지

dev김

안드로이드 개발자로 만 4년이 좀 안되게 근무했었고 그 이상의 공백을 가지고 있다. 다시 현업에 복귀하기 위한 노력의 흔적을 담으려고 한다.

,

Fingerprint Authentication 지문인식 마시멜로 6.0에 등장

지문인증은 앱마다 각각 해줘야 함 

구글 샘플 FingerDialog / FingerprintDialog / AsymmetricFingerprintDialog 


비대칭 키 사용 tech16/FingerprintManagerCompatSample

1. 비대칭키 쌍 생성 2. 서버 쪽 공개키 등록 3. 사용자에게 지문인증 요청 

4. 사용자의 지문인증 5. 비밀키로 서명한 데이터 서버 전송 

6. 공개키 서명 데이터 검증


퍼미션 사용 android.permission.USE_FINGERPRINT


FingerprintManagerCompat


잠금화면에 보안락이 설정돼 있는가? KeyguardManager.isKeyguardSecure()

지문이 등록돼 있는가 FingerprintManager.hasEnrolledFingerprints()


ConfirmCredential 잠금화면을 앱 내에서 사용 가능 / 구글 샘플 동일이름

1. 키를 생성 2. 미인증 상태로 키에 액세스 예외 감지 인증 실행여부 선택 

3. 시스템에 인증처리 위임 4. 인증 결과 수신

블로그 이미지

dev김

안드로이드 개발자로 만 4년이 좀 안되게 근무했었고 그 이상의 공백을 가지고 있다. 다시 현업에 복귀하기 위한 노력의 흔적을 담으려고 한다.

,

인앱빌링 https://developer.android.com/google/play/billing/

Android Studio->Preferences->System Settings->Android SDK->SDK Tools->

Google Play Billing Library 설치


SDK/extras/google/play_billing에 IInAppBillingService.aidl 샘플과 테스트 앱 존재

file->openProject->import and android code sample->Trivial Drive 가능(깃허브도)


퍼미션 사용 추가 com.android.vending.BILLING

aidl은 main/aidl/에 추가


iab 처리 흐름

1. os버전에 따른 iab지원 확인 (IabHelper의 startSetup())

os2.2이상에서 google play 최신인가?

IInAppBillingService에 연결됐나? 연결됐다면 IAB3 지원하는가?

2. 결제 요청 (IabHelper의 launchPurchaseFlow())

3. 결제 응답 검증 (IabHelper의 handleActivityResult()) 공개키는 서버에 둘 것

4. 포인트 부여 서버에서 하는 경우가 많음

5. 소비 (IabHelper의 consume())


미처리 결제 확인 (IabHelper의 queryPurchases())


특정 상품의 ID를 지정해 결제 요청 정적 응답 단위 테스트

테스트 계정으로 결합 테스트(테스트 계정으로 등록)알파 or 베타 실제 기기만 가능

실제 결제 운영 테스트(알파 or 베타) 선불신용카드?나 잔고없는 체크카드 확인

블로그 이미지

dev김

안드로이드 개발자로 만 4년이 좀 안되게 근무했었고 그 이상의 공백을 가지고 있다. 다시 현업에 복귀하기 위한 노력의 흔적을 담으려고 한다.

,
Rail 퍼포먼스 모델
https://developers.google.com/web/fundamentals/performance/rail
(웹의 Idle 제외) 

onCreate에서 StrictMode 사용
if(DEVELOP_MODE) //개발모드에서만 동작하도록
{ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build()); }
병목현상 StrictMode로 확인 정책위반시 penaltyLog() penaltyDeath() 

프로파일러 간이 스스로 System.nanoTime() 

Traceview 안드로이드 모니터 cpu탭 스톱워치 클릭으로 기록 

Systrace 4.3이상(4.1) 플랫폼툴즈systrace 
$cd android-sdk/platform-tools/systrace
$python systrace.py —time=10 -o /tmp/mynewtrace.html sched gfx view wm 
--app com.github.advanced_android.newgithubrepo
systrace도움말 https://developer.android.com/studio/command-line/systrace 

lint 레이아웃 최적화 lint --show 검사항목 리스트 
레이아웃 오른쪽 클릭->Inspect Code실행 

개발자 옵션 오버드로 활성화 

hierarchyviewer 에뮬레이터나 루팅단말이나 viewserver 도입 
https://github.com/romainguy/viewserver 

안드로이드 모니터 메모리 감시 

LeakCanary를 이용해 메모리 누수 방지 릴리즈와 테스트는 동작안함
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.3.1'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
releaseCompile '~~.leakcanary-android-no-op:1.3.1'(no op아무것도 하지않음)
application onCreate에서 LeakCanary.install(this); 
fragment는 RefWatcher 사용 refWatcher = LeakCanary.install(this);
application.refWatcher; 
 
HashMap 대신 ArrayMap SimpleArrayMap 사용해 메모리 최적화
오토박싱(int->Integer)을 피하기 위해 

HashMap 대신 SparseArrayCompat사용 LongSparseArray도 존재


블로그 이미지

dev김

안드로이드 개발자로 만 4년이 좀 안되게 근무했었고 그 이상의 공백을 가지고 있다. 다시 현업에 복귀하기 위한 노력의 흔적을 담으려고 한다.

,
안드로이드 리눅스 커널 상에 구축 리눅스의 보안 기능 이용
설치된 앱마다 UID GID를 부여(리눅스 상의 기능) 데이터 각각 사용
$adb shell 
$cd /data/data/
$ls -al | grep com.advanced (uid,gid 확인)

UID, GID가 같으면 저장소 공유 가능 sharedUserId 서명이 같아야 함
매니페스트 android:sharedUserId="com.advanced_android.uid"도 같아야 함
sharedUserId 릴리즈 된 앱에서 추가되면 업데이트 불가 삭제후 재설치
그럴 경우는 contentprovider를 사용

adb shell ps 프로세스 확인 앱이 독립되어 있는지 보기 위해

퍼미션
ContextCompat.checkSelfPermission() 퍼미션 허용여부 검사
ActivityCompat.requestPermissions() 퍼미션 요청
ActivityCompat.shouldShowRequestPermissionRationale() 트루면 설명이 필요

앱 내에 퍼미션 사용 (같은 서명 사용 필요)
앱 A의 액티비티를 퍼미션으로 보호


...
앱 B에서 앱 A를 사용하기 위해 퍼미션을 사용

퍼미션 스틸링 대응
$keytool -list -v -keystore ~/.android/debug.keystore -storepass android 해시값
1. 출력된 Hash(sha1)에서 콜론(:)을 제거한 값을 검사

2. getCallingActivity().getPackageName() 호출원의 패키지명 확인 해시 검출
public static String hash(Context ctx, String pkgName){
if(pkgname==null)return null;
try{
PackageManager pm = ctx.getPackageManager();
PackageInfo pkginfo = pm.getPackageInfo(pkgname, PackageManager.GET_SIGNATURES);
if(pkginfo.signatures.length != 1) return null;
Signature sig = pkginfo.signatures[0];
byte[] cert = sig.toByteArray();
byte[] sha1 = computeSha1(cert);
return byte2hex(sha1);
}catch (NameNotFoundException e){} 
return null;}
1과 2의 해시값이 같을때만 허용

AES 암호화
public static byte[] cipherEncrypt(byte[] buf, String key, String iv){
try{ SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), "AES");비밀키
IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());초기화 벡터
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, sksSpec, ivSpec);
return cipher.doFinal(buf);
} catch(Exception e){} finally{} 
return null;
}
sharedPreference 난독화 키를 난독화하고 상수로 사용
네트워크에서 native라이브러리 로드X 앱내에서만O 자바 동적코드로딩X

자바스크립트 인터페이스 사용은 개발사, 협력사 도메인만
@JavascriptInterface 어노테이션을 붙여야 함(17이상 호출가능 4.2 없으면 에러)

https SSLHandshakeException 인증서 검증 오류 우회하지 말고 제대로 처리할 것

webview file:// 스킴 취약성 http 와 https만 허용하도록

시스템 취약성 해소는 SecureRandom
SecureRandom의 취약성은 아래 사이트에서 대처
https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html

보안 팁 페이지 https://developer.android.com/training/articles/security-tips


블로그 이미지

dev김

안드로이드 개발자로 만 4년이 좀 안되게 근무했었고 그 이상의 공백을 가지고 있다. 다시 현업에 복귀하기 위한 노력의 흔적을 담으려고 한다.

,

values-v21 vaules에 각각 styles등록하여 버전별 동작가능하게


머티리얼 디자인 지원 라이브러리 appbarlayout collapsingtoolbarlayout 

coordinatorlayout floatingactionbutton navigatorview snackbar tablayout 

textinputlayout


5.0롤리팝api21↑ Theme.Material 지원 <style name="AppTheme" 

parent="android:Theme.Material">...</style>


v7 appcompat Theme.AppCompat 테마 이용시 머티리얼 디자인 테마 적용

(edittext spinner checkbox radiobutton checkedtextview switchcompat)


api21 미만 불가한 기능

(액티비티 전환, reveal애니메이션, elevation shadow, ripple event, 곡선 모션)


Theme.Material 사용시 Ripple Effect 적용됨 

android:selectableItemBackground도 마찬가지


Drawable에 리플 이펙트 적용

<ripple xmlns~ android:color="@color/accent_dark"><item>

<shape android:shape="oval">

<solid android:color="?android:colorAsscent"/>

</shape></item></ripple>


터치 포인트에 리플 이펙트 시작 

drawableHotspotChanged콜백 Drawable에 터치 위치 전달


오픈소스 리플 이펙트

https://github.com/traex/RippleEffect

https://github.com/balysv/material-ripple


팔레트 Pallette p = Pallette.from(bitmap).generate(); 

p.getVibrantSwatch().getRgb();

vibrant 선명한 muted 부드러운


support design 

implementation 'com.android.support:design:23.2.1'


NavigationView(좌측 메뉴) 

tablayout(탭레이아웃 viewpager와 사용권장)

toolbar(상단액션바) 

coordinatorlayout(스크롤 뷰 위치 크기 동적 관리 ex콜랩스 툴바)


<coordinatorlayout><appbarlayout><collapsingtoolbarlayout> 

<toolbar/> <include layout""/>

</collapsingtoolbarlayout></appbarlayout></coordinatorlayout>


appbarlayout의 자식요소(collapsingtoolbarlayout)에는 app:layout_scrollFlags를 설정하여 스크롤 시 동작 지정가능(tech12/coordinatorLayout02)

scroll|exitUntilCollapsed 아래로 minHeight까지 작아짐 위로 maxHeight까지

scroll|enterAlways 아래로 minHeight관계없이 사라짐 위로 바로 표시(퀵 리턴)

scroll|enterAlwaysCollapsed 아래로 minHeight관계없이 사라짐 위에서 당기면 표시


floatingactionbutton(둥근버튼) snackbar(하단 팝업 토스트)

floatingactionbutton 

app:layout_anchor="@id/app_bar" 

app:layout_anchorGravity="bottom|end" 위치 지정

앱바가 최소 크기에 가까워지면 fab도 숨겨짐

블로그 이미지

dev김

안드로이드 개발자로 만 4년이 좀 안되게 근무했었고 그 이상의 공백을 가지고 있다. 다시 현업에 복귀하기 위한 노력의 흔적을 담으려고 한다.

,

머티리얼 디자인 가이드 https://material.io/design/introduction


https://material.io/design/layout/component-behavior.html#


버튼 Flat(고정)/Raised(컨텐츠 속)/FloatingAction/Dropdown(스피너 대신)


인터랙션 RippleDrawable(탭 물결),

ViewAnimationUtils.createCircularReveal(화면 전환물결),


SharedElements(뷰 전체화면),

ActivityTransitionAnimation(5.0롤리팝API21이후)

블로그 이미지

dev김

안드로이드 개발자로 만 4년이 좀 안되게 근무했었고 그 이상의 공백을 가지고 있다. 다시 현업에 복귀하기 위한 노력의 흔적을 담으려고 한다.

,

컬러는 HSB(Hue, Saturation, Brightness) 색상, 채도, 명도 다루기 용이

2가지가 같으면 가까운 색감이고 어울림 / 명도 채도가 같으면 톤이 같다


adobe color http://color.adobe.com/ko/create/color-wheel/

https://material.io/design/layout/spacing-methods.html#


근접 정렬 반복 대비


폰트 Roboto(스마트 디바이스 고려 영문) 

Noto(구글 어도비 다국어 지원 Roboto와 조합사용)


폰트 패밀리 Typeface.DEFAULT DEFAULT_BOLD MONOSPACE SANS_SERIF SERIF

폰트 스타일 Typeface.NORMAL BOLD ITALIC BOLD_ITALIC 한국어 italic 최소사용


그리드 레이아웃 여백을 8의 배수로 dp 설계하면 다화면 대응 용이


디자인 경쟁자 분석 proscons 리스트 기능별 장단점 정리 표


고객평가 린캔버스:고객의 문제/문제의 해결책/측정 가능한 지표/고유의 가치 제안/

경쟁우위/인지.배포 경로/타깃이 될 고객 목표/초기 고객군/비용 구조/수익원


프로토타이핑

1.페이퍼 프로토타이핑/스케치, 

2.디자인 목:Prott(prottapp.com)/POP/Flinto/InVision, 

3.데모(실제 앱)

블로그 이미지

dev김

안드로이드 개발자로 만 4년이 좀 안되게 근무했었고 그 이상의 공백을 가지고 있다. 다시 현업에 복귀하기 위한 노력의 흔적을 담으려고 한다.

,
CI 지속적 통합
빌드 -> 테스트 -> 인스펙션 -> 디플로이 -> 피드백 -> 커밋, 정기,릴리즈 빌드
Circle CI 가벼워 도입장벽이 낮고 Saas 환경에서(유사 travis CI, Codeship)
Jenkins 프로젝트 설정 모두 가능 서버 운영 환경 정비

리눅스에 젠킨스 설치
서버 확인
$uname -a 
자바 설치
$sudo apt-get install openjdk-7-jre 
$sudo apt-get install openjdk-7-jdk
$java -version
$javac -version
젠킨스 설치
$wget -q -0 - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
$sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources. list.d/jenkins.list
$sudo apt-get update
$sudo apt-get install jenkins
http://localhost:8080에 접속해서 확인
sudo cat /var/lib/jenkins/secrets/initialAdminPassword 패스워드 복사후 잠금해제

install suggested plugins
계정 생성하고 필요한 권한 설정

안드로이드 환경 구축
젠킨스 사용자로 변경
$sudo su jenkins
$cd ~
안드로이드 sdk 다운로드
$wget http://dl.google.com/android/android-sdk_r24.4.1-linux.tgz
$tar -xvf android-sdk_r24.4.1-linux.tgz
$rm android-sdk_r24.4.1-linux.tgz
필요한 패키지 설치
$android-sdk-linux/tools/android update sdk --no-ui --all --filter "platform-tools"
$android-sdk-linux/tools/android update sdk --no-ui --all --filter "tools"
$android-sdk-linux/tools/android update sdk --no-ui --all --filter "android-23"
$android-sdk-linux/tools/android update sdk --no-ui --all --filter "extra-android-m2repository"
$android-sdk-linux/tools/android update sdk --no-ui --all --filter "sys-img-x86_64-android-23"
빌드 자동화
디버그
git으로 프로젝트 클론 git plugin github plugin 설치
https://localhost:8080/pluginManager/available 플러그인 관리 페이지
깃허브에서 프로젝트 클론(소스코드 관리 -> git -> repository url 지정)
build에서 execute shell 실행 스크립트 입력란에 ./gradlew assembleDebug 입력
결과물 저장 기능으로 다운로드 할 수 있게 한다
Android sdk 경로 Jenkins 관리->시스템 속성->Global properties에서 환경변수 설정
설정 마치고 build now

깃허브 빌드하기
지정한 시간마다 작업 설정 항목 중 빌드 유발에서 Poll SCM 설정
깃허브의 푸시를 트리거로 깃허브 쪽에서 Webhooks 설정에서 Jenkins URL 지정
플러그인O : Jenkins 빌드 유발에서 Github hook trigger for GitScm polling 에 체크
플러그인X : 포스트되는 데이터를 직접 처리해서 빌드

릴리스 빌드 자동
./gradlew assembleRelease
app/sample.keystore
app/signingconfig.properties
storePassword=password
keyAlias=sample
keyPassword=password
릴리즈용 설정
signingConfigs{ release { 
def configFile = file("signingconfig.properties")
def props = new Properties()
props.load(new FileInputStream(configFile))

storeFile file("sample.keystore")
storePassword props.storePassword
keyAlias props.keyAlias
keyPassword props.keyPassword } }
buildTypes{ release{ signingConfig signingConfigs.release } }
암호화된 서명 파일 준비
공개키 만들기
$openssl rand 32 -out key -base64
$cat key
암호화
$openssl enc -e -aes128 -kfile key -in app/signingconfig.properties -out app/signingconfig.properties.aes128
$openssl enc -e -aes128 -kfile key -in app/sample.keystore -out app/sample.keystore.aes128
커밋
$git add app/signingconfig.properties.ae128 app/sample.keystore.aes128
$git commit -m "add signing secure files."
복호화 execute shell
$echo "공개키" > key
$openssl enc -d -aes128 -kfile key -in app/signingconfig.properties.aes128 -out app/signingconfig.properties
$openssl enc -d -aes128 -kfile key -in app/sample.keystore.aes128 -out app/sample.keystore
테스트 자동화 execute shell
./gradlew clean test

코드 커버리지 시각화 플러그인(Jacoco Cobertura)

android emulator plugin 설치 후
빌드환경 설정
run an android emulator during build
run emulator with properties

인스펙션 자동화
android lint (그레이들 이용하면 설정없이 그대로 실행가능 ./gradlew lint
https://wiki.jenkins.io/display/jenkins/android+lint+plugin

findbugs ./gradlew lint findbugs 설정(app/build.gradle)
apply plugin: 'findbugs'
task findbugs(type:FindBugs, dependsOn:assembleDebug)
{ ignoreFailures = true
effort="max"
reportLevel = "medium"
excludeFilter = new File("config/findbugs/filter.xml")
classes = files("build/intermediates/classes/")
source 'src/main'
include '**/*.java'
reports{
xml.enabled = true
html.enabled = false }
classpath=files() }
분석대상제외 지정 filter.xml










그밖의 코드분석도구 Checkstyle PMD

디플로이 자동화
DeployGate 디버그 빌드 deploygate.com

DeployGate 설정(app/build.gradle)
deploygate
{ username="USER_NAME" token="TOKEN"
apks{ release{ sourceFile= file("[release apk file path]") }
debug{ sourceFile= file("[debug apk file path]") } } }
./gradlew uploadDeployGate

구글 플레이 개발자 API
developers.google.com/android-publisher/ 를 이용해 자동화가능

Circle CI
circleci.com
커밋할때마다 빌드 release 브랜치는 릴리즈 빌드 나머지는 디버그
테스트 실행 결과 파일 저장
코드 분석 결과 파일 저장
DeployGate에 Apk업로드

Circle CI 설정(circle.yml)

빌드환경설정
dependencies:
pre:
- echo y | android update sdk --no-ui --all --filter 
"android-23,build-tools-23.0.3,extra-android-m2repository,extra-android-support"
실행할 커맨드 설정
test:
override:
-if ["$CIRCLE_BRANCH" = "release" ]; then
./gradlew assembleRelease;
else
./gradlew assembleDebug;
fi
- ./gradlew lint; cp -r app/build/outputs $CIRCLE_ARTIFACTS
- ./gradlew findbugs; cp -r app/build/reports $CIRCLE_ARTIFACTS
apk가 생기면 마지막으로 DeployGate에 업로드
deployment:
test:
branch: /*/
commands:
- ./gradlew uploadDeployGate
블로그 이미지

dev김

안드로이드 개발자로 만 4년이 좀 안되게 근무했었고 그 이상의 공백을 가지고 있다. 다시 현업에 복귀하기 위한 노력의 흔적을 담으려고 한다.

,