您的位置:首页 > 移动开发 > Android开发

Android多线程任务优化1:探讨AsyncTask的缺陷

2014-03-29 15:50 477 查看

Android多线程任务优化1:探讨AsyncTask的缺陷

0

作者:zhanhong4更新于 05月17日访问(1464)评论(0

导语:在开发Android应用的过程中,我们需要时刻注意保障应用的稳定性和界面响应性,因为不稳定或者响应速度慢的应用将会给用户带来非常差的交互体验。在越来越讲究用户体验的大环境下,用户也许会因为应用的一次Force Close(简称FC)或者延迟严重的动画效果而卸载你的应用。由于现在的应用大多需要异步连接网络,本系列文章就以构建网络应用为例,从稳定性和响应性两个角度分析多线程网络任务的性能优化方法。

概述:为了不阻塞UI线程(亦称主线程),提高应用的响应性,我们经常会使用新开线程的方式,异步处理那些导致阻塞的任务(如要了解Android异步处理的实现方式和原理,请先阅读《Android异步处理系列文章索引》)。

AsyncTask是Android为我们提供的方便编写异步任务的工具类,但是,在了解AsyncTask的实现原理之后,发现AsyncTask并不能满足我们所有的需求,使用不当还有可能导致应用FC。

本文主要通过分析AsyncTask提交任务的策略和一个具体的例子,说明AsyncTask的不足之处,至于解决办法,我们将在下篇再讲解。

分析:

AsyncTask类包含一个全局静态的线程池,线程池的配置参数如下:

1
2
3
4
5
6
7
8
9


private static final int CORE_POOL_SIZE =5;//5个核心工作线程
private static final int MAXIMUM_POOL_SIZE = 128;//最多128个工作线程
private static final int KEEP_ALIVE = 1;//空闲线程的超时时间为1秒

private static final BlockingQueue<Runnable> sWorkQueue =
new LinkedBlockingQueue<Runnable>(10);//等待队列

private static final ThreadPoolExecutorsExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue,sThreadFactory);//线程池是静态变量,所有的异步任务都会放到这个线程池的工作线程内执行。


我们这里不详细讲解ThreadPoolExecutor的原理,但将会讲解一个异步任务提交到AsyncTask的线程池时可能会出现的4种情况,并会提出在Android硬件配置普遍较低这个客观条件下,每个情况可能会出现的问题。

1、线程池中的工作线程少于5个时,将会创建新的工作线程执行异步任务(红色表示新任务,下同)



2、线程池中已经有5个线程,缓冲队列未满,异步任务将会放到缓冲队列中等待



3、线程池中已经有5个线程,缓冲队列已满,那么线程池将新开工作线程执行异步任务



问题:Android的设备一般不超过2个cpu核心,过多的线程会造成线程间切换频繁,消耗系统资源。

4、线程池中已经有128个线程,缓冲队列已满,如果此时向线程提交任务,将会抛出RejectedExecutionException



问题:抛出的错误不catch的话会导致程序FC。

好吧,理论分析之后还是要结合实际例子,我们通过实现一个模拟异步获取网络图片的例子,看看会不会出现上面提到的问题。

例子:使用GridView模拟异步加载大量图片

ActivityA.java

  1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207


package com.zhuozhuo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;

public class ActivityA extends Activity {

private GridView mGridView;
private List<HashMap<String, Object>> mData;

private BaseAdapter mAdapter;
private ProgressDialog mProgressDialog;

private static final int DIALOG_PROGRESS = 0;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mGridView = (GridView) findViewById(R.id.gridview);
mData = new ArrayList<HashMap<String,Object>>();
mAdapter = new CustomAdapter();

mGridView.setAdapter(mAdapter);
}

protected void onStart () {
super.onStart();
new GetGridDataTask().execute(null);//执行获取数据的任务
}

@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_PROGRESS:
mProgressDialog = new ProgressDialog(ActivityA.this);
mProgressDialog.setMessage("正在获取数据");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);

return mProgressDialog;

}
return null;
}

class CustomAdapter extends BaseAdapter {

CustomAdapter() {

}

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

@Override
public Object getItem(int position) {
return mData.get(position);
}

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder vh;
if(view == null) {
view = LayoutInflater.from(ActivityA.this).inflate(R.layout.list_item, null);
vh = new ViewHolder();
vh.tv = (TextView) view.findViewById(R.id.textView);
vh.iv = (ImageView) view.findViewById(R.id.imageView);
view.setTag(vh);
}
vh = (ViewHolder) view.getTag();
vh.tv.setText((String) mData.get(position).get("title"));
Integer id = (Integer) mData.get(position).get("pic");
if(id != null) {
vh.iv.setImageResource(id);
}
else {
vh.iv.setImageBitmap(null);
}

FifoAsyncTask task = (FifoAsyncTask) mData.get(position).get("task");
if(task == null || task.isCancelled()) {
Log.d("Test", "" + position);
mData.get(position).put("task", new GetItemImageTask(position).execute(null));//执行获取图片的任务
}

return view;
}

}

static class ViewHolder {
TextView tv;
ImageView iv;
}

class GetGridDataTask extends FifoAsyncTask<Void, Void, Void> {

protected void onPreExecute () {
mData.clear();
mAdapter.notifyDataSetChanged();

showDialog(DIALOG_PROGRESS);//打开等待对话框
}

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

try {
Thread.sleep(500);//模拟耗时的网络操作
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i = 0; i < 200; i++) {
HashMap<String, Object> hm = new HashMap<String, Object>();
hm.put("title", "Title");
mData.add(hm);
}

return null;
}

protected void onPostExecute (Void result) {
mAdapter.notifyDataSetChanged();//通知ui界面更新
dismissDialog(DIALOG_PROGRESS);//关闭等待对话框
}

}

class GetItemImageTask extends FifoAsyncTask<Void, Void, Void> {

int pos;

GetItemImageTask(int pos) {
this.pos = pos;
}

@Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(2000); //模拟耗时的网络操作
} catch (InterruptedException e) {
e.printStackTrace();
}
mData.get(pos).put("pic", R.drawable.icon);
return null;
}

protected void onPostExecute (Void result) {
mAdapter.notifyDataSetChanged();//通知ui界面更新
}

}

}




由运行图可见

当网络情况较差,异步任务不能尽快完成执行的情况下,新开的线程会造成listview滑动不流畅。当开启的工作线程过多时,还有出现FC的可能。

至此,你还相信万能的AsyncTask吗?

总结:

AsyncTask可能存在新开大量线程消耗系统资源和导致应用FC的风险,因此,我们需要根据自己的需求自定义不同的线程池.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: