雖然網路上文章滿多的,但還是紀錄一下以免忘記。
會有這個問題產生主要是在Android 4.0版本以上才會出現,確切原因不清楚,但應該是增加安全
性理由所以才修改成這樣的(?)
這問題就是如同字面上意思:
你在主線程上面呼叫Network類相關API
這時候該如何解決呢??
1)使用分流法(開個新的Thread)來操作網路,但如果我就是要調整並顯示UI介面呢?
2)在setContentView(R.layout.activity_main)下面新增
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
即可解決該問題
2014年8月11日 星期一
[Android/Java] Thread(執行緒)的使用+UDP Send / Receive+Socket
在使用Thread的時候,必須要先知道執行緒(Thread)是甚麼?
否則只是看看範例,卻只得其一不得其二。
執行緒是一種可以在程式背景執行的一個Process(程序),同時可以有多個執行緒一起執行。
由於可以多線程,因此大大增加可用性。
Thread的結構大概就是
public class ChatServer extends Thread{
private DatagramSocket server = null;
private static final int PORT = 4000;
//宣告變數
public ChatServer() throws IOException {
server = new DatagramSocket(PORT);
Log.d("User","new server socket");
}
//建構子
public void run(){
byte[] byte100 = new byte[100];
DatagramPacket dPacket = new DatagramPacket(byte100, 100);
String txt;
try{
Log.d("User","runing run()");
while(true){
server.receive(dPacket);
Log.d("User","While 1");
while(true)
{
//印出來到螢幕上
Log.d("User","While 2");
if(true) break;
}
//CloseSocket(client);//關閉
}
}
catch(IOException e)
{}
}
//主要在跑的地方(主程式)
在呼叫Thread的時候,以上面範例為例,我們呼叫時使用:
chatserver = new ChatServer();
new這個class,他會跑進建構子裡面,但還不會進去run()函式
chatserver.start();
執行run()這個函式,由於run裡面有while()迴圈,所以會一直跑不停
(在這兩行外面要夾try catch否則會有error)
-----------------------------------------------------------------------------------------------------------
上面解釋完Thread了,下面要講的是如何在Android上面使用執行緒的概念做UDP的發送與接收,
但這目前有個問題就是模擬器(emulator)與電腦的連線還沒有試做成功,在Emulator-Emulator是可
以成功傳送-接收的。
傳送部分:
目標:寫一個Thread,讓他不停傳送給指定IP+指定Port
內容:
public class ChatSender extends Thread{
public void run(){
DatagramSocket socket = null;
try
{
socket = new DatagramSocket();
InetAddress serverAddress = InetAddress.getByName("127.0.0.1");
Log.d("IP Address", serverAddress.toString());
String str = "Test Thread sender";
//創建一個用於發送的DatagramPacket對象
DatagramPacket packet=new DatagramPacket(str.getBytes(),str.length(),serverAddress,4000);
//發送數據
while(true)
{
socket.send(packet);
Thread.sleep(1000);
}
}
catch(SocketException e)
{
e.printStackTrace();
String error = e.toString();
Log.e("Error by Sender", error);
}
catch(UnknownHostException e)
{
e.printStackTrace();
String error = e.toString();
Log.e("Error by Sender", error);
}
catch(IOException e)
{
e.printStackTrace();
String error = e.toString();
Log.e("Error by Sender", error);
}
catch(Exception e)
{
e.printStackTrace();
String error = e.toString();
Log.e("Error by Sender", error);
}
finally{
if(socket != null){
socket.close();
}
}
}
}
否則只是看看範例,卻只得其一不得其二。
執行緒是一種可以在程式背景執行的一個Process(程序),同時可以有多個執行緒一起執行。
由於可以多線程,因此大大增加可用性。
Thread的結構大概就是
public class ChatServer extends Thread{
private DatagramSocket server = null;
private static final int PORT = 4000;
//宣告變數
public ChatServer() throws IOException {
server = new DatagramSocket(PORT);
Log.d("User","new server socket");
}
//建構子
public void run(){
byte[] byte100 = new byte[100];
DatagramPacket dPacket = new DatagramPacket(byte100, 100);
String txt;
try{
Log.d("User","runing run()");
while(true){
server.receive(dPacket);
Log.d("User","While 1");
while(true)
{
//印出來到螢幕上
Log.d("User","While 2");
if(true) break;
}
//CloseSocket(client);//關閉
}
}
catch(IOException e)
{}
}
//主要在跑的地方(主程式)
在呼叫Thread的時候,以上面範例為例,我們呼叫時使用:
chatserver = new ChatServer();
new這個class,他會跑進建構子裡面,但還不會進去run()函式
chatserver.start();
執行run()這個函式,由於run裡面有while()迴圈,所以會一直跑不停
(在這兩行外面要夾try catch否則會有error)
-----------------------------------------------------------------------------------------------------------
上面解釋完Thread了,下面要講的是如何在Android上面使用執行緒的概念做UDP的發送與接收,
但這目前有個問題就是模擬器(emulator)與電腦的連線還沒有試做成功,在Emulator-Emulator是可
以成功傳送-接收的。
傳送部分:
目標:寫一個Thread,讓他不停傳送給指定IP+指定Port
內容:
public class ChatSender extends Thread{
public void run(){
DatagramSocket socket = null;
try
{
socket = new DatagramSocket();
InetAddress serverAddress = InetAddress.getByName("127.0.0.1");
Log.d("IP Address", serverAddress.toString());
String str = "Test Thread sender";
//創建一個用於發送的DatagramPacket對象
DatagramPacket packet=new DatagramPacket(str.getBytes(),str.length(),serverAddress,4000);
//發送數據
while(true)
{
socket.send(packet);
Thread.sleep(1000);
}
}
catch(SocketException e)
{
e.printStackTrace();
String error = e.toString();
Log.e("Error by Sender", error);
}
catch(UnknownHostException e)
{
e.printStackTrace();
String error = e.toString();
Log.e("Error by Sender", error);
}
catch(IOException e)
{
e.printStackTrace();
String error = e.toString();
Log.e("Error by Sender", error);
}
catch(Exception e)
{
e.printStackTrace();
String error = e.toString();
Log.e("Error by Sender", error);
}
finally{
if(socket != null){
socket.close();
}
}
}
}
大概可以看到DatagramSocket+DatagramPacket這兩個API,
DatagramSocket我的理解是用於Socket連線,而DatagramPacket則是負責傳送的封包內容。
*DatagramPacket(封包的byte[]陣列,封包的長度,IP位址,Port);
至於InetAddress的部分是為了要確定本機IP是多少。
接收部分:
目標:寫一個Thread,讓他不停到指定的Port接收封包,直到有接收到才印出來,之後繼續hold住等待封包傳送過來。
內容:
public class ChatServer extends Thread{
private DatagramSocket server = null;
private static final int PORT = 4000;
public ChatServer() throws IOException {
server = new DatagramSocket(PORT);
Log.d("User","new server socket");
}
public void run(){
byte[] byte100 = new byte[100];
DatagramPacket dPacket = new DatagramPacket(byte100, 100);
String txt;
try{
Log.d("User","runing run()");
while(true){
server.receive(dPacket);
Log.d("User","While 1");
while(true)
{
//印出來到螢幕上
Log.d("User","While 2");
if(true) break;
}
//CloseSocket(client);//關閉
}
}
catch(IOException e)
{}
}
private void CloseSocket(DatagramSocket socket) throws IOException{
socket.close();
}
}
目標:寫一個Thread,讓他不停到指定的Port接收封包,直到有接收到才印出來,之後繼續hold住等待封包傳送過來。
內容:
public class ChatServer extends Thread{
private DatagramSocket server = null;
private static final int PORT = 4000;
public ChatServer() throws IOException {
server = new DatagramSocket(PORT);
Log.d("User","new server socket");
}
public void run(){
byte[] byte100 = new byte[100];
DatagramPacket dPacket = new DatagramPacket(byte100, 100);
String txt;
try{
Log.d("User","runing run()");
while(true){
server.receive(dPacket);
Log.d("User","While 1");
while(true)
{
//印出來到螢幕上
Log.d("User","While 2");
if(true) break;
}
//CloseSocket(client);//關閉
}
}
catch(IOException e)
{}
}
private void CloseSocket(DatagramSocket socket) throws IOException{
socket.close();
}
}
UDP接收的部分同樣也需要DatagramSocket/DatagramPacket,不過剛剛是Send 現在則是receive
為了存放接收的資料,前面會先new一個byte[]陣列用以存放資料。
在server.receive(dPacket);這邊如果沒有資料進來的話,他不會往下跑,會一直等待在這邊,直到接
收到資料才會執行下一行。這是必須特別注意的!
MainActivity的部分:
public class MainActivity extends ActionBarActivity {
private Handler exHandler;
private TextView txv;
private ChatServer chatserver;
private ChatSender chatsender;
public static InetAddress IP;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txv = (TextView) findViewById(R.id.textView);
try{
//IP = InetAddress.getLocalHost();
chatserver = new ChatServer();
chatserver.start();
chatsender = new ChatSender();
chatsender.start();
Log.d("User","Thread start...");
}catch(Exception e)
{
String str = e.toString();
Log.e("Error by User", str);
}
}
protected void onPause(){
super.onPause();
if(chatserver != null)
{
if(!chatserver.isInterrupted())
{
chatserver.interrupt();
}
}
}
}
然後要記得AndroidManifest.xml這邊要增加INTERNET的Uses-Permission
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.thread_example"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.thread_example.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>
結論: UDP 傳送及接收其實不容易,看似簡單但很多細節如果沒有注意到就會失敗!