2014-05-01 2 views
0

AsyncTask를 사용하여 URL을 열고, 서버에 액세스하고, 콘텐츠를 가져 와서 기본 활동의 목록보기에 표시합니다. 추출 된 컨텐츠는 신문의 제목과 웹 사이트에 대한 URL로 구성되며, "읽음"버튼을 클릭하면 두 번째 활동에서 WebView에 표시됩니다. 프로그램을 곧바로 작성했는데 작동하지만 되돌아 보았을 때 부적절한 부분을 발견 했으므로 주로 코드가 어떻게 작동하는지 분명히 밝히고 싶습니다. 여기에 주요 활동에 대한 코드입니다 :android AsyncTask 및 UI 스레드 상호 작용

package com.example.newsapp; 

public class MainActivity extends Activity { 

    static final private String LOG_TAG = "main"; 
    private ArrayList<Content> aList; 

    private class Content{ 

     Content() {}; 
     public String title; 
     public String url; 
    } 

    private class MyAdapter extends ArrayAdapter<Content>{ 

     int resource; 


     public MyAdapter(Context _context, int _resource, List<Content> titles) { 
      super(_context, _resource, titles); 
      resource = _resource; 
     // this.context = _context; 
     } 

     @Override 
     public View getView(int position, View convertView, ViewGroup parent) { 
      LinearLayout newView; 

      final Content content = getItem(position); 

      // Inflate a new view if necessary. 
      if (convertView == null) { 
       newView = new LinearLayout(getContext()); 
       String inflater = Context.LAYOUT_INFLATER_SERVICE; 
       LayoutInflater vi = (LayoutInflater) getContext().getSystemService(inflater); 
       vi.inflate(resource, newView, true); 
      } else { 
       newView = (LinearLayout) convertView; 
      } 

      // Fills in the view. 
      TextView tv = (TextView) newView.findViewById(R.id.listText); 
      ImageButton b = (ImageButton) newView.findViewById(R.id.listButton); 
      b.setBackgroundResource(0); 
      tv.setText(content.title); 
      Typeface type = Typeface.createFromAsset(getAssets(),"LiberationSerif-BoldItalic.ttf"); 
      tv.setTypeface(type); 

      // Sets a listener for the button, and a tag for the button as well. 
      b.setTag(Integer.toString(position)); 
      b.setOnClickListener(new View.OnClickListener() { 

       @Override 
       public void onClick(View v) { 
        // Reacts to a button press. 
        Intent intent = new Intent(MainActivity.this, WebPage.class); 
        Bundle bundle = new Bundle(); 
        bundle.putString("URL", content.url); 
        intent.putExtras(bundle); 
        startActivity(intent); 
       } 
      }); 
      return newView; 
     }  
    } 

    class MyAsyncTask extends AsyncTask<String, String, String> { 
     private ProgressDialog progressDialog = new ProgressDialog(MainActivity.this); 
     InputStream inputStream = null; 
     String result = ""; 
     Content content; 

     protected void onPreExecute() { 
      super.onPreExecute(); 
      progressDialog.setMessage("Downloading the news..."); 
      progressDialog.show(); 
      progressDialog.setOnCancelListener(new OnCancelListener() { 
       public void onCancel(DialogInterface arg0) { 
        MyAsyncTask.this.cancel(true); 
       } 
      }); 
     } 

     @Override 
     protected String doInBackground(String... params) { 

      String url_select = params[0]; 

      ArrayList<NameValuePair> param = new ArrayList<NameValuePair>(); 

      try { 
       // Set up HTTP post 
       // HttpClient is more then less deprecated. Need to change to URLConnection 
       HttpClient httpClient = new DefaultHttpClient(); 

       HttpPost httpPost = new HttpPost(url_select); 
       httpPost.setEntity(new UrlEncodedFormEntity(param)); 
       HttpResponse httpResponse = httpClient.execute(httpPost); 
       HttpEntity httpEntity = httpResponse.getEntity(); 

       // Read content & Log 
       inputStream = httpEntity.getContent(); 
       } catch (UnsupportedEncodingException e1) { 
        Log.e("UnsupportedEncodingException", e1.toString()); 
        e1.printStackTrace(); 
       } catch (ClientProtocolException e2) { 
        Log.e("ClientProtocolException", e2.toString()); 
        e2.printStackTrace(); 
       } catch (IllegalStateException e3) { 
        Log.e("IllegalStateException", e3.toString()); 
        e3.printStackTrace(); 
       } catch (IOException e4) { 
        Log.e("IOException", e4.toString()); 
        e4.printStackTrace(); 
       } 
      // Convert response to string using String Builder 
      try { 
       BufferedReader bReader = new BufferedReader(new InputStreamReader(inputStream, "iso-8859-1"), 8); 
       StringBuilder sBuilder = new StringBuilder(); 
       String line = null; 
       while ((line = bReader.readLine()) != null) { 
        sBuilder.append(line + "\n"); 
       } 
       inputStream.close(); 
       result = sBuilder.toString(); 
      } catch (Exception e) { 
       Log.e("StringBuilding & BufferedReader", "Error converting result " + e.toString()); 
      } 
      return result; 
     } // protected Void doInBackground(String... params) 


     protected void onPostExecute(String result) { 
      //parse JSON data 
      try { 
       super.onPostExecute(result); 
       Log.i(LOG_TAG, result); 
       JSONObject object = new JSONObject(result); 
       JSONArray jArray = object.getJSONArray("sites"); 
       for(int i=0; i < jArray.length(); i++) { 
        JSONObject jObject = jArray.getJSONObject(i); 
        content = new Content(); 
        if (jObject.has("title") && jObject.has("url")){ 
         content.title = jObject.getString("title"); 
         content.url = jObject.getString("url"); 
         aList.add(content); 
         aa.notifyDataSetChanged(); 
        } 
       } // End Loop 
       progressDialog.dismiss(); 
      } catch (JSONException e) { 
      // progressDialog.dismiss(); 
       Log.e("JSONException", "Error: " + e.toString()); 
      } 

     } // protected void onPostExecute(String result) 
    } 

    private MyAdapter aa; 
    private MyAsyncTask loadTask; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 
     loadTask = new MyAsyncTask(); 
     loadTask.execute("http://luca-ucsc.appspot.com/jsonnews/default/news_sources.json"); 
     aList = new ArrayList<Content>(); 
     aa = new MyAdapter(this, R.layout.list_element, aList); 
     ListView myListView = (ListView) findViewById(R.id.listView1); 
     myListView.setAdapter(aa); 
     aa.notifyDataSetChanged(); 
    } 

    public void refresh(View v){ 
     if (loadTask.getStatus() == AsyncTask.Status.FINISHED){ 
      aList.clear(); 
      aa.notifyDataSetChanged(); 
      new MyAsyncTask().execute("http://luca-ucsc.appspot.com/jsonnews/default/news_sources.json"); 
     } 
    } 


    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     // Inflate the menu; this adds items to the action bar if it is present. 
     getMenuInflater().inflate(R.menu.activity_main, menu); 
     return true; 
    } 

} 

그래서 당신이 onCreate() 만 후 loadTask.execute(), 나는 alist와 AA의 객체를 생성 할 것을 알 수 있습니다,하지만 난 이미 AsyncTaks 클래스에 onPostExecute()에서 그들을 사용하고 있습니다 따라서 onPostExecute()과 UI가 동일한 스레드에 있으므로 onPostExecute()의 코드가 먼저 실행되어야하므로 여기에서 어떤 일이 발생하는지 명확하지 않습니다.

나는 나에게 더 논리적이지만, 응용 프로그램이 방법을 충돌하는 onPostExecute()에 내가

aList = new ArrayList<Content>(); 
aa = new MyAdapter(this, R.layout.list_element, aList); 

