Android0912<十七>(Android 网络URLConnection)(ing多线程下载)

时间:2021-01-04 21:13:58

使用URLConnection

URLConnection是一个读取或写入的网址的连接,使用URLConnection之前必须连接到远程资源配置。实例URLConnection是不可重用的:你必须使用一个不同的实例为每个连接到资源。创建一个URLConnection实例,一般只需要new()出一个URL对象,并传入目标网址,然后调用一下openConnection()方法即可,如下:

  URL url=new URL("http://www.360.com");
URLConnection connection=url.openConnection();

之后在调用connection的getInputStream()方法就可以得到服务器返回的输入流了,(前提是服务器为运行状态)接着就对输入流进行读取。读取完成后要记得将输入流关掉,如下:

InputStream is=connection.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String line=br.readLine();
StringBuffer buffer=new StringBuffer();
while(line!=null){
buffer.append(line);
line=br.readLine();
}
Message msg = handler.obtainMessage();
msg.what=URL_CONENT;
msg.obj=buffer.toString().trim();
handler.sendMessage(msg);
br.close();
is.close();

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity">


<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="网络连接内容"/>

<Button
android:id="@+id/btn_network"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="连接网络"/>

<TextView
android:id="@+id/text_connection"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button btn_network;
private Button btn;
private TextView textView_connection;
private static final int URL_CONENT=0x23;
//在这里对UI进行更新,将结果显示到界面上
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case URL_CONENT:
String s= (String) msg.obj;
textView_connection.setText(s);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_network= (Button) findViewById(R.id.btn_network);
textView_connection= (TextView) findViewById(R.id.text_connection);
btn_network.setOnClickListener(this);
btn= (Button) findViewById(R.id.button);
btn.setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_network:
//开启线程对UI界面进行更新,将结果显示到界面上
new Thread(new Runnable() {
@Override
public void run() {
connectServerlet();
}
}).start();
break;
case R.id.button:
Intent intent=new Intent(MainActivity.this,NetworkActivity.class);
startActivity(intent);
}


}

private void connectServerlet() {
try {
URL url=new URL("http://192.168.0.82:8080/MyServersTest");
// URL url=new URL("http://www.360.com");
URLConnection connection=url.openConnection();
InputStream is=connection.getInputStream();
//下面对获取到的输入流进行读取
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String line=br.readLine();
StringBuffer buffer=new StringBuffer();
while(line!=null){
buffer.append(line);
line=br.readLine();
}
//将读取到的结果存放到Message中
Message msg = handler.obtainMessage();
msg.what=URL_CONENT;
msg.obj=buffer.toString().trim();
handler.sendMessage(msg);
br.close();
is.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

要访问网络还要开启权限,在AndroidManifest.xml中:

 <uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>

Android0912<十七>(Android 网络URLConnection)(ing多线程下载)

下载

单线程下载

需要利用AsyncTask来执行下载任务,要重写AsyncTask的四个方法

public class DownloadActivity extends Activity implements View.OnClickListener{
private Button btn_single_downlaod;
private Button btn_more_download;
private ProgressBar mDownloadProgressbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_download);
mDownloadProgressbar= (ProgressBar) findViewById(R.id.progress_download);
btn_single_downlaod= (Button) findViewById(R.id.single_download);
btn_single_downlaod.setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.single_download:
SingleDownloadTask task=new SingleDownloadTask();
//调用execute()方法启用SingleDownloadTask任务
task.execute();
break;
}
}
class SingleDownloadTask extends AsyncTask<String,Integer ,String>{
@Override
protected void onPreExecute() {
super.onPreExecute();
mDownloadProgressbar.setVisibility(View.VISIBLE);
}

@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//在这里更新下载速度
mDownloadProgressbar.setProgress((int) (values[0]*100.0/values[1]));

}

@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
//在这里提示下载结果
btn_single_downlaod.setText("下载完成!");
}

@Override
protected String doInBackground(String... params) {
//执行具体的下载任务
try {
// URL url=new URL("http://192.168.0.30:8080/MyWebTest/music/aa.mp3");
URL url=new URL("http://192.168.0.82:8080/MyServersTest/Music/rainbow.mp3");
URLConnection connection=url.openConnection();
//connection.getContentLength()方法返回响应头字段指定的字节的内容长度
int length=connection.getContentLength();
// publishProgress(length);
//读取输入流的内容
InputStream is=connection.getInputStream();
File file=new File (Environment.getExternalStorageDirectory(),"rainbow.mp3");
if (!file.exists()){
file.createNewFile();
}
//将读取的内容写入文件
FileOutputStream os=new FileOutputStream(file);
byte[] array=new byte[1024];
int sum=0;
int index=is.read(array);
while(index!=-1){
os.write(array,0,index);
sum=+index;
//调用publishProgress(Progress...)来更新任务的进度
publishProgress(sum,length);
index=is.read(array);
}
os.flush();
os.close();
is.close();


} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

return null;
}
}
}

服务器端的待下载文件
Android0912<十七>(Android 网络URLConnection)(ing多线程下载)
下载过程
Android0912<十七>(Android 网络URLConnection)(ing多线程下载)
下载到模拟器里的文件
Android0912<十七>(Android 网络URLConnection)(ing多线程下载)

