Android-네트워킹

7 분 소요

스레드 & 핸들러

  • mainactivity에서 스레드를 따로 하나 생성하고 거기에서 값을 조작시키고 메인스레드에서 UI를 통제하는건 표준자바와 같기 때문에 당연히 되는 원리
  • 근데 만약에 UI를 제어하는걸 따로 만든 스레드클레스에다가 넣고 하려고하면 메인스레드와의 충돌이 일어나서 앱이 죽는다.
  • 이럴 때 필요한게 핸들러이다.
  • 핸들러클래스 생성하고 따로 만든 스레드에선 값을 조작하고 그 조작한 값을 Message 객체에다가 Bundle형태로 넣어서 값을 저장시키고 핸들러클래스에서 다시 그 메세지를 받아와서 값을 UI제어를 통해 보여준다. 이렇게 하면 메인스레드가 핸들러에서 하는 UI제어이기 때문에 인정!하고 충돌이 일어나지않는다.
  • 그런데 이런 Handler로 접근하는건 굉장히 복잡하다. 숫자만 바뀌게하는 UI제어 뿐만아니라 프로그레스바를 늘리고 하는 상황이 발생할 수도 있다. 그래서 좀 더 단순한 형태를 사용한다.

  • 단순한 형태
    • 일단 Thread클래스를 따로 만드는게 아니라 button안에서 runnable을 implement한다.
    • Thread만드는 방법은 2가지인데 하나가 Thread 클래스를 따로 정의해서 사용하는거고 하나는 밑에처럼 runnable을 implement하는 방식
    • 아래와 같이 한다.
        Handler handler2 = new Handler();
        textView = (TextView) findViewById(R.id.textView);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {
                int value = 0;
                boolean running = true;
                @Override
                public void run() {
                    while(running) {
                        value ++;
                        handler2.post(new Runnable() {
                            @Override
                            public void run() {
                                textView.setText("현재 값 : " + value);
                                // 이렇게 handler를 통해 접근하는건 가능하다.
                            }
                        });
                        try {
                            Thread.sleep(1000);
                        } catch (Exception e){
                        }
                    }
                }
            }).start();
        }
        });
      

AsyncTask

  • 핸들러를 사용하는 코드가 복잡하게 느껴지는 이유는 어떤 코드는 스레드 안에서 실행되고 어떤 코드는 UI를 접근하기 위해 핸들러 안에서 실행되어야 한다는 것을 구분해야 한다는 것 때문입니다.
  • 이런 혼란스러운 면을 조금이라도 줄일 수 있도록 AsyncTask에서는 하나의 클래스 안에서 스레드 안에 넣을 코드와 UI를 접근할 코드를 모두 넣어둘 수 있도록 했습니다.

AsyncTask에서 메소드 실행 순서 예시

  • 스레드 안에서 실행될 코드는 doInBackground 메소드 안에 넣어두고 UI를 접근할 코드는 onPreExecute, onProgressUpdate, onPostExecute에 넣어둘 수 있습니다.
  • AsyncTask도 스레드를 실행하는 것과 같기 때문에 스레드 안에서 실행될 대부분의 코드는 doInBackground 안에 들어가 있게 되며 중간중간 화면에 표시하기 위한 코드 실행을 위해 onProgressUpdate 메소드가 호출됩니다.
  • onProgressUpdate 메소드는 doInBackground 메소드 안에서 publishProgress 메소드가 호출 될 때 마다 자동으로 호출됩니다.
    package com.example.progress;
    
    import android.os.AsyncTask;
    import android.os.Handler;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.ProgressBar;
    import android.widget.Toast;
    
    public class MainActivity extends AppCompatActivity {
    
        ProgressBar progressBar;
        Handler handler = new Handler();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            progressBar = (ProgressBar) findViewById(R.id.progressBar);
            Button button = (Button) findViewById(R.id.button);
    
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
    
                    ProgressTask task = new ProgressTask();
                    task.execute("시작");
                    // 시작이라는 문자열은 아무 의미없다. 다만 첫번째 매개변수로 들어갈 뿐이다.
                }
            });
        }
    
        class ProgressTask extends AsyncTask<String, Integer, Integer> {
            //이걸 객체생성해서 실행하면 자동적으로 doInBackground메소드가 실행이 된다.
            //생성자같은 느낌인듯하다.
            int value = 0;
            @Override
            protected Integer doInBackground(String... strings) 
                while (true) {
                    if (value>100) {
                        break;
                    }
                    value++;
    
                    publishProgress(value);
                    // 이걸 하면 자동으로 onProgressUpdate가 자동으로 호출된다.
                    // 그리고 이 값이 onProgressUpdate의 매개변수로 들어간다.
                    try {
                        Thread.sleep(200);
                    } catch (Exception e){
                    }
                }
                return value;
            }
            @Override
            protected void onProgressUpdate(Integer... values) {
                //UI업데이트
                super.onProgressUpdate(values);
                progressBar.setProgress(values[0].intValue());
                //위에 있는 publishProgress에서 받아온 값
            }
            @Override
            protected void onPostExecute(Integer integer) {
                // 마지막에 과정이 다 끝나고 나면 이 메소드가 호출된다.
                super.onPostExecute(integer);
                Toast.makeText(getApplicationContext(), "완료됨", Toast.LENGTH_LONG).show();
            }
        }
    

