一. 前言
关于网络编程更加详细的内容可以参考:Java 网络
二. AsyncTask
1. AsyncTask是什么?
AsyncTask是Android封装的一个轻量级的异步类,可以在线程池中执行异步任务,并可以将执行进度和结果传递给UI线程。
AsyncTask的内部封装了两个线程池(SerialExecutor
、THREAD_POOL_EXECUTOR
)和一个Handler
。
其中SerialExecutor
线程池用于任务的排队,让需要执行的多个耗时任务,按顺序排列,THREAD_POOL_EXECUTOR
线程池才真正地执行任务,Handler
用于从工作线程切换到主线程。
作用:
- 实现多线程:在工作线程中执行任务,如 耗时任务。
- 异步通信、消息传递:实现工作线程 & 主线程(
UI
线程)之间的通信,即:将工作线程的执行结果传递给主线程,从而在主线程中执行相关的UI
操作
2. AsyncTask的泛型参数
1 2 3 4 5
| public abstract class AsyncTask<Params, Progress, Result>{} #Params:开始异步任务执行时传入的参数类型,对应excute()中传递的参数 #Progress:异步任务执行过程中,返回下载进度值的类型; #Result:异步任务执行完成后,返回的结果类型,与doInBackground()的返回值类型保持一致 #如果AsyncTask确定不需要传递具体参数,那么这三个泛型参数可以用Void来代替。
|
3. AsyncTask的核心方法
1 2 3
| @MainThread protected void onPreExecute() {} #此方法在doInBackground方法之前执行,在主线程中,用于进行界面初始化操作
|
1 2 3
| @WorkerThread protected abstract Result doInBackground(Params... params) #此方法在WorkerThread执行,用来处理耗时任务,此方法内不能更新UI
|
1 2 3 4 5 6
| @MainThread protected void onProgressUpdate(Progress... values) #当在后台任务中调用了publishProgress(Progress…)方法后,这个方法就很快会被调用, #方法中携带的参数就是在后台任务中传递过来的。 #在这个方法中可以对UI进行操作,在主线程中进行 #利用参数中的数值就可以对界面元素进行相应的更新。
|
1 2 3 4
| @MainThread protected void onPostExecute(Result result) #当doInBackground(Params…)执行完毕并通过return语句进行返回时,这个方法就很快会被调用。 #返回的数据来进行一些UI操作,在主线程中进行
|
上面几个方法的调用顺序:
onPreExecute() –> doInBackground() –> publishProgress() –> onProgressUpdate() –> onPostExecute() (md info supported)
1 2 3 4 5 6 7 8
| @MainThread protected void onCancelled(Result result) { onCancelled(); } #在主线程中执行,当异步任务取消时,onCancelled()会被调用, #这个时候onPostExecute()则不会被调用 AsyncTask中的cancel()方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务。就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。
|
4. AsyncTask的使用
创建一个类TestTask继承AsyncTask,重写上面的几个方法,使用new TestTask().execute(参数)调用。
注意事项:
异步任务的实例必须在UI线程中创建,即AsyncTask对象必须在UI线程中创建。execute(Params… params)方法必须在UI线程中调用。
一个任务实例只能执行一次,如果执行第二次将会抛出异常。
内存泄漏,静态内部类持有外部类的引用。如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。
生命周期:activity distory 进行回收cancel。
三. 请求网页的小例子
源码地址
1. 代码
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
| public class MainActivity extends AppCompatActivity {
private EditText request_text; private Button request_btn; private TextView show_text;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
request_text = findViewById(R.id.request_text); request_btn = findViewById(R.id.request_btn); show_text = findViewById(R.id.show_text);
request_btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new RequestNetworkDataTask().execute(request_text.getText().toString()); } }); }
private URL getURL(String urlString){ try { return new URL(urlString); } catch (MalformedURLException e) { e.printStackTrace();
Toast.makeText(this, "URL是非法的", Toast.LENGTH_SHORT).show(); } return null; }
private String requestData(String urlString){ URL url = getURL(urlString);
if (url == null){ return null; }
try { HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setConnectTimeout(30000); connection.setRequestMethod("GET");
connection.connect();
int responseCode = connection.getResponseCode(); String responseMessage = connection.getResponseMessage();
if (responseCode == HttpURLConnection.HTTP_OK){ InputStream inputStream = connection.getInputStream();
InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); char[] buffer = new char[1024]; reader.read(buffer); String content = new String(buffer); return content; }
} catch (IOException e) { e.printStackTrace();
Toast.makeText(this, "读写错误", Toast.LENGTH_SHORT).show(); }
return null; }
class RequestNetworkDataTask extends AsyncTask<String,Integer,String>{
@Override protected void onPreExecute() { super.onPreExecute(); }
@Override protected String doInBackground(String... params) { String result = requestData(params[0]);
return result; }
@Override protected void onPostExecute(String result) { super.onPostExecute(result);
show_text.setText(result); } } }
|
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
| <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="swu.xl.requesthtml">
<uses-permission android:name="android.permission.INTERNET"/>
<application android:allowBackup="true" android:icon="@mipmap/view" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
</manifest>
|
2. 运行结果

四. 下载文件的小例子
1. 代码
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
| public class MainActivity extends AppCompatActivity {
private Button download; private ProgressBar progressBar; private TextView progressText; private EditText editText; private DownloadFileTask downloadFileTask;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
editText = findViewById(R.id.edit_url); download = findViewById(R.id.download); progressBar = findViewById(R.id.progress_bar); progressText = findViewById(R.id.progress_text);
download.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) { downloadFileTask = new DownloadFileTask(); downloadFileTask.execute(editText.getText().toString()); v.setClickable(false); } }); }
@Override protected void onDestroy() { super.onDestroy();
downloadFileTask.cancel(false); }
private class DownloadFileTask extends AsyncTask<String,Integer,String> { @Override protected void onPreExecute() { super.onPreExecute();
progressBar.setProgress(0); progressText.setText("开始下载"); }
@Override protected String doInBackground(String... strings) {
try { URL url = new URL(strings[0]); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.connect(); int contentLength = connection.getContentLength(); InputStream is = connection.getInputStream();
File externalFilesDir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
File file = new File(externalFilesDir, "text.apk"); OutputStream os = new FileOutputStream(file);
int downloadSize = 0; int len; byte[] bytes = new byte[1024]; while ((len = is.read(bytes)) != -1){ os.write(bytes,0,len);
downloadSize += len; int progress = (int) ((downloadSize / (contentLength * 1.0)) * 100); publishProgress(progress); System.out.println("下载进度:"+progress); }
os.flush(); is.close(); os.close();
return is.toString();
} catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
return null; }
@Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values);
progressBar.setProgress(values[0]); progressText.setText("下载进度:"+values[0]+"%"); }
@Override protected void onPostExecute(String s) { super.onPostExecute(s); progressBar.setProgress(100); progressText.setText("下载完毕"); } } }
|
2. 运行结果

下载好之后,去手机内存中找到下载的文件,发现的确下载成功。
3. 源码
DownloadFile
参考文章
AsyncTask
Android 多线程:手把手教你使用AsyncTask