android AsyncTask和UI线程交互

时间:2014-05-01 06:01:30

标签: android android-listview android-asynctask

我使用AsyncTask打开URL,访问服务器,获取内容并在主活动的列表视图中显示它们。提取的内容包括报纸的标题和网站的URL,如果"读取"将在第二个活动中显示在WebView上。单击按钮。我直接编写了程序并且它可以工作,但是当我回头看它时,我发现了一些似乎不合理的东西,所以主要是我想弄清楚代码是如何工作的。以下是主要活动的代码:

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;
    }

}

因此,您只能在loadTask.execute() onCreate()之后看到,我是否为alist和aa创建了对象,但我已经在AsyncTaks的onPostExecute()中使用了它们class,所以我不太清楚这里发生了什么,因为onPostExecute()和UI在同一个线程上,所以onPostExecute()中的代码应该先执行。

我以为我应该把

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

进入onPostExecute(),这对我来说更符合逻辑,但应用程序会以这种方式崩溃。另外我认为删除aa.notifyDataSetChanged();中的onPostExecute()不应该是一个问题,因为它也在onCreate()方法中,但这实际上会导致列表视图为空白,没有任何内容。实际上,将loadTask.execute()之后的任何代码放入onPostExecute()方法的if块会导致某些问题,或者导致应用崩溃。如果有人可以提供一些见解或提示,那将是很好的。谢谢你的阅读。

1 个答案:

答案 0 :(得分:4)

后台任务完成其工作后,在UI线程上调用

onPostExecute。您无法保证与UI线程上的其他调用相关的此调用的时间。

由于您已经自己实施getView,因此我建议您扩展BaseAdapter而不是ArrayAdapter,并实施其他一些必需的方法。它并不难,您可以使用任何数据结构来支持适配器。假设您使用List<Content>来支持适配器,您可以编写一个方法来交换列表,如下所示:

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

AsyncTask中,您可以完全控制Params,Progress和Result参数化类型。他们不必都是String。你可以这样做:

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

Params的String是URL(与您现在相同)。 Void for Progress,因为您无论如何都不会发布进度。 List<Content>表示结果,因为这是您在完成任务后最终想要的结果。

您应该在doInBackground中完成所有工作。没有理由将String反序列化为JSONArray并在onPostExecute中混淆,特别是因为这发生在主线程上。重写doInBackground以返回List<Content>,onPostExecute中您需要的只是:

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

现在您可以创建一次适配器(在onCreate()中),只需在适当的时候交换列表。