소켓으로 클라이언트 - 서버 네트워킹 구축하기

  • 서버측
  1. ChatService.java
     package com.example.server;
        
     import android.app.Service;
     import android.content.Intent;
     import android.os.IBinder;
     import android.util.Log;
        
     import java.io.ObjectInputStream;
     import java.io.ObjectOutputStream;
     import java.net.ServerSocket;
     import java.net.Socket;
        
     public class ChatService extends Service {
         public ChatService() {
         }
        
         @Override
         public IBinder onBind(Intent intent) {
             throw new UnsupportedOperationException("Not yet implemented");
         }
        
         @Override
         public void onCreate() {
             super.onCreate();
        
             ServerThread thread = new ServerThread();
             thread.start();
         }
        
         @Override
         public int onStartCommand(Intent intent, int flags, int startId) {
             return super.onStartCommand(intent, flags, startId);
         }
        
         @Override
         public void onDestroy() {
             super.onDestroy();
         }
        
         class ServerThread extends Thread {
             // 이 스레드 안에서 서버를 실행을 하는 메소드를 만들 것이다.
             @Override
             public void run() {
                 int port = 5001;
                 try {
                     ServerSocket server = new ServerSocket(port);
                     // 이렇게 하면 서버가 실행이 되는데 5001번이라는 포트에서 대기하고 있는다.
                     Log.d("ServerThread", "서버가 실행됨");
        
                     while(true) {
                         Socket socket = server.accept();
                         // accept는 대기를 하고 있다가 클라이언트가 접속을 하게되면 socket이라는 객체가 리턴이 된다.
                         // 이 socket객체를 통해서 클라이언트가 요청한 정보를 받을 수 있다.
        
                         ObjectInputStream instream = new ObjectInputStream(socket.getInputStream());
                         // socket에 들어있는 클라이언트의 요청정보를 objectinputstream에 저장해서 참조변수 instream에다가 저장한다.
                         // 그럼 이제 instream을 이용해서 클라이언트쪽에서 보내온 정보를 읽어낼 수 있다.
                         Object input = instream.readObject();
                         Log.d("ServerThread", "input : "+ input);
        
                         // 밑의 코드는 이제 클라이언트 쪽으로 데이터를 보낼 때 사용하는 코드이다.
                         ObjectOutputStream outstream = new ObjectOutputStream(socket.getOutputStream());
                         outstream.writeObject(input+"from server");
                         //outputstream은 항상 버퍼에 남아있을 수 있으니까 꼭 flush를 해줘야한다.
                         outstream.flush();
                         Log.d("ServerThread", "output 보냄.");
        
                         socket.close();
                     }
                 } catch (Exception e) {
                 }
             }
         }
     }
    
  2. MainActivity.java
    package com.example.server;
    
    import android.content.Intent;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button button = (Button) findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //ServerThread thread = new ServerThread();
                    //thread.start();
    
                    // 직접 스레드를 메인엑티비티에 구현하는건 좋지않은 방법이라 service클래스를 만들어서 관리한다.
                    Intent intent = new Intent(getApplicationContext(), ChatService.class);
                    startService(intent);
                }
            });
        }
    }
    
  • 클라이언트측
  1. MainActivity.java
     package com.example.socket;
        
     import android.os.Handler;
     import android.support.v7.app.AppCompatActivity;
     import android.os.Bundle;
     import android.util.Log;
     import android.view.View;
     import android.widget.Button;
     import android.widget.TextView;
        
     import java.io.ObjectInputStream;
     import java.io.ObjectOutputStream;
     import java.net.Socket;
        
     public class MainActivity extends AppCompatActivity {
         TextView textView;
         Handler handler = new Handler();
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.activity_main);
        
        
             textView = (TextView) findViewById(R.id.textView);
             Button button = (Button) findViewById(R.id.button);
             button.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
                     ClientThread thread = new ClientThread();
                     thread.start();
                 }
             });
         }
        
         class ClientThread extends Thread{
             @Override
             public void run() {
                 String host = "localhost";
                 int port = 5001;
        
                 try {
                     Socket socket = new Socket(host, port);
        
                     // 서버로 데이터 보내기
                     ObjectOutputStream outstream = new ObjectOutputStream(socket.getOutputStream());
                     outstream.writeObject("안녕!!");
                     outstream.flush();
                     Log.d("ClientThread", "서버로 보냄");
        
                     // 서버에서 데이터 받기
                     ObjectInputStream instream = new ObjectInputStream(socket.getInputStream());
                     final Object input = instream.readObject();
                     Log.d("ClientThread", "받은 데이터 : " + input);
        
                     handler.post(new Runnable() {
                         @Override
                         public void run() {
                             textView.setText("받은 데이터 : " + input) ;
                         }
                     });
                 } catch (Exception e) {
                     e.printStackTrace();
                 }
             }
         }
     }
    

