服务中的 Xmpp 连接在其进程完成后关闭

Posted

技术标签:

【中文标题】服务中的 Xmpp 连接在其进程完成后关闭【英文标题】:Xmpp connection in service gets closed when its process is finished 【发布时间】:2013-12-18 13:19:14 【问题描述】:

我正在开发一个 android 聊天应用程序。我在应用程序中有一个活动和一个服务。这里的服务包含所有连接功能,活动和服务之间的通信是使用活页夹完成的。我的服务在后台运行但是一段时间后,当进程完成时,连接会自动关闭,我在模拟器的设置菜单中看到了这一点。我已经看到了两个选项,其中两个首先克服了这个问题,即在一个单独的进程中运行服务,我现在试图避免。另一个是在单独的线程中运行连接,但我需要有人来帮助我,因为我无法这样做。这是我的代码

Service.java

    public class MainService extends Service
private XMPPConnection connection;
private final IBinder mBinder = new MyBinder();

    @Override
public IBinder onBind(Intent arg0) 
    // TODO Auto-generated method stub
    return mBinder;


    public int onStartCommand(Intent intent, int flags, int startId) 
Log.d("service","started");
new Connect().execute("");
return START_STICKY;
    

    private class Connect extends AsyncTask<String, Void, String> 

@Override
protected String doInBackground(String... params) 
    ConnectionConfiguration connConfig = new ConnectionConfiguration(SERVICE,PORT);
    XMPPConnection connection = new XMPPConnection(connConfig);
    try 
        connection.connect();
        Log.i("XMPPChatDemoActivity",
                "Connected to " + connection.getHost());
     catch (XMPPException ex) 
        Log.e("XMPPChatDemoActivity", "Failed to connect to "
                + connection.getHost());
        Log.e("XMPPChatDemoActivity", ex.toString());
        setConnection(null);
    
    try 
        // SASLAuthentication.supportSASLMechanism("PLAIN", 0);
        connection.login(USERNAME, PASSWORD);
        Log.i("XMPPChatDemoActivity",
                "Logged in as " + connection.getUser());

        // Set the status to available
        Presence presence = new Presence(Presence.Type.available);
        connection.sendPacket(presence);
        setConnection(connection);
        Roster roster = connection.getRoster();
        Collection<RosterEntry> entries = roster.getEntries();
        for (RosterEntry entry : entries) 
            Log.d("XMPPChatDemoActivity",
                    "--------------------------------------");
            Log.d("XMPPChatDemoActivity", "RosterEntry " + entry);
            Log.d("XMPPChatDemoActivity",
                    "User: " + entry.getUser());
            Log.d("XMPPChatDemoActivity",
        



        "Name: " + entry.getName());
            Log.d("XMPPChatDemoActivity",
                    "Status: " + entry.getStatus());
            Log.d("XMPPChatDemoActivity",
                    "Type: " + entry.getType());
            Presence entryPresence = roster.getPresence(entry
                    .getUser());

            Log.d("XMPPChatDemoActivity", "Presence Status: "
                    + entryPresence.getStatus());
            Log.d("XMPPChatDemoActivity", "Presence Type: "
                    + entryPresence.getType());
            Presence.Type type = entryPresence.getType();
            if (type == Presence.Type.available)
                Log.d("XMPPChatDemoActivity", "Presence AVIALABLE");
            Log.d("XMPPChatDemoActivity", "Presence : "
                    + entryPresence);

        
     catch (XMPPException ex) 
        Log.e("XMPPChatDemoActivity", "Failed to log in as "
                + USERNAME);
        Log.e("XMPPChatDemoActivity", ex.toString());
        setConnection(null);
    
    return null;       


    public void setConnection(XMPPConnection connection) 
