你的位置:首页 > 操作系统

[操作系统]android菜鸟学习笔记24

主要是基于HTTP协议与服务端进行交互。

涉及到的类和接口有:URL、HttpURLConnection、HttpClient等

URL

 

使用一个String类型的url构造一个URL对象,如:

URL url = new URL(http://10.0.2.2/index.php);

 

 

openConnection()方法返回一个对指定url的资源的连接。返回类型是URLConnection,但是,由于这里我们一般用的是http协议,所以返回的实际是HttpURLConnection对象,故一般可以进行类型转换。

HttpURLConnection conn = (HttpURLConnection)url.openConnection();

 

HttpURLConnection:

继承自URLConnection,常用方法有:

 

getInputStream()方法用来获取用来从网络连接中读取数据的输入流。

 

getOutputStream()方法用于获取用于向网络连接中写入数据的输出流。

 

setDoOutput()和setDoInput()方法分别用来设置是否允许向网络写入或读取数据。

 

setReadTimeout()方法用于设置读取数据超时时限。

 

setConnectTimeout()方法用于设置网络连接超时时限。

 

setRequestMethod()方法用于设置请求的方法,一般常用”GET”或”POST”。

 

getResponseCode()和getResponseMessage()方法分别用来获取服务端的响应码及响应信息字符串描述。

 

Disconnect()方法用于关闭网络连接。

1.使用HttpURLConnection与服务端进行交互

布局文件代码:

 1 <LinearLayout ="http://schemas.android.com/apk/res/android" 2  3   ="http://schemas.android.com/tools" 4  5   android:layout_width="match_parent" 6  7   android:layout_height="match_parent" 8  9   android:orientation="vertical"10 11   tools:context="cn.csc.start.MainActivity" >12 13   <EditText14 15     android:id="@+id/et_username"16 17     android:layout_width="match_parent"18 19     android:layout_height="wrap_content"20 21     android:hint="@string/et_username_text"/>22 23   <EditText24 25     android:id="@+id/et_password"26 27     android:layout_width="match_parent"28 29     android:layout_height="wrap_content"30 31     android:inputType="textPassword"32 33     android:hint="@string/et_password_text"/>34 35   <Button36 37     android:id="@+id/btn_login"38 39     android:layout_width="wrap_content"40 41     android:layout_height="wrap_content"42 43     android:text="@string/btn_text"44 45     />46 47 </LinearLayout>

一个简单的php文件,模拟服务器端:

 1 <?php 2  3 if(isset($_REQUEST["username"])&& isset($_REQUEST["password"])){ 4  5    $username = $_REQUEST["username"]; 6  7    $password = $_REQUEST["password"]; 8  9    if($username == "zhangsan" && $password == "123"){10 11      echo "login success";12 13    }else{14 15      echo "login failure";16 17    }18 19 }else{20 21    echo "no field allowed empty string";22 23 }24 25 ?>

客户端提交用户名密码,客户端返回登录的结果。

 注意,在模拟器中用IP:10.0.2.2代表电脑的IP地址。

1.1使用GET方式与服务端进行交互

 1 public class MainActivity extends ActionBarActivity implements OnClickListener { 2  3  4  5   protected static final int OK = 0; 6  7    protected static final String TAG = "MYLOG"; 8  9    private EditText et_username;10 11    private EditText et_password;12 13    @Override14 15   protected void onCreate(Bundle savedInstanceState) {16 17     super.onCreate(savedInstanceState);18 19     setContentView(R.layout.activity_main);20 21     Button btn = (Button) findViewById(R.id.btn_login);22 23     btn.setOnClickListener(this);24 25     et_username = (EditText) findViewById(R.id.et_username);26 27     et_password = (EditText) findViewById(R.id.et_password);28 29 }30 31  @Override32 33  public void onClick(View view) {34 35     // TODO Auto-generated method stub36 37     if(view.getId() == R.id.btn_login){38 39        get_test();40 41     }42 43  }44 45 private void get_test() {46 47     // TODO Auto-generated method stub48 49     String username = et_username.getText().toString();50 51     String password = et_password.getText().toString();52 53     try {54 55       URL url = new URL("http://10.0.2.2/index.php?username="+username+"&password="+password);56 57       HttpURLConnection conn = (HttpURLConnection) url.openConnection();58 59       conn.setRequestMethod("GET");60 61       conn.setConnectTimeout(5000);62 63       conn.setReadTimeout(5000);64 65       if(conn.getResponseCode() == 200){66 67          InputStream is = conn.getInputStream();68 69          BufferedReader br = new BufferedReader(new InputStreamReader(is));70 71          String result = br.readLine();72 73          Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();74 75           conn.disconnect();76 77        }78 79       80 81     } catch (Exception e) {82 83       // TODO Auto-generated catch block84 85        e.printStackTrace();86 87     }88 89  }90 91 }

1.2使用POST方式与服务端进行交互

添加一个post_test方法:

 1 private void post_test(){ 2  3     try { 4  5       URL url = new URL("http://10.0.2.2/index.php"); 6  7       HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 8  9       conn.setRequestMethod("POST");10 11       conn.setDoOutput(true);12 13       conn.setConnectTimeout(5000);14 15       conn.setReadTimeout(5000);16 17       String username = et_username.getText().toString();18 19       String password = et_password.getText().toString();20 21       OutputStream os = conn.getOutputStream();22 23       String req = "username="+username+"&password="+password;24 25       26 27        os.write(req.getBytes());28 29       if(conn.getResponseCode()== 200){30 31          InputStream is = conn.getInputStream();32 33          BufferedReader br = new BufferedReader(new InputStreamReader(is));34 35          String result = br.readLine();36 37          Toast.makeText(this, result, Toast.LENGTH_LONG).show();38 39           is.close();40 41           os.close();42 43           conn.disconnect();44 45        }       46 47     } catch (Exception e) {48 49       // TODO Auto-generated catch block50 51        e.printStackTrace();52 53     }54 55 }

然后在onClick()中调用该方法。

注意到,这里由于只有一个线程,即在主线程(UI线程)中请求服务器,若有耗时操作,此时应用是不会响应用户操作的,这就是传说中的ANR异常

所以,就必须新建一个子线程执行耗时操作。

修改get_test()方法,新建一个线程执行该方法中的操作:

 1 private void get_test() { 2  3     new Thread(new Runnable() { 4  5        @Override 6  7       public void run() { 8  9          String username = et_username.getText().toString();10 11          String password = et_password.getText().toString();12 13          try {14 15             URL url = new URL("http://10.0.2.2/index.php?username="+username+"&password="+password);16 17             HttpURLConnection conn = (HttpURLConnection) url.openConnection();18 19             conn.setRequestMethod("GET");20 21             conn.setConnectTimeout(5000);22 23             conn.setReadTimeout(5000);24 25             if(conn.getResponseCode() == 200){26 27                InputStream is = conn.getInputStream();28 29                BufferedReader br = new BufferedReader(new InputStreamReader(is));30 31                String result = br.readLine();32 33                Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();34 35                conn.disconnect();36 37             }38 39            40 41          } catch (Exception e) {42 43             // TODO Auto-generated catch block44 45             e.printStackTrace();46 47           }48 49        }50 51     }).start();52 53  }

这时重新运行程序,会发现新的问题:

提示,无法在非UI线程中进行UI操作,因为这样不安全。

为了解决这个问题,android提供了一套消息处理机制,很像win32中的消息循环机制。

下面简单说明下这套机制:

 

引入消息机制后:

 

这套消息机制主要涉及到两个类:HandlerMessage

Handler

一般需要自定义Handler继承Handler类,并重写消息处理方法,一般将其对象在UI线程中定义,这样就可以在其消息处理方法中进行UI操作了。

常用方法:

 

消息处理回调函数,一般自定义的Handler类都要重写该方法,添加自己需要的消息处理业务逻辑。

 

sendMessage(Message msg)发送一条消息。

Message

消息类,其对象作为消息实体被handler对象发送或者接收处理。

 

使用无参构造创建一个消息对象,一般在发送之前还需要设置其两个主要的属性:

 

what属性,可以用来存放消息的类型

 

obj属性,可以用来存放想要传送的消息内容或者传递的其他参数等。

 

修改上面的应用,添加消息处理机制:

在MainActivity中添加个Handler类型的私有属性:

 1 private Handler handler = new Handler(){ 2  3      public void handleMessage(android.os.Message msg) { 4  5         if(msg.what == OK){ 6  7            String str = msg.obj.toString(); 8  9            Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();10 11           12 13          }14 15       }16 17 };

重写的消息处理函数,很简单,就是把服务端返回的信息通过Toast显示出来。

添加一个方法,新建线程请求服务端数据,并通过消息机制发送消息:

 1 private void multi_thread_test(){ 2  3      new Thread(new Runnable() { 4  5          6  7          @Override 8  9         public void run() {10 11            // TODO Auto-generated method stub12 13            try {14 15               URL url = new URL("http://10.0.2.2/index.php");16 17               HttpURLConnection conn = (HttpURLConnection) url.openConnection();18 19               conn.setRequestMethod("POST");20 21               conn.setDoOutput(true);22 23               conn.setConnectTimeout(5000);24 25               conn.setReadTimeout(5000);26 27               String username = et_username.getText().toString();28 29               String password = et_password.getText().toString();30 31               OutputStream os = conn.getOutputStream();32 33               String req = "username="+username+"&password="+password;34 35              36 37               os.write(req.getBytes());38 39               if(conn.getResponseCode()== 200){40 41                  InputStream is = conn.getInputStream();42 43                  BufferedReader br = new BufferedReader(new InputStreamReader(is));44 45                  String result = br.readLine();46 47                  Message msg = new Message();48 49                  msg.what = OK;50 51                  msg.obj = result;52 53                  handler.sendMessage(msg);54 55                  is.close();56 57                  os.close();58 59                  conn.disconnect();60 61               }          62 63            } catch (Exception e) {64 65               // TODO Auto-generated catch block66 67               e.printStackTrace();68 69            }70 71          }72 73       }).start();74 75 }

 

这样,在请求服务端数据时,应用还能响应用户的交互。

 

2.还有另一种与服务端进行交互的方式,那就是使用HttpClient

一般通过:

 HttpClient client = new defaultHttpClient() 构造出HttpClient对象。

HttpClient类最主要的一个方法就是execute()方法。

 

常用的是只有一个参数的重载形式,注意到其参数类型是HttpUriRequest类型。

查看帮助手册:

 

HttpUriRequest是一个接口

 

已知的直接实现类,有那么多个,最常用的是HttpGet和HttpPost,从名字中便可知道,一个用于get方式与服务端交互,一个用于post方式与服务端交互。

HttpGet的使用比较简单

 

一般直接使用传入String类型的url构造出HttpGet对象,然后作为execute()的参数即可完成get方式请求。

HttpPost的使用要稍微麻烦一点,因为GET方式直接在URL中传入了请求参数,而POST方式需要另外一种传参方式。

POST方式的参数需要使用一个List<NameValuePair>类型的对象传递,

NameValuePair是一个接口,一般使用的是其直接实现类BasicNameValuPair类:

如:

1 List<NameValuePair> params = new ArrayList<NameValuePair>();2 3 params.add(new BasicNameValuePair("username", username));4 5 params.add(new BasicNameValuePair("password", password));

要POST的参数存储完成,如何通过HttpPost提交呢?

注意到,帮助手册中关于HttpPost的信息:

 

HttpPost是通过继承HttpEntitiyEnclosingRequestBase类间接实现HttpUriRequest接口的。

该类中有一个方法:

 

可以通过setEntity()方法将参数传递给HttpPost对象,具体做法如下:

1 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params,"utf-8");2 3 httppost.setEntity(entity);

下面在MainActivity中添加方法,通过HttpClient方法与服务端进行交互:

 1 private void client_get_test(){ 2  3      new Thread(new Runnable() { 4  5          6  7          @Override 8  9         public void run() {10 11            // TODO Auto-generated method stub12 13            String username = et_username.getText().toString();14 15            String password = et_password.getText().toString();16 17            HttpClient client = new DefaultHttpClient();18 19            HttpGet get = new HttpGet("http://10.0.2.2/index.php?username="+username+"&password="+password);20 21            try {22 23               HttpResponse response = client.execute(get);24 25               if(response.getStatusLine().getStatusCode() == 200){26 27                  HttpEntity entity = response.getEntity();28 29                  String result = EntityUtils.toString(entity, "utf-8");30 31                  Message msg = new Message();32 33                  msg.what = OK;34 35                  msg.obj = result;36 37                  handler.sendMessage(msg);38 39               }40 41            } catch (ClientProtocolException e) {42 43               // TODO Auto-generated catch block44 45               e.printStackTrace();46 47            } catch (IOException e) {48 49               // TODO Auto-generated catch block50 51               e.printStackTrace();52 53            }54 55          }56 57       }).start();58 59    }

 

使用HttpClient采用GET方式向服务端请求数据

 1 private void client_post_test(){ 2  3      new Thread(new Runnable() { 4  5          6  7          @Override 8  9         public void run() {10 11            // TODO Auto-generated method stub12 13           14 15            try {16 17               String username = et_username.getText().toString();18 19               String password = et_password.getText().toString();20 21               HttpClient client = new DefaultHttpClient();22 23               HttpPost post = new HttpPost("http://10.0.2.2/index.php");24 25               List<NameValuePair> params = new ArrayList<NameValuePair>();26 27               params.add(new BasicNameValuePair("username", username));28 29               params.add(new BasicNameValuePair("password", password));30 31               UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params,"utf-8");32 33               post.setEntity(entity);34 35               HttpResponse response = client.execute(post);36 37               if(response.getStatusLine().getStatusCode() == 200){38 39                  HttpEntity entity1 = response.getEntity();40 41                  String result = EntityUtils.toString(entity1, "utf-8");42 43                  Message msg = new Message();44 45                  msg.what = OK;46 47                  msg.obj = result;48 49                  handler.sendMessage(msg);50 51               }52 53            } catch (ClientProtocolException e) {54 55               // TODO Auto-generated catch block56 57               e.printStackTrace();58 59            } catch (IOException e) {60 61               // TODO Auto-generated catch block62 63               e.printStackTrace();64 65            }66 67          }68 69       }).start();70 71 }

使用HttpClient采用POST方式向服务端请求数据。

以上就是向服务端请求数据的简单学习。