안드로이드 스튜디오 커맨드 빌드 Command Build

반응형
728x170

안드로이드 스튜디오는 일반적인 AVD를 사용한 빌드뿐 아니라

 

gradle을 사용하여 커맨드로 빌드를 할 수 있다

 

https://developer.android.com/studio/build/building-cmdline

 

명령줄에서 앱 빌드  |  Android 개발자  |  Android Developers

Gradle 래퍼 명령줄 도구를 이용하여 Android 프로젝트에서 사용할 수 있는 모든 빌드 작업의 실행 방법을 알아보세요.

developer.android.com

 

하지만 초보자들이 이 문서를 보고 그대로 커맨드를 넣을 경우

 

작동하지 않기 때문에 이 블로그를 쓴다


1. 안드로이드 스튜디오에서 터미널창 열기

 

먼저 터미널 창을 열어야한다

 

안드로이드 스튜디오 하단을 보면 Terminal이 있는데 이를 클릭한다

 

터미널 창을 클릭

 

터미널을 열었을 경우 현재 위치가 현재 열고 있는 프로젝트의 최상위에 있는지 확인한다

 

제일 끝에 프로젝트 이름이 있을 경우 OK!

현재 위치가 현재 프로젝트의 최상위 루트인지 확인한다


2. 현재 프로젝트의 빌드 이름 확인

300x250

안드로이드 스튜디오의 왼쪽 밑을 확인하면

 

"Build Variants" 라는 것이 있다. 이를 클릭하면 다음과 같은 창이 뜬다

 

여기 app의 오른쪽에 적혀있는 stagingBeta라는 것이 빌드 이름이다

 

이건 프로젝트마다 다 다르니 기억해놓는다

 


3. 터미널에서 gradlew 를 사용해서 커맨드 빌드를 한다

 

다음과 같이 입력한다

 

./grdlew assembleStagingBeta

 

즉, assemble을 위에서 확인한 빌드 이름에 붙여줘야 한다

 

그리고 엔터를 누르면

 

이렇게 뜨면서 알아서 빌드가 시작된다 (개인 정보가 있어서 많이 잘랐습니다 ㅠ)

 

이렇게 하면 커맨드 빌드를 하여 Lint error 같은 것들을 확인할 수 있다!

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."

반응형

댓글()

[초급]안드로이드 DataStore 사용방법 A to Z

반응형
728x170

이전에 안드로이드에서 사용했던 

 

PreferenceDataStore는 API29부터 deprecated되어있다

 

그 이후 비슷한 SharedPreferen라는 것이 있는데 얘보다 더 좋은 놈이 나왔다

 

그게 바로

 

DataStore이다!!


1. 기존의 SharedPreference와 비교했을 때 장단점

 

SharedPreference와 DataStore 비교

가장 큰 차이점은 다음과 같다

 

DataStore는 Flow를 통해 데이터를 collect하여 받을 수 있다!

 

이는 상당히 큰 장점이다

 

그 이유는 어떠한 데이터를 DataStore를 통해 넣었고

 

특정 위치에서 해당 데이터를 collect 하고 있었다면

 

데이터를 저장한 순간 인지할 수 있기 때문이다!

300x250

2. 사용방법

 

https://developer.android.com/jetpack/androidx/releases/datastore?hl=ko 

 

DataStore  |  Android 개발자  |  Android Developers

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. DataStore 비동기적이고 일관된 트랜잭션 방식으로 데이터를 저장하여 SharedPreferences의 취약점을 극복하세요.

developer.android.com

 

일단 사용을 하기 위해선 depenedency 정의가 필요하다

dependencies{

    // Preferences Data Store
    implementation "androidx.datastore:datastore-preferences:1.0.0-alpha07"
    implementation "androidx.datastore:datastore-core:1.0.0-alpha07"

    // Preferences Data Store를 사용하기 위해선 Coroutine이 필요함
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
}

먼저 DataStore를 사용하기 위한 depenedency 와 코루틴을 사용하기 위한 depenedency 가 필요하다

 

그럼 이제 사용해보겠다

 

<layout 정의>

 

먼저 레이아웃은 그림과 같이 대충 정의한다 (참고로 main_activity.xml이다)

 

main_activity.xml

1. EditText에 데이터를 입력한다

