为什么我的服务会启动两次?

6
为什么我的服务会启动两次?我返回了START_STICKY。
我是通过Eclipse的“Run As”安装的,然后在Windows控制台窗口中启动它:
D:\>adb shell am startservice --user 0 -a android.intent.action.MAIN -n "com.sandbox.mq/.MainService"
Starting service: Intent { act=android.intent.action.MAIN cmp=com.sandbox.mq/.MainService }

在Logcat窗口中,我看到它已经运行两次! :
09-07 21:49:19.427: D/MQ(14027): Hi, the system is up! Today is: Sep 7, 2014 9:49:19 PM
09-07 21:49:19.427: V/MQ(14027): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-07 21:49:19.427: V/MQ(14027): BackgroundThread0. Thread id = 866 sent message 0
09-07 21:49:19.427: V/MQ(14027): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-07 21:49:19.437: V/MQ(14027): BackgroundThread0. Thread id = 869 sent message 0
09-07 21:49:19.437: V/MQ(14027): MessageHandler on thread Thread id = 1 received message 0
09-07 21:49:19.437: V/MQ(14027): MessageHandler on thread Thread id = 1 received message 0

当我通过adb安装时,它仍会启动两次,但输出不同:

adb install MQ.apk

--

09-07 22:07:28.567: D/MQ(14642): Hi, the system is up! Today is: Sep 7, 2014 10:07:28 PM
09-07 22:07:28.567: V/MQ(14642): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-07 22:07:28.577: V/MQ(14642): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-07 22:07:28.577: V/MQ(14642): BackgroundThread0. Thread id = 872 sent message 0
09-07 22:07:28.577: V/MQ(14642): MessageHandler on thread Thread id = 1 received message 0

包 com.sandbox.mq;

public class StartMainService extends Application {
    final static String TAG = "MQ";

    public void onCreate() {
        super.onCreate();
        Context context = getApplicationContext();
        Log.d(TAG, "Hi, the system is up! Today is: " + DateFormat.getDateTimeInstance().format(new Date()));
        Intent startServiceIntent = new Intent(context, MainService.class);
        context.startService(startServiceIntent);
    }
}

--

public class MainService extends Service {
    final static String TAG = "MQ";
    BackgroundThread0 bthread0;
    BackgroundThread1 bthread1;

    public class MqBinder extends Binder {
        public MqBinder(Context ctxt) {
            Log.v(TAG, "MqBinder() " + "Thread id = "
                    + Thread.currentThread().getId());
        }
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return new MqBinder(this);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.v(TAG, "onStartCommand(). I am INSIDE THE main sERVICE "
                + "Thread id = " + Thread.currentThread().getId());
        bthread0 = new BackgroundThread0();
        if (!bthread0.isAlive()) {
            bthread0.start();
        } else {
            Log.v(TAG, "onStartCommand(). bthread0 was already started");
        }
        bthread1 = new BackgroundThread1();
        if (!bthread1.isAlive()) {
            bthread1.start();
        } else {
            Log.v(TAG, "onStartCommand(). bthread1 was already started");
        }
        return START_STICKY;
    }

    private class BackgroundThread0 extends Thread {
        Handler b1Handler;

        @Override
        public void run() {
            super.run();
            b1Handler = bthread1.b1Handler;
            Message msg = b1Handler.obtainMessage(MessageHandler.TYPE0);
            b1Handler.sendMessage(msg);
            Log.v(TAG, "BackgroundThread0. " + "Thread id = "
                    + Thread.currentThread().getId() + " sent message "
                    + msg.what);
        }

    }

    private class BackgroundThread1 extends Thread {
        public BackgroundThread1() {
            super();
            b1Handler = new MessageHandler();
        }

        Handler b1Handler;

        @Override
        public void run() {
            super.run();
            Looper.prepare();
            Looper.loop();
        }

    }

    private static class MessageHandler extends Handler {
        static final int TYPE0 = 0;
        static final int TYPE1 = 1;
        static final int TYPE2 = 2;

        public MessageHandler() {

        }

        @Override
        public void handleMessage(Message msg) {
            Log.v(TAG, "MessageHandler on thread " + "Thread id = "
                    + Thread.currentThread().getId() + " received message "
                    + msg.what);

            switch (msg.what) {
            case TYPE0:
                break;
            case TYPE1:
                break;
            case TYPE2:
                break;
            }
            super.handleMessage(msg);
        }
    }
}

--

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.sandbox.mq" >

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

    <uses-sdk
        android:minSdkVersion="19"
        android:targetSdkVersion="19" />

    <application
        android:name="com.sandbox.mq.StartMainService"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:persistent="true"
        android:theme="@style/AppTheme" >
        <service android:name="com.sandbox.mq.MainService" android:exported="true"> 
             <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>          
        </service>        
    </application>

</manifest>

更新:

根据Ian在下面的回答中所述,当我注释掉onCreate中的startService调用时,它只会被调用一次。

09-08 09:29:10.393: D/MQ(4746): Hi, the system is up! Today is: Sep 8, 2014 9:29:10 AM
09-08 09:29:10.393: V/MQ(4746): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 09:29:10.393: V/MQ(4746): BackgroundThread0. Thread id = 1050 sent message 0
09-08 09:29:10.393: V/MQ(4746): MessageHandler on thread Thread id = 1 received message 0

我的原始问题已得到回答,但我想知道...为什么BackgroundThread1的线程ID与主服务线程ID = 1相同?

3个回答

10

它会启动两次是因为你启动了两次:

  1. adb shell am startservice启动服务
  2. 该进程启动,触发您的ApplicationonCreate(),它也启动服务。

每当有东西启动您的服务时,都会调用onStartCommand。如果您只想在服务的生命周期中执行一次操作,则应在服务的onCreate()方法中执行操作或在onStartCommand中再次执行工作之前进行检查(即,在创建/重新启动bthread0之前检查它是否为空)。


我该如何仅启动应用程序进程以便启动服务?我尝试了 D:\>adb shell am start -a android.intent.action.MAIN -n "com.sandbox.mq/.MainService",但出现以下错误: Starting: Intent { act=android.intent.action.MAIN cmp=com.sandbox.mq/.MainService } Error type 3 Error: Activity class {com.sandbox.mq/com.sandbox.mq.MainService} does not exist. - likejudo
正如清单所示,应用程序只是各种高级Android组件的容器。在您的情况下,您的进程/应用程序启动的唯一原因是如果启动了您的服务,因此您唯一的选择就是启动服务。 - ianhanniballake
但仅仅从 Eclipse 中运行“Run As”并不能运行应用程序。在应用程序中注释掉 startService 调用,这样可以确保它只运行一次吗?答案是:是。 - likejudo
为什么BackgroundThread1的线程ID与主服务线程ID = 1相同? - likejudo
正确的做法是不要在Application onCreate中启动服务,这样可以避免服务启动两次的问题。请注意,如果您没有一个活动(正如您所注意到的,“MAIN”和“LAUNCHER”意图过滤器只适用于活动),那么从Play商店安装您的应用程序的用户将无法启动您的应用程序。如果您对服务有其他问题,请提出新的问题。 - ianhanniballake
在此创建了一个新问题:http://stackoverflow.com/questions/25730340/why-does-one-background-thread-have-the-same-thread-id-as-the-main-service-threa - likejudo

1

我通过将启动服务的意图移动到我的MainActivity中来解决了这个问题,正如ianhanniballake在评论部分提到的那样。希望能对其他人有所帮助 :)


0

我曾经遇到过同样的问题,通过放置解决了它。

START_NOT_STICKY

替代

START_STICKY

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接