安卓 网络编程

第九章 网络编程

安卓系统提供了以下几种方式实现网络通信:Socket通信 , HTTP通信 ,URL通信和WebView。

其中最常用的就是HTTP通信。本章介绍如何在手机端使用HTTP协议与服务器端相互通信。

9.1 HTTP协议简介

​ HTTP 即超文本传输协议,它规定了浏览器和服务器之间通信的规则。

​ HTTP是一种 请求/ 相应式的协议,当客户端与服务器段建立连接后,向服务器发送的请求称作HTTP请求。服务器收到请求后会做出响应,称为HTTP响应。

9.2 访问网络

​ 安卓对HTTP通信提供了很好的支持,通过标准的java类HttpURLConnection便可实现基于基于URL的请求和响应功能。HttpURLConnection继承自URLConnection,用它可以发送和接收任何类型和长度的数据,也可以设置请求方式。也可以设置请求方式与超时时间。

9.2.1 HttpURLConnection

URL url = new URL("http://itcast.cn"); //获得要访问资源的url对象
HttpURLConnection conn = (HttpURLConnection) url.openConnection();//获取连接
conn.setRequestMethod("GET");//设置请求方式
conn.setConnectTimeout(5000);//设置超时时间
InputStream is = conn.getInputStream();//获取服务器返回的输入流
conn.disconnect();//关闭连接

上面演示了与服务器建立连接并获取服务器返回的数据的过程。需要注意的是,在使用HttpURLConnection对象访问网络时需要设置超时时间,以防止连接被阻塞时无响应,影响用户体验。

9.2.2 GET 与 POST请求方式

请求方式是指请求指定资源的不同方式。

  1. GET方式提交数据

    GET方式是以实体的方式得到请求URL所指向的资源信息,它向服务器提交的参数跟在请求URL后面。使用GET方式访问网络URL的长度一般要小于1KB。

  2. POST方式发出请求,需要在请求后附加实体。它向服务器提交的参数在请求后的实体中,POST方式对URL长度是没有限制的。使用POST方式请求时,请求参数跟在请求实体中。用户不能在浏览器中看到向服务器提交的请求参数,因此POST相对GET方式安全。

    String path = "http://192.168.1.100:8080/web/LoginServlet";
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            conn.setConnectTimeout(5000);
            conn.setRequestMethod("POST");
            //准备数据并给参数编码
            String data = "username = "+ URLEncoder.encode("zhangsan")+"&password="+URLEncoder.encode("123");
            //设置请求头数据提交方式,这里是以form表单的方式提交
            conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
            //设置提交数据的长度
            conn.setRequestProperty("Content-Length",data.length()+"");
            //post方式,实际上是浏览器把数据写给了服务器
            conn.setDoOutput(true); //设置允许向外写数据
            OutputStream os = conn.getOutputStream();
            os.write(data.getBytes()); //将参数写给服务器
            int code = conn.getResponseCode();
            if(code == 200) {
                InputStream is = conn.getInputStream();
            }
    

    注意:提交中文是容易出现乱码,一定要注意编码方式与解码方式是相匹配的

9.2.3Handler消息机制

​ 当程序启动时,安卓首先会开启一个UI线程(主线程),UI线程负责管理UI界面的控件,并进行事件分发。例如,当单击UI界面的Button时安卓会分发事件到Button上,来响应要执行的操作,如果执行的是耗时操作,比如访问网络读取数据,并将获取到的结果返回到UI界面上,此时就会出现假死现象,如果5s还没有完成,会收到安卓系统的一个错误提示“ 强制关闭 ”。这时,初学者会想到把那些操作放到子线程中完成,但在安卓中更新UI界面只能在主线程中完成,其他线程是无法直接对主线程操作的。

​ 为了解决以上问题,安卓提供了一种异步回调机制Handler,由Handler来负责与主线程进行通信。一般情况下,在主线程中绑定了Handler对象,并在事件触发上面创建子线程用于完成某些耗时操作,当子线程中的工作完成后,会向Handler发送一个已完成的信号(Message对象),当Handler接收到信号后,会对主线程UI进行更新操作。

