회사마다 고유 프로세스 & CI/CD가 있고 정리가 필요합니다.
개발 산출물을 올리는 Android의 경우는 프로젝트 단위로 올리지만...
RecyclerView 이용률이 정말 높습니다.
약간만 개발을 해보면 => 코드가 외워집니다
Fragment도 유명
intent부터 어려워합니다. => 원론적인 이야기
개발자들마다 성향 차이가 정말 많다
팀에 들어가서 개발을 lead, 기술 추구하고 싶다면 => 아키텍처, 전체적인 구조에 집중하는 것이 맞다
계속 그 수준에서만 놀게 될 수 있습니다.
아키텍처를 파악하지 않으면 한계가 존재한다
명시적 intent : 내부적으로 사용
암시적 intent : 외부적으로 사용
구글링을 통해서 또는 기본 앱은 소스가 open되어 있어요
안드로이드 앱에서 social login을 제공하고 싶다면 핸드폰에 있는 네이버, 카카오를 활용합니다.
카카오, 네이버 개발자 사이트 참고
명시적 intent : class 명 => 오타가 나면 컴파일 에러가 발생합니다.
암시적 intent : action, category, data 정보 등을 제공합니다.
action 문자열은 컴파일러가 체크할 수 없어 => 문자열이라
Error => Runtime Error가 나서 intent 정보 잘못되었다고 에러 발생
실행할 activity가 없을 때, 1개일 때, 여러 개일 때 시스템이 어떻게 처리하는가?
없을 때 : intent를 시작한 곳에 오류가 발생
1개일 때 : 문제없이 실행합니다.
n개일 때 : 사용자 선택으로 하나만 실행합니다.
ex) 갤러리 앱의 UI 스타일을 따라가는게 좋지 않을 수도..
그만큼 이용이 된다는 것은, 경쟁력을 가진다
구글 지도의 activity intent 정보와 동일 정보를 카카오에서 지정해버리면?
클릭했을 때, 구글/네이버/카카오 지도를 선택해서 보게 하는 예시
n개일 때, 사용자 선택으로 하나만 실행합니다.
만약 다 실행된다면, 사용자 악성 앱일 수도? => 유저 선택
시스템 다이얼로그 창이 뜬 경우를 볼 수 있습니다.
인텐트로 실행할 Activity가 없을 수도 있는 상황을 고려
val intent = Intent("ACTION_HELLO")
try {
startActivity(intent)
}
catch(e: Exception) {
Toast.makeTest(this, "no app...", Toast.LENGTH_SHORT).show()
}
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("geo:37.7749,127.4194))
startActivity(intent)
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("geo:37.7749,127.4194))
intent.setPackage("com.google.android.apps.maps")
startActivity(intent)
LifeCycle : Activity가 생성되어 소멸하기까지의 과정
실전에서 그만큼 성능이 중요시되는 부분
Activity 상태는 3가지로 구분!
setContentView : 화면출력 함수가 아님 => 화면 버퍼출력 함수
일반적으로는 view를 최초의 한 번만 출력하고, 데이터만 교체합니다.
start, Resume은 동일한 뷰를 반복 출력하기 때문에 문제가 생깁니다.
출력 내용이 다 다르다면? 여러번 호출해도 에러나지 X
무조건 맨 마지막 명령이 나옵니다. => 기본 화면 위에 view를 올리는 것
화면이 나오고 있는데 시스템이 죽지는 않은 경우 => 메모리가 부족해서
시스템에서 우선순위가 낮은 것들부터 KILL(비활성화 상태가 오래된 경우)
활성 상태는 우선순위가 높음
모든 함수를 다 쓰라는 게 아닌, 적절하게 생명주기 함수를 사용하라는 것
과도한 네트워크 트래픽을 발생시키는 앱 = 악성 앱으로 평가받는 경우가 많음
액티비티가 종료되면 객체가 소멸하므로 액티비티의 데이터는 모두 사라집니다
상태를 저장한다는 것은 액티비티가 종료되어 메모리의 데이터가 사라지더라도
화면을 회전하면 액티비티가 종료되었다가 나옵니다. 따라서 액티비티의 데이터는 초기화됩니다.
개발자 편하자고 User-friendly를 포기한다?
클라이언트 Device에서 백엔드 데이터를 direct로 취급을 한다?
방화벽 설정을 해서 특정 IP에서만 들어올 수 있게 해줌
데이터 영속화 = 여기서 로컬을 얘기하는 것
누군가의 도움받지 않고 화면을 출력하는 것 = 프론트
프로세스가 종료되어도 데이터가 유실되면 X => 영속화
로컬에다가 => 파일이 삭제될 일이 없어
쉽게 생각할 수 있는 데이터 영속화 주제
문자열, 숫자, boolean 값이라면 쓰지 말자 => 훨씬 더 쉬운 게 있음
image 때문에 로컬에 읽고 쓰는 것은 많이 빈번합니다
Map(key, value) : file(xml) 파일
이미지는 직접 handling, 문자열, 숫자 값은 put으로 저장 끝
클라이언트 side에 저장을 많이 하지만, 로컬 Device에 저장을 한다는 것은 server-side에 업로드할 필요가 없는 데이터를 저장한다.
sharedPreference는 구조화가 안 되어 건 바이 건
로컬 데이터베이스를 쓰는 이유는 대량의 데이터를 구조화하기 위해서 사용합니다.
네트워킹이 안 되었을 때, 어떻게 움직일까?
테스트는 무조건 필수
대부분의 비즈니스 데이터는 서버 데이터 => 로컬에 다 저장을 함
네트워크 트래픽이 과도해질 수 있어서
또한, 엘리베이터에서 실행하면 화면이 안 보일때 => 사용자는 안 된다는 불편함을 느낌
이미지도 데이터 => 서버에서 쓰지만, 로컬에다가 다 캐싱을 합니다.
DB가 제일 걷어내기 힘들다.
SQLite : 프론트 쪽 데이터베이스 => 경량 DB 목적
가전제품, 펌웨어에 들어갔었음
몇몇의 한계가 존재합니다.
구글에서 ORM 제공할 수 없다 => 모바일 쪽에서 => performance가 나오지 않음
질의문 작성하기
SQLite를 사용하려면 SQLiteDatabase라는 API 이용
SQLiteDatabase객체는 OpenOrCreateDatabase() 함수를 호출해서 얻습니다.
val db = openOrCreateDatabase("testdb", Context.MODE_PRIVATE, null)
db.execSQL(~~~)
동기면 select, insert의 returnType이 같을 수 없음
비동기면 함수가 하나여도 상관이 없습니다.
JDBC의 ResultSet과 완전히 동일한 Cursor
SQLiteOpenHelper 클래스를 이용하면 데이터베이스 프로그램을 좀 더 구조적으로 작성할 수 있습니다.
SQLiteOpenHelper는 추상 클래스이므로 이를 상속받아 하위 클래스를 작성
DBMS를 위한 것
onCreate() : App이 설치된 후 SQLiteOpenHelper 클래스가 이용되는 순간 한 번 호출합니다.
onUpgrade() : 생성자에 지정한 DB 버전 정보가 변경될 때마다 호출됩니다.
데이터베이스 관리적인 부분을 SQLiteOpenHelper 클래스를 통해 추상화 시켜 다른 쪽에서 신경쓰지 않게 하자는 목적
class DBHelper(context: context): SQLiteOpenHelper(context, "testdb", null, 1) {
override fun onCreate(db: SQLiteDatabase?) {
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
}
}
val db = SQLiteDatabase = DBHelper(this).writableDatabase
안드로이드 앱에서 파일을 다룰 때는 대부분 java.io 패키지에서 제공하는 클래스를 이용
내장과 외장이 구분이 된다.
핸드폰을 USB로 연결해서 보면 외장 메모리 밖에 안 보입니다. - 내장 메모리는 우리가 볼 수 없습니다.
외장 메모리는 앱별 저장소, 공용 저장소가 존재합니다.
App의 패키지명으로 디렉토리를 만들어 주는데, 이 디렉터리가 바로 App의 내장 메모리 공간
파일을 내장 메모리에 저장하려면 java.io의 File 클래스를 이용
Environment.getExternalStorageState() 함수러 외장 메모리를 사용할 수 잇는지부터 확인
내장 메모리는 어차피 자기 밖에 이용안함
외장은 다름 => 매니페스트 설정
자기 정보를 노출하는 것은 민감할 수 있음 => 퍼미션 보호되어 있는 정보를 이용한다? => 에러가 발생합니다.
<manifest ...>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:requestLegacyExternalStorage="true">
</application>
</manifest>
앱별 저장소 이용
외장 메모리 공간 = 앱별 저장소 + 공용 저장소
앱별 저장소는 개별 앱에 할당된 공간
앱별 저장소의 파일을 외부 앱에서 접근하게 하려면 파일 프로바이더로 공개해야 합니다.
외장 메모리의 앱별 저장소 위치는 getExteranlFileDir를 이용
프로젝트의 res/xml 디렉토리에 xml 파일을 만들고 이 파일에서 외부 앱에 공개할 경로를 지정
플랫폼 API에서 제공하는 클래스로, 데이터를 key-value 형태로 저장
영속적으로 저장할 데이터가 있지만, 많지도 않고 구조화가 필요 없음
대표적 예) 앱 설정(~할 거냐, 말 거냐, dialog-> 글 입력받는 설정, 알림 설정정)
UI도 중요하지 않으면 => Event도 안 중요해짐
설정과 관련된 xml 파일만 만들면... (설정 화면은 거의 입력이 많지 않음)
Fragment에서 이 xml파일 설정을 한줄만 하는 것으로 작성할 수 있음
SharedPreferences
를 사용하는 방법
카카오톡 => 로그인 지속 기법을 이용해
설정 데이터가 있는지 파악하는 것입니다.
보안 : 서버에서 인증받은 토큰
설정 저장말고 추가 이벤트 처리가 필요함
설정화면 구성 시, Activity를 직접 작성하는 일이 거의 없음
manifest에 설정이 되어 있어 => 우클릭 해서 설정을 실행시킬 수 있습니다.
스마트폰 정보 구하기
전화 상태 변화 감지하기 : PhoneStateListener
스마트폰의 상태를 파악하는 방법은 PhoneStateListener, TelephonyCallback을 이용하는 방법
비즈니스 벤더에서 이용할 가치가 있는 것이 있고, 아닌 것이 존재합니다.
callState : 전화 대기 상태
전화 걸려오는 상황이 User 입장에서 가장 중요한 상태입니다
setLocation : 위치 이용하는 것
스마트폰의 GPS 정보 이용
하나의 기지국이 관리하는 영역을 cell이라고 합니다.
다른 cell로 이동했다 => 그 때마다 사용 => third party에서는 의미 x
TelephonyManager : getLine1Number : 스마트폰의 전화번호
ConnectivityManager : 네트워크 접속 정보
데이터 통신을 위한 네트워크 상태 정보(현재 네트워킹 가능하냐, 불가능하냐)
가능하다면? WIFI인지 이통사 네트워크인지 확인이 가능합니다.
디바이스의 일시적인 문제로 서비스를 띄울 수 없습니다
와 같은 문구 출력이 이 정보를 활용해서 가능합니다.
NetworkRequest 객체에 네트워크 타입을 설정한 후 객체를 requestNetwork 함수의 두 번째 매개변수로 지정
안드로이드 앱은 네트워크 통신을 할 때 기본으로 HTTPS 보안 프로토콜을 사용
만약 일반 HTTPS 프로토콜로 통신하려면 특정 도메인만 허용하도록 선언
<uses-permission android:name="android.permission.INTERNET" />
Retrofit : HTTP 통신을 간편하게 만들어 주는 lib
네트워크 통신 정보만 주면 그대로 네트워크 프로그래밍을 대신 구현
동작 방식
annotation을 활용합니다.
라이브러리 선언
Retrofit은 JSON이나 XML 데이터를 모델(VO 클래스) 객체로 변환
JSON, XML을 파싱하는 라이브러리가 필요
파싱 lib에 맞는 converter lib가 필요
니네가 원하는 JSON Parser를 하나 설정해서 사용하자
모델 클래스 선언
모델 클래스의 property에 데이터가 자동으로 저장되는 기본 규칙은 데이터의 키와 프로퍼티 이름을 매칭
만약 키와 프로퍼티 이름이 다를 때는 @SerializedName이라는 annotation으로 명시
서비스 인터페이스 정의
@GET은 서버와 연동할 때 GET 방식으로 해달라는 의미
@Query는 서버에 전달되는 데이터
@Url은 요청 URL
Retrofit 초기화 => 인터페이스 타입의 서비스 객체 얻기
인터페이스의 함수를 호출하면 네트워크 통신을 시도
이미지 처리하기, 이미지 핸들링에 가장 유리
네트워크 상의 이미지 다운로드에 가장 핵심
Glide.with(this)
.load(R.drawable.seoul)
.into(binding.resultView)