网络编程

1.从网络上获取数据(重点)

1、发送请求:GET
2、接收服务器端返回的响应数据

使用代码实现的步骤:

1、创建URL,打开一个HTTP的连接;
2、设置请求头信息:GET(GET、POST)
3、接收服务器端返回的响应数据,响应码:200 ok,404没有找到资源 ,503服务器端内部错误
4、把接收的二进制数据转换成图片

使用代码实现的步骤:

1、创建URL,打开一个HTTP的连接;
2、设置请求头信息:GET(GET、POST)
3、接收服务器端返回的响应数据,响应码:200 ok,404没有找到资源 ,503服务器端内部错误
4、把接收的二进制数据转换成图片

模版代码:

    1、创建一个URL对象,打开一个HTTP连接
        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

    2、设置请求头信息:GET(GET、POST)

        conn.setRequestMethod("GET");
        conn.setConnectTimeout(5000);

    3、接收服务器端返回的响应数据,响应码:200 ok,404没有找到资源 ,503服务器端内部错误

        int code = conn.getResponseCode();
        if(code == 200){
              InputStream is = conn.getInputStream();
       }

2.访问网络的Android应用都必须加上访问互联网的权限:

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

网络在主线程上的异常: android.os.NetworkOnMainThreadException
从Androi4.0开始,google更加UI界面运行的流畅性,强制要求访问网络的操作不能在主线程中进行,只能在子线程中进行。

activity中的oncreate方法和单击事件的方法都是运行在主线程中的。
只有创建UI界面的那个线程才能修改UI: Only the original thread that created a view hierarchy can touch its views.

主线程(UI线程),只有主线程才能修改UI。如果子线程修改UI,系统验证当前线程是不是主线程,如果不是主线程,就会终止运行。

消息处理(重点)

1、在主线程中创建handler
private Handler handler = new Handler(){

    //接收消息并处理消息
    @Override
    public void handleMessage(Message msg) {
        // TODO Auto-generated method stub
        super.handleMessage(msg);
    }

};

//2、在子线程中得到handler的引用,并发送消息给主线程

  Message msg = new Message();
  msg.obj = bm;
  handler.sendMessage(msg);

//3、在主线程中修改UI
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
Bitmap bm = (Bitmap) msg.obj;

        iv.setImageBitmap(bm);
    }

消息处理机制原理

所有使用UI界面的操作系统,后台都运行着一个死循环,在不停的监听和接收用户发出的指令,一旦接收指令就立即执行。

Looper Message Handler三者之间的关系:(重点)

当我们的Android应用程序的进程一创建的时候,系统就给这个进程提供了一个Looper,Looper是一个死循环,它内部维护这个一个消息队列,Loop不停地从消息队列中取消息(Message),取到消息就发送给了Handler,最后Handler根据接收到的消息去修改UI。

3.消息处理常用的API:

//第一个API 
runOnUiThread

//第二个API
handler.postDelayed

//第三个API
//handler.postAtTime

网络请求数据

1、使用GET方式
使用GET方式向服务器端提交数据,是把提交的参数组频道url的后面:
http://192.168.12.28:8080/web/servlet/LoginServlet?username=232&password=sdfsd
最大提交4k,windows上最多允许提交1k。

2、使用POST方式

与GET方式的区别:
1、设置请求方式为POST

connection.setRequestMethod("POST");
connection.setConnectTimeout(5000);

2、设置表单类型及数据长度

connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length", data.length()+"");

3、设置连接允许向服务器写数据

connection.setDoOutput(true);
connection.getOutputStream().write(data.getBytes());

使用HttpURLConnection方式发送Get请求

public class MainActivity extends Activity {

    private EditText et_qq;
    private EditText et_pwd;