2. save 버튼을 통해 dataStore에 입력한 값을 저장한다

3. "READ TEXT", "READ INT"를 통해 데이터를 읽어온다

4. 읽어온 데이터는 TextView에 출력한다

 

그 다음으로는 MainActivity를 정의한다

 

 

<MainActivty.kt>

 

MainActivity의 전체 코드는 제일 아래에 적어놓겠다

 

먼저 전역 변수로 DataStore를 컨트롤할 dataStore라는 인스턴트

 

dataStore의 이름은 보다시피 "settings"로 했다

 

DataStore를 저장하거나 읽어올 객체를 만든다 = stringKey, numberKey

class MainActivity : AppCompatActivity() {

    private val dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
    private val stringKey = stringPreferencesKey("textKey")
    private val numberKey = intPreferencesKey("numberKey")

 

다음으로 

 

저장 버튼에 대한 정의를 한다

 

정규식을 사용하여 입력한 값이 String 형태인지 아니면 Int 형태인지 확인한다

 

(정규식이 뭔지 모른다면?? 아래 링크 참조)

https://mmol.tistory.com/272

 

코틀린 안드로이드 정규식(Regex) 활용하기 -기본부터 심화

안드로이드에서 정규식은 주로 회원가입 같이 입력한 값이 특정한 양식을 따르는지 확인할 때 주로 사용됩니다. 1. 기본 개념 및 양식 ^ : 문자열의 시작을 의미. $ : 문자열의 끝을 의미.  . : 문

mmol.tistory.com

 

이렇게 따로 나누어서 저장하는 이유는 Int인지 String인지에 따라 사용해야하는 함수가 다르기 때문이다

 

(Int는 setInt로 String은 setText로 내가 커스텀한 함수를 사용해서 저장할거기 때문)

 

DataStore의 저장은 비동기로 이루어지기 때문에 여기서 Coroutine을 사용해야한다

binding.save.setOnClickListener {
            val data = binding.editText.text.toString()
            val regex = Regex("-?\\d+(\\.\\d+)?")

            if (data.isNotEmpty()) {
                CoroutineScope(Dispatchers.Main).launch {
                    if (regex.matches(data)){
                        // 숫자일 경우
                        Log.d("MainActivity", "숫자 저장")
                        setInt(data.toString().toInt())
                    }
                    else {
                        // 문자일 경우
                        Log.d("MainActivity", "문자열 저장")
                        setText(data.toString())
                    }
                }
            } else {
                Toast.makeText(this, "무언가를 입력하세요", Toast.LENGTH_SHORT).show()
            }
        }
        
private suspend fun setText(text: String) {
    this.dataStore.edit { preferences ->
        preferences[stringKey] = text
    }
}

private suspend fun setInt(number: Int) {
    this.dataStore.edit { preferences ->
        preferences[numberKey] = number
    }
}

 

다음으로 저장한 데이터를 꺼내본다

 

여기서 catch를 사용한 이유는 혹시 위에서 찾으려고 하는 dataStore의 이름 "settings"가 없다면

 

바로 에러를 띄우기 때문이다

 

그렇기 때문에 값을 저장하지 않고 데이터를 읽으려고 하면 에러가 발생하기 때문에 catch를 사용했다

 

그리고 map 함수를 사용해서 혹시 해당 preference에 아무 값이 없다면 즉, null이라면 ""을 넣어주게 했다

 

정상적으로 불러왔다면 textData에는 저장한 String 데이터가 Flow 형태로 들어있을 것이다.

 

그렇기 때문에 textData를 collect해서 해당 값을 사용할 수 있게된다

 

Int 부분도 동일하다

 

binding.readText.setOnClickListener {
        val textData: Flow<String> = this.dataStore.data
            .catch { exception ->
                if (exception is IOException) {
                    emit(emptyPreferences())
                } else {
                    throw exception
                }
            }
            .map { preferences ->
                preferences[stringKey] ?: ""
            }
        CoroutineScope(Dispatchers.Main).launch {
            textData.collect {
                binding.textView.text = it
            }
        }
    }

    binding.readInt.setOnClickListener {
        val intData: Flow<Int> = this.dataStore.data
            .catch { exception ->
                if (exception is IOException) {
                    emit(emptyPreferences())
                } else {
                    throw exception
                }
            }
            .map { preferences ->
                preferences[numberKey] ?: 0
            }
        CoroutineScope(Dispatchers.Main).launch {
            intData.collect {
                binding.textView.text = it.toString()
            }
        }
    }
}

 

