下面是关于Android后台线程和UI线程通讯实例的详细攻略。
什么是Android后台线程和UI线程通讯
Android应用开发中,我们常常需要在后台线程中执行一些耗时的操作,比如说网络请求或者复杂的计算任务。但是,在后台线程中我们是不能进行UI操作的。如果需要更新UI,我们就需要用到Android的线程通讯机制。Android提供了很多种线程通讯的方式,最常用的方式是使用Handler。
如何使用Handler进行线程通讯
1. 创建Handler对象
我们首先需要创建一个Handler对象,它可以用来向UI线程发送消息。下面演示了一个创建Handler对象的简单示例:
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 在这里更新UI操作
}
};
2. 在后台线程中发送消息
在后台线程中,我们可以使用Handler的sendMessag方法来向UI线程发送消息。下面是一个简单的示例:
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 执行耗时操作
// ...
// 向UI线程发送消息
mHandler.sendMessage(mHandler.obtainMessage(0, "Hello, world!"));
}
});
thread.start();
在上面的代码中,我们首先创建了一个新的线程,在这个线程中执行了一些耗时的操作。然后,我们使用Handler的sendMessage方法,向UI线程发送了一条消息。我们可以使用obtainMessage方法来创建一个Message对象,并指定messageId和message内容。
3. 接收并处理消息
最后,在UI线程中,我们需要处理从后台线程发送过来的消息。在上面我们已经创建了一个Handler对象,我们需要重写Handler的handleMessage方法,来处理从后台线程发送过来的消息。下面是一个简单的示例:
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
String content = (String) msg.obj;
// 更新UI操作,比如显示Toast
Toast.makeText(MainActivity.this, content, Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
};
在上面的代码中,我们重写了Handler的handleMessage方法,并使用switch语句来判断不同的messageId。如果messageId为0,我们就从Message对象中取出message内容,并使用Toast来显示。如果messageId是其他值,我们就什么也不做。
示例1:线程池中更新ListView
下面我们通过一个示例来更加详细地说明如何使用Handler进行线程通讯。在这个示例中,我们创建了一个线程池,用来执行一些网络请求,然后将结果显示在ListView中。
1. 在布局文件中添加ListView
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
2. 创建一个实体类
我们首先创建一个实体类,并定义一些属性,用于存储网络请求的结果。
public class Item {
private String title;
private String description;
public Item(String title, String description) {
this.title = title;
this.description = description;
}
// getter 和 setter 方法
}
3. 创建一个ListView的适配器
我们接着创建一个ListView的适配器,用来将Item对象显示在ListView中。
public class MyAdapter extends ArrayAdapter<Item> {
private int mResourceId;
public MyAdapter(Context context, int resource, List<Item> objects) {
super(context, resource, objects);
mResourceId = resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(mResourceId, null);
viewHolder = new ViewHolder();
viewHolder.titleView = (TextView) view.findViewById(R.id.title_view);
viewHolder.descriptionView = (TextView) view.findViewById(R.id.description_view);
view.setTag(viewHolder);
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
Item item = getItem(position);
viewHolder.titleView.setText(item.getTitle());
viewHolder.descriptionView.setText(item.getDescription());
return view;
}
private class ViewHolder {
TextView titleView;
TextView descriptionView;
}
}
在上面的代码中,我们使用了ViewHolder模式来提高ListView的性能。ViewHolder是一个静态的内部类,用来存储ListView中每个子项的控件实例。当convertView不为空时,说明ListView中已经有缓存的子项,可以直接使用,不用再去生成。
4. 在Activity中初始化ListView
我们在Activity中初始化ListView,并使用MyAdapter来给ListView设置适配器。
public class MainActivity extends AppCompatActivity {
private ListView mListView;
private MyAdapter mAdapter;
private List<Item> mItemList = new LinkedList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.list_view);
mAdapter = new MyAdapter(this, R.layout.list_item, mItemList);
mListView.setAdapter(mAdapter);
}
}
5. 使用线程池执行网络请求
我们接着在MainActivity中使用线程池来执行网络请求,并更新ListView。在这个示例中,我们直接模拟了一个网络请求,请根据实际情况替换成自己的实现。
private ExecutorService mThreadPool = Executors.newCachedThreadPool();
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
Item item = (Item) msg.obj;
mItemList.add(item);
mAdapter.notifyDataSetChanged();
break;
default:
break;
}
}
};
private void doRequest() {
mThreadPool.execute(new Runnable() {
@Override
public void run() {
// 模拟网络请求
String title = "Title";
String description = "Description";
try {
Thread.sleep(3000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
Item item = new Item(title, description);
mHandler.sendMessage(mHandler.obtainMessage(0, item));
}
});
}
在上面的代码中,我们首先创建了一个线程池,在线程池中执行网络请求。当请求完成后,我们使用Handler发送一条消息,携带Item对象的引用。在Handler的handleMessage方法中,我们从Message对象中取出Item引用,并将它添加到ListView的数据源中,然后通知适配器更新数据。
6. 调用doRequest方法
最后,在Activity的onCreate方法中调用doRequest方法,执行网络请求。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.list_view);
mAdapter = new MyAdapter(this, R.layout.list_item, mItemList);
mListView.setAdapter(mAdapter);
doRequest();
}
示例2:异步任务更新UI界面
除了使用线程池来执行耗时操作,Android还提供了AsyncTask这个类来方便我们进行异步操作。AsyncTask类可以让我们在后台线程中执行某些耗时的操作,并在执行完毕后将结果返回到UI线程中。下面我们通过一个示例来演示AsyncTask的使用方法。
1. 创建AsyncTask类
我们首先创建一个AsyncTask类,用来执行耗时操作。下面是示例代码:
private class MyTask extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... voids) {
// 执行耗时操作
// 模拟一个耗时的操作
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello, world!";
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
// 在这里更新UI操作
// 更新TextView的内容
mTextView.setText(result);
}
}
在上面的代码中,我们继承了AsyncTask类,并重写了它的doInBackground和onPostExecute方法。在doInBackground方法中,我们模拟了一个耗时的操作,在耗时操作完成后我们返回了一个字符串。在onPostExecute方法中,我们使用从doInBackground方法返回的结果来更新UI界面,比如更新了一个TextView的内容。
2. 创建MyTask对象,并执行
我们接着在Activity中创建MyTask对象,并调用它的execute方法,来执行我们定义的异步任务。
private MyTask mMyTask;
private void startTask() {
mMyTask = new MyTask();
mMyTask.execute();
}
在上面的代码中,我们直接调用MyTask的execute方法,即可开始执行异步任务。注意,我们在启动异步任务之后,不能再次调用它的execute方法,否则会抛出异常。
两个示例到此已完成。通过以上两个示例的演示,我们了解了Android中后台线程和UI线程之间通讯的使用方法,以及如何使用线程池和AsyncTask异步处理耗时任务,并将结果更新到UI界面中。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Android后台线程和UI线程通讯实例 - Python技术站