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

안드로이드 onCreate에 값

0 추천

onPostExecute 부분의 data를 onCreate로 보내주고싶은데 어떻게 해야할지 모르겠습니다.

웹->jsp->데이터베이스->jsp->앱을 통해서 값을 가지고왔습니다.

이것좀알려주소서 (170 포인트) 님이 2021년 12월 12일 질문
이것좀알려주소서님이 2021년 12월 12일 수정

4개의 답변

+1 추천
 
채택된 답변

코드를 올리기 전에 답변을 작성했네요. 방법은 어쨋든 동일합니다. HttpConnection 에 리스너를 하나 달아서 처리하시면 됩니다.

public class HttpConnection extends AsyncTask<URL, Integer, String> {
      public interface Listener {
         void onTaskError(String message);
         void onTaskSuccess(String data);
      }
 
     @Nullable private Listener listener;
     public HttpConnection(Listener listener) {
         this.listener = listener;
     }

     @Override
     proteced String doInBackground(URL...urls) [
          try {
              ...
               return connection.request(urls[0]);
           } catch (Exception e). {
                return null;
           }
     }

     @Override
     protected void onPostExecute(String result) {
          if (result == null) {
                 listener.onTaskError("데이터 처리 중 오류발생.");
                 return;
          }

          listener.onTaskSuccess(result);
     }
 }

 

 

 

spark (226,380 포인트) 님이 2021년 12월 12일 답변
spark님이 2021년 12월 12일 수정
public interface Listener {
            public void onTaskError(String message);
            public void onTaskSuccess(String data);
        }
interface부분에서 Inner classes cannot have static declarations 이러한 오류가떠서  클래스에다가 static을 붙이면
Non-static field 'loadingBar' cannot be referenced from a static context
이러한 오류가뜹니다....
HttpConnection이 static class가 되면 loadingBar는 액티비티 내의 뷰이기 때문에 static이 아니라서, HttpConnection class 에서는 접근할 수가 없어요.

HttpConnection을 일단은 액티비티에서 분리해서 별도의 클래스로 만드세요. 이게 더 유연한 코드입니다.

두가지 접근 방법이 생각이 나는데요. 첫번째는
HttpConnection.execute를 호출하기 전에 로딩바를 보여주고, 리스너로 응답을 받으면 감추는 겁니다.

     showLoadingIndicator();
     HttpConnection.execute(...);

      public void onTaskError(Exception ex) {
            hideLoadinIndicator();
            showError("Something went wrong");
       }
          
      public void onTaskSuccess(MyData data) {
            hideLoadinIndicator();
            updateUi(data);
      }

      private void showLoadingIndicator() {
           loadingBar.setVisibility(View.VISIBLE);
      }

      private void hideLoadinIndicator() {
          loadingBar.setVisibility(View.GONE);
      }

다른 방법은 HttpConnection.Listener에 메소드를 추가해서 처리하는 겁니다.
     
      public interface Listener {
          public void onTaskLoading();
          public void onTaskError(Exception ex);
          public void onTaskSuccess(String data);
      }

필요하다면
     public void onTaskError(Exception ex);
     public void onTaskSuccess(String data);

두메소드는 Exception과 data를 별개의 클래스 만들어서 하나로 처리할 수 있습니다.

public static class HttpResult {
    private fianl boolean success;
    private final String message;
    private final String data;
   // 생성자, getter, setter 생략
}

      public interface Listener {
          public void onTaskLoading();
          public void onTaskCompleted(HttpResult result);
      }
두번째 방법을 쓴다면, 아래처럼 AsyncTask를 수정할 수 있을 겁니다.
public class HttpConnection extends AsyncTask<URL, Integer, HttpResult> {
      ....
 
    @Override
    protected void onPreExecute() {
         //액티비티에서는 onTaskLoading메소드에서 로딩바를 보여줍니다.
         listener.onTaskLoading();
     }

     @Override
     proteced String doInBackground(URL...urls) [
          try {
              ...
               String data =  connection.request(urls[0]);
               return new HttpResult(true, null, data);
           } catch (Exception e). {
                return new HttpResult(false, e.getMessage(), null);
           }
     }
 
     @Override
     protected void onPostExecute(HttpResult result) {
          //액티비티에서는 onTaskCompleted메소드에서 로딩바를 감춥니다.
          listener.onTaskCompleted(result);
     }
 }
spark님이 알려주신대로 해봣는데 무슨 오류가 너무 많이 떠서 어떻게 해줘야할지 감이 안잡힙니다...
@Nullable private Listener listener;
     public HttpConnection(Listener listener) {
         this.listener = listener;
     }
