iOS2021. 6. 11. 14:33

앞서 안드로이드에서 Coroutine과 CompletableFuture를 알아 보았다.

제목에서 보면 알수 있듯이 개발자들은 지속적으로 게을러지기 위해 노력하고 있다는것을 알 수 있다.

비동기 호출도 가장 적은 타이핑을 목표로 하고 있다.

 

비동기 처리를 하는 가장 고전적인 방법은 콜백함수를 인자로 하여 처리과 완료되면 콜백으로 처리결과를 전달하여 다음 단계로 넘어가도록 하는 방법이 되겠다.

알다시피 이는 콜백지옥을 수반하게 되며 이게 싫었던 개발자는 Rx나 CompletableFuture같은것들을 고안하고 사용하기 시작한다.

 

그것도 싫었던 그들은 Coroutine같은것을 다시한번 고안해 냈으며 마치 동기호출을 하는 코드처럼 비동기코드를 짜기 시작했다.

 

안드로이드에서는 Coutine으로 아래와 같이 마치 동기호출처럼 비동기 함수들을 호출할 수 있다.

suspend iWantEatRamyeon(money) {
  try {
    val ramyeon = buyRamyeon(money)
    val pot = getPot()
    val cookedRamyeon = doBoilPot(ramyeon, pot)
    val ramyeonInDish = moveToDish(cookedRamyeon)
    eatIt(ramyeonInDish)
  } catch (e: Exception) {
    // 실패케이스 처리
  }
}

 

 

iOS에서는 swift 5에서 아래와 같이 사용 가능하다.

func iWantEatRamyoen(money) async {
    val ramyeon = await buyRamyeon(money: money)
    val pot = await getPot()
    val cookedRamyeon = await doBoilPot(ramyeon:ramyeon, pot: pot)
    val ramyeonInDish = await moveToDish(ramyeon: cookieRamyeon)
    eat(ramyeon: remyeonInDish)
    print("Server response: \(response)")
}

 

async 메서드에서 비동기메서드로 호출하려면 메서드 정의시 아래와 같이 정의해야 한다.

 

func buyRamyeon(money: money) async -> Ramyeon {
	// get a ramyeon 
	var ramyeon = getRamyeon()
	return ramyeon
}

 

DispatchQueue와 블럭코딩을 이용하여 위와 동일한 로직을 작성해보자.

그러면 위 코드가 얼마나 코딩량을 줄여주는지 알게 될것이다.

 

개발자는 게을러져야 하나보다... 머리는 빼고...

Posted by 삼스
Android2021. 6. 7. 15:29

 

사용자의 단말을 특정하기 위해서 예전부터 사용해오던 것들은 점차적으로 구글이 보안을 강화하는 정책으로 변경되면서 사용이 불가하게 변화하고 있다.

 

deviceId, imei, meid, ANDROID_ID, SERIAL, MAC address 등이 많이 사용되어 왔는데

대부분 deprecated되었고 ANDROID_ID는 기기 재설정시 변경되고 SERIAL은 Android10부터 'unknown'을 반환한다.

 

2021년 현재는 SSAID가 대체재로서 사용 가능하다.

동일단말에서 같은 개발자의 키스토어로 빌드된 앱들 끼리는 같은 SSAID를 사용할 수 있다. 

 

아래 코드는 안드로이드 버번과 상관없이 항상 유니크한 아이디를 반환해주는 메서드이다.

 

 

    private fun getUniqueDeviceIdentifier(): String? {
        val telephonyManager: TelephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
        var uniqueId: String? = null
        try {
            telephonyManager.deviceId?.let {
                uniqueId = it
            }
            if (uniqueId==null && Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
                telephonyManager.imei?.let { uniqueId = it }
                if (uniqueId==null) {
                    telephonyManager.meid?.let { uniqueId = it }
                }
            }
        } catch(e: Exception) {}

        if (uniqueId==null) {
            uniqueId = Build.SERIAL
        }
        if (uniqueId==null || uniqueId=="unknown") {
            val wifiManager: WifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
            val wifiInfo = wifiManager.connectionInfo
            uniqueId = wifiInfo.macAddress
        }
        if (uniqueId==null || uniqueId=="02:00:00:00:00:00") {
            uniqueId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
        }
        return uniqueId
    }

 

Posted by 삼스
Android2021. 6. 2. 13:34


비동기로 파일을 다운로드 후 해당 파일을 로드하는 로직을 코루틴으로 작성하면 아래와 비슷한 로직으로 구성이 될것이다.

CoroutineScope(Dispatchers.IO).launch { // CoroutineScope를 IO스레드에서 launch
    if (downloadAsync(filePath, fileUrl)) { // 다운로드 코루틴메서드 다른 쓰레드에 영향을 주지 않는다.
        loadFromLocal(file, loadListener)  // 다운로드가 성공하면 다운로드된 파일을 로드한다.
    }
}


downloadAsync가 파일을 다운로드하는 코루틴 메서드로 suspend키워드와 coroutineScope을 지정하여 
다른 스레드에 영향을 주지 않고 동작하게 한다.


suspend fun downloadAsync(filePath: String, fileUrl: String): Boolean = coroutineScope {
    if (L.DBG) {
        Log.v(TAG, "downloadAsync : $filePath, $fileUrl")
    }
    return@coroutineScope download(filePath, fileUrl)
}

이걸 CompletableFuture에서 사용하고자 한다면 아래와 같은 형태가 될것이다.

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
downloadAsync2(filePath, fileUrl)
        .thenAccept { r ->
            if (r) {
                loadFromLocal(file, loadListener)
            }
        }
}


downloadAsync2는 CompletableFuture를 반환하며 thenAccept로 콜백을 받아서 처리할 수 있다.

@RequiresApi(Build.VERSION_CODES.N)
fun downloadAsync2(filePath: String, fileUrl: String): CompletableFuture<Boolean> {
    return CompletableFuture.supplyAsync {
        return@supplyAsync download(filePath, fileUrl)
    }
}

 

두가지 코드는 완전히 동일하게 동작한다.
어떤게 더 좋아 보이는가??
난 개인적으로 코루틴이 나아 보인다.
왜냐하면 CompletableFuture의 경우 안드로이드 N부터 지원하는데다 코루틴이 좀더 사용이 쉽다.

Posted by 삼스