FRONT/AOS

Android WebView 로컬스토리지 상태값 실시간 변화시키기

nkm 2024. 11. 11. 17:27
728x90
반응형

목표

1. Android에서 WebView를 활성화하고 @javascriptInterface를 통해서 네이티브 코드를 직접 제어한다.

2. evaluateJavaScript를 이용하여 JAVASCRIPT를 실행시킨다 블루투스 연결을 하고 난 이후 하드웨어와 통신 이후에 WebView에 있는 LocalStroage의 값을 변화시킨다.

 

목표 1. Android 에서 WebView를 활성화 시키고 @javascriptInterface를 통해 네이티브 코드 제어하기

@JavascriptInterface는 JavaScript에서 Android의 특정 메서드를 호출할 수 있도록 허용하는 데 사용됩니다. 이 기능을 통해 웹 페이지(JavaScript)에서 네이티브 Android 코드를 직접 호출할 수 있습니다. 이 코드에서는 ConnectedKey와 gpsHandler 인스턴스에 @JavascriptInterface를 적용하여, 웹 페이지에서 해당 클래스의 메서드를 사용할 수 있도록 하고 있습니다.

현재 저의 코드는 WebViewActivity에서 진행하고 있습니다. 해당 코드에서는 웹뷰에서 처리해야하는 팝업창, 바텀바, 다이얼로그, 등 다양한 핸들러를 구성하여 쿠키와 로컬스토리지를 다 구성해둔 상태입니다.

 

그래서 일단 집중적으로 목표에 맞는 코드 구성을 작성해보려고합니다.

주요 구성 요소

  1. ConnectedKey 클래스
  2. gpsHandler (GPSHandler 클래스의 인스턴스)

각 부분 설명

1. ConnectedKey 클래스의 @JavascriptInterface 메서드

ConnectedKey는 @JavascriptInterface로 노출된 메서드들을 통해 JavaScript에서 호출할 수 있게 됩니다. 이 클래스는 WebViewActivity의 WebView에 addJavascriptInterface 메서드로 연결됩니다.

  • setupWebView 함수의 아래 코드에서 ConnectedKey 객체를 _NativeApp라는 이름으로 WebView에 연결합니다. 
  •  
binding.webView.addJavascriptInterface(ConnectedKey(this@WebViewActivity, binding.webView), "_NativeApp")
  • 이제 JavaScript에서 _NativeApp.메서드명()으로 ConnectedKey 클래스의 @JavascriptInterface로 노출된 메서드를 호출할 수 있습니다.
  • 예를 들어, ConnectedKey_open(uuid)와 같은 메서드를 호출하여 BLE 장치와의 연결 작업을 JavaScript에서 시작할 수 있습니다.

이 부분은 `ConnectedKey` 클래스의 인스턴스를 WebView에 JavaScript 인터페이스로 추가하여 JavaScript 코드가 Android 네이티브 메서드에 접근할 수 있도록 하는 역할을 합니다. 여기서는 `ConnectedKey` 클래스의 인스턴스를 `"_NativeApp"`이라는 이름으로 WebView에 연결하고 있습니다.

binding.webView.addJavascriptInterface(ConnectedKey(this@WebViewActivity, binding.webView), "_NativeApp")



이 코드는 다음과 같은 의미를 갖습니다:

### 1. `addJavascriptInterface` 메서드
`addJavascriptInterface`는 Android의 WebView에서 JavaScript와 네이티브 Android 코드 간의 상호작용을 가능하게 합니다. 이 메서드는 JavaScript가 Android의 메서드를 호출할 수 있도록 합니다.

형식:

webView.addJavascriptInterface(Object, String)



- **Object**: JavaScript에서 호출하고자 하는 메서드들이 포함된 객체를 지정합니다.
- **String**: JavaScript에서 이 객체를 참조할 이름을 지정합니다.

### 2. `ConnectedKey` 인스턴스를 연결
`ConnectedKey`는 네이티브 메서드를 `@JavascriptInterface`로 노출하는 클래스입니다. 이 클래스의 인스턴스를 WebView와 연결함으로써, `ConnectedKey` 클래스의 메서드들이 JavaScript에서 직접 호출될 수 있습니다.

ConnectedKey(this@WebViewActivity, binding.webView)