이부분의 HttpConnection을 또 넣어서 지금 에러가 발생하여 서버쪽과도 에러가 뜹니다. HttpConnection을 따로 만들어서 해봤습니다.
지금 HttpConnection.class -> onPostExecute 부분에서
Map.class->oncreate로 데이터를 보내고 싶은데 여기 부분에서 콜백을 하여 보낼수 있다고 하신거같은데 이 콜백 기능을 어떻게 넣어줘야하는지 감이 안잡힙니다.
제 소스를 다보여드리고 싶은데 저렇게 올리는건 어떻게 올릴수 잇나요?
public class map extends AppCompatActivity {

    private EditText sendMessage;
    private TextView receiveMessage;
    private ProgressBar loadingBar;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        String con[] = data.split("/");
        double con1=Double.parseDouble(con[0]);
        double con2=Double.parseDouble(con[1]);

        setContentView(R.layout.map);
        LinearLayout linear = findViewById(R.id.linear);
        TMapView tMapView = new TMapView(map.this);
        tMapView.setSKTMapApiKey("---키값-----");
        tMapView.setCenterPoint(con1,con2);
        linear.addView(tMapView);
        setContentView(linear);

        Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.icon1);

        TMapPoint tPoint = tMapView.getLocationPoint();
        double Latitude = tPoint.getLatitude();
        double Longitude = tPoint.getLongitude();
        tMapView.setCenterPoint(Longitude, Latitude);

        tMapView.setIcon(icon);
        tMapView.setIconVisibility(true);

        tMapView.setTrackingMode(true);
        tMapView.setZoomLevel(16);


        sendMessage=findViewById(R.id.et_message);
        receiveMessage=findViewById(R.id.tv_receive);
        loadingBar=findViewById(R.id.progressBar);

        HttpConnect connection;

        Button send=findViewById(R.id.btn_sendData);



        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (sendMessage.getText().toString().length() == 0 ) {
                    Toast.makeText(getApplicationContext(),
                            getResources().getString(R.string.send_message_empty),
                            Toast.LENGTH_SHORT).show();
                    return;
                }
                URL url = null;
                try {
                    String path = getResources().getString(R.string.server_url) + "?msg=";
                    String query = URLEncoder.encode(sendMessage.getText().toString(),"UTF-8");
                    url = new URL(path + query);
                }catch (MalformedURLException | UnsupportedEncodingException e) {
                    e.printStackTrace();
                    Toast.makeText(getApplicationContext(),
                            e.getMessage(), Toast.LENGTH_SHORT).show();
                }
                new HttpConnection().execute(url);
            }

        });
    }

    private class HttpConnection extends AsyncTask<URL, Integer,String> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            loadingBar.setVisibility(View.VISIBLE);
        }

        @Override
        protected String doInBackground(URL... urls) {
            String data = "";

            if (urls.length == 0) {
                return "URL is empty.";
            }
            try {
                RequestHttpURLConnection connection = new RequestHttpURLConnection();
                data = connection.request(urls[0]);
            }catch (Exception e) {
                data = e.getMessage();
            }
            return data;
        }

        @Override
        protected void onPostExecute(String data) {
            super.onPostExecute(data);
            loadingBar.setVisibility(View.INVISIBLE);

        }

    }


}
+1 추천

관련코드를 올려주시면 더 좋을 것 같은데. 질문을 토대로 추측해보면, AsyncTask를 사용하고 계신데 onPostExecute에서 서버로 부터 데이터를 받고나서 이걸 AsyncTask를 사용하고 있는 액티비티에서 접근하고 싶은데, 이게 잘 안되시는거죠?

콜백을 이용하시면 됩니다. 예를 들면,

 public class MyAsyncTask extends AsyncTask<Void, Void, MyData> {
      public interface Listener {
          public void onTaskError(Exception ex);
          public void onTaskSuccess(MyData data);
      }

     @Nullable private Listener listener;
     public MyAsyncTask(Listener listener) {
         this.listener = listener;
     }

     // 나머지 메소드 생략
     @Override
     protected void onPostExecute(String result) {
         try {
                MyData myData = parseJSON(result);
                onTaskSuccess(myData);    
          } catch (Exception e) {
                listener.onTaskError(e);
          }
     }
 }

public class MyActivity extends AppCompatActivity implements MyAsyncTask.Listener {

        public void onCreate(Bundle saveInstance) {
              super.onCreate(saveInstance);
              setContentView(...);
      
              fetchData();
        }

        public void fetchData() {
             // MyActivity가 MyAsyncTask.Listener타입이므로 MyAsyncTask생성자에 this를 넘겨줌.
             MyAsyncTask task = new MyAsyncTask(this);
             task.execute();
        }

       // MyAsyncTask.Lisener 인터페이스를 구현
       @Override
       public void onTaskError(Exception ex) {
            showError("Something went wrong");
       }
         
