我有一个包含多个项目的Listview,每个项目都有2个按钮。我想要做的是当我点击一个按钮时,它会通过HttpPost将一些数据发送到服务器。我在我的Adapter.getView()中放了一个Thread,但它不能正常工作,因为适配器在异步线程中很常见。你知道我怎么能让它发挥作用?
我的ListView:
// ... 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
感谢的, 阿克拉姆
答案 0 :(得分:2)
此错误主要描述您正在尝试从主线程之外的其他线程更新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;
}
答案 1 :(得分:1)
我更喜欢保持简单。 Thread
和Handler
分别是重要的java和android概念,但在响应UI事件时执行http在Android中非常常见,他们发明了一个名为AsyncTask的神奇助手。
创建此类的匿名实例,其中包含对封闭的Activity类的内部引用(即,实际上,在您的活动中定义类,而不是使其成为static
)。这是开发人员在设计Android UI和线程模式时所考虑的Java的这些特性。
来自上述文档:
执行异步任务时,任务将完成......
doInBackground(Params...)
完成执行后,后台计算完成后,在线程上调用
onPreExecute()
,在后台线程上立即调用。此步骤用于执行可能需要很长时间的后台计算。 ...
onPostExecute(Result)
,。后台计算的结果作为参数传递给此步骤。
[我已经省略了额外的细节,也是我的重点]。
你的适配器的getView
应该引用一个异步任务,该任务将信息从一个代码块传递到UI线程上可以使用的代码块中,该代码块包含所有同步的http请求。触摸其视图(您在此处更新列表视图)。
NB 请勿忘记在任务完成后检查列表视图是否与该数据相关,或者,由于AbsListView
查看回收,您将数据放入显然是错误的单元格的风险。
最佳,
汤姆。