- `this@WebViewActivity`: `ConnectedKey` 클래스의 생성자에서 `Context`와 `WebView`가 필요하므로, 이 `Context`로 `WebViewActivity`의 인스턴스를 전달합니다.
- `binding.webView`: 현재 `WebView` 객체를 `ConnectedKey`에 전달합니다. 이는 `ConnectedKey`에서 `evaluateJS` 메서드 등과 같은 WebView 관련 작업을 할 수 있게 합니다.

#### `_NativeApp` 이름
`"_NativeApp"`라는 이름은 JavaScript가 `ConnectedKey` 객체를 참조할 때 사용하는 이름입니다. JavaScript에서 `ConnectedKey` 클래스에 정의된 메서드들을 사용하려면 다음과 같이 호출할 수 있습니다:

// JavaScript에서 _NativeApp 객체에 접근하여 ConnectedKey의 메서드 호출
_NativeApp.메서드명(파라미터);


예를 들어, `ConnectedKey`에 `@JavascriptInterface`로 정의된 `ConnectedKey_open(uuid)` 메서드를 호출하려면:


// JavaScript에서 Android의 ConnectedKey_open 메서드 호출
_NativeApp.ConnectedKey_open("your-uuid-value");



### 전체적인 동작 과정
1. **WebView에서 JavaScript 활성화**: `setupWebView` 메서드 내에서 `webView.settings.javaScriptEnabled = true`로 JavaScript를 활성화합니다.
2. **addJavascriptInterface로 연결**: `ConnectedKey` 클래스 인스턴스를 `"_NativeApp"` 이름으로 WebView에 추가하여 JavaScript가 `_NativeApp` 객체로 Android 메서드를 호출할 수 있게 합니다.
3. **JavaScript에서 Android 메서드 호출**: JavaScript에서 `_NativeApp` 객체를 통해 Android의 `ConnectedKey` 클래스 메서드를 호출하여 BLE 작업, 위치 정보 요청 등 다양한 네이티브 기능을 수행합니다.

이를 통해 Android의 네이티브 코드와 웹(JavaScript) 사이에 상호작용할 수 있습니다.

2. WebView 설정 관련 코드

JavaScript와의 브릿지를 활성화하기 위해 WebView의 설정을 조정하고 JavaScript 실행을 허용해야 합니다.

  • JavaScript 실행 허용: webView.settings.javaScriptEnabled = true로 JavaScript를 활성화합니다
binding.webView.settings.javaScriptEnabled = true
  • 다중 창 지원: setSupportMultipleWindows(true)를 사용하여 WebView에서 JavaScript로 다중 창을 여는 것을 허용합니다.
settings.setSupportMultipleWindows(true)

3. WebChromeClient와 WebViewClient

이 WebView 설정에서는 JavaScript 알림, 파일 선택기, 위치 권한 등을 처리하기 위해 WebChromeClient와 WebViewClient도 설정합니다. 이 두 객체는 다음과 같은 역할을 합니다.

  • WebChromeClient: JavaScript의 alert, confirm, 파일 선택기, 위치 권한 요청을 처리합니다.
  • WebViewClient: WebView의 기본 동작을 확장하거나 수정할 수 있습니다.

 

2. evaluateJavaScript를 이용하여 JAVASCRIPT를 실행시킨다 이후 WebView에 있는 LocalStroage의 값을 변화시킨다.

 

class ConnectedKey(private val context: Context, private val webView: WebView) {
// JavaScript 코드 실행 함수
    fun evaluateJS(script: String, callback: (String?) -> Unit) {
        webView.post {
            webView.evaluateJavascript(script) { value ->
                callback(value)
            }
        }
    }
 }
class BluetoothManager(private val context: Context, private val connectedKey: ConnectedKey? = null) {
// 명령 완료 후 처리하는 함수
    private fun handleCommandCompletion(uuid: String, rssi: Int) {
        if (isOpenCommand) {
            // 문 열림 명령이 완료되었음을 저장
            isOpen[uuid] = 1
            Log.d("Bluetooth", "문 열림 완료: UUID=$uuid, RSSI=$rssi, isOpen=$isOpen")
            saveDoorStatus(uuid, true, rssi)

            // localStorage 업데이트
            uuidSecretKeyMap[uuid]?.let { secretKey ->
                updateLocalStorageStatus(uuid, secretKey, 1)
            }
        } else {
            // 문 닫힘 명령 완료 시 처리
            isOpen[uuid] = 0
            Log.d("Bluetooth", "문 닫힘 완료: UUID=$uuid, RSSI=$rssi, isOpen=$isOpen")
            saveDoorStatus(uuid, false, rssi)

            // localStorage 업데이트
            uuidSecretKeyMap[uuid]?.let { secretKey ->
                updateLocalStorageStatus(uuid, secretKey, 0)
            }
        }
        Log.d("Bluetooth", "handleCommandCompletion: 최종 carList 상태: $carList")
        Log.d("Bluetooth", "handleCommandCompletion: 최종 isOpen 상태: $isOpen")
    }

    // localStorage의 UUID 상태 업데이트 함수
    private fun updateLocalStorageStatus(uuid: String, secretKey: String, status: Int) {
        val script = """
            var item = localStorage.getItem('ConnectedKey_UUID');
            if (item) {
                var updatedItem = item.split(',').map(function(pair) {
                    if (pair.includes('$uuid[$secretKey]')) {
                        return '$uuid[$secretKey]:$status';
                    }
                    return pair;
                }).join(',');
                localStorage.setItem('ConnectedKey_UUID', updatedItem);
            }
        """
        connectedKey?.evaluateJS(script) { result ->
            Log.d("BluetoothManager", "JavaScript 실행 결과: $result")
        }
    }
}

ConnectedKey라는 Bridge를 담은 Class에서 evaluateJS 함수를 정의하고 BluetoothManager에서 호출한 뒤 블루투스 연결이 완료된다면 해당 값을 저장하여 ConnectedKey_UUID라는 키를 가진 로컬스토리지 값의 저장한다라는 전체적인 로직으로 진행합니다. 주요 함수들을 정리한다면  아래와 같습니다.


### 주요 흐름 요약
1. `handleCommandCompletion` 메서드: 블루투스 명령(문 열기/닫기) 완료 후 상태를 업데이트합니다.
2. `updateLocalStorageStatus` 메서드: JavaScript를 실행하여 `localStorage`에 값을 설정합니다.
3. `evaluateJS` 메서드: Android 네이티브 코드에서 JavaScript를 실행하기 위한 도구로 활용됩니다.

### 각 부분 설명

#### 1. `handleCommandCompletion` 메서드
`handleCommandCompletion` 메서드는 문이 열리거나 닫힌 후 BLE 명령 처리가 완료되었을 때 호출됩니다. 여기서 UUID와 상태를 업데이트하고, 그 결과를 `localStorage`에 반영합니다.

private fun handleCommandCompletion(uuid: String, rssi: Int) {
    if (isOpenCommand) {
        // 문 열림 명령이 완료되었음을 저장
        isOpen[uuid] = 1
        Log.d("Bluetooth", "문 열림 완료: UUID=$uuid, RSSI=$rssi, isOpen=$isOpen")
        saveDoorStatus(uuid, true, rssi)

        // localStorage 업데이트
        uuidSecretKeyMap[uuid]?.let { secretKey ->
            updateLocalStorageStatus(uuid, secretKey, 1)
        }
    } else {
        // 문 닫힘 명령 완료 시 처리
        isOpen[uuid] = 0
        Log.d("Bluetooth", "문 닫힘 완료: UUID=$uuid, RSSI=$rssi, isOpen=$isOpen")
        saveDoorStatus(uuid, false, rssi)

        // localStorage 업데이트
        uuidSecretKeyMap[uuid]?.let { secretKey ->
            updateLocalStorageStatus(uuid, secretKey, 0)
        }
    }
    Log.d("Bluetooth", "handleCommandCompletion: 최종 carList 상태: $carList")
    Log.d("Bluetooth", "handleCommandCompletion: 최종 isOpen 상태: $isOpen")
}



1. `isOpenCommand` 플래그에 따라 명령이 문 열기인지 닫기인지를 판별합니다.
   - 문 열기 명령(`isOpenCommand = true`)의 경우, `isOpen` 맵에 `UUID` 키로 값 `1`을 저장하여 열림 상태로 기록합니다.
   - 문 닫기 명령(`isOpenCommand = false`)의 경우, `isOpen` 맵에 `UUID` 키로 값 `0`을 저장하여 닫힘 상태로 기록합니다.
   
