..
This commit is contained in:
@@ -23,6 +23,7 @@ import android.content.ComponentCallbacks2
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import bums.lunatic.launcher.helpers.HourlyLogWriter
|
||||
import bums.lunatic.launcher.helpers.PrefHelper
|
||||
import bums.lunatic.launcher.home.Base64ImageCache
|
||||
import bums.lunatic.launcher.home.Base64RequestHandler
|
||||
import bums.lunatic.launcher.utils.Blog
|
||||
import com.squareup.picasso.OkHttp3Downloader
|
||||
@@ -45,6 +46,7 @@ internal class LunaticLauncher : Application() {
|
||||
appContext = this
|
||||
// Base.initialize(this)
|
||||
PrefHelper.initialize(this)
|
||||
Base64ImageCache.init(this)
|
||||
val dir = File("/storage/emulated/0/bums_ob/BUM'S PACED /scraped/logs")
|
||||
///BUM'S PACED/pdfs
|
||||
if (!dir.exists()) {
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
package bums.lunatic.launcher.home
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import android.util.Base64
|
||||
import androidx.collection.LruCache
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.squareup.picasso.Request
|
||||
import com.squareup.picasso.RequestHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.math.BigInteger
|
||||
import java.security.MessageDigest
|
||||
|
||||
/**
|
||||
* Base64 데이터를 URL 키와 함께 저장하는 메모리 캐시 (Singleton 또는 DI로 관리)
|
||||
* Base64 데이터를 URL 키와 함께 저장하는 2단계 캐시 (메모리 + 파일)
|
||||
* 1. 메모리 캐시(LruCache)를 먼저 확인하여 빠른 접근을 제공합니다.
|
||||
* 2. 메모리에 없는 경우 파일 캐시를 확인하여 디스크 영속성을 지원합니다.
|
||||
* OOM(메모리 부족)을 피하기 위해 비트맵 자체가 아닌 Base64 문자열을 저장합니다.
|
||||
*/
|
||||
object Base64ImageCache {
|
||||
@@ -17,22 +27,83 @@ object Base64ImageCache {
|
||||
private val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
|
||||
private val cacheSize = maxMemory / 8
|
||||
|
||||
// Key: Image URL, Value: Base64 Data String
|
||||
// L1 Cache: Memory (Key: Image URL, Value: Base64 Data String)
|
||||
private val lru: LruCache<String, String> = LruCache(cacheSize)
|
||||
|
||||
// L2 Cache: File System
|
||||
private var cacheDir: File? = null
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
/**
|
||||
* 캐시를 사용하기 전에 반드시 Application 클래스 등에서 초기화해야 합니다.
|
||||
* @param context 애플리케이션 컨텍스트
|
||||
*/
|
||||
fun init(context: Context) {
|
||||
// 앱의 캐시 디렉토리 내에 이미지 캐시 전용 폴더 생성
|
||||
cacheDir = File(context.cacheDir, "base64_image_cache").apply { mkdirs() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 메모리 캐시와 파일 캐시에 Base64 데이터를 저장합니다.
|
||||
* 파일 저장은 백그라운드에서 비동기적으로 수행됩니다.
|
||||
*/
|
||||
fun put(url: String, base64Data: String) {
|
||||
// 1. 메모리 캐시에 저장
|
||||
if (lru.get(url) == null) {
|
||||
lru.put(url, base64Data)
|
||||
}
|
||||
|
||||
// 2. 파일 캐시에 비동기적으로 저장
|
||||
coroutineScope.launch {
|
||||
cacheDir?.let {
|
||||
try {
|
||||
val file = File(it, url.toMd5())
|
||||
file.writeText(base64Data, Charsets.UTF_8)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace() // 실제 앱에서는 로깅 라이브러리 사용 권장
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 메모리 또는 파일 캐시에서 Base64 데이터를 가져옵니다.
|
||||
* 파일 캐시에서 가져온 경우, 메모리 캐시에도 추가합니다.
|
||||
*/
|
||||
fun get(url: String): String? {
|
||||
return lru.get(url)
|
||||
// 1. 메모리 캐시에서 먼저 조회
|
||||
lru.get(url)?.let { return it }
|
||||
|
||||
// 2. 메모리에 없으면 파일 캐시에서 조회
|
||||
cacheDir?.let {
|
||||
try {
|
||||
val file = File(it, url.toMd5())
|
||||
if (file.exists()) {
|
||||
val base64Data = file.readText(Charsets.UTF_8)
|
||||
// 파일에서 읽은 데이터를 메모리에 올려서 다음 접근 속도를 높임
|
||||
lru.put(url, base64Data)
|
||||
return base64Data
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* URL을 MD5 해시로 변환하여 안전한 파일 이름으로 사용합니다.
|
||||
*/
|
||||
private fun String.toMd5(): String {
|
||||
val md = MessageDigest.getInstance("MD5")
|
||||
return BigInteger(1, md.digest(toByteArray())).toString(16).padStart(32, '0')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Picasso가 요청한 URL이 Base64ImageCache에 있는지 확인하는 커스텀 핸들러
|
||||
* (이 클래스는 변경할 필요가 없습니다)
|
||||
*/
|
||||
class Base64RequestHandler : RequestHandler() {
|
||||
|
||||
@@ -44,7 +115,7 @@ class Base64RequestHandler : RequestHandler() {
|
||||
*/
|
||||
override fun canHandleRequest(data: Request): Boolean {
|
||||
val url = data.uri.toString()
|
||||
// data: 스킴이 아니고, 우리 캐시에 URL 키가 존재할 때만 true 반환
|
||||
// data: 스킴이 아니고, 우리 캐시(메모리 또는 파일)에 URL 키가 존재할 때만 true 반환
|
||||
return !url.startsWith(DATA_SCHEME) && Base64ImageCache.get(url) != null
|
||||
}
|
||||
|
||||
|
||||
@@ -947,6 +947,7 @@ class GeckoWeb : BWebview {
|
||||
"WebtoonContents"-> {
|
||||
}
|
||||
"MSG" -> {
|
||||
context.toast("Received Msg privates form ${lPortMessage.msg}")
|
||||
}
|
||||
"SHOWVIEWER" -> {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user