티스토리 뷰
업무에서 와이파이 다이렉트를 통해서 파일을 송신해야할 일이 생겼습니다.
그래서 구글링을 해서 막 예제를 찾았는데, 막상 찾은 예제들이 제대로 동작도 안하고 소스 파악도 힘들었습니다.
- 구글링을 통해 찾을수 있는 예제(https://github.com/ahmontero/wifi-direct-demo) ; 이런 종류의 예제가 많이 보입니다.
그래서 저 나름대로 정리해서 이미지를 전송하는 샘플을 하나 만들어봤는데요,
혹시 저와같은 고생(?)을 하시는분들에게 도움이 되길 바랍니다.
아래는 제가 만든 와이파이 샘플의 주소입니다.
https://github.com/karrel84/WifiDirectSample
AndroidManifest.xml
와이파이 다이렉트를 이용하기위해서는
<uses-feature
android:name="android.hardware.wifi.direct"
android:required="true" />
기능 사용이 필요합니다.
그리고 제 예제에서는 아래와같은 권한이 필요했습니다.
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
이중에 WRITE_EXTERNAL_STORAGE 와 ACCESS_COARSE_LOCATION 권한은 코드상에서 요청해줘야합니다.
private fun checkPermission() {
TedPermission.with(this)
.setPermissionListener(permissionlistener)
.setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
.setPermissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION
)
.check()
}
특히 다른것보다
Manifest.permission.ACCESS_COARSE_LOCATION
권한이 필요한데, 구글링을 해서 얻은 샘플에서는 위의 권한을 요청하고 있지 않습니다.
그래서 6.0 미만의 안드로이드 기기에서는 검색된 기기가 나오지 않는 현상이 발생했고 이 부분때문에 조금 삽질을 하게되었습니다. (권한 문제일거라는 생각을 하면서도 삽질 ㅠ)
WifiDirectBroadcastReceiver.kt
와이파이의 상태 등에 대해서 수신받을 수 있는 브로드캐스트 리시버를 만들어서 등록해주어야 합니다.
수신 받을 액션들은 아래와 같습니다.
private fun setupIntentFilter() {
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)
intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION)
}
WIFI_P2P_STATE_CHANGED_ACTION
- Wi-Fi p2p의 활성화 여부를 나타내는 브로드캐스트 동작입니다. 추가 EXTRA_WIFI_STATE는 상태 정보를 int로 제공합니다.
WIFI_P2P_PEERS_CHANGED_ACTION
- 사용 가능한 피어 목록이 변경되었음을 나타내는 브로드캐스트 의도 작업입니다. 피어 검색, 손실 또는 업데이트의 결과로 보낼 수 있습니다.
WIFI_P2P_CONNECTION_CHANGED_ACTION
- Wi-Fi p2p 연결 상태가 변경되었음을 나타내는 브로드캐스트 의도 작업입니다. 하나의 추가 EXTRA_WIFI_P2P_INFO는 WifiP2pInfo 개체 형식의 p2p 연결 정보를 제공합니다. 또 다른 추가 EXTRA_NETWORK_INFO는 네트워크 정보를 NetworkInfo 형식으로 제공합니다. 세 번째 추가 정보는 그룹의 세부 정보를 제공합니다.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
- 이 디바이스 세부 정보가 변경되었음을 나타내는 브로드캐스트 의도 작업입니다.
참고 : WifiP2pManager
WifiP2pManager
WifiP2pManager 를 통해서 주변의 peer 를 탐색하거나 원하는 피어로 연결 요청이 가능합니다.
private fun setupWifiP2pManager() {
manager = getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager
channel = manager?.initialize(this, mainLooper, null)
}
WifiP2pInfo
Wifi 다이렉트로 연결이 완료되면 WIFI_P2P_CONNECTION_CHANGED_ACTION 액션을 통해서 WifiP2pInfo를 받을 수 있습니다.
WifiP2pInfo 에는 와이파이 그룹에서의 owner인지 확인해주는 플래그등을 참조할 수 있습니다.
이 예제에서는(다른 구글 예제와 마찬가지로..) owner 가 서버가되고 owner가 아니면 클라이언트가 외어서 클라이언트 -> 오너 로 사진을 전송하는 방식으로 구현하였습니다.
ServerFragment.kt
서버를 구현합니다. 약속된 8988 포트를 통해서 클라이언트가 송신하는 파일을 특정 폴더에 저장하는 로직입니다.
private fun runServer() {
disposable?.dispose()
disposable = Observable.just("")
.subscribeOn(Schedulers.io())
.map {
val serverSocket = ServerSocket(8988)
val client = serverSocket.accept()
val savePath = "${Environment.getExternalStorageDirectory()}/${activity?.packageName}/${System.currentTimeMillis()}.png"
val f = File(savePath)
val dirs = File(f.parent)
if (!dirs.exists())
dirs.mkdirs()
f.createNewFile()
val inputstream = client.getInputStream()
copyFile(inputstream, FileOutputStream(f))
serverSocket.close()
f
}
.doOnError {
it.printStackTrace()
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
runServer()
textView.text = "${it.path} 로 파일을 저장하는데 성공하였습니다.\n${Calendar.getInstance().time}}"
}
}
PictureFragment.kt
파일을 송신하는 로직입니다.
host 의 주소는 wifip2pinfo 객체에서 groupOwnerAddress로 얻어올 수 있습니다.
private fun sendPictureFile(uri: Uri) {
val host = info.groupOwnerAddress
val socket = Socket()
val port = 8988
println("fileUri : $uri, host : $host, port : $port")
try {
socket.bind(null)
socket.connect(InetSocketAddress(host, port), 5000)
val outputStream = socket.getOutputStream()
val cr = activity?.contentResolver
var inputStream: InputStream? = null
try {
inputStream = cr?.openInputStream(uri)
} catch (e: FileNotFoundException) {
e.printStackTrace()
}
val buf = ByteArray(1024)
val total = File(uri.path).length()
var sum: Long = 0
try {
var len = inputStream!!.read(buf)
sum += len
while (len != -1) {
outputStream.write(buf, 0, len)
len = inputStream.read(buf)
sum += len
SendFilePercentEvent.send(total, sum)
println("copyFile len : $len")
}
outputStream.close()
inputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
} catch (e: IOException) {
e.printStackTrace()
} finally {
if (socket != null) {
if (socket.isConnected) {
try {
socket.close()
} catch (e: IOException) {
// Give up
e.printStackTrace()
}
}
}
}
}
스크린샷
성의없는 스샷 (ㅇㅇ)
대략 제가 만든 예제는 위와같이 구성되어있습니다.
관련된 일을 진행하실때 누군가에겐 도움이 되길 바랍니다.
'개발 > ANDROID' 카테고리의 다른 글
비트맵을 겹쳐서 OOM 극복하기 (0) | 2019.04.01 |
---|---|
[트랜스퍼모드] xPermode 적용기 (0) | 2019.02.11 |
안드로이드 투명 인디케이터 만들기 (0) | 2018.10.17 |
fontFamily (0) | 2018.10.15 |
Android Flavors (0) | 2018.10.01 |
- Total
- Today
- Yesterday