2. `saveDoorStatus(uuid, 상태, rssi)`: 블루투스 연결의 결과 상태와 `RSSI`를 저장하여 앱 내의 상태를 업데이트합니다.
   
3. `localStorage` 업데이트:
   - `uuidSecretKeyMap`에서 현재 UUID에 대한 `secretKey`를 찾고, `updateLocalStorageStatus` 메서드를 호출하여 `localStorage`의 상태를 업데이트합니다.

#### 2. `updateLocalStorageStatus` 메서드
이 메서드는 JavaScript 코드 조각을 생성하여 `evaluateJavaScript`를 사용해 WebView의 `localStorage`에 값을 설정합니다.

private fun updateLocalStorageStatus(uuid: String, secretKey: String, status: Int) {
    val script = """
        var item = localStorage.getItem('ConnectedKey_UUID');
        if (item) {
            var updatedItem = item.split(',').map(function(pair) {
                if (pair.includes('$uuid[$secretKey]')) {
                    return '$uuid[$secretKey]:$status';
                }
                return pair;
            }).join(',');
            localStorage.setItem('ConnectedKey_UUID', updatedItem);
        }
    """
    connectedKey?.evaluateJS(script) { result ->
        Log.d("BluetoothManager", "JavaScript 실행 결과: $result")
    }
}



1. **JavaScript 코드 조각 생성**:
   - `val script` 변수에 `localStorage` 값을 업데이트하는 JavaScript 코드를 문자열 형태로 저장합니다.
   
2. **localStorage에서 값 읽기 및 분할**:
   - `localStorage.getItem('ConnectedKey_UUID')`를 통해 `ConnectedKey_UUID` 키에 해당하는 값을 가져옵니다. 이 값이 있다면, 다음 작업을 수행합니다.
   - `item.split(',')`을 통해 `ConnectedKey_UUID`의 값(예: `"UUID1[SecretKey1]:1,UUID2[SecretKey2]:0"`)을 쉼표로 분할하여 배열로 만듭니다. 각 배열 요소는 `"UUID[SecretKey]:status"` 형식의 문자열입니다.

3. **값 업데이트 및 저장**:
   - `.map()`을 사용하여 `uuid`와 `secretKey`가 포함된 항목을 찾고, `status` 값을 변경합니다.
   - 업데이트된 배열을 다시 `join(',')`으로 연결하여 `updatedItem` 문자열로 만든 뒤, 이를 `localStorage.setItem('ConnectedKey_UUID', updatedItem)`을 통해 `localStorage`에 저장합니다.

4. **evaluateJavaScript 실행**:
   - `evaluateJavaScript`를 통해 `script` 코드를 WebView에서 실행하여 `localStorage`의 `ConnectedKey_UUID` 값을 실시간으로 업데이트합니다.
   - 업데이트 후 `result` 콜백을 사용해 `JavaScript 실행 결과`를 로그로 출력하여 성공 여부를 확인할 수 있습니다.

#### 3. `evaluateJavaScript`를 통한 `localStorage` 값 변경

`evaluateJavaScript` 메서드는 Android의 네이티브 코드에서 WebView 내 JavaScript를 실행할 수 있는 방법을 제공합니다. 이를 통해 WebView의 `localStorage`를 실시간으로 수정할 수 있습니다.

- `evaluateJavaScript(script: String, callback: (String?) -> Unit)` 메서드는 `WebView`에서 `JavaScript` 코드(`script`)를 실행하고, 그 결과를 `callback`으로 반환합니다.
  
- 이 코드는 Bluetooth 통신 후 상태가 변경될 때마다 `localStorage` 값을 즉시 업데이트하여, WebView의 JavaScript 측에서도 새로운 상태 값을 실시간으로 반영할 수 있게 합니다.

### 요약

1. **handleCommandCompletion**: 블루투스 명령 완료 후, 명령 종류에 따라 `localStorage` 업데이트를 위한 UUID, secretKey, 상태값을 결정합니다.
2. **updateLocalStorageStatus**: JavaScript 코드 조각을 생성하여 `evaluateJavaScript`로 WebView의 `localStorage` 값을 실시간으로 업데이트합니다.
3. **evaluateJavaScript 사용**: Android 네이티브 코드에서 JavaScript 코드를 WebView에서 실행하여, 블루투스 작업 완료 후 `localStorage` 상태값을 변경합니다.

728x90
반응형