​ Handler机制主要包括4个关键对象,分别是Message,Handler,MessageQueue,Looper。下面对这四个关键对象进行简要介绍。

  1. Message

    Message是在线程之间传递的消息,它可以在内部携带少量信息,用于在不同的线程之间交换信息。Meessage的what字段可以用来携带一些整型数据,obj字段可以用来携带一个Object对象。

  2. Handler

    Handler是处理者的意思,它主要用于发送消息和处理消息。一般使用Handler对象的sendMessage方法发送消息,发送的消息经过一系列的处理之后,最终会传递到Handler对象的handlerMessage方法中。

  3. MessageQueue

    MessageQueue是消息队列的意思,它主要用来存放通过Handler发送的消息。通过Handler发送的消息会存在MessageQueue中等待处理,每个线程只有一个MessageQueue对象。

  4. Looper

    Looper是每个线程中MessageQueue的管家。调用Looper的loop方法后,就会进到一个无限循环中。每当发现MessageQueue中存在一条消息,就将它取出,并传递到Handler的handlerMessage方法中。此外,每个线程也只会有一个Looper对象。在主线中创建Handler对象时,系统已经默认存在一个Looper对象,所以不用手动创建。而在子线程中的Handler对象需要调用Looper.loop方法开启消息循环。

9.2.4 实战演练----网络图片浏览器

  1. 创建程序

    创建一个ImageView程序,布局代码如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/line1"
                android:orientation="horizontal"
                android:layout_marginTop="5dp">
                <EditText
                    android:layout_width="0dp"
                    android:layout_weight="5"
                    android:layout_height="wrap_content"
                    android:id="@+id/et1"
                    android:hint="请输入路径"></EditText>
                <Button
                    android:layout_width="0dp"
                    android:layout_weight="1"
                    android:layout_height="wrap_content"
                    android:text="浏览"
                    android:textSize="19sp"
                    android:id="@+id/bt1"
                    android:onClick="click"></Button>
            </LinearLayout>
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_below="@+id/line1">
                
            </ImageView>
        </RelativeLayout>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  2. 编写界面交互代码

    使用HttpURLConnection获取指定地址的网络图片,并将服务器返回的图片展示在界面上,代码如下:

    package cn.luoxin88.imageview;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.Manifest;
    import android.content.pm.PackageManager;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.text.TextUtils;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.ImageView;
    import android.widget.Toast;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class MainActivity extends AppCompatActivity {
    
        protected static final int CHANGE_UI = 1;
        protected static final int ERROR = 2;
        private static final int REQUEST_CODE_ASK_PERMISSIONS = 123;
        private static EditText et;
        private static ImageView iv;
        //主线程创建消息处理器
        private static Handler handler = new Handler() {
            public void handlerMessage(android.os.Message msg) {
                if(msg.what == CHANGE_UI){
                    Bitmap bitmap = (Bitmap)msg.obj;
                    iv.setImageBitmap(bitmap);
                }
                else if(msg.what == ERROR) {
                    // Toast.makeText(MainActivity.this,"显示图片错误",Toast.LENGTH_SHORT).show();
                }
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            et = (EditText)findViewById(R.id.et1);
            iv = (ImageView) findViewById(R.id.iv1);
        }
        public void click(View view) {
            final String path = et.getText().toString().trim();
            if(TextUtils.isEmpty(path)) {
                Toast.makeText(this,"图片路径不能为空",Toast.LENGTH_SHORT).show();
            }
            else {
                System.out.println(path);
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
                    int hasReadSmsPermission = checkSelfPermission(Manifest.permission.INTERNET);
                    if (hasReadSmsPermission != PackageManager.PERMISSION_GRANTED) {
                        requestPermissions(new String[]{Manifest.permission.READ_SMS}, REQUEST_CODE_ASK_PERMISSIONS);
                        return;
                    }
                }
                System.out.println(1);
                //子线程访问网络,安卓4.0后访问网络不能放在主线程
                new Thread() {
                    private HttpURLConnection conn;
                    private Bitmap bitmap;
                    public void run() {
                        //连接服务器GET请求获取图片
                        try {
                            URL url = new URL(path);
                            conn = (HttpURLConnection) url.openConnection();
                            conn.setRequestMethod("GET");
                            conn.setConnectTimeout(5000);
                            int code = conn.getResponseCode();
                            System.out.println(code);
                            if(code == 200) {
                                InputStream is = conn.getInputStream();
                                //将流转换为Bitmap对象
                                bitmap = BitmapFactory.decodeStream(is);
                                //将更改主界面的消息发给主线程
                                Message message = new Message();
                                message.what = CHANGE_UI;
                                message.obj = bitmap;
                                handler.sendMessage(message);
                            }
                            else {
                                //请求服务器失败
                                Message msg = new Message();
                                msg.what = ERROR;
                                handler.sendMessage(msg);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                            Message msg = new Message();
                            msg.what = ERROR;
                            handler.sendMessage(msg);
    
                        }
                        conn.disconnect();
                    }
                }.start();
            }
        }
    }
    
    
  3. 添加权限

    由于网络图片需要访问网络,因此需要在Manifest文件中配置INTENET权限

    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    
  4. 运行程序

    输入https://www.photophoto.cn/m6/018/030/0180300388

上一篇:JDBC整理


下一篇:Android 网络:使用URLConnection提交请求,获取html代码加载WebView