<결과>

 

String 데이터를 저장하고 불러오기
Int 데이터를 저장하고 불러오기


3. MainActivity의 전체 코드

import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStore
import com.example.selftest.databinding.ActivityMainBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import java.io.IOException

class MainActivity : AppCompatActivity() {

    private val dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
    private val stringKey = stringPreferencesKey("textKey")
    private val numberKey = intPreferencesKey("numberKey")
    private val booleanKey = booleanPreferencesKey("booleanKey")

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.save.setOnClickListener {
            val data = binding.editText.text.toString()
            val regex = Regex("-?\\d+(\\.\\d+)?")

            if (data.isNotEmpty()) {
                CoroutineScope(Dispatchers.Main).launch {
                    if (regex.matches(data)) {
                        // 숫자일 경우
                        Log.d("MainActivity", "숫자 저장")
                        setInt(data.toString().toInt())
                    } else {
                        // 문자일 경우
                        Log.d("MainActivity", "문자열 저장")
                        setText(data.toString())
                    }
                }
            } else {
                Toast.makeText(this, "무언가를 입력하세요", Toast.LENGTH_SHORT).show()
            }
        }

        binding.readText.setOnClickListener {
            val textData: Flow<String> = this.dataStore.data
                .catch { exception ->
                    if (exception is IOException) {
                        emit(emptyPreferences())
                    } else {
                        throw exception
                    }
                }
                .map { preferences ->
                    preferences[stringKey] ?: ""
                }
            CoroutineScope(Dispatchers.Main).launch {
                textData.collect {
                    binding.textView.text = it
                }
            }
        }

        binding.readInt.setOnClickListener {
            val intData: Flow<Int> = this.dataStore.data
                .catch { exception ->
                    if (exception is IOException) {
                        emit(emptyPreferences())
                    } else {
                        throw exception
                    }
                }
                .map { preferences ->
                    preferences[numberKey] ?: 0
                }
            CoroutineScope(Dispatchers.Main).launch {
                intData.collect {
                    binding.textView.text = it.toString()
                }
            }
        }
    }

    private suspend fun setText(text: String) {
        this.dataStore.edit { preferences ->
            preferences[stringKey] = text
        }
    }

    private suspend fun setInt(number: Int) {
        this.dataStore.edit { preferences ->
            preferences[numberKey] = number
        }
    }
}

이상입니다!

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."

반응형

댓글()

코틀린 안드로이드 정규식(Regex) 활용하기 -기본부터 심화

반응형
728x170

안드로이드에서 정규식은 주로 회원가입 같이 입력한 값이 특정한 양식을 따르는지 확인할 때 주로 사용됩니다.

 


1. 기본 개념 및 양식

 

^ : 문자열의 시작을 의미.
$ : 문자열의 끝을 의미.
 . : 문자 한 개를 의미. '.'이 위치한 곳에 어떤 문자든지 1개의 문자가 들어감.
[ ] : 대괄호에 있는 문자 중 한 개를 의미. [abc]는 a, b, c 중 하나를 선택.
[^] : not의 의미로, 대괄호에서 쓴다면 [^abc] : a, b, c 제외하고 나머지를 의미.
| : or을 의미. a|b : a 또는 b.
() : 공통되는 부분을 묶을 때, 서브 패턴을 지정할 때 사용. abc|abd -> ab(c|d)로 바꿀 수 있음.
? : 문자가 0회 또는 1회 등장. a? b는 a가 나올 수도, 없을 수도 있음. ab, b.
* : 문자가 0회 이상 등장. a*b : b, ab, aaab, aaab..
+ : 문자가 1회 이상 등장. a+b : ab, aab, aaab..
{n} : 문자가 n개 나옴. a {2} b : aab
{n,} : 문자가 n개 이상 나옴. a {2,} b : aab, aaab, aaaab..
{n, m} : 문자가 n개 이상 m개 이하로 나옴. a {1,3 } b : ab, aab, aaab
\s : 공백 제거
\t : 탭
\d : 숫자, [0-9]와 동일
\b : 단어의 경계, 문자 사이의 공백
\w : 알파벳이나 숫자, [a-zA-Z0-9_]와 동일

 

