Today's

길을 나서지 않으면 그 길에서 만날 수 있는 사람을 만날 수 없다

모바일 앱(안드로이드)

개발일기 # 번외편 : 안드로이드 앱도 웹 서버가 될까 ?

Billcorea 2022. 11. 7. 23:52
반응형

https://github.com/ahmedmolawale/AndroidNanoHttpd

 

GitHub - ahmedmolawale/AndroidNanoHttpd: A sample android project to showcase the use of NanoHttpd in Android.

A sample android project to showcase the use of NanoHttpd in Android. - GitHub - ahmedmolawale/AndroidNanoHttpd: A sample android project to showcase the use of NanoHttpd in Android.

github.com

오늘은 번외 편으로 안드로이드를 이용한 웹서버 간략 구현에 대해서 알아볼까 합니다.  이 포스팅은 위 링크의 글을 참조하였음을 밝혀 둡니다. 

 

Gradle 추가

// nano HTTP 구현
implementation 'com.nanohttpd:nanohttpd:2.2.0'

이거 하나 추가 하면 끝입니다.  다음은 Local에서 WebServer로 사용할 코드를 구현해 보겠습니다. 


import android.content.Context
import android.os.StatFs
import android.util.Log
import androidx.core.content.ContextCompat
import fi.iki.elonen.NanoHTTPD
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.net.Inet4Address
import java.net.NetworkInterface
import java.net.SocketException

class LocalWebserver(context: Context, port: Int) : NanoHTTPD(port) {

    var context: Context? = context
    val MIME_JAVASCRIPT = "text/javascript"
    val MIME_CSS = "text/css"
    val MIME_JPEG = "image/jpeg"
    val MIME_PNG = "image/png"
    val MIME_SVG = "image/svg+xml"
    val MIME_JSON = "application/json"
    val MIME_GIF = "image/gif"
    val MIME_BMP = "image/bmp"
    var mimeType = MIME_HTML
    val folderName = "portfolio" //name of the folder holding the asset of the page you wanna load

    val TAG = "LocalWebserver"

    override fun serve(session: IHTTPSession?): Response? {
        val uri = session?.uri
        try {
            when {
                uri!!.endsWith(".js") -> {
                    mimeType = MIME_JAVASCRIPT
                }
                uri.endsWith(".css") -> {
                    mimeType = MIME_CSS
                }
                uri.endsWith(".html") -> {
                    mimeType = MIME_HTML
                }
                uri.endsWith(".jpeg") -> {
                    mimeType = MIME_JPEG
                }
                uri.endsWith(".png") -> {
                    mimeType = MIME_PNG
                }
                uri.endsWith(".jpg") -> {
                    mimeType = MIME_JPEG
                }
                uri.endsWith(".svg") -> {
                    mimeType = MIME_SVG
                }
                uri.endsWith(".bmp") -> {
                    mimeType = MIME_BMP
                }
                uri.endsWith(".gif") -> {
                    mimeType = MIME_GIF
                }
                uri.endsWith(".json") -> {
                    mimeType = MIME_JSON
                }
            }
        } catch (e: Exception) {
            Log.e(TAG, "MINE ERROR ... ${e.message}")
        }
        Log.e(TAG, "${getLocalIpAddress()} ${getStoragePath()}")
        val root = "${getStoragePath()}${File.separator}"
        var fis: FileInputStream? = null
        val file = File(
            root +
                    "${folderName}/${uri}"
        )
        try {
            if (file.exists()) {
                fis = FileInputStream(file);
            }
        } catch (ioe: IOException) {
            Log.e("Httpd %s", ioe.toString())
        }
        return newFixedLengthResponse(
            Response.Status.OK,
            mimeType,
            fis,
            file.length()
        )
    }

    private fun getStoragePath(): String? {
        var path: String? = null
        var space: Long = 0
        val files: Array<File> = ContextCompat.getExternalFilesDirs(context!!, null)
        // go through the options to choose one with more available storage capacity
        for (f in files) {
            val stat = StatFs(f.path)
            val blockSize = stat.blockSizeLong
            val totalBlocks = stat.blockCountLong

            // check if storage capacity is more than the previous one
            if (totalBlocks * blockSize > space) {
                space = totalBlocks * blockSize
                path = f.path
            }
        }
        return path
    }

    private fun getLocalIpAddress(): String {
        try {
            val en = NetworkInterface.getNetworkInterfaces()
            while (en.hasMoreElements()) {
                val intf = en.nextElement()
                val enumIpAddr = intf.inetAddresses
                while (enumIpAddr.hasMoreElements()) {
                    val inetAddress = enumIpAddr.nextElement()
                    if (!inetAddress.isLoopbackAddress && inetAddress is Inet4Address) {
                        return inetAddress.getHostAddress().toString()
                    }
                }
            }
        } catch (ex: SocketException) {
            ex.printStackTrace()
        }
        return ""
    }
}

코드는 참조한 링크의 내용을 그대로 구현 했습니다. 잘 모르는 부분도 있고 해서요. 코드를 보면 local IP을 확인하고, 해당 아이피를 기준으로 웹서버를 구동하는 그런 정도입니다.  추가적으로 해야 할 부분은 이제 화면에서 보여줄 html 코드를 구현해 보는 것인데, 

 

아직은 그럴 생각이 없기 때문에 그냥 이렇게 구현 하는 정도까지만 포스팅을 해 두겠습니다. 이제 실행을 해 볼까요?

 

웹서버 구동

var localWebserver = LocalWebserver(this@MainActivity, 8035)
localWebserver.start()

 이런 정도의 코드 구현으로 구현이 됩니다. 그러면 위 구현된 코드에서 getStoreagePath()에서 구해온 경로에 index.html을 등을 넣어 주면 호출이 되는 것을 알 수 있었습니다. 

 

구동된 로그 샘플

크롬 등으로 현재 테스트 중인 앱의 서버에 접속을 해 보고 그 실행 로그가 출력되는 것을 확인하였습니다.  지금은 내가 사용하는 스마트폰에 wifi debugging을 실행하고 앱을 빌드해서 실행해 본 것입니다.

 

이것으로 로컬 웹서버가 필요한 경우 구현해 볼 수 있을 것 같습니다.

반응형