我一直试图找到为什么我的处理程序根本没有触发事件,但我没有找到它的运气。我将发布整个班级,希望有人注意到一个愚蠢的错误或其他什么。
该类只管理一些套接字的生命周期(我这样做是因为Android设备在关闭它们后不久就重新打开端口时似乎非常情绪化)。这个类是一个单例,因为我想知道我到处使用什么端口。
package com.ltd.jeefo.alex.routeassistant.wifi.sync.utils;
import android.annotation.SuppressLint;
import android.os.Handler;
import android.support.annotation.Nullable;
import com.ltd.jeefo.alex.routeassistant.logging.ILog;
import com.ltd.jeefo.alex.routeassistant.logging.ScopedLogger;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.HashMap;
/**
* Created by Alex on 16/02/18.
*/
public class SocketsManager {
private static final SocketsManager ourInstance = new SocketsManager();
private static final Handler handler = new Handler();
public static SocketsManager getInstance() {
return ourInstance;
}
@SuppressLint("UseSparseArrays")
private static final HashMap<Integer, ManagedServerSocket> serverSockets = new HashMap<>();
private SocketsManager() {
}
/**
* Method used for obtaining the server socket for a given port
* @param port the port where we want the socket to be opened
* @return the server socket to be used or null if we couldn't start a server socket at that port
*/
public ManagedServerSocket getSocketAtPort(final int port) {
ManagedServerSocket managedServerSocket;
if (serverSockets.containsKey(port)) {
managedServerSocket = serverSockets.get(port);
} else {
managedServerSocket = new ManagedServerSocket(port, handler);
serverSockets.put(port, managedServerSocket);
}
return managedServerSocket;
}
/**
* Closes all the sockets after some delay
*
* @param msDelay the delay in milliseconds
*/
public void closeAllSocketsAfterDelay(final long msDelay) {
for (final ManagedServerSocket managedSocket : serverSockets.values()) {
managedSocket.RequestSocketClosingAfterDelay(msDelay);
}
}
enum ServerSocketStatus {
OPEN_NOT_REQUESTED,
OPEN_FAILED,
OPENED, // there is always a close request started by default, just in case
USER_CLOSE_REQUESTED,
CLOSED
}
public class ManagedServerSocket {
private final ILog logger;
private final static long DEFAULT_CLOSING_TIME_MILLS = 3600000; // one hour
private final static long DEFAULT_RETRY_TIME_MILLS = 15000; // 15 seconds
private final Handler mHandler;
private final int mPort;
private ServerSocket mSocket;
private SocketsManager.ServerSocketStatus serverSocketStatus;
private Long closingTime;
private Runnable defaultClosingRunnable;
private Runnable explicitClosingRunnable;
private ManagedServerSocket(final int port, final Handler handler) {
this.mPort = port;
this.mHandler = handler;
logger = new ScopedLogger(null, ManagedServerSocket.class, "Port", Integer.toString(mPort));
serverSocketStatus = SocketsManager.ServerSocketStatus.OPEN_NOT_REQUESTED;
defaultClosingRunnable = new Runnable() {
@Override
public void run() {
try {
logger.Warn("Server socket closing automatically at port %s after long delay",
Integer.toString(mPort));
mSocket.close();
serverSocketStatus = SocketsManager.ServerSocketStatus.CLOSED;
closingTime = new Date().getTime();
} catch (IOException e) {
e.printStackTrace();
logger.Error(e, "Failed to close server socket at port %s after delay", Integer.toString(mPort));
}
}
};
explicitClosingRunnable = new Runnable() {
@Override
public void run() {
try {
logger.Debug("Socket closing after delay");
mSocket.close();
serverSocketStatus = SocketsManager.ServerSocketStatus.CLOSED;
closingTime = new Date().getTime();
} catch (IOException e) {
e.printStackTrace();
logger.Error(e, "Failed to close server socket after delay");
}
}
};
}
public void RequestSocketClosingAfterDelay(long delayMills) {
logger.Debug("Close socket after delay %s ms", Long.toString(delayMills));
switch (serverSocketStatus) {
case OPEN_NOT_REQUESTED:
logger.Warn("Closing port that was never opened");
return;
case OPEN_FAILED:
logger.Error("Cannot close a socket that failed to open");
return;
case CLOSED:
logger.Warn("Cannot request closing an already closed socket");
return;
case OPENED:
mHandler.removeCallbacks(defaultClosingRunnable);
break;
case USER_CLOSE_REQUESTED:
logger.Warn("Requested to close a socket that was already requested for closing");
mHandler.removeCallbacks(explicitClosingRunnable);
break;
}
mHandler.postDelayed(explicitClosingRunnable, delayMills);
serverSocketStatus = SocketsManager.ServerSocketStatus.USER_CLOSE_REQUESTED;
}
@Nullable
private ServerSocket RequestSocketForUsage() {
boolean socketIsOpened = true;
switch (serverSocketStatus) {
case USER_CLOSE_REQUESTED:
mHandler.removeCallbacks(explicitClosingRunnable);
break;
case OPENED:
mHandler.removeCallbacks(defaultClosingRunnable);
break;
case CLOSED:
case OPEN_FAILED:
case OPEN_NOT_REQUESTED:
socketIsOpened = tryOpenServerSocket();
break;
}
if (!socketIsOpened) {
serverSocketStatus = SocketsManager.ServerSocketStatus.OPEN_FAILED;
logger.Error("Failed to open the socket server and return it");
return null;
}
// Add back the default closing handler now
mHandler.postDelayed(defaultClosingRunnable, DEFAULT_CLOSING_TIME_MILLS);
serverSocketStatus = SocketsManager.ServerSocketStatus.OPENED;
return mSocket;
}
@Nullable
public Socket accept() throws IOException {
ServerSocket socket = RequestSocketForUsage();
if (socket != null) {
return socket.accept();
} else {
return null;
}
}
public SocketsManager.ServerSocketStatus getStatus() {
return serverSocketStatus;
}
private boolean tryOpenServerSocket() {
int attempt = 0;
do {
++attempt;
try {
this.mSocket = new ServerSocket(mPort);
break;
} catch (Exception e) {
logger.Warn(e, "Failed to start ServerSocket on attempt: %s", Integer.toString(attempt));
synchronized (this) {
try {
wait(DEFAULT_RETRY_TIME_MILLS);
} catch (InterruptedException e1) {
logger.Error("Retry wait() interrupted");
e1.printStackTrace();
}
}
}
} while (attempt < 6);
if (this.mSocket == null) {
logger.Error("Failed to start ServerSocket");
return false;
} else {
logger.Info("ServerSocket started");
return true;
}
}
}
}
----------------------------------------- LATER EDIT - ----------------------------------------
我总是在asyncTasks中触发getSocketAtPort()方法,这似乎是问题的一部分。
我现在在一个活动内部(在onCreate()内部)打开一个套接字(使用getSocketAtPort()),然后在asyncTasks中调用它。当我这样做时,所有处理程序都开始工作(包括asyncTask中请求的那些)。我的猜测是问题与线程有关。