多线程下载

多线程下载就是将一个文件分为几分,交给相应的线程进行处理,由文件长度和分数得到每个线程应该处理的问件长度和开始、结尾处理的部分,最后按照线程的顺序将每个线程的文件拼接起来。在最后面的饿结尾处不是都能很好的分配完,所以要特殊处理最后一小段。其他部分基本和单线程一致。这里的多线程需要一个自定义的线程来处理被分割的各个文件。

activity_download.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">

<ProgressBar
android:id="@+id/progress_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"/>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<Button
android:id="@+id/single_download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="单线程下载"
/>

<Button
android:id="@+id/multi_download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="多线程下载"
/>

</LinearLayout>
</LinearLayout>

MultiThread.java

public class MultiThread extends Thread{
public MultiThread(long start, long end, String url, String filePath) {
this.start = start;
this.end = end;
this.urlPath = url;
this.filePath = filePath;
}
private int sum=0;
private long start;
private long end;
private String urlPath;
private String filePath;

public int getSum() {
return sum;
}

@Override
public void run() {
try {
URL url=new URL(urlPath);
//URL的openConnection()返回这个网址所指的资源的新连接。
URLConnection connection=url.openConnection();
//设置允许用户交互。
connection.setAllowUserInteraction(true);
//设置指定的请求头字段的值。价值只能被当前的URLConnection实例使用。这种方法只能在建立连接之前调用。
//第一个参数是设置请求与的头值 第二个参数指定属性的新值
connection.setRequestProperty("Range", "bytes=" + start + "-"
+ end);
//读取输入流的内容
InputStream is= connection.getInputStream();
byte [] array=new byte[1024];
is.read(array);
File file=new File(filePath);
//RandomAccessFile允许在随机访问方式中读取和写入文件。这是不同于单向顺序访问一个输入或输出提供。
// 如果在读/写模式下打开文件,则可使用写操作。在每次操作之后,可以向前和向后移动读写操作的位置。
//RandomAccessFile(file,"rw")构建了一个基于文件的RandomAccessFile实例,根据模式访问字符串打开它。
RandomAccessFile randomAccessFileile=new RandomAccessFile(file,"rw");
//RandomAccessFile的seek()方法将该文件的文件指针移到一个新的位置,从下面的读、写或跳过操作来完成
randomAccessFileile.seek(start);
int index=is.read(array);
while (index!=-1){
//RandomAccessFile的write()方法写bytecount字节从字节数组缓冲区这个文件,
// 当前文件指针开始使用byteoffset作为第一位在缓冲区获取字节。
randomAccessFileile.write(array,0,index);
sum+=index;
index=is.read(array);
}
randomAccessFileile.close();
is.close();
Log.d("此次长度",""+(end-start));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

DownloadActivity.java

 private void MultiDownloadTask() {
new Thread(new Runnable() {
@Override
public void run() {
try {
String urlPath="http://192.168.0.82:8080/MyServersTest/Picture/imagebac.jpg";
URL url=new URL(urlPath);
//URL的openConnection()返回这个网址所指的资源的新连接。
URLConnection connection=url.openConnection();
//connection.getContentLength()方法返回响应头字段指定的字节的内容长度
length = connection.getContentLength();
Log.d("下载文件的长度",""+length);
//Environment.getExternalStorageDirectory()将文件下载的位置,"image.jpg"下载文件的名字
File file=new File(Environment.getExternalStorageDirectory(),"image.jpg");
//判断文件是否存在,不存在,则新建文件
if (!file.exists()){
file.createNewFile();
}
//创建线程数组
MultiThread[] threads =new MultiThread[5];
//给线程数组赋值
for (int i=0;i<5;i++){
MultiThread thread=null;
//将最后一个线程和其他线程区分开来
if (i==4){
//最后一个线程处理的长度到文件末尾
thread=new MultiThread(length / 5*4, length , urlPath, file.getAbsolutePath());
Log.d("完成情况","最后看阶段段完成"+thread.getSum());
}else {
//非末尾线程处理的长度为文件总程度/总线程个数
thread=new MultiThread(length / 5 * i, length / 5 * (i + 1)-1, urlPath, file.getAbsolutePath());
Log.d("完成情况","第"+i+"段完成"+thread.getSum());
}
//启动线程
thread.start();
//给线程数组赋值
threads[i]=thread;
}
//将各个线程的文件拼接起来
boolean isFinish=true;
while(isFinish){
int sum=0;
for (MultiThread thread:threads){
sum+= thread.getSum();
Log.d("文件长度",""+sum+"本次完成"+thread.getSum());
}
//将处理文件长度返回给主线程,对UI进行更新
Message msg= handler.obtainMessage();
msg.what=0x23;
msg.arg1=sum;
handler.sendMessage(msg);
if(sum+10>= length){
isFinish=false;
}

Thread.sleep(1000);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}

这里将多线程下载提成一个方法,最后在多线程下载的按钮点击事件里实现。其结果和单线程一样。