雖然網路上文章滿多的,但還是紀錄一下以免忘記。
會有這個問題產生主要是在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 傳送及接收其實不容易,看似簡單但很多細節如果沒有注意到就會失敗!