【问题标题】:Android: startService won't start target serviceAndroid:startService 不会启动目标服务
【发布时间】:2014-11-23 17:23:20
【问题描述】:

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

@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>

我正在为 socket.io 使用 Gottox java 客户端。但是套接字甚至没有启动,所以这不是问题(并且在服务之外使用它是有效的)。

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 不在主线程中调用。但如果它是一个例外,就会引发。

【问题讨论】:

    标签: java android socket.io


    【解决方案1】:

    我不确定你在期待什么,但这条线:

    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>
    

    【讨论】:

    • 我添加了意图过滤器,因为它以前不起作用,而且它也不能以这种方式工作。此外,甚至没有达到 onCreate ,因此阻止 startService 无关紧要,因为该服务永远不会启动。
    【解决方案2】:

    乍一看,service.runWhenConnected() 调用是错误的,因为它在主 UI 线程上执行网络代码。不推荐(我会说禁止),因为它可能会阻塞 UI 线程并导致 onCreate 根本没有完成。我认为您可能会在 logcat 中看到有关此案例的错误消息(在 UI 线程上运行网络代码)。

    ADDED1: 同样在 Service 的 onCreate 中设置 WebSocketService.instance 是不对的。 instance 应该在构造函数中设置,或者在附近设置(例如在getInstance() 中)。当你打电话时

    final WebSocketService service = WebSocketService.getInstance();
    

    'instance' 尚未设置(Service 的 onCreate 尚未被调用 - Activity 和 Service 的 onCreate 方法都在同一个 UI 线程上调用,因此 Service 的 onCreate 不会被调用,除非Activity 的 onCreate 完成。startService() 只是 Service 的 onCreate 的“延迟调用”)。所以代码

    service.runWhenConnected(...);
    

    很可能会抛出NullPointerException

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

    ADDED2:如果你想从一个 Activity 处理你的服务,你应该使用 binding,它是 Android 中针对这种情况的标准模式:developer.android.com: Bound Services。不要通过静态变量直接访问服务的实例。请改用绑定

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

    ADDED4:这篇帖子Bind service to FragmentActivity or Fragment? 解释说Fragments 也不是绑定的好地方,但您可以将Service 绑定到Application 的上下文中strong> 对象,并避免 Activity 重新创建工作流出现问题。

    【讨论】:

    • 好吧,我不知道 onCreate 是按顺序调用的。现在可以了。
    猜你喜欢
    • 2011-12-24
    • 2014-03-21
    • 1970-01-01
    • 2015-12-17
    • 1970-01-01
    • 2019-05-15
    • 1970-01-01
    • 2014-04-07
    • 1970-01-01
    相关资源
    最近更新 更多