this.connection = connection;
if (connection != null) 
    // Add a packet listener to get messages sent to us
    PacketFilter filter = new MessageTypeFilter(Message.Type.chat);
    connection.addPacketListener(new PacketListener() 
        public void processPacket(Packet packet) 
            Message message = (Message) packet;
            if (message.getBody() != null) 
                String fromName = StringUtils.parseBareAddress(message
                        .getFrom());
                Log.i("XMPPChatDemoActivity", "Text Recieved " + message.getBody()
                        + " from " + fromName );
            
        
    , filter);

    

   public class MyBinder extends Binder 
    
        MainService getService() 
        return MainService.this;
      
      

    public XMPPConnection getconnection()
    
if (connection != null) 
    Log.d("MainService","connection send");
    return connection;  

else

    Log.d("MainService","connection null");
return null;    

主要活动

    public class XMPPChatDemoActivity extends Activity 
private MainService service;
private XMPPConnection connection;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    if(!isMyServiceRunning())  
    
    startService(new Intent(this,MainService.class));
    
    
 @Override
  protected void onResume() 
    bindService(new Intent(this, MainService.class), mConnection,
        Context.BIND_AUTO_CREATE);
    super.onResume();
  

  @Override
  protected void onPause() 
    unbindService(mConnection);
    super.onPause();
  
     private ServiceConnection mConnection = new ServiceConnection() 

        public void onServiceConnected(ComponentName className, IBinder binder) 
          service = ((MainService.MyBinder) binder).getService();
          Log.d("Service","Connected");
          Toast.makeText(XMPPChatDemoActivity.this, "Connected",                              Toast.LENGTH_SHORT)
              .show();
            connection=service.getconnection();
        

        public void onServiceDisconnected(ComponentName className) 
            connection=null;
            service = null;
        
      ;
private boolean isMyServiceRunning() 
    ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) 
        if (MainService.class.getName().equals(service.service.getClassName())) 
            return true;
        
    
    return false;

【问题讨论】:

【参考方案1】:

你们俩都提到了看似解决方案并没有解决问题:Android 会在资源不足时终止服务。通常,您可以通过以下任一方式解决此问题

    将服务标记为前台(在这种情况下不推荐1

    将服务标记为STICKY,以便在被杀死后重新启动

我真的推荐使用STICKY 方法。使用前台活动运行服务会更简单,因为您只需添加一行额外的代码,但它也会阻止 Android 在其他地方释放急需的资源。如果您的服务作为前台运行并遭受内存泄漏或由于某些其他原因导致内存使用率过高,则设备将变慢并且 Android 无法修复这种情况。

因此,最好的方法是将您的服务标记为STICKY,并在重新启动后恢复其状态。所有优秀的 Android XMPP 客户端都会这样做。

1:前台服务适用于重启会导致不良用户体验的服务,例如如果音乐播放器服务重新启动,用户会注意到它。这是适合将服务标记为前台的典型示例。

【讨论】:

我的服务已经被标记为粘性你可以在服务代码中看到 onstartcommand 返回值是粘性的。我的服务在后台运行并且可以接收传入消息但是当相应的进程关闭时连接变为null 并且我无法接收消息。我已经阅读了您之前的答案,您告诉创建一个新线程,因为服务没有任何与之关联的进程,所以我创建了异步任务,但它仍然没有帮助..... 一个线程总是有一个与之关联的进程。我可能说过,维护连接实例的 Android 服务应该在一个额外的线程中运行/使用循环器,这样 UI 线程就不会阻塞,如果它正在做任何工作。 最好的解决方案是在服务被杀死并重新启动后重新创建状态,这包括重新建立 XMPP 连接。 这就是每个好的 Android XMPP 客户端所做的。

以上是关于服务中的 Xmpp 连接在其进程完成后关闭的主要内容,如果未能解决你的问题,请参考以下文章

面向连接的Socket服务端关闭问题

使用 XMPP 关闭 GCM 连接服务器的套接字

GCM XMPP 交付回执发送其他机器,GCM 收到其确认后关闭连接

XMPP android中的连接因错误而关闭

为啥 MongooseIM 会在 60 秒后关闭 websocket 连接?

使用 xmedianet 库在“资源绑定”进程上挂起的 XMPP 连接