總結一下后台服務編程,以及socket實現是一些注意事項。
1,service的生命周期:
//startService() 啟動Service 其生命周期為context.startService() ->onCreate()- >onStart()->Service running-->context.stopService() | ->onDestroy() ->Service stop
//對於bindService()啟動Service: context.bindService()->onCreate()->onBind()->Service running-->onUnbind() -> onDestroy() ->Service stop
2,怎么拿到service實例(binder接口)
3,startService()及bindService()使用
4,socket的accept,read,write阻塞解決
好吧,直接上代碼
GamepadSocketServer.java
package com.example.gamepaddemo.gamepadserver; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.URLDecoder; import org.json.JSONException; import org.json.JSONObject; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; /** * @author zhouxueliang * 1、接收連接請求,控制手柄界面 * 2、發送數據 */ public class GamepadSocketServer extends Service { public static final int GAMEPAD_REQUEST_START_ACTIVITY = 0; public static final int GAMEPAD_REQUEST_STOP_ACTIVITY = GAMEPAD_REQUEST_START_ACTIVITY +1; /** * 監聽端口 */ private static final int PORT = 24000; /** * SocketServer開關標志 */ public boolean isSocketServerStarted = false; /** * 客戶端Socket */ private Socket clientSocket = null; private DataInputStream cInputStream = null; private DataOutputStream cOutputStream = null; /** * Server Socket */ private ServerSocket serverSocket = null; /** * 線程切換使用handler */ private Handler workHandler = new Handler(); /** * 界面回調handler */ private Handler helperListener = null; /** * 服務啟動是啟動socket監聽 */ @Override public void onCreate() { startSocketServer(); super.onCreate(); } /** * 服務銷毀是是停止socket監聽 */ @Override public void onDestroy() { stopSocketServer(); helperListener = null; super.onDestroy(); } /** * 啟動socket監聽 */ public void startSocketServer() { if (!isSocketServerStarted) { try { /** * 啟動ServerSocket */ serverSocket = new ServerSocket(PORT); isSocketServerStarted = true; } catch (IOException e) { e.printStackTrace(); } /** * Accept Socket */ new Thread(new AcceptRunnable()).start(); } } /** * 停止socket監聽 */ public void stopSocketServer() { if (isSocketServerStarted && serverSocket != null) { try { isSocketServerStarted = false; if (clientSocket !=null && !clientSocket.isClosed()) { clientSocket.close(); clientSocket = null; } serverSocket.close(); serverSocket = null; } catch (IOException e) { e.printStackTrace(); } } } /** * 設置界面回調監聽者 */ public void setHelperListener(Handler listener) { helperListener = listener; } /** * Socket Accept 線程(非阻塞) */ public class AcceptRunnable implements Runnable { @Override public void run() { while (isSocketServerStarted) { try { /** * 監聽連接, 阻塞 */ final Socket client = serverSocket.accept(); /** * 回調主線程 */ workHandler.post(new Runnable() { @Override public void run() { onGetClient(client); } }); } catch (IOException e) { e.printStackTrace(); } } } } /** * Socket read 線程(非阻塞) */ public class RecieverRunnable implements Runnable { private boolean runnable = true; @Override public void run() { while (runnable) { String inputString = null; int len = 0; try { /** * 等待read數據包大小,阻塞 */ len = cInputStream.readInt(); /** * read到數據包大小,read數據,阻塞 */ if (len > 0) { byte[] input = new byte[len]; int l = -1; int readlen = 0; while(len-readlen > 0 && (l = cInputStream.read(input, readlen , len-readlen)) != -1){ readlen += l; } inputString = URLDecoder.decode(new String(input), "UTF-8"); } } catch (IOException e) { runnable = false; e.printStackTrace(); } /** * 解析請求 */ final RequestInfo requestInfo = parseInputString(inputString); if (requestInfo != null) { /** * read請求,回調主線程 */ workHandler.post(new Runnable() { @Override public void run() { onGetRequest(requestInfo); } }); } } } } /** * accept請求,回調主線程 */ private void onGetClient(Socket client) { if(initClientSocket(client)==0) { ; } } /** * read(request)請求,回調主線程 */ private void onGetRequest(RequestInfo info) { if(info != null) { if (info.target.equals("GamepadRequest")) { if (info.type == 0) { stopGamepadActivity(info); } else { startGamepadActivity(info); } } } } /** * 請求拉起界面 */ private void startGamepadActivity(RequestInfo info) { if (helperListener !=null) { Message message = helperListener.obtainMessage(GAMEPAD_REQUEST_START_ACTIVITY, info); helperListener.sendMessage(message); } } /** * 請求關閉界面 */ private void stopGamepadActivity(RequestInfo info) { if (helperListener !=null) { Message message = helperListener.obtainMessage(GAMEPAD_REQUEST_STOP_ACTIVITY, info); helperListener.sendMessage(message); } } /** * 往客戶端發送數據 */ public void respondMessage(JSONObject obj) { try { if (cOutputStream != null) { String utf8String = new String(obj.toString().getBytes(), "UTF-8"); cOutputStream.writeInt(utf8String.getBytes().length); cOutputStream.write(utf8String.getBytes()); cOutputStream.flush(); } } catch (IOException e) { e.printStackTrace(); } } /** * 初始化客戶端數據 */ private int initClientSocket(Socket cSocket) { try { /** * 重置 */ if (cInputStream != null) { cInputStream.close(); cInputStream = null; } if (cOutputStream != null) { cOutputStream.close(); cOutputStream = null; } if (clientSocket != null) { clientSocket.close(); clientSocket = null; } /** * 重設 */ clientSocket = cSocket; cInputStream = new DataInputStream(clientSocket.getInputStream()); cOutputStream = new DataOutputStream(clientSocket.getOutputStream()); new Thread(new RecieverRunnable()).start(); } catch (IOException e) { e.printStackTrace(); return -1; } return 0; } /** * 服務的Binder */ public GamepadServerBinder mBinder = new GamepadServerBinder(); public class GamepadServerBinder extends Binder { public GamepadSocketServer getService() { /** * 返回service實例 */ return GamepadSocketServer.this; } } /** * 綁定服務 */ @Override public IBinder onBind(Intent intent) { return mBinder; } /** * 重新綁定服務 */ @Override public void onRebind(Intent intent) { super.onRebind(intent); } /** * 解析數據 */ private RequestInfo parseInputString(String inputString) { if (inputString != null && inputString.length() > 0) { JSONObject object = null; RequestInfo request = null; try { object = new JSONObject(inputString); if (object != null) { request = new RequestInfo(); request.target = object.getString("target"); request.type = object.getInt("type"); request.peerId = object.getString("peerId"); request.pcClientVersion = object.getString("pcClientVersion"); } return request; } catch (JSONException e) { e.printStackTrace(); } } return null; } /** * 請求數據結構 */ public static class RequestInfo { String target; int type; String peerId; String pcClientVersion; } }
GamepadServerHelper.java
package com.example.gamepaddemo.gamepadserver; import java.lang.ref.WeakReference; import org.json.JSONException; import org.json.JSONObject; import com.example.gamepaddemo.gamepadview.GamepadActivity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; /** * 負責界面與socket服務的交互 * @author zhouxueliang */ public class GamepadServerHelper { private boolean isServiceStarted = false; /** * service */ private GamepadSocketServer gamepadServer = null; private Context mContext = null; /** 單例 **/ private static GamepadServerHelper instance; private static Object instanceLock = new Object();
public static GamepadServerHelper getInstance(Context context) { if (instance == null) { synchronized(instanceLock){
if (instance == null) {
instance = new GamepadServerHelper(context);
}
}
} return instance; }
private GamepadServerHelper(Context context) { mContext = context; } /** * 監聽service回調 */ private MessageListener gamepadServerMessageListener = new MessageListener() { @Override public void handleMessage(Message msg) { switch (msg.what) { case GamepadSocketServer.GAMEPAD_REQUEST_START_ACTIVITY: { RequestInfo info = (RequestInfo)msg.obj; Intent intent = new Intent(new Intent(mContext, GamepadActivity.class)); intent.putExtra("type", info.type); mContext.startActivity(intent); } break; case GamepadSocketServer.GAMEPAD_REQUEST_STOP_ACTIVITY: { Intent intent = new Intent(new Intent(mContext, GamepadActivity.class)); intent.putExtra("type", 0); mContext.startActivity(intent); } break; default: break; } } }; private Handler helperHander = new StaticHandler(gamepadServerMessageListener); /** * ServiceConnection,獲取service實例 */ private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { gamepadServer = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { gamepadServer = ((GamepadSocketServer.GamepadServerBinder)service).getService(); gamepadServer.setHelperListener(helperHander); } }; /** * 啟動手柄服務 */ public synchronized void startService(){ if(!isServiceStarted) { isServiceStarted = true; Intent intent = new Intent(); intent.setClass(mContext, GamepadSocketServer.class); mContext.startService(intent); mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } } /** * 關閉手柄服務 */ public synchronized void stopService(){ if(isServiceStarted) { isServiceStarted = false; Intent intent = new Intent(); intent.setClass(mContext, GamepadSocketServer.class); mContext.unbindService(mConnection); mContext.stopService(intent); gamepadServer = null; } } /** * 發送消息 */ public void sendMessage(JSONObject obj) { gamepadServer.respondMessage(obj); } /** * 用來消除Handle可能導致的泄漏 * 原在Util里,鑒於項目獨立性,直接移植 */ public static class StaticHandler extends Handler { WeakReference<MessageListener> listener; /** * * @param listener * 必讀: 此listener必須由Activity實現該接口(推薦)或者是宿主Activity的類成員 : 這里是弱引用, * 不會增加變量的引用計數, 使用匿名變量會導致listener過早釋放(請參考此類的引用示例) */ public StaticHandler(MessageListener listener) { this.listener = new WeakReference<MessageListener>(listener); } public StaticHandler(Looper looper, MessageListener listener) { super(looper); this.listener = new WeakReference<MessageListener>(listener); } @Override public void handleMessage(Message msg) { MessageListener listener = this.listener.get(); if (listener != null) { listener.handleMessage(msg); } } } /** * 原在Util里,鑒於項目獨立性,直接移植 */ public interface MessageListener { public void handleMessage(Message msg); } }