      public void onTaskSuccess(MyData data) {
            updateUi(data);
      }
}

 

MyTask에 리스너를 달아서 MyTask의 onPostExecute에서 Listener의 메소드를 호출해 줍니다. Button.setOnClickListener(..) 에 View.OnClickListener를 넘겨주는 거랑 같은 패턴입니다. 리스너를 등록할테니 서버에서 데이터가 오면 리스너를 통해서 알려달라고 하는거죠.

참고로 Android 11 부터는 AsyncTask를 더이상 지원하지 않습니다.

spark (226,380 포인트) 님이 2021년 12월 12일 답변
0 추천

음... 먼저 콜백, 리스너, 옵저버 패턴 등에 대해서 이해를 하셔야만 될 것 같은데요. 콜백은 버튼에 Button.setOnClickLisetner(new View.OnClickListener()...) 한거랑 같다고 보시면 됩니다. 시스템에 리스너를 등록해 두고 시스템이 클릭이 발생하면 리스너에 있는 메소드를 호출해 주는 겁니다. 마찬가지로 AsyncTask에 리스너를 등록해 두고 해당 이벤트가 발생하면 등록된 리스너의 메소드를 통해 알려주는 겁니다. 이 메카니즘이 이해가 안되시면, 위의 코드는 사용하시기가 좀 그럴 듯 합니다. 모바일앱 개발은 이런 리스너를 엄청 나게 자주 사용하게 되기 때문에 반드시 이해를 하시고 넘어가셔야 하는데, 별로 대단한 것이 아닙니다.
이미 필요한 소스는 다 보신 상태예요. 그래도 다시 설명드리면...

1. 먼저 HttpConnection을 별도의 클래스 파일로 분리하세요.

//HttpConnection.java
public class HttpConnection extends AsyncTask<URL, Integer,String>{...}


2. 리스너로 사용할 인터페이스를 추가합니다. 헷갈려 하시니까 이것도 별도의 클래스 파일로 선언합니다. 리스너를 사용하는 이유는 HttpConnection에서 네트워크 작업이 언제 끝날 지 알 수가 없기 때문입니다. 이해가 더 잘가시도록 한가지 예를 들게요. 어떤 가게에 사고 싶은 물건이 있는데, 재고가 없어요. 그 가게 주인이 쪽지하나를 내밀면서 "여기 연락처와 어떤 물건이 필요한지 알려주시면 물건이 들어오면 알려드릴게요"라고 했습니다.  그럼, 나는 그 쪽지에 내 연락처와 원하는 물건을 적어주면 되죠. 그럼, 나중에 그 물건이 들어오면, 주인으로부터 연락을 받겠죠. 리스너(=콜백)가 이 쪽지에 해당하는 겁니다. 콜백 메소드들은 물건에 해당한다고 보면 되구요. 쪽지에 내용을 적는 행위는 아래에 나오는 setListener가 되는 거구요.

//HttpConnectionListener.java
public interface HttpConnectionListener { 
    void onTaskError(String erroMessage);
    void onTaskSuccess(String data);
}

 

3. HttpConnection 클래스에 HttpConnectionListener를 추가합니다. 이 리스너를 통해 외부와 통신을 할 겁니다. 이 리스너를 나중에 액티비티 쪽에서 setListener를 통해 등록하고 리스너의 메소드가 호출이 되면 처리하면 되겠죠. 버튼 클릭 리스너와 같은 동작 원리입니다.

public class HttpConnection extends AsyncTask<URL, Integer,String> {
        private HttpConnectionListener listener;
        public void setListener(HttpConnectionListener listener) {
            this.listener = listener;
        }
        ...
}

 

4. HttpConnection에서 해당 이벤트가 발생하면, listener를 통해 알려줍니다. 매장에 물건이 들어오면(네트워크를 통해 서버로 부터 응답을 받으면), 알려달라고 한 고객에게 알려줘야 겠죠?

public class HttpConnection extends AsyncTask<URL, Integer,String> {

        // onPreExecute 삭제. 로딩바 보여주는 걸 HttpConnection을 호출하는 쪽에서 처리할 겁니다.

        @Override
        protected String doInBackground(URL... urls) {
            String data = "";

            if (urls.length == 0) {
                return "URL is empty.";
            }
            try {
                RequestHttpURLConnection connection = new RequestHttpURLConnection();
                return connection.request(urls[0]);  // 성공하면 서버에서 받은  String을 리턴.
            }catch (Exception e) {
                return null;  // 에러가 나면 null을 리턴.
            }
        }