    private Handler handler = new Handler(){
        public void handleMessage(android.os.Message msg) {
            String result = (String) msg.obj;
            Toast.makeText(MainActivity.this, result, 0).show();

        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et_qq = (EditText) findViewById(R.id.et_qq);


        et_pwd = (EditText) findViewById(R.id.et_pwd);
    }

    public void login(View view){
        final String qq = et_qq.getText().toString().trim();
        final String pwd = et_pwd.getText().toString().trim();
        final String urlStr = "http://192.168.12.28:8080/web/servlet/LoginServlet";

        if(TextUtils.isEmpty(qq) || TextUtils.isEmpty(pwd)){
            Toast.makeText(this, "请输入qq号码或者密码", 0).show();
            return;
        }else{
            String data = "?username="+URLEncoder.encode(qq)+"&password="+URLEncoder.encode(pwd);
            final String path = urlStr + data;
            new Thread(){
                public void run() {
                    try {
                        URL url = new URL(path);
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

                        connection.setRequestMethod("GET");
                        connection.setConnectTimeout(5000);
                        int code = connection.getResponseCode();
                        if(code == 200){
                            InputStream is = connection.getInputStream();
                            String result = StreamTools.readStream(is);

                            Message msg= Message.obtain();
                            msg.obj = result;
                            handler.sendMessage(msg);
                        }else
                        {
                            //
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                };
            }.start();
        }
    }


}
    /**
     * 把输入流转换成一个字符串
     * @param is 输入流
     * @return 返回一个字符串,如果转换失败就返回一个空字符
     */
    public static String readStream(InputStream is){

        try {

            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            byte[] buffer = new byte[1024];
            int len = -1;
            while((len = is.read(buffer)) != -1){
                 baos.write(buffer, 0, len);
                }
            is.close();
            return new String(baos.toByteArray());
        } catch (Exception e) {
            return "";
        }
    }
}

使用HttpURLConnection方式发送Post请求

public class MainActivity extends Activity {

    private EditText et_qq;
    private EditText et_pwd;

    private Handler handler = new Handler(){
        public void handleMessage(android.os.Message msg) {
            String result = (String) msg.obj;
            Toast.makeText(MainActivity.this, result, 0).show();


        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et_qq = (EditText) findViewById(R.id.et_qq);


        et_pwd = (EditText) findViewById(R.id.et_pwd);
    }

    public void login(View view){
        final String qq = et_qq.getText().toString().trim();
        final String pwd = et_pwd.getText().toString().trim();
        final String urlStr = "http://192.168.12.28:8080/web/servlet/LoginServlet";

        if(TextUtils.isEmpty(qq) || TextUtils.isEmpty(pwd)){
            Toast.makeText(this, "请输入qq号码或者密码", 0).show();
            return;
        }else{                             
            final String data = "username="+URLEncoder.encode(qq)+"&password="+URLEncoder.encode(pwd);
            final String path = urlStr ;
            new Thread(){
                public void run() {
                    try {
                        URL url = new URL(path);
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

                        //设置请求方式为POST
                        connection.setRequestMethod("POST");
                        connection.setConnectTimeout(5000);
                        //设置表单类型及数据长度
                        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                        connection.setRequestProperty("Content-Length", data.length()+"");
                        //设置连接允许向服务器写数据
                        connection.setDoOutput(true);
                        connection.getOutputStream().write(data.getBytes());

                        int code = connection.getResponseCode();
                        if(code == 200){
                            InputStream is = connection.getInputStream();
                            String result = StreamTools.readStream(is);

                            Message msg= Message.obtain();
                            msg.obj = result;
                            handler.sendMessage(msg);
                        }else
                        {
                            //
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                };
            }.start();
        }
    }
}
    /**
     * 把输入流转换成一个字符串
     *
     * @param is 输入流
     * @return 返回一个字符串,如果转换失败就返回一个空字符
     */
    public static String readStream(InputStream is) {

        try {

            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            byte[] buffer = new byte[1024];
            int len = -1;
            while ((len = is.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            is.close();
            return new String(baos.toByteArray(), "gb2312");
        } catch (Exception e) {
            return "";
        }
    }
}

async httpclient (重点)

使用GET方式向服务器端提交数据:

                 //1、创建一个浏览器
                AsyncHttpClient client  = new AsyncHttpClient();
                //2、设置请求方式及访问路径,并处理服务器端返回的结果
                client.get(path, new AsyncHttpResponseHandler() {

                     /**
                      * 处理成功的结果
                      */
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
                                Toast.makeText(MainActivity.this, "请求成功,"+new String(responseBody), 0).show();                      
                    }


                     /**
                      * 处理失败的结果
                      */
                    @Override
                    public void onFailure(int statusCode, Header[] headers,
                            byte[] responseBody, Throwable error) {
                        Toast.makeText(MainActivity.this, "请求失败", 0).show();    

                    }
                });

//

使用POST方式向服务器端提交数据的模版代码:


1、创建一个浏览器
        AsyncHttpClient client = new AsyncHttpClient();
2、封装提交的参数
        RequestParams params = new RequestParams();
        params.add("username", qq);
        params.add("password", pwd);
3、设置请求方式及访问路径,并处理响应结果
        client.post(urlStr, params, new AsyncHttpResponseHandler() {

            @Override
            public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
                Toast.makeText(MainActivity.this, "请求成功,"+new String(responseBody), 1).show();
            }

            @Override
            public void onFailure(int statusCode, Header[] headers,
                    byte[] responseBody, Throwable error) {
                Toast.makeText(MainActivity.this, "请求失败", 1).show();
            }
        });

上传文件:
2、封装提交的参数
RequestParams params = new RequestParams();
params.put(“file”, file);

多线程下载的步骤

1、需要知道服务器端文件的小大;
2、等分文件为若干份;
3、知道每个线程下载数据的起始位置和结束位置;
4、创建子线程下载数据; conn.setRequestProperty(“Range”,”bytes=0-2”);
5、每个子线程什么时间下载完毕。循环更新一个数值

使用XUtils实现多线程下载,包括断点续传的功能(重点)

  HttpUtils http = new HttpUtils();
    HttpHandler handler = http.download("http://192.168.12.28:8080/tomcat.png",
        "/mnt/sdcard/tomcat.png",
        true, // 如果目标文件存在,接着未完成的部分继续下载。服务器不支持RANGE时将从新下载。
        true, // 如果从请求返回信息中获取到文件名,下载完成后自动重命名。
        new RequestCallBack<File>() {

            @Override
            public void onStart() {
                tv.setText("正在下载...");
            }

            @Override
            public void onLoading(long total, long current, boolean isUploading) {
                tv.setText(current + "/" + total);
            }

            @Override
            public void onSuccess(ResponseInfo<File> responseInfo) {
                tv.setText("下载完成:" + responseInfo.result.getPath());
            }


            @Override
            public void onFailure(HttpException error, String msg) {
                tv.setText("下载失败");
            }
    });

多线程断点续传下载

多线程下载的实现过程:
1.首先得到下载文件的长度,然后设置本地文件的长度。
HttpURLConnection.getContentLength();
RandomAccessFile file = new RandomAccessFile(“FeiQ.exe”,”rwd”);
file.setLength(filesize); //设置本地文件的长度
2.根据文件长度和线程数计算每条线程下载的数据长度和下载位置。
如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如图所示。

3.使用Http的Range头字段指定每条线程从文件的什么位置开始下载,下载到什么位置为止.
如:指定从文件的2M位置开始下载,下载到位置4M为止,代码如下:
HttpURLConnection.setRequestProperty(“Range”, “bytes=2097152-4194303”);
4.保存文件
使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。
RandomAccessFile threadfile = new RandomAccessFile(“QQWubiSetup.exe “,”rwd”);
threadfile.seek(2097152); //从文件的什么位置开始写入数据

public class DownloadUtils {

private int stopThreadCount;// 停止的线程数量
private int threadCount;// 线程总数
private Handler handler;
private LinearLayout llProgressBarGroup;

public void useMultiThreadDownloadFile(String path, int threadCount, Handler handler, LinearLayout llProgressBarGroup) throws Exception {
this.threadCount = threadCount;
this.handler = handler;
this.llProgressBarGroup = llProgressBarGroup;
stopThreadCount = 0;

// 1. 获取服务器端, 文件的长度.
int length = getFileLength(path);
if(length != -1) {
System.out.println("文件总长度: " + length);

// 2. 在本地创建一个文件长度为服务器返回的长度.
File file = new File(Environment.getExternalStorageDirectory(), 
path.substring(path.lastIndexOf("/") + 1));
System.out.println(file.getName());
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.setLength(length);

// 3. 根据线程数量, 计算每个线程所要下载数据的开始,结束索引.
int blockSize = length / threadCount;

for (int i = 0; i < threadCount; i++) {
int startIndex = i * blockSize;
int endIndex = (i + 1) * blockSize - 1;
if(i == threadCount -1) {
    endIndex = length -1;
          }

// 4. 开始多线程下载.
new DownloadThread(path, startIndex, endIndex, i).start();
    }
} else {
    System.out.println("获取文件长度失败: " + length);
}
}

private int getFileLength(String path) throws Exception {
HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);
conn.connect();
int responseCode = conn.getResponseCode();
if(responseCode == 200) {
int contentLength = conn.getContentLength();
conn.disconnect();
return contentLength;
}
return -1;
}

class DownloadThread extends Thread {

private String path;
private int startIndex;
private int endIndex;
private int threadID;

public DownloadThread(String path, int startIndex, int endIndex, int threadID) {
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadID = threadID;
this.path = path;
}

@Override
public void run() {
HttpURLConnection conn = null;
try {
// 开始下载前, 需要先判断是否是断点下载, 如果是. 继续下载
File progressFile = new File(Environment.getExternalStorageDirectory(), "线程" + threadID + ".cfg");
int total = 0;
if(progressFile.exists() && progressFile.length() > 0) {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(progressFile)));
total = Integer.valueOf(br.readLine());
br.close();
startIndex += total;
System.out.println(Thread.currentThread().getName() + "继续下载: " + startIndex);
}

conn = (HttpURLConnection) new URL(path).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
conn.connect();
int responseCode = conn.getResponseCode();
if(responseCode == 206) {
int contentLength = conn.getContentLength();
System.out.println(Thread.currentThread().getName() + "开始下载: length=" + contentLength);
InputStream is = conn.getInputStream();

File file = new File(Environment.getExternalStorageDirectory(), 
path.substring(path.lastIndexOf("/") + 1));
RandomAccessFile raf = new RandomAccessFile(file, "rw");

raf.seek(startIndex);

// 设置进度条
ProgressBar bar = (ProgressBar) llProgressBarGroup.getChildAt(threadID);
bar.setMax(contentLength);
bar.setProgress(total);


RandomAccessFile progressRAF = null;

byte[] buffer = new byte[1024 * 1024];
int len = -1;
while((len = is.read(buffer)) != -1) {
raf.write(buffer, 0, len);
total += len;
bar.setProgress(total);
// 终止下载时, 需要记住当前线程已下载了多少个字节. 便于下次下载时继续下载.
progressRAF = new RandomAccessFile(progressFile, "rwd");
progressRAF.write(String.valueOf(total).getBytes());
progressRAF.close();
}
is.close();
raf.close();
System.out.println(Thread.currentThread().getName() + ": 下载成功, startIndex=" 
    + startIndex + ", endIndex=" + endIndex);
}
} catch (Exception e) {
      e.printStackTrace();
} finally {
if(conn != null) {
    conn.disconnect();
      }

// 清除配置文件
stopThreadCount ++;
if(stopThreadCount == threadCount) {
handler.sendEmptyMessage(MainActivity.SUCCESS);
System.out.println("清除配置文件.");

    File file = null;
    for (int i = 0; i < threadCount; i++) {
file = new File(Environment.getExternalStorageDirectory(), "线程" + i + ".cfg");
      if(file.delete()) {
        System.out.println("删除成功: " + file.getName());
          }
    }
      }
    }
      }
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值