最近一段時間在斷斷續續地學習Android應用開發的一些知識,許多APP運行時都要服務器進行通信,socket可以很好完成這樣一個功能。我在網上找到了一些手機和服務器通過socket通信的例子,然後挑了其中兩個進行整合,供給像我一樣的Android socket初學者進行參考。
兩個例子的鏈接:
例子一:http://blog.csdn.net/wuchuanpingstone/article/details/6617276
例子二:http://blog.csdn.net/x605940745/article/details/17001641
我把兩個例子整合成以下內容:
一、Android socket使用時要注意的地方
1、在Android主線程中不能有訪問socket的操作,否則會報異常。
2、在Android工程配置文件中要進行網絡權限聲明: <uses-permission android:name="android.permission.INTERNET"/>
3、對數據進行發送接收操作時,首先獲取socket流,然後可以封裝成BufferedWriter和BufferedReader:
br = new BufferedReader(new InputStreamReader(s.getInputStream()));//把socket輸入流封裝成BufferedReader
os=new PrintWriter(new BufferedWriter(new OutputStreamWriter(s.getOutputStream())), true);//把socket輸出流封裝成PrintWriter
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">4、對socket進行讀寫之前還要判斷,連接是否還存在:</span>
if(!s.isClosed()&&s.isConnected()){
//進行socket讀寫操作<span style="white-space:pre">
}
5、由於在主線程中不能訪問網絡的操作,所以socket通信還涉及到多線程的知識點;同樣,UI控件的更新只能在主線程中進行,所以還涉及到Android消息處理機制相關知識點。
二、實例代碼
Android客戶端代碼:
AndroidManifest.xml配置文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.client"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission
android:name="android.permission.INTERNET"/>
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="21" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
activity_main:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<EditText
android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="to send to the server" />
<Button
android:id="@+id/send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="send"
android:layout_below="@id/input"/>
<TextView
android:id="@+id/show"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/send"/>
</LinearLayout>
MainActivity.java:
package com.example.client;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity {
// 定義界面上的兩個文本框
EditText input;
TextView show;
// 定義界面上的一個按鈕
Button send;
Handler handler;
// 定義與服務器通信的子線程
ClientThread clientThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
input = (EditText) findViewById(R.id.input);
show = (TextView) findViewById(R.id.show);
send = (Button) findViewById(R.id.send);
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 如果消息來自子線程
if (msg.what == 0x123) {
// 將讀取的內容追加顯示在文本框中
show.append("\n" + msg.obj.toString());
}
}
};
clientThread = new ClientThread(handler);
// 客戶端啓動ClientThread線程創建網絡連接、讀取來自服務器的數據
new Thread(clientThread).start();
send.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
// 當用戶按下按鈕之後,將用戶輸入的數據封裝成Message
// 然後發送給子線程Handler
Message msg = new Message();
msg.what = 0x345;
msg.obj = input.getText().toString();
clientThread.revHandler.sendMessage(msg);
input.setText("");
} catch (Exception e) {
}
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
ClientThread.java:
package com.example.client;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
public class ClientThread implements Runnable {
private Socket s;
// 定義向UI線程發送消息的Handler對象
Handler handler;
// 定義接收UI線程的Handler對象
Handler revHandler;
// 該線程處理Socket所對用的輸入輸出流
BufferedReader br = null;
//OutputStream os = null;
PrintWriter os = null;
public ClientThread(Handler handler) {
this.handler = handler;
}
@Override
public void run() { //接收數據線程是發送數據線程的子線程
s = new Socket();
try {
s.connect(new InetSocketAddress("121.248.52.236", 3000), 5000);
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
// os = s.getOutputStream();
os=new PrintWriter(new BufferedWriter(new OutputStreamWriter(s.getOutputStream())), true);
// 啓動一條子線程來讀取服務器相應的數據
new Thread(new Runnable() {
@Override
public void run() {
String content = null;
// 不斷的讀取Socket輸入流的內容
try {
while ((content = br.readLine()) != null) {
// 每當讀取到來自服務器的數據之後,發送的消息通知程序
// 界面顯示該數據
if(!s.isClosed()&&s.isConnected()){
Message msg = new Message();
msg.what = 0x123;
msg.obj = content;
handler.sendMessage(msg);
}
}
} catch (IOException io) {
io.printStackTrace();
}
}
}).start();
// 爲當前線程初始化Looper
Looper.prepare();
// 創建revHandler對象
revHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 接收到UI線程的中用戶輸入的數據
if (msg.what == 0x345) {
// 將用戶在文本框輸入的內容寫入網絡
try {
if(!s.isClosed()&&s.isConnected()){
//os.write((msg.obj.toString() + "\r\n").getBytes("gbk"));
os.println(msg.obj.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
// 啓動Looper
Looper.loop();
} catch (SocketTimeoutException e) {
Message msg = new Message();
msg.what = 0x123;
msg.obj = "網絡連接超時!";
handler.sendMessage(msg);
} catch (IOException io) {
io.printStackTrace();
}
}
}
Java服務端代碼:
MyService.java:
package com.android.net;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class MyService {
// 定義保存所有的Socket
public static List<Socket> socketList = new ArrayList<Socket>();
public static void main(String[] args) throws IOException {
System.out.println("Server start...");
ServerSocket server = new ServerSocket(3000);
while(true){
Socket s=server.accept();
socketList.add(s);
//每當客戶端連接之後啓動一條ServerThread線程爲該客戶端服務
new Thread(new ServiceThreada(s)).start();
}
}
}
ServiceThreada.java:
package com.android.net;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class ServiceThreada implements Runnable {
// 定義當前線程處理的Socket
Socket s = null;
// 該線程所處理的Socket所對應的輸入流
BufferedReader br = null;
public ServiceThreada(Socket s) {
this.s = s;
try {
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
String content = null;
//採用循環不斷的從Socket中讀取客戶端發送過來的數據
while((content=readFromClient())!=null){
//遍歷socketList中的每個Socket
//將讀取到的內容每個向Socket發送一次
// for(Socket s:MyService.socketList)
if(!s.isClosed()&&s.isConnected()){
//OutputStream os;
PrintWriter os=null;
try {
//os = s.getOutputStream();
os=new PrintWriter(new BufferedWriter(new OutputStreamWriter(
s.getOutputStream())), true);
if(content.equals("quit")){
System.out.println("user"+s.getInetAddress()+"offline");
//os.write(("From server:"+"user"+s.getInetAddress()+"offline"+"\n").getBytes("gbk"));
os.println("From server:user:"+s.getInetAddress()+"offline");
s.close();
}else{
System.out.println("From client:"+content);
//os.write(("From server:"+content+"\n").getBytes("gbk"));
os.println("From server:"+content);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
// 定義讀取客戶端的信息
public String readFromClient() {
try {
if(!s.isClosed()&&s.isConnected()){
return br.readLine();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}