Android - Socket 功能在 Service 中實現【這才是實際的使用情況】


前幾天學習了 Android 下 Socket 編程,由於個人是剛開始學習 Android 相應的知識。所以特意將學習中的代碼與過程,寫成 BLOG,如:http://blog.csdn.net/91program/article/details/39177401
學習 Socket 編程是有目的的,需要完成在手機與 PC 之間的通訊。通訊的內容是將手機上播放的 MP3 信息,通過 Socket 傳輸到 PC 端。
在參考網上相關 Socket 的文章后,基本上完成了 Socket 功能。所以就繼續學習 Android 下音樂播放器的實現。在實現音樂播放器過程中,發現由於音樂播放器至少要有播放列表和正在播放兩個 Activity,這樣問題就來了:
(1). Socket 只是在第一個 Activity 中實現了,當這個 Activity 活動時沒有問題。但此 Activity 非活動時,不能處理 Socket。
(2). 當反復進入第一個 Activity 時,會出現 Socket 初始化報錯的問題。出現這樣的錯誤,是由於 Sokcet 的初始化放在第一個 Activity 的 onCreate 中。

由於在做音樂播放器時使用了 Service,所以想到用 Serivce 來處理 Socket 應該沒有問題。但是否有其它的方法呢?由於個人是剛剛接觸 Android 編程,就不能確定這個問題了!
在 CSDN Android 論壇提問,帖子:http://bbs.csdn.net/topics/390884423。得到的答案是:(1) Serivce; (2) 也可以更改activity的啟動方式,讓串口不重復創建。顯然,第二種方法還沒有接觸過。采用第一種 Serivce 來實現更可靠一些。

首先,實現 Socket Service。

 

package com.jia.leozhengfirstapp;


import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;


import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.util.Log;


public class SocketService extends Service {


  private Socket clientSocket = null;
  private ServerSocket mServerSocket = null;


  private SocketAcceptThread socketAcceptThread = null;
  private SocketReceiveThread socketReceiveThread = null;


  private SocketReceiver socketReceiver;


  public static final String SOCKER_ACTION = "com.jia.Socket.Control";
  public static final String SOCKER_RCV = "com.jia.Socket.ReceiveStr";


  private boolean stop = true;


  @Override
  public IBinder onBind(Intent intent) {
    // TODO Auto-generated method stub
    return null;
  }
   @Override
  public void onCreate() {
          super.onCreate();
          Log.d("service", "socket service created");
          socketReceiver = new SocketReceiver();
          IntentFilter filter = new IntentFilter();
          filter.addAction(SOCKER_ACTION);
          registerReceiver(socketReceiver, filter);


          socketAcceptThread = new SocketAcceptThread();
            // 開啟 Socket 監聽線程
            socketAcceptThread.start();
   }


  @Override
  public void onStart(Intent intent, int startId) {
     Log.d("service", "socket service start");


  }


  @Override
  public void onDestroy() {
  Log.d("service", "socket service destroy!");


  }


  public class SocketReceiver extends BroadcastReceiver {


    @Override
    public void onReceive(Context context, Intent intent) {
      String action = intent.getAction();
            if(action.equals(SOCKER_ACTION)) {
              String sub_action = intent.getExtras().getString("ACTION");
              if(sub_action.equals("reconnect")) {
                Log.d("service", "socket service: reconnect.");


                 socketAcceptThread = new SocketAcceptThread();
                  // 開啟 Socket 監聽線程
                  socketAcceptThread.start();
              }
            }
    }
  }


  private class SocketAcceptThread extends Thread
  {
       @Override
       public void run()
       {
         Log.d("service", "socket service - SocketAcceptThread::run");
           try {
               // 實例化ServerSocket對象並設置端口號為 12589
               mServerSocket = new ServerSocket(12589);
           } catch (IOException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }


           try {
               // 等待客戶端的連接(阻塞)
               clientSocket = mServerSocket.accept();
           } catch (IOException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }


           socketReceiveThread = new SocketReceiveThread(clientSocket);
           stop = false;
           // 開啟接收線程
           socketReceiveThread.start();


             Intent sendIntent = new Intent(SOCKER_RCV);
             sendIntent.putExtra("action", "ClientIP");
             sendIntent.putExtra("content", clientSocket.getInetAddress().getHostAddress());
             // 發送廣播,將被Activity組件中的BroadcastReceiver接收到
             sendBroadcast(sendIntent);
       }
   }


   private class SocketReceiveThread extends Thread
   {
       private InputStream mInputStream = null;
       private byte[] buf;
       private String str = null;
       Socket sUsed;


