【问题标题】:HttpPost inside getView from Adapter来自适配器的getView中的HttpPost
【发布时间】:2013-07-01 21:18:45
【问题描述】:

我有一个包含多个项目的 Listview,每个项目都有 2 个按钮。我想要做的是当我点击一个按钮时,它会通过 HttpPost 向服务器发送一些数据。我在我的 Adapter.getView() 中放置了一个线程,但它不起作用,因为适配器在异步线程中已经存在。你知道我怎样才能让它发挥作用吗?

我的列表视图:

// ... OnCreate ListView ...
// ... new ListAppTask().execute();
// ...
public class ListAppTask extends AsyncTask<Void, Void, List<ProductExtended>> {

    protected List<ProductExtended> doInBackground(Void... args) {
        Product product = new Product();
        List<ProductExtended> products = product.getLastProducts(getApplicationContext());

        return products;
    }

    protected void onPostExecute(List<ProductExtended> result) {
        data.addAll(result);
        adapter.notifyDataSetChanged();
        if (progressDialog != null) {
            progressDialog.dismiss();
            progressDialog = null;
        }
    }
}

我的适配器:

    public class PackageAdapter extends BaseAdapter {

        private List<ProductExtended> data;
        private Context context;

        public PackageAdapter(Context context, List<ProductExtended> data) {
            this.context = context;
            this.data = data;
        }

        @Override
        public int getCount() {
            return data.size();
        }

        @Override
        public ProductExtended getItem(int position) {
            return data.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            final ProductExtended item = getItem(position);
            ViewHolder holder;
            if (convertView == null) {
                LayoutInflater li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = li.inflate(R.layout.package_row, parent, false);
                holder = new ViewHolder();
                holder.btnBa = (LinearLayout) convertView.findViewById(R.id.idBonneaffaire);
                holder.btnJl = (LinearLayout) convertView.findViewById(R.id.idJelai);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }


            holder.btnBa.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {

                        new Thread() {

                            @Override
                            public void run() {
                                // ---------------- PROBLEM HERE -------------------
                                HttpClient httpclient = new DefaultHttpClient();
                                HttpPost httppost = new HttpPost("My URL");
                                // Execute HTTP Post Request
                                try {
                                    HttpResponse response = httpclient.execute(httppost);
                                    String reponse = EntityUtils.toString(response.getEntity());

                                } catch (UnknownHostException e) {
                                } catch (HttpHostConnectException e) {
                                } catch (ClientProtocolException e) {
                                } catch (IOException e) {
                                }
                            }
                        }.start();
                }
            });

          // Same for the second LinearLayout considered as Button



            return convertView;
        }

        static class ViewHolder {
            LinearLayout btnBa;
            LinearLayout btnJl;
        }

并显示错误:

Only the original thread that created a view hierarchy can touch its views. adapter

谢谢, 阿克拉姆

【问题讨论】:

  • 像往常一样,人们会喜欢代码 sn-p,以及您的精确错误代码('不工作=?')。另外-出于兴趣,您能否更准确地说明您的适配器在异步线程中的含义-严格来说,它应该只在 UI 线程上更新。谢谢。

标签: android android-listview androidhttpclient


【解决方案1】:

这个错误主要描述了你试图从另一个线程而不是主线程更新 UI。如果您出于某种原因尝试在您的 onClickListener 提供的线程的 Run 方法中触摸您的视图,就会发生这种情况。 它在您的示例中不可见,但您可以检查您在声明“// 第二个 LinearLayout 被视为按钮时所做的工作”吗?

为防止发生此异常,您可以在需要更新 UI 时使用 android.os.Handler 在主线程上执行 Runnable。

例如:

Handler mHandler = new Handler();

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    final ViewHolder holder;
    if(convertView == null){
        convertView = mInflater.inflate(R.layout.item_simple_row, parent, false);
        holder = new ViewHolder();
        holder.textView = (TextView) convertView.findViewById(R.id.txt);
        holder.button = (Button) convertView.findViewById(R.id.btn);
        convertView.setTag(holder);
    }else{
        holder = (ViewHolder) convertView.getTag();
    }

    final String url = getItemAtPosition(position);
    holder.textView.setText(url);
    holder.button.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            final Thread worker = new Thread(new Runnable(){

                @Override
                public void run()
                {
//WATCH OUT IF YOU TOUCH YOUR VIEW HERE WITHOUT USING A HANDLER YOU MAY GET THE ERROR YOUR ARE TALKING ABOUT

                    HttpGet request = new HttpGet(url);
                    DefaultHttpClient httpClient = new DefaultHttpClient();
                    try {
                        final HttpResponse response = httpClient.execute(request);

                        mHandler.post(new Runnable() {

                            @Override
                            public void run() {
//UPDATE YOUR UI HERE
                                holder.textView.setText(url+ " : " +response.getStatusLine());
                            }
                        });

                    } catch (ClientProtocolException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });

            worker.start();


        }
    });

    return convertView;
}

【讨论】:

  • 问题是我没有设置线程“final”,谢谢你的回答。
【解决方案2】:

我更喜欢保持简单。 ThreadHandler 分别是重要的 java 和 android 概念,但是响应 UI 事件的 http 在 Android 中非常普遍,他们发明了一个名为 AsyncTask 的神奇助手。

使用对封闭 Activity 类的内部引用创建此类的匿名实例(即,实际上,在您的 Activity 中定义该类并且不要使其成为 static)。开发人员在设计 Android UI 和线程模式时考虑到的正是这些 Java 特性。

来自上面的文档:

当一个异步任务执行时,任务经过...

doInBackground(Params...)onPreExecute() 完成执行后立即在后台线程上调用。此步骤用于执行可能需要很长时间的后台计算。 ...

onPostExecute(Result)在后台计算完成后在 UI 线程上调用。后台计算的结果作为参数传递给这一步。

[我省略了额外的细节,也是我的重点]。

您的适配器的getView 应该引用一个异步任务,该任务将信息从一个执行的代码块(其中包含所有同步 http 请求)从 UI 线程传递到 UI 线程上可用于触摸的代码块它的视图(您在此处更新您的列表视图)。

注意不要忘记在任务完成后检查您的列表视图是否仍然与该数据相关,或者,由于AbsListView 视图回收,您将冒着将数据放入明显错误的单元格的风险。

最好的,

汤姆。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多