3

我们准备了一个带有服务的 Android 应用程序,该服务与我们的服务器保持MQTT 连接。该服务从其 onStartCommand 返回一个 *START_STICKY*,以便 Android 重新启动服务,以防它因资源短缺而终止服务。但问题是,该服务经常被 Android OS 杀死。它有时会在几秒钟内终止服务一次,即使设备上没有其他进程在运行(使用 2GB 内存)。为什么 Android 如此频繁地杀死我的服务?如何减少重启次数?我的服务应该尽可能少地被杀死,因为它断开了我的 tcp 连接并且客户端必须再次重新连接,从而在我们的服务器上造成相当大的负载。这段代码有什么问题?谢谢

public class GTAndroidMQTTService extends Service implements MqttCallback {

    private void init() {
        this.clientId = Settings.System.getString(getContentResolver(), Secure.ANDROID_ID);
    }

    @Override
    @Deprecated
    public void onStart(Intent intent, int startId) {
        logger("onStart() called");
        super.onStart(intent, startId);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        logger("onStartCommand() called");
        if (client == null) {
            try {

                init();

                conOpt = new MqttConnectOptions();
                conOpt.setCleanSession(false);
                conOpt.setUserName("...");
                conOpt.setPassword("...");

                try {
                    char[] keystorePass = getString(R.string.keystorepass).toCharArray();

                    KeyStore keyStore = KeyStore.getInstance("BKS");
                    keyStore.load(getApplicationContext().getResources().openRawResource(R.raw.prdkey),
                            keystorePass);

                    TrustManagerFactory trustManagerFactory = TrustManagerFactory
                            .getInstance(KeyManagerFactory.getDefaultAlgorithm());

                    trustManagerFactory.init(keyStore);

                    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
                            .getDefaultAlgorithm());
                    kmf.init(keyStore, keystorePass);

                    SSLContext sslContext = SSLContext.getInstance("TLS");

                    sslContext.init(kmf.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

                    conOpt.setSocketFactory(sslContext.getSocketFactory());
                } catch (Exception ea) {
                }

                client = new MqttClient(this.mqttURL, clientId, new MqttDefaultFilePersistence(folder));
                client.setCallback(this);

                conOpt.setKeepAliveInterval(this.keepAliveSeconds);

            } catch (MqttException e) {
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        if (intent == null) {
            Log.i("TAG", "Android restarted the service[START_STICKY]");
            if (client != null) {
                tryToEstablishConnection();
            }
        }
        return START_STICKY;
    }

    public void unsubscribe(String topicName) throws MqttException {
        try {
            client.unsubscribe(topicName);
        } catch (Exception e) {
            Log.i("TAG", "Unsubscribing from topic \"" + topicName + "has failed: " + e.toString());
        }
    }

    private void retry() {
        try {
            notifyUserWithServiceStatus("Status Changed", "Status", "Connecting");
            client.connect(conOpt);
            notifyUserWithServiceStatus("Status Changed", "Status", "User Connected #" + (++retrycnt));
        } catch (Exception e) {
            notifyUserWithServiceStatus("Status Changed", "Status", "Cannot Connect");
            e.printStackTrace();
        }
    }

    public void subscribe(String topicName, int qos) throws MqttException {
        try {
            client.subscribe(topicName, qos);
        } catch (Exception e) {
        }
    }

    public void disconnect() {
        try {
            client.disconnect();
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        logger("onBind() called");
        return null;
    }

    @Override
    public void onCreate() {
        logger("onCreate() called");
        super.onCreate();
    }

    @Override
    public void connectionLost(Throwable arg0) { // Connection lost
        notifyUserWithServiceStatus("Status Changed", "Status", "Connection Lost!");
        tryToEstablishConnection();
    }

    private void tryToEstablishConnection() {
        if (!retrying) {
            retrying = true;
            new Thread(new Runnable() {

                @Override
                public void run() {
                    for (;;) {
                        try {
                            if (isOnline() && !isConnected()) {
                                retry();
                                Thread.sleep(RETRY_INTERVAL);
                            } else if (isConnected()) {
                                retrying = false;
                                break;
                            } else if (!isOnline()) {
                                retrying = false;
                                break;
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    }

    private class NetworkConnectionIntentReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context ctx, Intent intent) {
            PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
            WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MQTT");
            wl.acquire();

            if (isOnline() && !isConnected())
                notifyUserWithServiceStatus("Status Changed", "Status", "Online but not connected");
            else if (!isOnline())
                notifyUserWithServiceStatus("Status Changed", "Status", "Connection Lost!");

            tryToEstablishConnection();
            wl.release();
        }
    }

    private boolean isConnected() {
        try {
            return client.isConnected();
        } catch (Exception e) {
            return false;
        }
    }

    private boolean isOnline() {
        ConnectivityManager conMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo i = conMgr.getActiveNetworkInfo();
        if (i == null)
            return false;
        if (!i.isConnected())
            return false;
        if (!i.isAvailable())
            return false;
        return true;
    }

    @Override
    public void onDestroy() {
        logger("onDestroy() called");
        try {
            client.disconnect();
            Log.i("TAG", "Service stopped");
        } catch (MqttException e) {
            e.printStackTrace();
        }
        super.onDestroy();
    }

    @Override
    public void deliveryComplete(IMqttDeliveryToken arg0) {
        // TODO Auto-generated method stub
    }
}
4

3 回答 3

0

一些背景:

创建服务时,您必须确保您的工作是在后台线程中启动的。IntentService 在后台线程上运行,而 Service 在主线程上运行。

默认情况下,服务在托管它的应用程序的主线程中运行

来源:http: //developer.android.com/guide/components/services.html

看看http://developer.android.com/guide/components/services.html#ExtendingIntentService

阅读下面的类似问题。

类似的答案:Service vs IntentService

Service 可以在没有 UI 的任务中使用,但不应该太长。如果需要执行长任务,则必须在 Service 中使用线程。

另外,我建议阅读 CommonsWare 对如何始终在后台运行服务的回答?

我的建议:

我会转移到 IntentService 或 WakefulIntentService 并考虑以固定间隔更新信息,而不是使用恒定的 tcp 连接。基于 HTTP 的 API 可以通过 SSL 提供相同的信息。

于 2013-10-16T14:10:33.087 回答
0

听起来好像您的服务正在应用程序进程中运行;它与您的活动直接相关吗?

您将希望完全以不同的过程运行它;您可以通过在清单中添加以下声明来做到这一点:

<service
    android:name=".ServiceClassName"
    android:process=":yourappname_background" >

然后对任何接收器声明也使用相同的 android:process 属性。

于 2013-10-10T18:42:09.957 回答
0

绑定到您的服务是否使其保持活力?

于 2013-10-10T20:07:35.030 回答