1? ? ? AsyncTask異步任務(wù)
Application Not Responding,應(yīng)用程序無響應(yīng)
Android系統(tǒng)規(guī)定只有UI線程(主線程)能夠修改UI界面,但是如果在UI線程中執(zhí)行耗時操作,則會導(dǎo)致線程阻塞,影響用戶體驗,如果耗時操作導(dǎo)致阻塞時間過長,則有可能會引起系統(tǒng)ANR
產(chǎn)生的原因
用戶在界面上的操作是由主線程處理,如果主線程被阻塞,那么就沒有辦法響應(yīng)用戶的操作,這時候可以看做是程序就“卡住了”,如果阻塞超過5秒,就會導(dǎo)致ANR。
如何避免ANR?
我們只需要將會造成主線程阻塞的操作(耗時操作)放在子線程中就可以了。
注意事項
1、超過5秒沒有響應(yīng)用戶操作才會導(dǎo)致ANR,那么是不是5秒之內(nèi)的耗時操作就可以在主線程中執(zhí)行?
不是的,即使沒有導(dǎo)致ANR也會影響用戶的體驗,一般200ms以上的操作都屬于耗時操作應(yīng)該放在子線程中執(zhí)行
1、? ANR的產(chǎn)生不只是因為主線程被阻塞,根本的原因是程序沒有響應(yīng)用戶的操作超過5秒,才會導(dǎo)致ANR。
但是,Android系統(tǒng)規(guī)定,和界面UI更新相關(guān)的操作必須在主線程中執(zhí)行,如果在子線程中對UI界面進(jìn)行更新操作,就會拋出異常,導(dǎo)致程序奔潰。
異常信息:android.view.ViewRootImpl$CalledFromWrongThreadException: Only theoriginal thread that created a view hierarchy can touch its views.
問題:執(zhí)行耗時操作需要在子線程中完成,但是子線程不能更新UI,怎么辦?
答:Android提供了消息機(jī)制可能解決這個問題,原理就是子線程中如果需要更新UI,就“通知”主線程更新UI
Android提供了以下方式解決上面的問題:
1、Handler消息機(jī)制(后面講)
2、AsyncTask異步任務(wù)
【
MainThread 主線程(UI線程):應(yīng)用啟動時創(chuàng)建,處理與UI相關(guān)事情,如點擊事件、數(shù)據(jù)更新
WorkThread 子線程(工作線程):Android 4.0之后UI線程不能訪問網(wǎng)絡(luò)資源或執(zhí)行耗時操作,必須開啟子線程
】
【
Android提供的異步任務(wù)類,主要作用是可以在子線程中執(zhí)行耗時操作,并通知主線程更新UI
功能
在類中實現(xiàn)異步操作,并提供回調(diào)方法反饋當(dāng)前異步執(zhí)行的程度,最后將反饋的結(jié)果提供給UI主線程
回調(diào)方法
1.onPreExecute: 線程任務(wù)開始執(zhí)行前的準(zhǔn)備工作(運行在主線程中)
2.doInBackground:在子線程中執(zhí)行耗時操作, 在線程中執(zhí)行的代碼(后臺開啟了線程并執(zhí)行這部分代碼)
3.onProgressUpdate: 在線程的執(zhí)行中需要執(zhí)行的界面操作(運行在主線程中)
4.onPostExecute:在子線程直線完畢之后調(diào)用(在主線程中執(zhí)行)
其它回調(diào)方法:
5.onCancelled()是當(dāng)調(diào)用cancel方法取消任務(wù)并doInBackground執(zhí)行完之后才會調(diào)用
常用方法
execute:執(zhí)行任務(wù)
cancel(boolean):取消任務(wù)
isCancelled():判斷當(dāng)前任務(wù)是否被取消
publishProgress(int progressValue):發(fā)布任務(wù)進(jìn)度
三個泛型參數(shù)
第一個泛型參數(shù):(定義)是傳遞給doInBackground方法的參數(shù)的類型(引用類型),由execute(…..)方法初傳入
第二個泛型參數(shù):(定義)是傳遞給onProgressUpdate方法的參數(shù)的類型,由publishProgress(。。。。。)方法傳入
第三個泛型參數(shù):是傳遞給onPostExecute方法的參數(shù)的類型,接收的是doInBackground方法的返回值
基本使用步驟
1、定義一個類,繼承自AsyncTask,并指定三個泛型
2、重寫回調(diào)方法
3、在UI線程中實例化AsyncTask對象
4、在UI線程中,執(zhí)行AsyncTask對象的execute(..)方法,開始執(zhí)行異步任務(wù)
5、在執(zhí)行過程中,可以通過調(diào)用cancel(true)方法,停止異步任務(wù)
使用AsyncTask必須遵守的準(zhǔn)則
三個泛型使用位置
第一個泛型:在doInBackground()方法中使用
第二個泛型:在onProgressUpdate()方法中使用
第三個泛型:在onPostExecute()方法中使用
AsyncTask的實例必須在UIthread中創(chuàng)建
execute方法必須在UI線程中被調(diào)用
一個AsyncTask實例只能被執(zhí)行一次,否則多次調(diào)用時將會出現(xiàn)異常
】
【
MarkdownPad?--:用于看.md文件格式的文檔工具。
Volley、XUtils、Universal-Image-Loader、okhttp、SlidingMenu、android-async-http、fresco
ButterKnife、EventBus
】
【
publicclassMainActivityextendsActivity {
@ViewInject(R.id.iv)ImageViewiv;
@Override
protectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
publicvoiddownloadImage(View v){
//下載圖片
HttpUtilshttpUtils =newHttpUtils();
Stringurl ="http://api.k780.com:88/?app=weather.future&weaid=1&&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json";
httpUtils.send(HttpMethod.GET, url,newRequestCallBack() {
@Override
publicvoidonFailure(HttpException arg0, String arg1) {
}
@Override
publicvoidonSuccess(ResponseInfo arg0) {
Toast.makeText(MainActivity.this, arg0.result,0).show();
}
});
}
}
】
【
當(dāng)有多個異步任務(wù)被同時啟動時,那么是多個同時執(zhí)行還是依次排隊執(zhí)行??
當(dāng)有多個異步任務(wù)通過execute方法被同時啟動時,是依次排隊執(zhí)行的,即
只有當(dāng)?shù)谝粋€啟動被啟動的異步任務(wù)執(zhí)行完畢后才會去執(zhí)行下一個
如何實現(xiàn)讓多條異步任務(wù)同時執(zhí)行??
1.當(dāng)多個異步對象通過execute方法啟動時,
* 特點:不管異步任務(wù)對象是否屬于同類別,使用都保持依次排隊執(zhí)行的特點
2. 如果需要讓多個異步任務(wù)同時執(zhí)行的話,可以選擇使用
*executeOnExecutor方法啟動異步任務(wù)
* 參數(shù):
* 1:Executor線程池對象,可以通過此對象指定同時可以有多少個異步任務(wù)一起運行
* 2:作用與execute方法中的參數(shù)作用完全一致
* 注意:此處的數(shù)字不要添加的太多,否則影響速度
指定同時執(zhí)行3條異步任務(wù),代碼如:
Executorexec= Executors.newFixedThreadPool(3);
newMyTask().executeOnExecutor(exec,pb);
newMyTask().executeOnExecutor(exec,pb2);
newMyTask().executeOnExecutor(exec,pb3);
】
【
publicclassMainActivityextendsActivity {
privateProgressBarpb,pb2,pb3;
@Override
protectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化控件
initView();
}
privatevoidinitView() {
pb= (ProgressBar)this.findViewById(R.id.progressBar1);
pb2= (ProgressBar)this.findViewById(R.id.progressBar2);
pb3= (ProgressBar)this.findViewById(R.id.progressBar3);
}
publicvoidbtnClick(View v){
Executor exec = Executors.newFixedThreadPool(3);//同時執(zhí)行的線程數(shù)
new MyTask().executeOnExecutor(exec,pb);
new MyTask().executeOnExecutor(exec,pb2);
new MyTask().executeOnExecutor(exec,pb3);
}
//異步任務(wù)
classMyTaskextendsAsyncTask{
ProgressBarcurrentPb;
@Override
protectedVoid doInBackground(ProgressBar...params) {
//TODOAuto-generated method stub
currentPb= params[0];
for(inti=0;i<=100;i++){
try{
Thread.sleep(100);
publishProgress(i);
}catch(InterruptedException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}
}
returnnull;
}
@Override
protectedvoidonProgressUpdate(Integer... values) {
//TODOAuto-generated method stub
super.onProgressUpdate(values);
currentPb.setProgress(values[0]);
}
}
}
】
【
publicclassTwoActivityextendsActivity {
/*
*實現(xiàn)多張圖片同時下載并且在圖片下載過程中時刻顯示圖片的下載進(jìn)度
*/
ImageViewiv,iv2,iv3;
ProgressBarpb,pb2,pb3;
String[]urls= {
"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png",
"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png",
"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png"};
@Override
protectedvoidonCreate(Bundle savedInstanceState) {
//TODOAuto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.two);
initView();
}
privatevoidinitView() {
iv= (ImageView) findViewById(R.id.imageView1);
iv2= (ImageView) findViewById(R.id.imageView2);
iv3= (ImageView) findViewById(R.id.imageView3);
pb= (ProgressBar) findViewById(R.id.progressBar1);
pb2= (ProgressBar) findViewById(R.id.progressBar2);
pb3= (ProgressBar) findViewById(R.id.progressBar3);
}
publicvoidclick(View v) {
Executor exec = Executors.newFixedThreadPool(2);
newImageTask().executeOnExecutor(exec,urls[0],iv,pb);
newImageTask().executeOnExecutor(exec,urls[1],iv2,pb2);
newImageTask().executeOnExecutor(exec,urls[2],iv3,pb3);
}
classImageTaskextendsAsyncTask {
ImageViewcurrentIv;
ProgressBarcurrentPb;
@Override
protectedBitmap doInBackground(Object...params) {
//TODOAuto-generated method stub
Stringurl = (String) params[0];
currentIv= (ImageView) params[1];
currentPb= (ProgressBar) params[2];
try{
HttpURLConnectionconn = (HttpURLConnection)newURL(url)
.openConnection();
conn.setRequestMethod("GET");
conn.connect();
if(conn.getResponseCode() == 200) {
InputStreamis = conn.getInputStream();
byte[] b =newbyte[1024];
intnum = -1;
ByteArrayOutputStreambos =newByteArrayOutputStream();
intcurrent = 0;
inttotal = conn.getContentLength();
while((num = is.read(b)) != -1) {
bos.write(b,0, num);
SystemClock.sleep(100);
current+= num;
publishProgress(total, current);
}
byte[]bitm = bos.toByteArray();
returnBitmapFactory.decodeByteArray(bitm,0, bitm.length);
}
}catch(MalformedURLException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}catch(IOException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}
returnnull;
}
@Override
protectedvoidonProgressUpdate(Integer... values) {
//TODOAuto-generated method stub
super.onProgressUpdate(values);
currentPb.setMax(values[0]);
currentPb.setProgress(values[1]);
}
@Override
protectedvoidonPostExecute(Bitmap result) {
//TODOAuto-generated method stub
super.onPostExecute(result);
if(result !=null){
currentIv.setImageBitmap(result);
}
}
}
}
】
【
publicclassMainActivityextendsActivity {
privateTextViewtv_count;
privateintcount= 0;
privateCountTaskcountTask;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_count= (TextView)this.findViewById(R.id.tv_count);
}
//開始計數(shù)
publicvoidstartCount(View v) {
if(countTask==null) {
countTask=newCountTask();
countTask.execute();
}
}
//暫停
publicvoidpauseCount(View v) {
if(countTask!=null) {
countTask.cancel(true);
countTask=null;
}
}
//重置
publicvoidreset(View v) {
pauseCount(null);
count= 0;
tv_count.setText("00:00:00");
}
//開啟異步任務(wù)
privateclassCountTaskextendsAsyncTask {
@Override
protectedVoid doInBackground(Void... params){
while(!isCancelled()) {
try{
Thread.sleep(10);
//通知主線程更新顯示
publishProgress(++count);
}catch(InterruptedException e) {
//TODOAuto-generated catch block
e.printStackTrace();
break;
}
}
returnnull;
}
@Override
protectedvoidonProgressUpdate(Integer... values) {
if(isCancelled()) {
return;
}
intss = values[0];
intmm = values[0] / 60;
inthh = values[0] / 60 / 60;
// tv_count.setText(values[0]+"");
tv_count.setText(hh +":"+ mm +":"+ ss);
}
//當(dāng)子線程成功執(zhí)行之后調(diào)用
@Override
protectedvoidonPostExecute(Void result) {
Log.i("mtag","onPostExecute");
Toast.makeText(MainActivity.this,"onPostExecute",0).show();
}
//當(dāng)被中斷取消時調(diào)用
@Override
protectedvoidonCancelled() {
Log.i("mtag","onCancelled");
}
}
}
】
Android4.0開始,Android系統(tǒng)規(guī)定不能在UI線程中執(zhí)行訪問網(wǎng)絡(luò)資源的操作,相關(guān)操作必須在子線程中完成
Android網(wǎng)絡(luò)訪問需要的權(quán)限:android.permission.INTERNET
【
/**
* 網(wǎng)絡(luò)訪問工具類
*
*@authorEvanYu
*@date2016.01.08
*/
publicclassHttpUtils {
/**
* 網(wǎng)絡(luò)訪問超時時間
*/
publicstaticfinalintTIMEOUT= 10000;
/**
* 通過get方式實現(xiàn)網(wǎng)絡(luò)請求
*
*@paramurl
*???????????訪問的url地址
*@return請求的結(jié)果,null代表請求失敗
*/
publicstaticbyte[] doGet(String url) {
HttpURLConnection conn =null;
try{
URL mUrl =newURL(url);
conn = (HttpURLConnection)mUrl.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(TIMEOUT);
conn.setReadTimeout(TIMEOUT);
conn.connect();
intcode= conn.getResponseCode();
if(code== 200) {
returnreadStream(conn.getInputStream());
}else{
thrownewRuntimeException("網(wǎng)絡(luò)訪問失?。?+ code);
}
}catch(Exception e) {
e.printStackTrace();
returnnull;
}finally{
if(conn!=null) {
conn.disconnect();
conn =null;
}
}
}
publicstaticbyte[] doPost(String url, String params) {
HttpURLConnection conn =null;
try{
URL mUrl =newURL(url);
conn = (HttpURLConnection)mUrl.openConnection();
conn.setRequestMethod("POST");
conn.setConnectTimeout(TIMEOUT);
conn.setReadTimeout(TIMEOUT);
//設(shè)置請求屬性
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", params.length() +"");
// Post請求必須要寫以下兩行代碼
conn.setDoInput(true);
conn.setDoOutput(true);
//將請求參數(shù)寫到請求體中
conn.getOutputStream().write(params.getBytes());
;
conn.connect();
intcode= conn.getResponseCode();
if(code== 200) {
returnreadStream(conn.getInputStream());
}else{
thrownewRuntimeException("網(wǎng)絡(luò)訪問失?。?+ code);
}
}catch(Exception e) {
e.printStackTrace();
returnnull;
}finally{
if(conn!=null) {
conn.disconnect();
conn =null;
}
}
}
privatestaticbyte[] readStream(InputStream is)throwsIOException {
ByteArrayOutputStream baos =newByteArrayOutputStream();
byte[] buf =newbyte[1024];
intlen = 0;
while((len = is.read(buf)) != -1) {
baos.write(buf, 0, len);
}
returnbaos.toByteArray();
}
}
】
使用ProgressBar控件顯示下載進(jìn)度
使用ProgressDialog顯示下載進(jìn)度
構(gòu)造方法:ProgressDialog(Contextcontext)
設(shè)置進(jìn)度條樣式:setProgressStyle(intstyle)
設(shè)置標(biāo)題:setTitle(CharSequence)
設(shè)置標(biāo)題圖標(biāo):setIcon(intresId)
設(shè)置顯示內(nèi)容:setMessage(CharSequencemessage)
設(shè)置進(jìn)度值:setProgress(intvalue)
顯示對話框:show()
關(guān)閉對話框:cancel()
【
publicclassMainActivityextendsActivity {
privateImageViewimg_view;
@Override
protectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
img_view= (ImageView)this.findViewById(R.id.img_view);
}
publicvoiddownloadImg(View v) {
// Stringurl=
//"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png";
Stringurl ="http://img1.imgtn.bdimg.com/it/u=335355609,381250936&fm=21&gp=0.jpg";
newMyAsynTask().execute(url);
}
privateclassMyAsynTaskextendsAsyncTask {
ProgressDialog pDialog;
@Override
protectedvoidonPreExecute() {
//開始下載之前先顯示一個對話框
pDialog=newProgressDialog(MainActivity.this);
pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pDialog.setMax(100);
pDialog.setTitle("下載圖片");
pDialog.setMessage("正在下載圖片.....");
pDialog.show();
}
@Override
protectedBitmap doInBackground(String...params) {
/*
* Stringurl=params[0]; byte[]data = HttpUtils.doGet(url);
* //將下載到的數(shù)據(jù)解析成一張圖片Bitmapbm=BitmapFactory.decodeByteArray(data,
* 0, data.length); SystemClock.sleep(2000);returnbm;
*/
HttpURLConnectionconn =null;
try{
Stringurl = params[0];
URLmurl =newURL(url);
conn= (HttpURLConnection) murl.openConnection();
conn.setRequestMethod("GET");
intcode = conn.getResponseCode();
if(code == 200) {
//獲取數(shù)據(jù)的總大小
inttatalSize = conn.getContentLength();
floatcurSize = 0;//記錄當(dāng)前已經(jīng)下載的大小
InputStreamis = conn.getInputStream();
ByteArrayOutputStreambaos =newByteArrayOutputStream();
byte[] buf =newbyte[10];
intlength = 0;
while((length = is.read(buf)) != -1) {
baos.write(buf,0, length);
curSize+= length;
publishProgress((int) ((curSize / tatalSize) * 100));
//模擬耗時操作
SystemClock.sleep(20);
}
//將下載到的數(shù)據(jù)解析成一張圖片
byte[]data = baos.toByteArray();
Bitmapbm = BitmapFactory.decodeByteArray(data, 0,
data.length);
returnbm;
}else{
thrownewRuntimeException("網(wǎng)路訪問失敗"+ code);
}
}catch(Exception e) {
e.printStackTrace();
}finally{
if(conn !=null){
conn.disconnect();
conn=null;
}
}
returnnull;
}
@Override
protectedvoidonProgressUpdate(Integer... values) {
pDialog.setProgress(values[0]);
}
@Override
protectedvoidonPostExecute(Bitmap result) {
img_view.setImageBitmap(result);
//取消顯示
// pDialog.dismiss();
pDialog.cancel();
}
}
}
】
數(shù)據(jù)網(wǎng):K780數(shù)據(jù)網(wǎng)、聚合數(shù)據(jù)等
K780測試賬號
Appkey:15250
Secret:2bbebb3e480a850df6daca0c04a954e1
Sign:f88a5cecc3cbd37129bc090c0ae29943
網(wǎng)絡(luò)訪問工具類的封裝
HttpUtils類
K780數(shù)據(jù)訪問工具類的封裝
K780Utils類
【
public classWeather {
privateStringdays;
privateStringtemperature;
publicWeather() {
}
publicWeather(String day, String temperature) {
this.days= day;
this.temperature= temperature;
}
publicString getDay() {
returndays;
}
public voidsetDay(String day) {
this.days= day;
}
publicString getTemperature() {
returntemperature;
}
public voidsetTemperature(String temperature) {
this.temperature= temperature;
}
@Override
publicString toString() {
return"Weather[day="+days+", temperature="+temperature+"]";
}
}
引入網(wǎng)絡(luò)訪問工具類
publicclassMainActivityextendsActivity {
privateTextViewtv_show;
@Override
protectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_show= (TextView) findViewById(R.id.tv_show);
}
publicvoiddownload(View v) {
Stringurl ="http://api.k780.com:88/?app=weather.future&weaid=1&&appkey=15250&sign=f88a5cecc3cbd37129bc090c0ae29943&format=json";
newMyTask().execute(url);
}
privateclassMyTaskextendsAsyncTask> {
@Override
protectedListdoInBackground(String... params) {
Stringdata;
try{
Listlist =newArrayList();
data=newString(HttpUtils.doGet(params[0]));
JSONObjectjsonObject =newJSONObject(data);
JSONArrayresult = jsonObject.getJSONArray("result");
for(inti = 0; i < result.length(); i++) {
JSONObjecttemp = result.getJSONObject(i);
Stringdays = temp.getString("days");
Stringtemperature = temp.getString("temperature");
list.add(newWeather(days, temperature));
}
returnlist;
}catch(JSONException e) {
//TODOAuto-generated catch block
e.printStackTrace();
}
returnnull;
}
@Override
protectedvoidonPostExecute(List result) {
// tv_show.setText(result);
for(Weather weather : result) {
tv_show.append(weather.toString() +"\n");
}
}
}
}
】