android app之間使用socket做溝通
最近在製作一個兩個app之間能夠互相溝通並且傳遞一些指令的專案,這種專案適合用socket而不是URLconnection,原因如下。
A socket can implement almost a duplex kind of functionality, whereas a url connection can connect to a given URI and read its content.
經過一些研究之後了解到,要建立一個 Socket 連線,我們必須寫兩支程式,分別代表 Client 端以及 Server 端。在網路上搜尋到一個多人聊天室的範例程式,如下:
Server(Server可以在能夠執行java的環境下直接執行)
1
2
client(android studio)
1
2
3
這邊值得一提的是,開Socket Server的主機ip要如何獲得,port可以隨意指定,但是ip要如何獲得呢?這邊提供一個方式
1
搭配Server.java在MainAvticity加上上面這段,可以印出IP。順帶一提的是,使用Android Studio的模擬器的話,模擬器server的IP為10.0.2.15。兩台模擬器之間要互相連線的話可以參照其他網路上設定虛擬IP的方法。
在別得class創建一個新的activity
最後提一下小心得,Socket的應用於app的小心得,由於創建clinet thread的activity跟發送資料與收到資料時的activity通常不會一樣,我們必須要所有的activity收到資料都要能夠透過那個資料,做一些處理UI的事情,例如創建新的activity或是更改UI的值。要如何做到呢?必須要獲得thread中的變數。
我們可以在thread的類中使用static去宣告需要跨activity的變數來使用。以上。或許不好的方法,但解決了我現在的問題。
A socket can implement almost a duplex kind of functionality, whereas a url connection can connect to a given URI and read its content.
Server(Server可以在能夠執行java的環境下直接執行)
1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.IOException; | |
import java.net.ServerSocket; | |
import java.net.Socket; | |
import java.util.ArrayList; | |
public class ChatServer { | |
// クライアント接続Socketリスト | |
public static ArrayList<Socket> socketList = new ArrayList<Socket>(); | |
public static void main(String[] args) throws IOException { | |
// ポート番号31234でサーバ起動 | |
ServerSocket server = new ServerSocket(31234); | |
System.out.println("start"); | |
while(true) { | |
// 接続があった際 | |
Socket socket = server.accept(); | |
// リストに追加 | |
socketList.add(socket); | |
// クライアントから接続があった場合、別スレッドを起動する | |
new Thread(new ServerTread(socket)).start(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.io.OutputStream; | |
import java.io.UnsupportedEncodingException; | |
import java.net.Socket; | |
import java.net.SocketException; | |
import java.util.Iterator; | |
public class ServerTread implements Runnable{ | |
Socket socket = null; | |
BufferedReader br = null; | |
public ServerTread(Socket socket) throws UnsupportedEncodingException, IOException { | |
this.socket = socket; | |
// INPUTストリーム初期化 | |
br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8")); | |
} | |
@Override | |
public void run() { | |
try{ | |
String content = null; | |
while ((content = readFromClient()) != null) { | |
// サーバのソケットリストを繰り返す | |
for (Iterator<Socket> it = ChatServer.socketList.iterator(); it.hasNext();) { | |
Socket s = it.next(); | |
try { | |
OutputStream os = s.getOutputStream(); | |
os.write((content + "\n").getBytes("utf-8")); | |
} catch (SocketException e) { | |
e.printStackTrace(); | |
// サーバのSocketリストから削除 | |
it.remove(); | |
} | |
} | |
} | |
} catch(Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
private String readFromClient() { | |
try { | |
return br.readLine(); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
// サーバのSocketリストから削除 | |
ChatServer.socketList.remove(socket); | |
} | |
return null; | |
} | |
} |
client(android studio)
1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<uses-permission android:name="android.permission.INTERNET"/> | |
<?xml version="1.0" encoding="utf-8"?> | |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
android:orientation="vertical" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<LinearLayout | |
android:orientation="horizontal" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content"> | |
<!-- チャット内容入力 --> | |
<EditText | |
android:id="@+id/input" | |
android:layout_width="280dp" | |
android:layout_height="wrap_content" /> | |
<Button | |
android:id="@+id/send" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:paddingLeft="8dp" | |
android:text="送信"/> | |
</LinearLayout> | |
<!-- サーバからのメッセージ受信 --> | |
<TextView | |
android:id="@+id/show" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:gravity="top" | |
android:background="#ffff" | |
android:textSize="14dp" | |
android:textColor="#f000"/> | |
</LinearLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.os.Message; | |
import android.support.v7.app.AppCompatActivity; | |
import android.os.Bundle; | |
import android.view.View; | |
import android.widget.Button; | |
import android.widget.EditText; | |
import android.widget.TextView; | |
import android.view.View.OnClickListener; | |
import android.os.Handler; | |
public class MainActivity extends AppCompatActivity { | |
EditText input; | |
TextView show; | |
Button send; | |
Handler handler; | |
// サーバと通信するスレッド | |
ClientThread clientThread; | |
@Override | |
public void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
// view取得 | |
input = (EditText) findViewById(R.id.input); | |
send = (Button) findViewById(R.id.send); | |
show = (TextView) findViewById(R.id.show); | |
handler = new Handler() | |
{ | |
@Override | |
public void handleMessage(Message msg) { | |
// サブスレッドからのメッセージ | |
if (msg.what == 0x123) { | |
// 表示する | |
show.append("\n" + msg.obj.toString()); | |
} | |
} | |
}; | |
clientThread = new ClientThread(handler); | |
// サーバ接続スレッド開始 | |
new Thread(clientThread).start(); | |
send.setOnClickListener(new OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
try { | |
// メッセージ送信 | |
Message msg = new Message(); | |
msg.what = 0x345; | |
msg.obj = input.getText().toString(); | |
clientThread.revHandler.sendMessage(msg); | |
// テキストクリア | |
input.setText(""); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.os.Handler; | |
import android.os.Looper; | |
import android.os.Message; | |
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.io.OutputStream; | |
import java.net.Socket; | |
import java.net.SocketTimeoutException; | |
public class ClientThread implements Runnable { | |
private Socket s; | |
private Handler handler; | |
public Handler revHandler; | |
BufferedReader br = null; | |
OutputStream os = null; | |
public ClientThread(Handler handler) { | |
this.handler = handler; | |
} | |
public void run() { | |
try { | |
s = new Socket("192.168.0.7", 31234); | |
br = new BufferedReader(new InputStreamReader( | |
s.getInputStream())); | |
os = s.getOutputStream(); | |
// スレッド起動 | |
new Thread() { | |
@Override | |
public void run() { | |
String content = null; | |
// Socketのinputストリーム読み取り | |
try { | |
while ((content = br.readLine()) != null) { | |
// Mainスレッドに通知 | |
Message msg = new Message(); | |
msg.what = 0x123; | |
msg.obj = content; | |
handler.sendMessage(msg); | |
} | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
}.start(); | |
// Lopper初期化 | |
Looper.prepare(); | |
revHandler = new Handler() { | |
@Override | |
public void handleMessage(Message msg) { | |
// UIスレッドメッセージ取得 | |
if (msg.what == 0x345) { | |
// サーバにチャット内容送信 | |
try { | |
os.write((msg.obj.toString() + "\r\n") | |
.getBytes("utf-8")); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
}; | |
// Looper起動 | |
Looper.loop(); | |
} catch (SocketTimeoutException e1) { | |
System.out.println("TIME OUT!!"); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
} |
這邊值得一提的是,開Socket Server的主機ip要如何獲得,port可以隨意指定,但是ip要如何獲得呢?這邊提供一個方式
1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.IOException; | |
import java.io.OutputStream; | |
import java.io.PrintStream; | |
import java.net.InetAddress; | |
import java.net.NetworkInterface; | |
import java.net.ServerSocket; | |
import java.net.Socket; | |
import java.net.SocketException; | |
import java.util.Enumeration; | |
public class Server { | |
MainActivity activity; | |
ServerSocket serverSocket; | |
String message = ""; | |
static final int socketServerPORT = 8080; | |
public Server(MainActivity activity) { | |
this.activity = activity; | |
Thread socketServerThread = new Thread(new SocketServerThread()); | |
socketServerThread.start(); | |
} | |
public int getPort() { | |
return socketServerPORT; | |
} | |
public void onDestroy() { | |
if (serverSocket != null) { | |
try { | |
serverSocket.close(); | |
} catch (IOException e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
} | |
} | |
private class SocketServerThread extends Thread { | |
int count = 0; | |
@Override | |
public void run() { | |
try { | |
serverSocket = new ServerSocket(socketServerPORT); | |
while (true) { | |
Socket socket = serverSocket.accept(); | |
count++; | |
message += "#" + count + " from " | |
+ socket.getInetAddress() + ":" | |
+ socket.getPort() + "\n"; | |
activity.runOnUiThread(new Runnable() { | |
@Override | |
public void run() { | |
activity.msg.setText(message); | |
} | |
}); | |
SocketServerReplyThread socketServerReplyThread = new SocketServerReplyThread( | |
socket, count); | |
socketServerReplyThread.run(); | |
} | |
} catch (IOException e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
} | |
} | |
} | |
private class SocketServerReplyThread extends Thread { | |
private Socket hostThreadSocket; | |
int cnt; | |
SocketServerReplyThread(Socket socket, int c) { | |
hostThreadSocket = socket; | |
cnt = c; | |
} | |
@Override | |
public void run() { | |
OutputStream outputStream; | |
String msgReply = "Hello from Server, you are #" + cnt; | |
try { | |
outputStream = hostThreadSocket.getOutputStream(); | |
PrintStream printStream = new PrintStream(outputStream); | |
printStream.print(msgReply); | |
printStream.close(); | |
message += "replayed: " + msgReply + "\n"; | |
activity.runOnUiThread(new Runnable() { | |
@Override | |
public void run() { | |
activity.msg.setText(message); | |
} | |
}); | |
} catch (IOException e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
message += "Something wrong! " + e.toString() + "\n"; | |
} | |
activity.runOnUiThread(new Runnable() { | |
@Override | |
public void run() { | |
activity.msg.setText(message); | |
} | |
}); | |
} | |
} | |
public String getIpAddress() { | |
String ip = ""; | |
try { | |
Enumeration<NetworkInterface> enumNetworkInterfaces = NetworkInterface | |
.getNetworkInterfaces(); | |
while (enumNetworkInterfaces.hasMoreElements()) { | |
NetworkInterface networkInterface = enumNetworkInterfaces | |
.nextElement(); | |
Enumeration<InetAddress> enumInetAddress = networkInterface | |
.getInetAddresses(); | |
while (enumInetAddress.hasMoreElements()) { | |
InetAddress inetAddress = enumInetAddress | |
.nextElement(); | |
if (inetAddress.isSiteLocalAddress()) { | |
ip += "Server running at : " | |
+ inetAddress.getHostAddress(); | |
} | |
} | |
} | |
} catch (SocketException e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
ip += "Something Wrong! " + e.toString() + "\n"; | |
} | |
return ip; | |
} | |
} |
new Thread(new Runnable() { @Override public void run() { ChatServer server = new ChatServer();
server.setContext(MainActivity.this);
server.start();
}
}).start();
搭配Server.java在MainAvticity加上上面這段,可以印出IP。順帶一提的是,使用Android Studio的模擬器的話,模擬器server的IP為10.0.2.15。兩台模擬器之間要互相連線的話可以參照其他網路上設定虛擬IP的方法。
在別得class創建一個新的activity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public void methodStartActivity(Context context,String s) { | |
Intent intent = new Intent(); | |
intent.setClass(context,MakeCafeLoading.class); | |
Bundle bundle = new Bundle(); | |
bundle.putString("name",s); | |
intent.putExtras(bundle); // 記得put進去,不然資料不會帶過去哦 | |
context.startActivity(intent); | |
} |
我們可以在thread的類中使用static去宣告需要跨activity的變數來使用。以上。或許不好的方法,但解決了我現在的問題。
留言
張貼留言