위의 \s, \t, \d, \b, \w는 대문자로 바꾸면 반대 의미가 됩니다. 

300x250

2. 사용 방법

 

정규식을 만드는 방법에는 여러가지 방법이 있지만

대표적인거 1개만 가르쳐드리겠습니다.

 

정규식 정의 방법

 val regex = Regex("[0-9|a-z]")

위와 같이 Regex 안에 정규식을 정의할 수 있습니다.

 

 

정규식을 사용해서 문자열 확인

 

아래의 코드는 Activity의 onCreate() 안에 정의했습니다.

// 웹 표준 형식 확인
val regexWeb = Regex("https://(.+)")
// 숫자만 있는지 확인
val regexOnlyNumber = Regex("-?\\d+(\\.\\d+)?")
// 파일 디렉토리인지 확인
val regexFileDirectory = Regex("(.+)/(.+)\\.(.+)")
// 숫자만 확인
val regexPickNumber = Regex("[^0-9]")

// 입력한 값이 https 웹 형식을 따르는지
binding.isHttpsWeb.setOnClickListener {
    if (isRegexRight(binding.editText.text.toString(), regexWeb)) {
        makeToast("알맞은 https 형식입니다.")
    } else {
        makeToast("틀린 https 형식입니다.")
    }
}

// 입력한 값에 숫자만 들어있는지 확인
binding.isOnlyNumber.setOnClickListener {
    if (isRegexRight(binding.editText.text.toString(), regexOnlyNumber)) {
        makeToast("숫자만 존재")
    } else {
        makeToast("문자 존재함")
    }
}

// 입력한 값이 파일 위치를 가리키는지 확인
binding.isFileDirectory.setOnClickListener {
    if (isRegexRight(binding.editText.text.toString(), regexFileDirectory)) {
        makeToast("올바른 파일 디렉토리")
    } else {
        makeToast("틀린 파일 디렉토리")
    }
}

// 입력한 값에서 숫자만 뽑아서 다시 반환하기
binding.pickOnlyNumbers.setOnClickListener {
    binding.textView.text =
        onlyRemainsCertainChar(binding.editText.text.toString(), regexPickNumber)
}

// 해당 정규식이 맞는지 아닌지 확인
private fun isRegexRight(text: String, regex: Regex): Boolean {
return text.matches(regex)
}

// 해당 정규식에 맞는 값을 제외하고는 전부 삭제하기
private fun onlyRemainsCertainChar(text: String, regex: Regex): String {
return text.replace(regex, "")
}

3. 주로 사용되는 정규식 모음

 

자주 사용될거로 예상되는 정규식만 따로 모아서 정의해봤습니다.

// 웹 표준 형식 확인
val regexWeb = Regex("https://(.+)")
// 숫자만 있는지 확인
val regexOnlyNumber = Regex("-?\\d+(\\.\\d+)?")
// 파일 디렉토리인지 확인
val regexFileDirectory = Regex("(.+)/(.+)\\.(.+)")
// 숫자만 확인
val regexPickNumber = Regex("[^0-9]")
// 이메일 양식인지 확인
val regexEmail = Regex("^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$")
// 전화번호 양식인지 확인
val regexPhoneNumber = Regex("^\\d{2,3} - \\d{3,4} - \\d{4}\$")
// 숫자와 문자 포함 형태의 6 ~ 20글자의 간단한 비밀번호
val regexSimplePassword = Regex("^[A-Za-z0-9]{6,20}\$")
// 숫자와 문자 포함 형태의 6글자 이상의 간단한 비밀번호
val regexSimplePassword2 = Regex("^[A-Za-z0-9]{6}\$")
// 특수문자, 문자, 숫자를 포함한 6 ~ 20글자의 복잡한 비밀번호
val regexComplexPassword = Regex("^.*(?=^.{8,15}\$)(?=.*\\d)(?=.*[a-zA-Z])(?=.*[!@#\$%^&+=]).*\$")

4. 결과 화면

 

저는 이런식으로 화면을 구성하여 표현했습니다.

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."

반응형

댓글()

모든 해결법! The emulator process for AVD Pixel_4_API_30 was killed -1

반응형
728x170

정말 이거 고친다고 개고생했습니다..

 

