看这里:Android必备:Android Socket编程的了解与学习整理
最近学习Android的过程中,由于项目、业务等因素影响,服务端通过Socket进行通信,于是开始学习Socket编程,之前的开发中,很少涉及此 方面的知识学习,本篇就来简单的整理一下,通过Android客户端进行Socket登录的demo,来进行Adnroid Socket编程的学习。
在开始学习之前,先来了解一下Socket,以下内容来自百度百科:
通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。
首先,来介绍一下项目的设计,只包含两个UI布局文件:login.xml和main.xml,对应登录页和主页,登录页包含一个用户名的输入框和登录按 钮,点击登录按钮,登录按钮显示文字“正在连接,请稍候...”,通过Socket进行登录,并跳转到主页,如果用户名是admin,则在主页显示“登录 成功!”反之显示“登录失败!”。
下面是demo运行后的具体效果图:
login.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content" > <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:inputType="textPersonName" android:hint="请输入用户名" android:ems="10" android:id="@+id/loginName" /> <Button android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="登录" android:id="@+id/loginBtn" /> </LinearLayout>
main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:id="@+id/mainText" /> </LinearLayout>
当然通过Socket进行通信的时候,我们需要app拥有网络访问即Internet或Wifi的权限,将下面两行添加到AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.INTERNET"/>
下面开始进行实例的编写,本篇的学习基于TCP/ IP 进行Socket通信,说是Android Socket编程,其实使用的是java.net包下提供的ServerSocket和Socket类,这是一种比较底层的编程方式,Socket类用来建立客户端程序,ServerSocket用来建立服务端程序,首先来看服务端的代码:
package com.xx566.socket.server; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class SocketServer { private static ServerSocket serverSocket; public static void main(String[] args) throws IOException { serverSocket = new ServerSocket(8888); while (true) { final Socket socket = serverSocket.accept(); try { // 获取输入流 BufferedReader inputStream = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 获取输出流 PrintWriter outputStream = new PrintWriter(socket.getOutputStream()); // 读取输入 String readString = inputStream.readLine(); if ("admin".equals(readString)) { outputStream.println("登录成功!"); } else { outputStream.println("登录失败!"); } outputStream.flush(); // 关闭 outputStream.close(); inputStream.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
我们在8888端口,实例化了Socket服务端,通过永真循环,来等待客户端Socket的连接。accept()方法返回一个对应客户端的Socket,服务端读取客户端输入,如果输入的是"admin",则输出"登录成功!",反之,输出"登录失败!",接下来,我们主要来看一下客户端LoginActivity的编写,需要注意的是,在Android4.0系统以上的系统中,是不允许在主线程中执行网络相关的请求,否则会抛出NetworkOnMainThreadException异常,所以需要单独的线程向服务端发送Socket,完整代码如下:
package com.xx566.socket; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.EditText; import java.io.*; import java.net.Socket; public class LoginActivity extends Activity { private Button loginBtn; private EditText loginName; PrintWriter outputStream; BufferedReader inputStream; Socket socket; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { //通过handler处理接收到的消息 if (msg.what == 1) { //跳转到主页面,显示登录结果 Intent intent = new Intent(LoginActivity.this, MainActivity.class); intent.putExtra("result", msg.getData()); startActivity(intent); } } }; /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.login); //登录按钮点击事件 loginBtn = (Button) findViewById(R.id.loginBtn); loginName = (EditText) findViewById(R.id.loginName); loginBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //点击登录后,显示正在连接服务器 loginBtn.setText("正在连接,请稍候..."); loginBtn.setClickable(false); final String userName = loginName.getText().toString(); //通过Socket登录服务器,简单的传递用户名 new Thread() { @Override public void run() { //处理接收到的消息 Message message = new Message(); message.what = 1; Bundle bundle = new Bundle(); String result = ""; try { socket = new Socket("192.168.0.32", 8888); outputStream = new PrintWriter(socket.getOutputStream()); inputStream = new BufferedReader(new InputStreamReader(socket.getInputStream())); outputStream.println(userName); outputStream.flush(); //读取结果 while (true) { result = inputStream.readLine(); if (!"".equals(result)) { break; } } //关闭 inputStream.close(); outputStream.close(); socket.close(); } catch (Exception e) { result = "网络异常!"; } bundle.putString("result", result); message.setData(bundle); //传递消息 handler.sendMessage(message); } }.start(); } }); } @Override protected void onResume() { super.onResume(); loginBtn.setText("登录"); loginBtn.setClickable(true); } }
这里使用了Android Handler机制进行了线程间消息的传递,主要是接收服务端响应的结果,启动MainActicity。MainActivity里面显示登录结果,代码如下:
package com.xx566.socket; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends Activity { private TextView mainText; /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mainText = (TextView) findViewById(R.id.mainText); Bundle bundle = getIntent().getExtras().getBundle("result"); mainText.setText(bundle.getString("result")); } }
完整项目地址:http://git.oschina.net/realfighter/SocketDemo