웹서버에서 http프로토콜을 사용한 데이터 송수신

    package com.example.http;
    
    import android.os.Handler;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.TextView;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    public class MainActivity extends AppCompatActivity {
    
        Handler handler = new Handler();
        EditText editText;
        TextView textView;
        String urlstr;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            editText = (EditText) findViewById(R.id.editText);
            textView = (TextView) findViewById(R.id.textView);
    
            Button button = (Button)findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    urlstr = editText.getText().toString();
                    RequestThread thread = new RequestThread();
                    thread.start();
                }
            });
        }
        class RequestThread extends Thread{
            @Override
            public void run() {
    
                try {
                    URL url = new URL(urlstr);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    
                    if(conn != null) {
                        conn.setConnectTimeout(10000);
                        conn.setDoInput(true);
                        conn.setDoOutput(true);
                        conn.setRequestMethod("GET");
                        int resCode = conn.getResponseCode();
                        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                        String line = null;
                        while(true) {
                            line = reader.readLine();
                            if(line ==null) {
                                break;
                            }
                            println(line);
                        }
                        reader.close();
                        conn.disconnect();
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
        public void println (final String data) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    textView.append(data + "\n");
                }
            });
        }
    }

Volley

  • Volley 라이브러리는 웹 요청과 응답을 단순화시키기 위해 만들어진 라이브러리들 중의 하나입니다.
  • 꼭 이 라이브러리를 사용해야 하는 것은 아니지만 앱에서 가장 많이 사용되는 라이브러리들 중의 하나이므로 어떻게 이 라이브러리를 사용하는지 알아두는 것이 좋습니다.
  • Volley를 사용하는 방법은 그리 어렵지 않습니다.
  • 먼저 요청(Request) 객체를 만들고 이 요청 객체를 요청 큐(RequestQueue)라는 곳에 넣어주기만 하면 됩니다.
  • 그러면 요청 큐가 알아서 웹서버에 요청하고 응답까지 받아 여러분이 사용할 수 있도록 지정된 메소드를 호출해줍니다.

  • Volley 라이브러리를 사용할 때의 가장 큰 장점은 스레드를 신경 쓰지 않아도 된다는 것입니다.
  • Volley가 가지는 몇 가지 장점들은 다음과 같습니다.
    • 네트워크 요청(Request) 우선 순위를 자동으로 관리한다.
    • 동시에 여러 네트워크 요청을 할 수 있다.
    • 요청을 할 때 Cache 적용 여부를 의식하지 않아도 된다.
  • 요청 큐가 내부에서 스레드를 만들고 웹서버에 요청하고 응답을 받고 나면 메인 스레드에서 결과를 처리할 수 있도록 만든 후 여러분이 설정한 리스너의 메소드를 호출해줍니다.
  • 따라서 화면에 결과를 표시할 때 핸들러를 사용하지 않아도 됩니다.
      package com.example.myvolley;
        
      import android.support.v7.app.AppCompatActivity;
      import android.os.Bundle;
      import android.view.View;
      import android.widget.Button;
      import android.widget.TextView;
        
      import com.android.volley.AuthFailureError;
      import com.android.volley.Request;
      import com.android.volley.Response;
      import com.android.volley.VolleyError;
      import com.android.volley.toolbox.StringRequest;
      import com.android.volley.toolbox.Volley;
        
      import java.util.HashMap;
      import java.util.Map;
        
      public class MainActivity extends AppCompatActivity {
          TextView textView;
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
        
              textView = (TextView) findViewById(R.id.textView);
              Button button = (Button) findViewById(R.id.button);
              if(AppHelper.requestQueue == null) {
                  AppHelper.requestQueue = Volley.newRequestQueue(getApplicationContext());
              }
        
        
              button.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      sendRequest();
                  }
              });
          }
        
          public void println(String data) {
              textView.append(data+"\n");
          }
        
          public void sendRequest() {
              String url = "http://www.google.co.kr";
              StringRequest request = new StringRequest(
                      Request.Method.GET,
                      url,
                      new Response.Listener<String>() {
                          @Override
                          public void onResponse(String response) {
                              println("응답 :  " + response.substring(0,500));
                          }
                      },
                      new Response.ErrorListener() {
                          @Override
                          public void onErrorResponse(VolleyError error) {
                              println("에러 : " + error.getMessage());
                          }
                      }){
                  @Override
                  protected Map<String, String> getParams() throws AuthFailureError {
                      Map<String, String> params = new HashMap<String,String>();
                      return params;
                  }
              };
              request.setShouldCache(false);
              AppHelper.requestQueue.add(request);
              println("요청 보냄");
          }
      }
    
  • AppHelper.java
     package com.example.myvolley;
        
     import com.android.volley.RequestQueue;
        
     public class AppHelper {
         public static RequestQueue requestQueue;
     }
    

