마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

안드로이드 서비스에서 소켓통신 관련 질문드립니다.

0 추천

안녕하세요 현재 안드로이드 개발중에 계속 막히는게 있어 질문드립니다.

 

현재 통신을 모든 액티비티에서 유지해야 하기 때문에 통신을 서비스에서 돌리고 있습니다.

 

LoginActivity(액티비티)

NetworkService(서비스)

SocketNetwork(쓰레드)

 

클래스 3개를 놓고 액티비티가 onCreate를 하면 아래의 코드를 실행하며,

 

bindService(new Intent(LoginActivity.this,  mService.getClass()), mConnection, Context.BIND_AUTO_CREATE);

 

서비스는 현재 아래와 같이 이루어져 있습니다.

public class NetworkService extends Service {

    private SocketNetwork soc;

    //서비스 바인더 내부 클래스 선언
    public class MainServiceBinder extends Binder {
        public NetworkService getService() {
            return NetworkService.this; //현재 서비스를 반환.
        }
    }

    @Override
    public void onCreate() {
        soc = new SocketNetwork(NetworkUtil.IP, NetworkUtil.PORT, LoginActivity.handler);
        soc.start();
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        soc.close();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    public void sendData(byte[] data) {
            soc.writeData(data);
    }

    public void setHandler(Handler handler) {
        soc.setHandler(handler);
    }

}

 

이제 문제는 액티비티에서 서비스의 sendData()를 호출할 때

java.lang.NullPointerException: Attempt to invoke virtual method 'void neighbor.com.mbis.MapUtil.Thread.SocketNetwork.writeData(byte[])' on a null object reference
                                                                     at neighbor.com.mbis.Network.NetworkService.sendData(NetworkService.java:56)
                                                                     at neighbor.com.mbis.Activity.LoginActivity.sendData(LoginActivity.java:371)
                                                                     at neighbor.com.mbis.Activity.LoginActivity.access$200(LoginActivity.java:47)
                                                                     at neighbor.com.mbis.Activity.LoginActivity$1.handleMessage(LoginActivity.java:156)

 

아래와 같이 서비스의 soc 객체가 null이라고 합니다;;

 

private NetworkService mService;
mService.sendData(Data.writeData);
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className, IBinder service) {
        NetworkService.MainServiceBinder binder = (NetworkService.MainServiceBinder) service;
        mService = binder.getService(); //서비스 받아옴
        mService.sendData(Data.writeData);
    }

    // Called when the connection with the service disconnects unexpectedly
    public void onServiceDisconnected(ComponentName className) {
        mService = null;
    }
};

 

바로 위에 있는 코드는 액티비티에서 서비스의 sendData를 호출 할 때 사용하는 방식이구요;;

 

너무 어렵습니다 제발 가르쳐주세요ㅠㅠ 왜 저 soc 객체는 계속 null인 걸까요??

개발꿈 (170 포인트) 님이 2016년 11월 9일 질문
개발꿈님이 2016년 11월 10일 수정

1개의 답변

0 추천
 
채택된 답변
NetworkService에 onBind함수가 null을 리턴하네요.

NetworkService의 멤버로 MainServiceBinder를 두고 이를 리턴하도록 해보세요.
Development Guy (70,570 포인트) 님이 2016년 11월 9일 답변
개발꿈님이 2016년 11월 10일 채택됨
정말 감사합니다.
그런데 이번엔 새로운 오류가 생겨버렸습니다.

android.os.NetworkOnMainThreadException

오류가 생겨버렸는데, 서비스에서 다른 쓰레드를 불러와서 통신을 했는데 왜 메인쓰레드에서 통신을 한다는 오류가 생기는지 모르겠습니다ㅠㅠ
SocketNetwork 라는 클래스를 본인이 직접 작성하신건가요?
내부적으로 Socket을 어떤식으로 초기화 하는지는 모르겠으나
Socket 인스턴스 초기화할때 IP와 Port를 파라미터로 하는 생성자는 내부적으로 connect도 같이 하기 때문에 Service의 onCreate에서 호출하게 되면 NetworkOnMainThreadException이 발생할 수 있습니다.
아 그렇군요! 정말 감사합니다.
SocketNetwork는 Thread를 상속받는 객체이고, 생성자는 아래와 같이 정의하였습니다.

    public SocketNetwork(String IP, int PORT, Handler mHandler) {
        runFlag = true;
        this.IP = IP;
        this.PORT = PORT;
        this.mHandler = mHandler;

        socket = new Socket();
        sAddress = new InetSocketAddress(IP, PORT);
    }

그렇다면 가장 현명한 방법은 어떤 것 인가요? 저번주부터 계속 고민중이라......
onStartCommand에 넣고 startService와 bindService를 같이 해도 같은 오류가 발생했었습니다
혹시 soc.writeData 라는 함수의 내부 동작이 어떻게 되나요?
onStartcommand함수는 메인쓰레드에서 동작하기때문에 스레드를 사용하지 않았다면 같은 에러가 발생합니다.
public void writeData(byte[] data) {
        if (socket != null && dos != null && data != null) {
            try {
                mv.setSr_cnt(mv.getSr_cnt() + 1);
                dos.write(data);
                Log.e("[sendData]", " send byte Data !!");
            } catch (IOException e) {
                mHandler.sendEmptyMessage(HandlerPosition.WRITE_SERVER_DISCONNECT_ERROR);
                Log.d("[sendData]", " Failed send byte Data !!");
            }
        }
    }

입니다. SocketNetwork에서 데이터를 전송할때는 따로 스레드를 생성하지 않습니다.
그럼 onStartCommand에서 writeData 를 호출할떄 쓰레드를 생성 해 주어야 합니다. 또는 IntentService를 사용하면 onHandleIntent를 onStartCommand처럼 사용하되 쓰레드 생성 없이 통신을 할 수 있습니다.
단, IntentService에서는 각 action이 Async하게 동작하기때문에 중간에 지연이 생기면 그다음 액션도 지연이 됩니다.
정말 감사합니다!!!!
한번 코딩 해 보고 다시 한 번 댓글 달도록 하겠습니다.
정말 감사합니다.
sendData 메소드에 스레드를 추가하니 잘 되고 있습니다. 정말 감사합니다.
혹시 실례가 안된다면 왜 스레드를 사용해야하는지 원리를 알 수 있을까요?
안드로이드에서 NetworkOnMainThreadException의 의미는 알고 계신가요?

안드로이드 OS에서 메인쓰레드에서 예측할수 없는 지연 발생에 대해서는 사용성을 고려하여 예외를 뱉어내도록 하였고 이중 일부가 통신쪽입니다.
Service 컴포넌트가 화면이 없어서 백그라운드 개념으로 Thread와 같은 개념으로 생각하고 계신거 같은데 화면없는 Activity라고 생각하여 Thread와는 다르게 보셔야 합니다.
바쁘실텐데 계속 친절히 설명 해 주셔서 정말 감사합니다.
많은 도움 얻고 갑니다.
감사합니다~
...