Android:startService不会启动目标服务

时间:2014-11-23 17:23:20

标签: java android socket.io

主要活动应该启动一个处理套接字通信的服务。这只是一个考验:

@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
  • 此外,使用和不使用intent过滤器也会发生同样的情况。
  • 我无法使用绑定,因为即使活动终止,我也需要服务继续运行。
  • 在主线程中未调用runWhenConnected。但是,如果它是一个例外,那就会被提出来。

2 个答案:

答案 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重新创建工作流程的问题。