2014-11-23 44 views
0

主要活动应启动处理套接字通信的服务。这只是一个测试:Android:startService不会启动目标服务

@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); 
     } 
    } 
} 

编辑:

  • 的WebSocketService 构造函数和的onCreate永远不会调用
  • 此外,无论是否使用意图过滤器都会发生相同的情况。
  • 我无法使用绑定,因为即使活动终止,我也需要该服务继续运行。
  • runWhenConnected不在主线程中调用。但是,如果这是一个例外将会提出。

回答

1

乍一看service.runWhenConnected()调用是错误的,因为它在主UI线程上执行网络代码。不建议(我会说禁止),因为它可能会阻止UI线程并导致onCreate不完成。我想你可能会在logcat中看到有关这种情况的错误消息(在UI线程上运行网络代码)。

ADDED1:也设置WebSocketService.instance在服务的onCreate是不正确的。 instance应该在构造函数中设置,或者靠近(例如,在getInstance()中)。当你调用

final WebSocketService service = WebSocketService.getInstance(); 

的“实例”并没有被设定(服务的onCreate没有被调用,但 - 这两个活动的和服务的onCreate方法被调用相同的UI线程上,所以服务的onCreate不会被调用除非Activity的onCreate完成。startService()只是Service的onCreate的“延迟呼叫”)。所以代码

service.runWhenConnected(...); 

最有可能抛出一个NullPointerException

一般情况下,对活动和服务使用静态变量是一种不好的做法,它可能会导致内存泄漏。

ADDED2:如果要对付从活动为您服务,您应该使用结合,它的标准模式中的Android这样的情况:developer.android.com: Bound Services。不要通过静态变量直接访问服务的实例。改为使用绑定

ADDED3:似乎在重新创建活动时(即在屏幕旋转时)服务绑定可能存在问题:Communicate with Activity from Service (LocalService) - Android Best Practices。绑定也必须重新创建。这篇文章提供了相当不错的信息和链接来阅读。

ADDED4:这个帖子Bind service to FragmentActivity or Fragment?解释片段也并不好去处约束力,但可以绑定的服务让您应用的背景下对象,而不是,并避免与活动的再创造工作流程问题。

+0

好吧,我不知道onCreate是打电话按顺序编辑。它现在有效。 – Totem 2014-11-23 20:40:36

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); 

或删除意图过滤器:

<service android:name="org.pickme.service.WebSocketService" android:enabled="true"> 
    </service> 
+0

我添加了意图过滤器,因为它之前没有工作,并且它也不以这种方式工作。另外,onCreate甚至没有达到,所以startService被阻塞是无关紧要的,因为服务永远不会启动。 – Totem 2014-11-23 20:02:32