Gson

  • Gson은 JSON 문자열을 객체로 변환해 주는 라이브러리입니다.
  • 즉, JSON 문자열이 자바 객체로 만들어질 수 있습니다.
  • Volley를 이용해 웹서버로부터 JSON 응답을 받았다면 Gson을 이용해 자바 객체로 바꾸고 그 객체 안에 들어있는 데이터를 사용하게 됩니다.
    public class MovieList {
        MovieListResult boxOfficeResult;
    }


    public class MovieListResult {
        String boxofficeType;
        String showRange;
        ArrayList<Movie> dailyBoxOfficeList = new ArrayList<Movie>();
    }


    public class Movie {
        String rnum;
        String rank;
        String rankInten;
        String rankOldAndNew;
        String movieCd;
        String movieNm;
        String openDt;
        String salesAmt;
        String salesShare;
        String salesInten;
        String salesChange;
        String salesAcc;
        String audiCnt;
        String audiInten;
        String audiChange;
        String audiAcc;
        String scrnCnt;
        String showCnt
    }


    public void processResponse(String response) {
        Gson gson = new Gson();
        MovieList movieList = gson.fromJson(response, MovieList.class);
    
        if(movieList != null) {
            int countMovie = movieList.boxOfficeResult.dailyBoxOfficeList.size();
            println("박스오피스의 타입 : " + movieList.boxOfficeResult.boxofficeType);
            println("응답받은 영화의 갯수 : " + countMovie);
    
        }
    }

이미지 다운로드

  • volley라이브러리는 이미지 다운로드받는 기능이 훌륭하지않다.
  • 그래서 AsyncTask를 이용한다.

카테고리:

업데이트:

댓글남기기