컬러는 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년이 좀 안되게 근무했었고 그 이상의 공백을 가지고 있다. 다시 현업에 복귀하기 위한 노력의 흔적을 담으려고 한다.

,

테스트

unit test Junit 

https://github.com/junit-team/junit/wiki/Getting-started


ui 테스트 개발중인 앱
Robolectric https://robolectric.org 안드로이드 sdk 에뮬레이트해서 테스트 진행
https://codelabs.developers.google.com/codelabs/android-testing/index.html?index=..%2F..index#6

Mockito https://mockito.org

onView(withId(editId)).perform(typeText("text"));
onView(withId(btnId)).perform(perform(click());
onView(withText("text")).check(matches(isDisplayed());

@Rule public ActivityTestRule<MainActivity> mMainActivityTestRule =
new ActivityTestRule<>(MainActivity.class);

@Test
onView(withText("message")).check(doesNotExist());
@Test
onView(withId(fabId)).perform(click());
onView(withText("message")).check(matches(isDisplayed());

UI테스트 에스프레소 gradlew connectedAndroidTest

androidTestImplementation('com.android.support.test:runner:0.4')
{ exclude module: 'support-annotations' }
androidTestImplementation('com.android.support.test:rules:0.4')
{ exclude module: 'support-annotations' }
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.1')
{ exclude module: 'support-annotations' }

exclude moduel 라이브러리 중복으로 인해 충돌시 설정


에스프레소 치트 시트 

https://developer.android.com/training/testing/espresso/cheat-sheet

https://github.com/googlesamples/android-topeka/tree/java


Espresso.onView / Espresso.onData / Intents.intended /  Intents.intending


UIautomator 다른 앱과의 연계 홈 화면에서의 동작 테스트

exclude 제외하고 러너와 룰러도 사용

androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'

https://developer.android.com/training/testing/ui-automator#java

$ANDROID_HOME/tools/uiautomatorviewer


단위 테스트는 가능한한 모두 UI테스트는 중요 화면과 기능으로 범위를 좁혀 작성

블로그 이미지

dev김

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

,
테스트 단위 테스트 / UI 테스트 / 인수 테스트 / 그 밖의 테스트
unit test 클래스 단위로 테스트
github.com/junit-team/junit/wiki/Getting-started
mockito.org mockito를 이용한 객체의 목화
testImplementation 'junit:junit:4.12'
testImplementation "org.mockito:mockito-core:1.+"
build.gradle android 내의 testOptions{ unitTests.returnDefaultValues = true }
유닛테스트 예외 던지지 않도록

mock(클래스) / spy(생성) / verify(객체, times(횟수)).함수명((클래스)any());
when(객체.함수).thenReturn(밸류); 호출했을 때 밸류를 넘긴다
아래는 기존 클래스 처리하지 않기 위해
doReturn(밸류).when(객체).함수(밸류);
doNothing().when(객체).함수(밸류);

@VisibleForTest 기능없지만 테스트용이라고 표시

Factory, InjectMock, DI 컨테이너 등 고려

테스트 도입시 코드를 새로 작성 or 기존 코드 편집시 테스트 준비
메서드가 길면 분리 / 리팩터링 리팩터->익스트랙트->메서드

테스트를 위해 메서드내 객체는 인수화
블로그 이미지

dev김

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

,

그래들 빌드 스크립트 자바와 유사한 그루비 언어로 기술

module 의 build.gradle

implementation fileTree(dir: 'libs', include: ['*.jar']) //libs 디렉토리의 모든 jar 로드


settings.gradle

include ':app', ':lib' 여러 개의 모듈 사용가능


proguard 난독화 app/proguard-rules.pro

한앱에서 메서드 수가 65536개를 넘으면 mutidex 기능 이용(프로가드로 해결가능)

디버그에 문제없으나 릴리즈에 문제 있으면 프로가드 설정 의심


gradle.properties build.gradle에서 참조 가능 변수 사용가능


local.properties sdk 위치 지정 gitignore할것


의존관계 프로젝트 build.gradle에 추가하여 설정가능 gradlew build로 실행

task build << {
    println '실행'
}
task configure << {
    println '설정'
}
build.dependsOn(configure); //빌드하기 전에 설정부터 한다

keystore파일의 위치와 패스워드 등 설정(app android 내에)

signingConfigs {
        release{
            storeFile file("release.keystore")
            storePassword "xxxxxxx"
            keyAlias "androidreleasekey"
            keyPassword "xxxxxxxx"
        }
    }

//효율적인 버전관리
def versionMajor = 1
def versionMinor = 0
def versionPatch = 0
def versionBuild = 0

versionCode versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
versionName "${versionMajor}.${versionMinor}.${versionPatch}"

빌드타입 release debug Stage


signingCongif signingCongif.release //사이닝키 설정

minifyEnabled true //프로가드 활성화

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'


applicationIdSuffix ".debug" 패키지 이름 끝에 .debug 추가

versionNameSuffix "-debug" 버전 이름 끝에 -debug 추가


initWith(buildTypes.debug) 디버그 설정을 이용해 빌드타입 만들기

applicationIdSuffix ".stage"


빌드타입 확인 뷰->툴 윈도우->빌트 버라이언츠

debuggable 디버그 가능 여부

jniDebuggable 네이티브 코드 디버그 가능 여부

manifestPlaceholders 매니페스트 내에서 플레이스 홀더 설정

multiDexEnabled 멀티덱스 활성화

shrinkResources 사용하지 않는 리소스 삭제


빌드타입별로 리소스 클래스 대체 가능


프로덕트 플레이버(구현 자체를 바꾸거나 별도의 apk 생성 : 유무료 버전)


applicationId 어플리케이션 ID

Dimension 여러 프로덕트 플레이버 apk 생성 가능

generatedDensities vector drawable에서 생성되는 이미지 화면 밀도 지정

manifestPlaceholders , multiDexEnabled , proguardFiles , signingConfig

testApplicationId 테스트용 어플리케이션 ID

versionCode , versionName 

android 내에 productFlavors{ pro{} free{} }


프로덕트 플레이버와 빌드타입 혼합가능 3*2=6


gradlew assembleDebug , assembleRelease 프로젝트/app/build/에 apk생성


buildConfigField "boolean", "ENABLE_DEBUG_TOOLS", "false" 디펄트컨픽에 추가

debug에만 true로 하면 디버그시에만 디버그 툴 사용


gradlew assembleDebug(assDeb) --debug --stacktrace


build.gradle에 

if(hasProperty("myparameter")){
println "My Parameter: "+myparameter
}

gradlew assembleDebug -Pmyparameter=test

My Parameter : test


gradlew androidDependencies 의존관계 확인

블로그 이미지

dev김

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

,

mvp model view presenter 프리젠터가 모델과 뷰 사이에서 인터랙션한다.

뷰와 프리젠터는 contract 내의 view와 model 인터페이스를 각각 구현해 인터랙션


mvvm model view viewmodel 

observablefield livedata 데이터바인딩과 사용하여 xml에서 뷰표시

아래 두줄은 블로거가 작성한 내용이다.

사용시 livedata는 데이터 표현 용으로는 적합했지만 이벤트 처리에는 좋지 않았다.

이벤트 처리 등에는 contract 인터페이스를 뷰에서 구현해 처리하는 것이 좋겠다.

블로그 이미지

dev김

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

,

서포트 라이브러리

v4 노티피케이션컴팻, 뷰페이저, 로더

v7 AppCompat, Theme.AppCompat, 툴바, CardView, RecyclerView, Annotation, Design 네비게이션 드로워, fab, snackbar 등


Headsu-Up Notification 알림이 앱 상단에 표시

.setPriority를 최대로 혹은? .setVibrate(퍼미션 필요) 확인필요


RecyclerView StaggeredGridLayoutManager 형태가 일정하지 않은 아이템


사이즈 고정시 setHasFixedSize(true)로 성능 개선


ItemDecoration 을 이용해 구분선 등 작업 offset(여백) onDraw(표시)


ItemTouchHelper 밀어서 삭제하거나 데이터를 이동 SimpleCallback 구현


블로그 이미지

dev김

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

,
ContentProvider는 안드로이드 스튜디오에서 생성마법사로 간단히 생성가능
exported 다른 앱에 공개할지 여부

adb 로 contentprovider에 접속
adb shell content query --uri content:://com.advanced.android/wordoftoday 
--projection _id:words:name
adb shell content query --uri content:://com.advanced.android/wordoftoday/0 
--projection _id:words:name
adb shell content query --uri content:://com.advanced.android/wordoftoday 
--projection _id:words:name --where "name='Taiki'"
adb shell content insert --uri content:://com.advanced.android/wordoftoday/ 
--bind name:s:Shunsuke --bind date:i:20151015 --bind words:s: '오늘은 멋진 날'
혹은 페이스북에서 개발한 Stetho를 이용

wakelock 화면 on cpu on full_wake_lock (deprecated)
화면 on(약간 어두움) cpu on screen_dim_wake_lock (deprecated)
화면 off cpu on partial_wake_lock
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 을 이용
서포트 라이브러리 WakefulBroadcastReceiver 사용 (60초만 얻고 해제됨) 퍼미션 사용
사용 후 WakefulBroadcastReceiver.completeWakefulIntent()로 해제

wakelock 상태 확인 adb shell dumpsys power
블로그 이미지

dev김

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

,

AppCompatActivity를 이용할 수 없을 때는 AppCompatDelegate를 이용한다.

AppCompatDelegate delegate = new AppCompatDelegate(this);
delegate.setContentView(R.layout.activity_main);


액티비티 수명주기는 익히 알고 있고 사용하고 있지만 

처리 예에 대한 가이드가 있어서 작성한다.


액티비티 수명주기와 처리 예


 메서드명 

 시점 

 처리 예 

 onCreate

 생성 시

 초기화 처리나 뷰 생성 

 onStart

 비표시 시

 통신이나 센서 처리 시작 

 onRestart

 표시 시(재시작)

 보통은 X

 onResume

 최전면 표시

 애니메이션 등 화면 갱신

 onPause

 일부 표시 상태 

 화면갱신 (일시)정지, 리소스 해제

 onStop

 비표시 상태

 통신이나 센서 처리 정지 

 onDestory

 폐기 시

 필요없는 리소스 해제, 액티비티 참조 정리 


같은 앱에서 시작된 액티비티는 같은 백스택에 쌓인다

taskAffinity의 속성에 따라 소속되는 태스크가 달라진다

launchMode에 따라 액티비티 생성여부, 새로운 태스크에 속하는 등 시작이 다르다


액티비티 재생성시 onSaveInstanceState/onRestoreInstanceState 에서 저장하고 복원


액티비티 런치모드 

 launchMode 

 내용 

 standard

 매번 액티비티의 인스턴스 새로 생성 기본값 

 singleTop 

 같은 액티비티가 최상위에서 실행중이면 생성x onNewIntent 

 singleTask

 1개의 태스크에 인스턴스 존재, 같은 액티비티 있으면 생성x

 singleInstance 

 1개의 태스크에 1개의 인스턴스만, 다른 액티비티 포함x, 

 같은 액티비티 생성x 


싱글태스크에서 taskAffinity를 설정하면 여러개의 태스크에서 

singleTast, singleInstance 로 설정하면 startActivityForResult 불가

바로 RESULT_CANCELED가 반환


adb 백스택 확인 : adb shell dumpsys activity activities


화면 밀도 dp와 px


 밀도

 dp

 px

 ldpi

 1dp

 0.75px 

 mdpi

 1dp

 1px

 hdpi

 1dp

 1.5px

 xhdpi

 1dp 

 2px

 xxhdpi

 1dp 

 3px

 xxxhdpi

 1dp

 4px


fragment lifecycle

 메서드명 

 시점 

 실행하는 처리의 예

 onAttach

 액티비티 연결

 getActivity()는 널

 onCreate

 생성 시

 초기화

 onCreateView

 생성 시

 뷰 생성

 onActivityCreated

 생성 시

 초기화, 뷰 생성

 onStart

 비표시 상태

 표시 전 시점

 onResume

 표시 시

 필요한 애니메이션 등 화면 갱신

 onPause

 일부 표시 상태

 화면갱신 처리 (일시)정지, 리소스 해제

 onStop

 비표시 상태

 비표시된 시점

 onDestoryView

 폐기 시

 필요 없는 리소스 해제

 onDestory

 폐기 시

 필요 없는 리소스 해제

 onDetach

 폐기 시

 필요 없는 리소스 해제


아래 두줄은 블로거가 추가한 내용이다.

기존 생성된 프래그먼트가 액티비티에 추가될 땐 onCreateView 이하부터 시작된다.

onCreate에서 해줘야 다시 반복되지 않는다.


customview 에서 layout 대신에 merge태그를 쓰면 기존 레이아웃에 포함된다


ui를 갖지 않는 headless 프래그먼트 뷰 재생성시에도 재생성되지 않는다

네트워크 탐지등에 사용

setRetainInstance(true)

블로그 이미지

dev김

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

,

기초적인 수준을 다루고 있는 튜토리얼 형식의 안드로이드 개발서적은 참 많다.

하지만 조금 더 높은 수준으로 올라가기 위한 책은 찾아보기 어려웠다.

그러다가 수개월전? 검색을 통해 알게 된 안드로이드 개발 레벨업 교과서를 빌려서 그 내용을 정리해서 다시 복습하려고 한다.


먼저 1장은 대부분 알고 있는 내용이 대부분이라 사소한 팁을 위주로 작성하려 한다.

알고 있는 부분은 작성하지 않았다.


개발장비(PC, Laptop)에 연결된 안드로이드 디바이스가 여러개라면 

shift를 누른 상태에서 단말기를 선택하면 한꺼번에 모두 설치 실행할 수 있다.


단축키 : 


find action ctrl+shift+A Android Studio의 기능을 검색


smart type completion ctrl+shift+space 자료형에 맞게 자동완성


complete current statement ctrl+shift+enter 현재 작성중인 구문 완성


parameter info ctrl+p 매개변수 정보 표시


extract ctrl+alt+v,f,m,c (variable, field, method, const) 자동 항목 완성


postfix 자동완성 변수.par (변수) 처럼 괄호처리 

.notnull null 체크 if

.var .field


alt+숫자(툴 윈도우 열기)


ctrl+e 최근 사용한 파일


shift+shift 통합검색(코드+리소스)

ctrl+shift+alt+n 코드 검색

ctrl+b 선언부 열기

ctrl+alt+h 메서드 호출한 곳 열기


디버깅시 마우스 오른쪽으로 브레이크 포인트 설정하면 조건부 설정가능

블로그 이미지

dev김

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

,