       SocketReceiveThread(Socket s)
       {
         Log.d("service", "socket service - SocketReceiveThread");
           try {
               // 獲得輸入流
               this.mInputStream = s.getInputStream();
               sUsed = s;
           } catch (IOException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }
       }


       @Override
       public void run()
       {
         Log.d("service", "socket service - SocketReceiveThread::run");
           while((!stop) && (!mServerSocket.isClosed()))
           {
               this.buf = new byte[2048];


               // 讀取輸入的數據(阻塞讀)
               try {
                   this.mInputStream.read(buf);
               } catch (IOException e1) {
                   // TODO Auto-generated catch block
                   e1.printStackTrace();
               }


               // 字符編碼轉換
               try {
                   this.str = new String(this.buf, "GB2312").trim();
               } catch (UnsupportedEncodingException e) {
                   // TODO Auto-generated catch block
                   e.printStackTrace();
               }


               Intent sendIntent = new Intent(SOCKER_RCV);
               sendIntent.putExtra("action", "RcvStr");
               sendIntent.putExtra("content", this.str);
               // 發送廣播,將被Activity組件中的BroadcastReceiver接收到
               sendBroadcast(sendIntent);
           }
       }
   }
}

 在每個 Activity 中處理 SOCKER_RCV action,以響應 Socket 狀態的變化和接收到數據。
Service 與 Activity 之間通訊需要使用到廣播: Broadcast。
(1) 在 Activity 中定義全局的變量,如下:

1 public static final String SOCKER_ACTION = "com.jia.Socket.Control";
2 public static final String SOCKER_RCV = "com.jia.Socket.ReceiveStr";
3 
4 SocketReceiver socketReceiver

(2) 在 Activity 的 onCreate 中注冊廣播和啟動 Socket Service,如下:

1 socketReceiver = new SocketReceiver();
2 IntentFilter socketIntentFilter = new IntentFilter();
3 socketIntentFilter.addAction(SOCKER_RCV);
4 registerReceiver(socketReceiver,socketIntentFilter);
5 
6 Intent socketIntent = new Intent();
7 socketIntent.setClass(MainActivity.this, SocketService.class);
8 startService(socketIntent);       // 啟動  Socket 服務

(3) SocketReceiver 是繼承自 BroadcastReceiver 的類,實現如下:

 1 public class SocketReceiver extends BroadcastReceiver {
 2 
 3 
 4   @Override
 5   public void onReceive(Context context, Intent intent) {
 6     // TODO Auto-generated method stub
 7     String action = intent.getAction();
 8           if(action.equals(SOCKER_RCV)) {
 9             String url = intent.getExtras().getString("action");
10             if(url.equals("ClientIP")) {
11               String strIP = intent.getExtras().getString("content");
12             }
13             else if(url.equals("RcvStr")) {
14               String strContent = intent.getExtras().getString("content");
15             }
16             else if(url.equals("Disconnect")) {
17               String strContent = intent.getExtras().getString("content");
18             }
19         }
20     }
21 }

(4) Socket 功能實現后,測試時發現客戶端(也就是 PC 端)斷開時手機端未檢測到 Socket 連接斷開。
以前使用 WinCE 時,Socket(TCP) 斷開時,無論是客戶端、還是服務器都可以檢測到 TCP 斷開的事件,並處理。但 Android 下的 Socket 編程機制竟然沒有這個東東。
一,測試時發現當 PC 端斷開后,手機端的服務程序在執行到下面的代碼段時不會阻塞,且函數的返回值是: -1。
二,在網上查找發現這個問題是 Android 下 Socket 都有的問題,可以通過發心跳包來處理。
所以將下面這段代碼:

 1 // 讀取輸入的數據(阻塞讀)
 2 try {
 3     this.mInputStream.read(buf);
 4 } catch (IOException e1) {
 5     // TODO Auto-generated catch block
 6     e1.printStackTrace();
 7 }
 8 修改為如下的代碼:
 9 try {
10     int length = this.mInputStream.read(buf);
11     if(-1 == length) {
12       try {
13         sUsed.sendUrgentData(0xff);
14       }
15       catch(Exception ex) {
16         // 鏈接已斷開
17         Log.v("service", "disconnect!!!");
18         stop = true;
19         if(null != mServerSocket) {
20           mServerSocket.close();
21         }
22 
23 
24         Intent sendIntent = new Intent(SOCKER_RCV);
25         sendIntent.putExtra("action", "Disconnect");
26         sendIntent.putExtra("content", "read is -1 & Urgent Exception!");
27         sendBroadcast(sendIntent);
28 
29 
30         continue;
31       }
32     }
33 } catch (IOException e1) {
34     // TODO Auto-generated catch block
35     e1.printStackTrace();
36 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM