为什么实例 ID 服务会自动启动,我该如何防止这种情况发生?

Why does the InstanceID service automatically start and how do I preven this?

我有一个使用 GCM 的 android 应用程序。我正在按照教程使用 InstanceIDListenerService class,我试图在用户输入一些信息的 "subscription" 页面之后将其作为 IntentService 触发。在此订阅页面之前,还有一些初步代码会在幕后的初始屏幕上触发。在我到达 SubscriptionActivity 之前,在 SplashScreen activity 中调用 InstanceIDListenerService 构造函数(随后调用 onHandleIntent)。为什么要这样做?意图服务是否可以自行启动?

我确实在 AndroidManifest.xml 文件中注册了该服务,当我注释掉以下行时,它不会触发实例自动创建,应用程序按预期工作(直到我需要当然使用实例...)

<service
    android:name=".service.receiver.InstanceIDListenerService"
    android:exported="false" >
    <intent-filter>
        <action android:name="com.google.android.gms.iid.InstanceID" />
    </intent-filter>
</service>

SplashScreen.java

public class SplashScreen extends Activity {

    private BroadcastReceiver dbInsertReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle bundle = intent.getExtras();
            if(bundle != null) {
                // Handle results and move to next activity, should
                // be the subscribe activity where I want the instance 
                // id listener to start.
            }
        }
    };

    private BroadcastReceiver providerXMLReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             Bundle bundle = intent.getExtras();
             if(bundle != null) {
                 // Handle results and start the next service                 
             }
         }
     }
 };

    /** Called when the activity is first created */
    @Override
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);

        setContentView(R.layout.activity_splash_screen);

        // Kick off the service download to update the provider data
        Intent intent = new Intent(this, ProviderDataService.class);
        startService(intent);
    }

    @Override
    protected void onStart() {
        super.onStart();
        registerReceiver(providerXMLReceiver, new IntentFilter(ProviderDataService.CHANNEL));
        registerReceiver(dbInsertReceiver, new IntentFilter(InternalDBService.NOTIFICATION));
    }

    @Override
    protected void onResume() {
        super.onResume();
        registerReceiver(providerXMLReceiver, new IntentFilter(ProviderDataService.CHANNEL));
    registerReceiver(dbInsertReceiver, new IntentFilter(InternalDBService.NOTIFICATION));
    }

    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(providerXMLReceiver);
        unregisterReceiver(dbInsertReceiver);
    }            

    private void moveToNextActivity(int subscriptionStatus) {
        if(subscriptionStatus == DBSchemaHelper.IS_SUBSCRIBED_NOT_RESPONDED) {
            Intent subscribeIntent = new Intent(SplashScreen.this, SubscribeActivity.class);
            startActivity(subscribeIntent);
        } else {
            // Create an Intent that will start the Menu-Activity.
            Intent mainIntent = new Intent(SplashScreen.this, MainActivity.class);
            startActivity(mainIntent);
        }

        this.finish();
    }

SubscribeActivity.java

public class SubscribeActivity extends CustomActionBarActivity {

    public static final int NO_SUBSCRIPTION_STATUS = -99;
    private DBMetaDataSource metaDao;
    private int subscribeResult;

    public SubscribeActivity() {
        metaDao = new DBMetaDataSource(this);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_subscribe);
    }    

    @Override
    protected void onStart() {
        super.onStart();
        registerReceiver(tokenResponseReceiver, new IntentFilter(InstanceIDListenerService.TAG));
    }

    @Override
    protected void onResume() {
        super.onResume();
        registerReceiver(tokenResponseReceiver, new IntentFilter(InstanceIDListenerService.TAG));
    }

    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(tokenResponseReceiver);
    }

    public void subscribeUser(View v) {
        EditText emailTextView = (EditText) findViewById(R.id.subscriptionUserEmail);
        String email = emailTextView.getText().toString();

        // This is the only place I am manually starting this service.
        // I have set a breakpoint here, but I never hit it and the service
        // starts on its own and I hit the breakpoints in the service's 
        // onHandleIntent method.
        Intent i = new Intent(this, InstanceIDListenerService.class);
        i.putExtra("email", email);
        startService(i);
    }

    public void goToNextActivity(View v) {
        // They pressed the button to NOT subscribe, so we are calling this from the
        // view rather than the intent receiver, meaning the view will not be null.
        if(v != null) {
            markUnsubscribed();
        }

        /* Create an Intent that will start the Menu-Activity. */
        Intent mainIntent = new Intent(SubscribeActivity.this, MainActivity.class);
        mainIntent.putExtra(MainActivity.SUBSCRIBE_STATUS_KEY, subscribeResult);
        startActivity(mainIntent);
    }

    private void markUnsubscribed() {
        metaDao.open(this);
        DBMeta metaData = metaDao.get();
        metaDao.update(Long.valueOf(metaData.getVersion()), metaData.getLastRunInMillis(), DBSchemaHelper.IS_SUBSCRIBED_RESPONDED_NO, null);
        metaDao.close();
    }

    private BroadcastReceiver tokenResponseReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            subscribeResult = intent.getIntExtra(InstanceIDListenerService.RESPONSE_KEY, NO_SUBSCRIPTION_STATUS);
            goToNextActivity(null);
        }
    };
}

您不应该自己启动 InstanceIDListenerService - 这是为了让系统在您需要通过回调刷新您的实例 ID 令牌时调用 onTokenRefresh() - 如果您还没有创建任何实例 ID 令牌,那么您将在第一次调用时无事可做。

如果您有其他工作要做,您应该使用自己的服务。