主要活动应该启动一个处理套接字通信的服务。这只是一个考验:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
this.setContentView(R.layout.contacts_test);
this.startService(new Intent(getApplicationContext(), WebSocketService.class));
final WebSocketService service = WebSocketService.getInstance();
service.runWhenConnected(new Runnable() {
@Override
public void run() {
service.getWebSocket().emit("login", new Credentials("...", "..."));
}
});
service.registerHandler("login successful", new WebSocketEventHandler() {
@Override
public void onEvent(WebSocket webSocket, Object... args) {
Log.i("SOCKET-IO", "Login successful");
}
});
}
您可以在下面找到该服务的代码。但在开始阅读之前,请注意控制流甚至不会达到服务构造函数或onCreate 方法;这是我无法理解的事情。该服务在清单中声明:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.gtd.test"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="15"/>
<application android:label="@string/app_name" android:icon="@drawable/tmp">
<activity android:name="MyActivity" android:label="@string/app_name" android:theme="@style/LightCustomTheme"> </activity>
<activity android:name="PickersTestActivity" android:theme="@style/LightCustomTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ContactsTestActivity" android:theme="@style/LightCustomTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<service android:name="org.pickme.service.WebSocketService" android:enabled="true">
<intent-filter>
<action android:name="org.pickme.service.WebSocketService" />
</intent-filter>
</service>
</application>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
</manifest>
我正在使用Gottox java客户端进行socket.io。但套接字甚至没有启动,所以不是问题(并且在服务之外使用它)。
package org.pickme.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import io.socket.IOAcknowledge;
import io.socket.IOCallback;
import io.socket.SocketIO;
import io.socket.SocketIOException;
import org.json.JSONObject;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class WebSocketService extends Service {
private static final String SERVER_ENDPOINT = "http://192.168.1.83:3000";
private static WebSocketService instance;
private SocketIO socket;
private WebSocket socketWrapper;
private boolean initialized;
private Thread connectionThread;
private IOCallback ioCallback;
private Runnable connectHandler, disconnectHandler;
private Map<String, List<WebSocketEventHandler>> eventHandlers;
public static WebSocketService getInstance() {
return instance;
}
public boolean isConnected() {
return socket.isConnected();
}
public boolean isInitialized() {
return initialized;
}
public WebSocket getWebSocket() {
return socketWrapper;
}
public WebSocketService() {
this.ioCallback = new CallbackStub();
this.eventHandlers = new HashMap<String, List<WebSocketEventHandler>>();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
this.initialized = false;
try {
this.socket = new SocketIO(SERVER_ENDPOINT);
this.initialized = true;
} catch (MalformedURLException e) {
this.initialized = false;
return;
}
this.initWrappers();
this.connectionThread.start();
WebSocketService.instance = this;
}
@Override
public void onDestroy() {
super.onDestroy();
this.socket.disconnect();
WebSocketService.instance = null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
private void initWrappers() {
final IOCallback callbackReference = this.ioCallback;
final SocketIO socketReference = this.socket;
this.socketWrapper = new WebSocket() {
@Override
public void emit(String event, Object... args) {
socketReference.emit(event, args);
}
@Override
public void disconnect() {
socketReference.disconnect();
}
};
this.connectionThread = new Thread(new Runnable() {
@Override
public void run() {
socketReference.connect(callbackReference);
}
});
}
public void runOnConnect(Runnable connectHandler) {
this.connectHandler = connectHandler;
}
public void runOnDisconnect(Runnable disconnectHandler) {
this.disconnectHandler = disconnectHandler;
}
public void runWhenConnected(Runnable runnable) {
if (this.isConnected() && (runnable != null))
runnable.run();
else
this.runOnConnect(runnable);
}
public void registerHandler(String event, WebSocketEventHandler handler) {
List<WebSocketEventHandler> handlersList;
if (eventHandlers.containsKey(event))
handlersList = eventHandlers.get(event);
else
eventHandlers.put(event, handlersList = new ArrayList<WebSocketEventHandler>());
handlersList.add(handler);
}
public void unregisterHandler(String event, WebSocketEventHandler handler) {
if (!eventHandlers.containsKey(event))
return;
List<WebSocketEventHandler> handlersList = eventHandlers.get(event);
handlersList.remove(handler);
}
public void unregisterHandlers(String event) {
if (!eventHandlers.containsKey(event))
return;
List<WebSocketEventHandler> handlersList = eventHandlers.get(event);
eventHandlers.clear();
eventHandlers.remove(event);
}
public void unregisterAllHandlers() {
eventHandlers.clear();
}
private class CallbackStub implements IOCallback {
@Override
public void onDisconnect() {
if (WebSocketService.this.disconnectHandler != null)
WebSocketService.this.disconnectHandler.run();
}
@Override
public void onConnect() {
if (WebSocketService.this.connectHandler != null)
WebSocketService.this.connectHandler.run();
}
@Override
public void onMessage(String data, IOAcknowledge ack) { }
@Override
public void onMessage(JSONObject json, IOAcknowledge ack) { }
@Override
public void on(String event, IOAcknowledge ack, Object... args) {
if (!WebSocketService.this.eventHandlers.containsKey(event)) {
Log.i("WEBSOCKET-SERVICE", event + " unhandled");
return;
}
List<WebSocketEventHandler> handlers = WebSocketService.this.eventHandlers.get(event);
for (WebSocketEventHandler handler : handlers)
handler.onEvent(WebSocketService.this.socketWrapper, args);
}
@Override
public void onError(SocketIOException socketIOException) {
Log.e("SOCKET-IO", socketIOException.getMessage(), socketIOException);
}
}
}
编辑:
答案 0 :(得分:2)
我不确定你的期望,但这一行:
this.startService(new Intent(getApplicationContext(), WebSocketService.class));
不是“阻止”或同步调用。所以当你这样称呼时:
final WebSocketService service = WebSocketService.getInstance();
该服务可能尚未实例化,这可能会返回“null”。
那就是说,你的核心问题在你的清单中。您正在添加需要设置操作的“过滤器”。看这篇文章:
Android Manifest- intent filter and activity
但有了这个:
<service android:name="org.pickme.service.WebSocketService" android:enabled="true">
<intent-filter>
<action android:name="org.pickme.service.WebSocketService" />
</intent-filter>
</service>
你需要这样做:
Intent myIntent = new Intent(getApplicationContext(), WebSocketService.class));
myIntent.setAction("org.pickme.service.WebSocketService");
this.startService(myIntent);
或删除intent-filter:
<service android:name="org.pickme.service.WebSocketService" android:enabled="true">
</service>
答案 1 :(得分:1)
乍一看service.runWhenConnected()
调用是错误的,因为它在主UI线程上执行网络代码。不建议(我说禁止)因为它可能会阻止UI线程并导致onCreate
完全无法完成。我想你可能会在logcat中看到关于这种情况的错误消息(在UI线程上运行网络代码)。
ADDED1 :在服务WebSocketService.instance
中设置onCreate
也不对。 instance
应该在构造函数中设置,或者在(例如getInstance()
内)附近设置。当你打电话
final WebSocketService service = WebSocketService.getInstance();
&#39;实例&#39;尚未设置(服务onCreate
尚未调用 - 在相同的UI线程上调用Activity和onCreate
方法,因此,除非活动onCreate
完成,否则服务onCreate
将被呼叫。startService()
只是一个&#34;延迟呼叫&#34;服务&#34} #39; s onCreate
)。所以代码
service.runWhenConnected(...);
最有可能抛出NullPointerException
。
通常,为活动和服务使用静态变量是一种不好的做法,它可能会导致内存泄漏。
ADDED2 :如果您想从活动中处理您的服务,您应该使用绑定,这是Android中的标准模式,用于此类情况:{{3} }。不要通过静态变量直接访问Service的实例。改为使用绑定。
ADDED3:似乎在重新创建活动时(即在屏幕上旋转),服务绑定可能会出现问题:developer.android.com: Bound Services。必须重新创建绑定。这篇文章提供了很好的信息和阅读链接。
ADDED4 :这篇文章Communicate with Activity from Service (LocalService) - Android Best Practices解释说片段也不是绑定的好地方,但您可以将服务绑定到应用程序的上下文对象,避免了Activity重新创建工作流程的问题。