을 넣어해야한다고 생각. 또한 방법이기 때문에 aa.notifyDataSetChanged();onPostExecute()에서 삭제해도 문제가되지 않지만 실제로는 목록보기가 내용없이 비어있게됩니다. 사실 loadTask.execute() 뒤에있는 코드를 onPostExecute() 메서드의 if 블록에 넣으면 문제가 발생하거나 응용 프로그램이 중단됩니다. 누군가 통찰력이나 힌트를 줄 수 있다면 좋을 것입니다. 읽어 주셔서 감사합니다.

+0

하지만 onPostExecute() 메소드에서 UI를 업데이트해야합니다. – Piyush

+0

@PiYusHGuPtA 나는 생각한다.notifyDataSetChanged()는 UI를 업데이트합니다. –

답변

4

onPostExecute은 백그라운드 작업이 완료된 후에 UI 스레드에서 호출됩니다. UI 스레드의 다른 호출과 관련하여이 호출의 타이밍을 보장 할 수 없습니다.

이미 사용자가 getView을 구현 중이므로 ArrayAdapter 대신 BaseAdapter을 확장하고 다른 몇 가지 필수 메소드를 구현하는 것이 좋습니다. 그것은 어렵지 않으며 어댑터를 백업하려는 데이터 구조를 사용할 수 있습니다. 어댑터를 백업 할 List<Content>를 사용하는 가정, 당신이 그렇게 같은 장소에서 목록을 교환하는 방법을 쓸 수 있습니다 :

public void swapList(List<Content> newList) { 
    this.list = newList; 
    notifyDataSetChanged(); 
} 

당신의 AsyncTask, 당신은에 Params, 진행의 완벽하게 제어 할 수 있고, 매개 변수화 된 유형의 결과. 모두가 String 일 필요는 없습니다. 대신 다음을 수행 할 수 있습니다.

private class myAsyncTask extends AsyncTask<String, Void, List<Content>> { 
    /* ... */ 
} 

Params의 String은 URL입니다 (지금까지와 동일). 어쨌든 진행 상황을 게시하지 않으므로 Void 진행 중입니다. 결과를 얻으려면 List<Content>이 필요합니다. 실제로 작업을 수행 한 후에 끝내기를 원하기 때문입니다.

모든 작업을 doInBackground에서해야합니다. JSONArray에 문자열을 비 직렬화하여 onPostExecute에있는 문자열로 처리해야 할 이유가 없습니다. 특히 주 스레드에서 발생합니다. List<Content>을 반환 doInBackground을 다시 작성하고, 당신이 onPostExecute에 필요한 것은 이것이다 :

public void onPostExecute(List<Content> result) { 
    adapter.swapList(result); 
} 

지금 한 번 어댑터를 만들 수 있습니다 (onCreate()에서)하고 적절한의 때마다 바로 목록을 교환합니다.

+0

onPostExecute는 주 스레드의 실행 루프에 메시지를 보냅니다. 메인 스레드가 준비가되면 실행됩니다. onCreate가 끝난 후 가능성이 가장 높습니다. – Arno

+0

설명해 주셔서 대단히 감사드립니다. 예, 저는 배경 직업과 출판을 분리하는 것이 훨씬 낫고 깨끗한 방법이라고 생각합니다. 하지만 난 아직도 arraylist 및 어댑터 개체를 onPostExecute 메서드에서 이전 방법으로 만들 수 없다는 것을 이해하지 못합니다. BaseAdapter를 확장하는 것이 왜 더 나은지 설명 할 수 있습니까? 기본 클래스에서 목록 필드를 찾지 못했기 때문에 notifyDataSetChanged() 메서드를 재정의해야합니까? 감사. –

+0

'notifyDataSetChanged()'를 오버라이드 할 필요가 없다. 어댑터를 지원하는 데이터 세트를 변경할 때마다 호출하면된다. 어쨌든 실제로는 어댑터의 핵심 인'getView()'를 이미 구현하고 있기 때문에'BaseAdapter'를 확장하여 그 동작을보다 잘 제어 할 수 있도록 제안했습니다. 원하신다면 매번 새로운 어댑터를'onPostExecute'에 만들 수 있습니다. – Karakuri