今天是个好日子。
在我的项目中,我使用Java-WebSocket库。我有 WebSocketService,我的自定义 WebSocket 在其中侦听和发送消息。一切都会好的,但是在每个 WebSocketClient TIMEOUT 或强制(当服务被破坏时)关闭后,在我的 App 进程中会出现过多的线程,这会导致 CPU 使用率最大化和电池快速放电。
我做错了什么?
我的 WebSocketService 类:
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.IBinder;
public class WebSocketService extends Service {
public enum ControlServiceType {
NONE, START_SEND_USER_LOCATION, STOP_SEND_USER_LOCATION
}
private static final String CLASS_PATH = WebSocketService.class.getName();
private static final String EXTRA_CONTROL_SERVICE_TYPE = CLASS_PATH + "EXTRA_CONTROL_SERVICE_TYPE";
private static final int SLEEP_TIME = 5 * 60 * 1000;
//VALUE's
private LocationController mLocationController;
private ConnectivityManager mConnectivityManager;
private WebSocket mWebSocket;
private Thread mThread;
private boolean mNetworkAvailable;
private boolean mSendUserLocationOnWebSocket;
public static void startService(Context context) {
final Intent intent = new Intent(context, WebSocketService.class);
context.startService(intent);
}
public static void stopService(Context context) {
final Intent intent = new Intent(context, WebSocketService.class);
context.stopService(intent);
}
public static void controlService(Context context, ControlServiceType type) {
final Intent intent = new Intent(context, WebSocketService.class);
intent.putExtra(EXTRA_CONTROL_SERVICE_TYPE, type.ordinal());
context.startService(intent);
}
public void onCreate() {
super.onCreate();
mLocationController = LocationController.getInstance(this);
mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
mNetworkAvailable = isNetworkAvailable(mConnectivityManager.getActiveNetworkInfo());
registerReceiver(mReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
mWebSocket = WebSocket.getInstance(this);
startSaveUserLocationOnWebSocket();
Utils.toLog("\tWebSocketService\tonCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Utils.toLog("\tWebSocketService\tonStartCommand");
if (intent != null) {
final ControlServiceType type = ControlServiceType.
values()[intent.getIntExtra(EXTRA_CONTROL_SERVICE_TYPE, 0)];
switch (type) {
case START_SEND_USER_LOCATION:
startSaveUserLocationOnWebSocket();
break;
case STOP_SEND_USER_LOCATION:
stopSaveUserLocationOnWebSocket();
break;
default:
break;
}
}
return super.onStartCommand(intent, flags, startId);
}
public void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
stopSaveUserLocationOnWebSocket();
Utils.toLog("\tWebSocketService\tonDestroy");
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void startSaveUserLocationOnWebSocket() {
mSendUserLocationOnWebSocket = true;
mThread = new SaveUserLocationOnWebSocket();
mThread.start();
}
private void stopSaveUserLocationOnWebSocket() {
mWebSocket.stopSocket();
mSendUserLocationOnWebSocket = false;
mThread.interrupt();
mThread = null;
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction()) && intent.getExtras() != null) {
mNetworkAvailable = isNetworkAvailable(mConnectivityManager.getActiveNetworkInfo());
if (mWebSocket != null) {
mWebSocket.setNetworkAvailable(mNetworkAvailable);
}
}
}
};
private boolean isNetworkAvailable(NetworkInfo ni) {
if (ni != null && ni.isConnected()) {
return true;
}
return false;
}
private class SaveUserLocationOnWebSocket extends Thread {
@Override
public void run() {
super.run();
mWebSocket.startSocket(mNetworkAvailable);
while (mSendUserLocationOnWebSocket) {
mLocationController.getLastKnownLocationFromBestProvider();
mWebSocket.sendMessage(MessageType.SAVE_USER_LOCATION);
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {
return;
}
}
}
}
}
我的自定义 WebSocket 类:
import android.content.Context;
import android.os.Handler;
import org.java_websocket.client.DefaultSSLWebSocketClientFactory;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.client.WebSocketClient.WebSocketClientFactory;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
public class WebSocket {
private static final String WEB_SOCKET_URI = "https://XX.XX.XX.XX:8888/echo/websocket";
private static final int DELAY_MILLIS = 10;
private static final int ERROR_DELAY_MILLIS = 1000;
//SINGLETON
private static WebSocket sWebSocket;
//BASE
private Context mContext;
//VALUE's
private URI mURI;
private WebSocketClientFactory mClientFactory;
private Handler mHandler;
private Runnable mRunnable;
private WebSocketClient mClient;
private List<String> mMessagesList;
private WebSocketMessages mMessages;
private WebSocketSecretary mSecretary;
private boolean mNetworkAvailable;
private boolean mSocketWork;
public static WebSocket getInstance(Context context) {
if (sWebSocket == null) {
synchronized (WebSocket.class) {
if (sWebSocket == null) {
sWebSocket = new WebSocket(context);
}
}
}
return sWebSocket;
}
private WebSocket(Context context) {
mContext = context.getApplicationContext();
try {
mURI = new URI(WEB_SOCKET_URI);
} catch (URISyntaxException e) {
Utils.toLog(e);
throw new IllegalStateException(e.getMessage());
}
try {
final KeyStore keyStore = getKeyStore(mContext);
final KeyManager[] keyManagers = getKeyManagers(keyStore);
final TrustManager[] trustManagers = getTrustManagers(keyStore);
mClientFactory = getWebSocketClientFactory(keyManagers, trustManagers);
} catch (Exception e) {
Utils.toLog(e);
}
mHandler = new Handler();
mRunnable = new Runnable() {
@Override
public void run() {
initWebSocketClient();
}
};
mMessagesList = new ArrayList<>();
mMessages = WebSocketMessages.getInstance(mContext);
mSecretary = new WebSocketSecretary(mContext);
}
public void startSocket(boolean networkAvailable) {
mSocketWork = true;
setNetworkAvailable(networkAvailable);
}
public void setNetworkAvailable(boolean networkAvailable) {
mNetworkAvailable = networkAvailable;
if (mNetworkAvailable) {
initWebSocketClient();
}
}
public void sendMessage(MessageType type) {
sendMessage(mMessages.getMessage(type));
}
public void sendMessage(String bytes) {
if (isOpen()) {
mClient.send(bytes);
} else {
mMessagesList.add(bytes);
}
}
public boolean isOpen() {
return mSocketWork && mNetworkAvailable &&
}
public void stopSocket() {
mSocketWork = false;
mMessagesList.clear();
if (mClient != null) {
mClient.close();
mClient = null;
}
}
private void initWebSocketClient() {
if (mClient != null) {
return;
}
mClient = getWebSocketClient();
mClient.connect();
}
private WebSocketClient getWebSocketClient() {
final WebSocketClient client = getWebSocketClient(mURI);
client.setWebSocketFactory(mClientFactory);
return client;
}
private WebSocketClient getWebSocketClient(URI uri) {
return new WebSocketClient(uri) {
@Override
public void onOpen(ServerHandshake serverHandshake) {
if (MApp.DEBUG) {
Utils.toLog("WebSocketClient\tonOpen");
}
send(mMessages.getByteMessage(MessageType.LOGIN_USER));
if (!mMessagesList.isEmpty()) {
final Iterator<String> iterator = mMessagesList.iterator();
while (iterator.hasNext()) {
send(iterator.next());
iterator.remove();
}
}
}
@Override
public void onMessage(String s) {
mSecretary.onMessage(s);
}
@Override
public void onError(Exception e) {
if (MApp.DEBUG) {
Utils.toLog("WebSocketClient\tonError");
Utils.toLog(e);
}
}
@Override
public void onClose(int code, String reason, boolean remote) {
if (mClient != null) {
mClient.close();
}
mClient = null;
if (mSocketWork && mNetworkAvailable) {
mHandler.postDelayed(mRunnable, code == -1 ? ERROR_DELAY_MILLIS : DELAY_MILLIS);
}
if (MApp.DEBUG) {
Utils.toLog("WebSocketClient\tonClose\tcode = " + code + "\treason = " + reason);
}
}
};
}
public static KeyStore getKeyStore(Context context) throws Exception {
final KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(context.getResources().openRawResource(R.raw.websocketcertificate),
"password".toCharArray());
return keyStore;
}
public static KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception {
final KeyManagerFactory keyFactory = KeyManagerFactory.
getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(keyStore, null);
return keyFactory.getKeyManagers();
}
public static TrustManager[] getTrustManagers(KeyStore keyStore) throws Exception {
final TrustManagerFactory trustFactory = TrustManagerFactory.
getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(keyStore);
return trustFactory.getTrustManagers();
}
private static WebSocketClientFactory getWebSocketClientFactory(KeyManager[] keyManagers,
TrustManager[] trustManagers)
throws Exception {
final SSLContext sslContext = SSLContext.getInstance("TLS", "HarmonyJSSE");
sslContext.init(keyManagers, trustManagers, new SecureRandom());
return new DefaultSSLWebSocketClientFactory(sslContext);
}
}
我正常的 WebSocket 工作日志:
D/mLog (28058): WebSocketService onCreate
D/mLog (28058): WebSocketService onStartCommand
D/mLog (28058): WebSocket : {"method":"saveUserLocation","params":{"lat":0,"lon":0}}
D/mLog (28058): WebSocketClient onOpen
D/mLog (28058): WebSocket : {"method":"loginUser","params":"apiKey"}
D/mLog (28058): WebSocketClient onClose code = 1006 reason =
D/mLog (28058): WebSocketClient onOpen
D/mLog (28058): WebSocket : {"method":"loginUser","params":"apiKey"}
D/mLog (28058): WebSocketClient onClose code = 1006 reason =
D/mLog (28058): WebSocketClient onOpen
D/mLog (28058): WebSocket : {"method":"loginUser","params":"apiKey"}
D/mLog (28058): WebSocketClient onClose code = 1006 reason =
D/mLog (28058): WebSocketClient onOpen
D/mLog (28058): WebSocket : {"method":"loginUser","params":"apiKey"}
D/mLog (28058): WebSocketClient onClose code = 1006 reason =
D/mLog (28058): WebSocketClient onOpen
D/mLog (28058): WebSocket : {"method":"loginUser","params":"apiKey"}
D/mLog (28058): WebSocket : {"method":"saveUserLocation","params":{"lat":0,"lon":0}}
D/mLog (28058): WebSocketClient onClose code = 1006 reason =
D/mLog (28058): WebSocketClient onOpen
D/mLog (28058): WebSocket : {"method":"loginUser","params":"apiKey"}
D/mLog (28058): WebSocketService onDestroy
D/mLog (28058): WebSocketClient onClose code = 1000 reason =
提前感谢!