        @Override
        protected void onPostExecute(String data) {
             if (data == null) {
                  // 고객님, 죄송합니다. 물건을 받는데 문제가 생겼습니다.
                  // data == null 이란 의미는 앞의 doInBackground에서 에러가 났다는 말이므로 리스너에 에러를 통보합니다.
                  if (listener != null) {
                       listener.onTaskError("여기에 의미있는 에레메세지");
                       return;
                  } 
             }

             // 고객님, 물건이 새로 들어왔습니다.
             // 에러가 아니면 성공이므로 리스너의 onTaskSuccess를 호출해 줍니다.
             if (listener != null) {
                 listener.onTaskSuccess(data); 
             }
        }
    }
}

이렇게 HttpConnection 과 이 클래스를 사용하는 외부 간에 리스너를 통해 메세지를 주고 받을 수 있는 준비가 되었습니다. 여기까지 이해가 가시면 좋겠네요. 만약 이해가 가지 않으시면 멈추신 상태로 자바와 기본적인 디자인 패턴 공부를 하시기를 추천드립니다. 

spark (226,380 포인트) 님이 2021년 12월 13일 답변
spark님이 2021년 12월 14일 수정
0 추천

앞에 이어서...

5. 이제 액티비티 쪽에서  HttpConnection을 사용할 차례입니다. 그런데 앞에서 둘 간의 메세지의 교환은 HttpConnectionListener를 통해 하기로 되었습니다. 따라서 액티비티는 HttpConnectionListener를 넘겨줘야 하겠죠. HttpConnectionListener는 인터페이스이므로 액티비티가 이걸 구현하도록 하면 되겠죠? 

public class map extends AppCompatActivity implements HttpConnectionListener {
     @Override
     public void onTaskError(String erroMessage) {
          // 에러처리
     }

     @Override 
     public void onTaskSuccess(String data) {
        // 성공시 필요한 처리
     }
}

 

6. 이제 액티비티에서 HttpConnection 을 생성하고 리스너를 등록해 주면, 네트워크 처리가 끝나면 리스너를 통해 이벤트를 받을 수 있겠죠.

public class map extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
         ...

        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ...
                executeHttpCall();
            }

        });
    }

   private void executeHttpCall() {
         URL url = makeHttpURL();
         if (url == null) {
                // 이런 상황은 실제로 일어나면 안되므로, 그냥 Exception을 떨궈서 앱이 크래시하도록 하는게 더 낫습니다. 
                // 그래야 조기에 버그를 쉽게 잡을 수 있습니다.
               Toast.makeText(this, "올바르지 않은 URL입니다.", Toast.LENGTH_SHORT).show();
                return;
          }
              
          HttpConnect connection = new HttpConnection();
          connection.setListener(this); //요기에서 액티비티가 HttpConnectionListener를 구현했으므로, 등록해주면 됩니다.
          connection.execute(url);
   }

   private URL makeHttpURL() {
         try {
              String path = getResources().getString(R.string.server_url) + "?msg=";
              String query = URLEncoder.encode(sendMessage.getText().toString(),"UTF-8");
              return new URL(path + query);
          }catch (MalformedURLException | UnsupportedEncodingException e) {
              e.printStackTrace();
               return null;
          }
   }

   @Override
     public void onTaskError(String erroMessage) {
         ...
     }

     @Override 
     public void onTaskSuccess(String data) {
        ...
     }
}

7. 로딩바 제어

로딩바는 HttpConnection.execute를 호출하기 전에 보여주고, 리스너의 이벤트를 받으면 감추면 됩니다.

public class map extends AppCompatActivity {

    ...

   private void executeHttpCall() {
         URL url = makeHttpURL();
         if (url == null) {
               ...
                return;
          }
              
          showLoadingIndicator();
          HttpConnect connection = new HttpConnection();
          connection.setListener(this); //요기에서 액티비티가 HttpConnectionListener를 구현했으므로, 등록해주면 됩니다.
          connection.execute(url);
   }

   private URL makeHttpURL() {
         ...
         showLoadingIndicator();
   }

  private void showLoadingIndicator() {
         loadingBar.setVisibility(View.VISIBLE);
   }

   private void hideLoadingIndicator() {
         loadingBar.setVisibility(View.GONE);
   }

   @Override
     public void onTaskError(String erroMessage) {
        hideLoadingIndicator();
         ...
     }

     @Override 
     public void onTaskSuccess(String data) {
        hideLoadingIndicator();
        ...
     }
}

 

 

설명이 한참 걸렸네요. 알고보면 별거 아닌데, 처음에 이해하는데 조금 시간이 걸릴 수 있습니다.  콜백이 있는 이유는 결과가 언제올지 모르는 비동기적인 상황을 처리하기 위함입니다.

아무튼 리스너에 대한 이해가 가셔서, 원하는 문제 해결을 하시길 바랍니다.

 

 

spark (226,380 포인트) 님이 2021년 12월 13일 답변
spark님이 2021년 12월 13일 수정
...