안드로이드 스튜디오 쓰레드 밖으로 값 가져오는 법?
MainActivity.java
public class MainActivity extends AppCompatActivity { String data=""; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.subactivity_main); new Thread() { @Override public void run() { String url = ""; JSONParser jParser = new JSONParser(); data = jParser.getJSONArrayFromUrl(url); try { JSONObject obj = new JSONObject(data); data=obj.getString("data"); System.out.println(data); } catch (Exception e) { e.printStackTrace(); } } }.start(); System.out.println(data); }}JSONParser.java
public class JSONParser { static InputStream is = null; static JSONArray jArrayObj = null; static String json = ""; public JSONParser() { } public String getJSONArrayFromUrl(String url) { try { DefaultHttpClient httpClient = new DefaultHttpClient(); // System.out.println(url); HttpGet httpGet = new HttpGet(url); HttpResponse httpResponse = httpClient.execute(httpGet); HttpEntity httpEntity = httpResponse.getEntity(); is = httpEntity.getContent(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } try { BufferedReader reader = new BufferedReader(new InputStreamReader( is, "utf-8"), 8); StringBuilder sb = new StringBuilder(); String line = null; while ((line = reader.readLine()) != null) { sb.append(line + "n"); } is.close(); json = sb.toString(); } catch (Exception e) { Log.e("Buffer Error", "Error converting result " + e.toString()); } return json; } }http://blog.naver.com/PostView.nhn?blogId=itkmj&logNo=220977728958
위 블로그의 소스를 조금 바꾸어서 쓴 코드 입니다
MainActivity 에서 data 값에 JSONParser 에서 리턴한 값을 저장하고 싶습니다
소스를 잘못 바꾸었나요?
초보라 어디가 문제인지 전혀 모르겠습니다
도움 주시면 감사하겠습니다
크게 두 가지 문제가 있습니다.
간단한 예시 코드를 보겠습니다. 질문자님의 코드와 비슷한 경우입니다.
class Main { private static volatile String test; public static void main(String... args) { test = "Hello"; new Thread() { @Override public void run() { try { Thread.sleep(10); } catch (InterruptedException e) {} MainActivity.this.test = "World!"; } }.start(); System.out.println(test); } }10밀리초 후에 test를 "World!"로 바꾸는 스레드를 시작시키고, test를 출력합니다.
"World!"가 아닌 "Hello"가 출력됩니다.
스레드를 시작시키기만 하고, 끝나기를 기다리지 않고 바로 다음 코드를 실행하기 때문입니다.이제 "Hello"가 아닌 "World!"가 출력되는 경우의 코드를 보겠습니다.
class Main { private static volatile String test; public static void main(String... args) throws InterruptedException { test = "Hello"; Thread th = new Thread() { @Override public void run() { try { Thread.sleep(10); } catch (InterruptedException e) {} MainActivity.this.test = "World!"; } }; th.start(); // 스레드를 시작시키고 th.join(); // 그 스레드가 끝날 때까지 기다립니다. System.out.println(test); } }test를 "World!"로 바꾸는 스레드가 끝난 다음에 test를 출력하면 드디어 "World!"가 나옵니다.
이게 첫번째 문제입니다. 실제로 값이 바뀌기 전에 값을 출력했습니다.
그런데 문제가 이것만이 아닙니다. (아직 질문자님의 코드에서는 나타나지 않은 문제입니다.)
두번째 문제는 그 값을 화면에 표시하는 방법입니다.
안드로이드 환경이라고 하셨는데,
class MainActivity extends AppCompatActivity { String test; protected void onCreate(@Nullable Bundle savedInstanceState) throws InterruptedException { super.onCreate(savedInstanceState); setContentView(R.layout.subactivity_main); this.test = "Hello"; // 여기부터 위와 같음 Thread th = new Thread() { @Override public void run() { try { Thread.sleep(10); } catch (InterruptedException e) {} MainActivity.this.test = "World!"; } }; th.start(); th.join(); // 여기까지 위와 같음 TextView tv = (TextView) findViewById(R.id.tv); tv.setText(this.test); // UI 접근 } }
안드로이드는 실제로 UI에 접근할 수 있는 특별한 스레드(이하 UI 스레드)가 있습니다.
화면 멈춤을 방지하기 위해 UI 스레드는 다른 작업이 끝나기를 기다릴 수 없으며,
UI에 접근할 수 있는 스레드는 UI 스레드 뿐입니다. 다른 스레드는 UI에 접근할 수 없습니다.첫번째 문제를 고친 코드입니다. (android:id가 "@+id/tv"인 TextView가 있다고 가정)
UI 스레드는 다른 작업이 끝나기를 기다릴 수 없다고 했습니다.
th.join();에서 오류가 발생합니다. onCreate는 UI 스레드에서 실행되기 때문입니다.이번에는 th.join(); 없이 동작하도록, 그 값을 사용하는 부분을 스레드 안으로 넣어보겠습니다.
class MainActivity extends AppCompatActivity { String test; protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.subactivity_main); this.test = "Hello"; new Thread() { @Override public void run() { try { Thread.sleep(10); } catch (InterruptedException e) {} MainActivity.this.test = "World!"; TextView tv = (TextView) findViewById(R.id.tv); tv.setText(MainActivity.this.test); // UI 접근 } }.start(); // 오류를 발생시키는 join을 없애고 그 다음 코드를 스레드 안으로 } }방금 전의 문제를 우회한 코드입니다. 이번에는 findViewById에서 오류가 발생합니다.
UI 스레드 외에 다른 스레드는 UI에 접근할 수 없다고 했습니다.
UI 스레드가 아닌 별도의 스레드에서 UI에 접근하려고 했기 때문입니다.그러면 어떻게 해야 할까요?
class MainActivity extends AppCompatActivity { String test; protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.subactivity_main); test = "Hello"; new Thread() { @Override public void run() { try { Thread.sleep(10); } catch (InterruptedException e) {} MainActivity.this.test = "World!"; new Handler().post(new Runnable() { @Override public void run() { TextView tv = (TextView) findViewById(R.id.tv); tv.setText(MainActivity.this.test); // UI 접근 } }); // 이 코드는 UI 스레드에서 실행되도록! } }.start(); } }UI 스레드가 아닌 별도의 스레드에서도 android.os.Handler의 post 메서드를 통해
특정 코드(Runnable)를 UI 스레드에서 실행되도록 할 수 있습니다!이렇게 하면 네트워크를 통해 가져오는 값이라도 화면에 표시할 수 있습니다!
안녕하세요 :)
해당 URL 에서 얼마나 많은 데이터를 불러오는지는 잘 모르겠지만.멀티쓰레드 개념은 메인으로 진행되는 쓰레드 외에 새롭게 가지를 쳐서 나가는 형태입니다.
코드로 보았을때 만약 URL에서 응답하는 데이터가 길거나 할경우 쓰레드가 돌고있는 동안 System.out.println 이 실행되는걸로 보여집니다.
쓰레드와 System.out.println을 따로 메소드화 시켜서 쓰레드가 성공적으로 데이터를 받아올때 메소드를 실행시켜 데이터가 잘 출력되는지 확인 해 보면될거같습니다.