이상하게 뭔 보안 문제인지 회사 컴에는 안드로이드 에뮬 실행이 안되더군요

 

개빡치는 안드로이드 에러들

 

그래서 3일간 인터넷을 뒤졌는데 해결하지 못했다가

 

갑자기 어느 웹사이트에서 힌트를 얻어서 해결하였습니다.

 

 


1. 안드로이드 에뮬레이터가 안되는 원인

 

일단 에뮬레이터가 안되는 원인이 뭘까요?

 

기본적으로 안드로이드 에뮬은 CPU에서 가상으로 기기를 만들고 그것을 실행합니다.

 

그게 안드로이드 스튜디오에서 에뮬레이터를 실행했을 때 우리들에게 보이는 결과입니다.

 

그럼 결국 에뮬레이터를 가상화하는 단계에서 뭔가 문제가 발생한거겠죠?

300x250

일단 아래의 내용을 전부 한번 해보시길 바랍니다.


2. 메인보드에 가상화 기능이 OFF 되있는거 아닐까?

 

애초에 이 기능이 없는 컴퓨터의 경우 에뮬레이터를 실행할 수 없습니다....

 

그래서 프로그래밍도 어느정도 성능이 되는 컴터로 사시길 바랍니다 ㅠ

 

아래는 각 제조사별 메인보드 가상화 기능 On 방법입니다.

 


일반 조립 PC용 메인보드 설정 방법


기가바이트 H811M-S2PV 및 해당 바이오스를 사용하는 모델들

설정 변경 후 저장해야 합니다.


삼성 DB-P150 포함 해당 모델 바이오스를 사용하는 PC들

CPU 가상화 기술 (VTx) 사용함으로 변경 후 F10번을 눌러 저장해 주세요.


LG에서 출시한 모델 중 해당 바이오스를 사용하는 PC들

해당 항목에서 찾을 수 있습니다.


HP EliteDesk800 G4 /HP ProoBook /HP ProDesk 모델 VT 설정


15-R113TU를 포함한 HP NoteBook 모델 (15인치 이상 모델들 다수 포함)

HP 데스크톱 구 모델 중 2세대 ~ 3세대 CPU를 사용하는 메인보드들

이 모델은 VTx 가 위 항목에 포함되어 있습니다.


HP ALL-IN-ONE PC 모델들 (20-C210KR 포함)-수정후 저장해야 합니다.

해당 모델은 위와 같은 방법으로 찾을 수 있으며, 설정 변경 후 F10을 눌러 적용해야 합니다.

HP ProBook 6560b 모델 외 구 모델 다수 포함 (노트북 구 2세대 ~3세대 CPU를 사용하는 모델)

해당 모델은 위 두 가지 항목에 체크가 되어야 합니다.


HP EliteBook 850 G3 포함한 기업용 노트북 모델들 포함 (6세대~7세대 제품들)

Advancd -> System Options으로 이동합니다.

해당 항목 중간에 Vritualization Technology (VTx)를 활성화시켜준 후 저장합니다.


Dell Latitude E7240 Ultrabook 및 해당 바이오스를 지원하는 제품들


AM4 라이젠 CPU를 지원하는 신형 메인보드 (MSI B350M PRO-VD PLUS 외 다수 모델)

OC 항목에 CPU Features를 선택합니다.

항목으로 진입하면 SVM Mode 항목을 Enabled로 변경 후 저장해야 합니다.

Asrock Z77 메인보드

Advanced -> CPU Configuration

Intel Virtualization Technology ->Enable


삼성 2014년 이전 PC 제품들

 

바이오스 설정에 들어갔는데 위와 같은 설정이 보이지 않는다면....?

 

CPU 자체에서 가상머신을 지원하지 않을 가능성이 매우 높습니다....ㅠ

 

이 이후의 방법은 다음 블로그에서 쓰겠습니다.

 


2편입니다!!

 

 

The emulator process for AVD Pixel_4_API_30 was killed -2

The emulator process for AVD Pixel_4_API_30 was killed -2에 대한 두 번째 글입니다! 1편에서는 단순히 컴퓨터 CPU의 가상화 기능을 ON 하는 기능을 알아봤습니다. 1편 블로그 - 컴퓨터 CPU의 가상화 기능 ON하..

mmol.tistory.com

 

반응형

댓글()