2013-03-07 57 views
0

和我之前的很多人一樣,我正在使用asmack和Openfire寫一個聊天應用程序。 這仍然是非常基本的,但我已經設法發送和接收消息與用戶在模擬器與火花和其他人一起。確保綁定服務

經過一些SO閱讀後,我決定爲我的XMPP連接創建一個服務,並將其綁定到每個活動。我目前有三項活動

  • MainActivity(用戶登錄和連接到XMPPconnection)。
  • RosterActivity(包含一個ListView用戶的聯繫人)
  • ChatActivity

我的問題是雙重的:

  1. 是否有必要每一次活動的服務綁定,或有可能只需將MainActivity綁定到它並將XMPPConnection作爲額外的參數傳遞給它?如果是這樣,那麼傳球怎麼辦呢?

  2. 我登錄並啓動RosterActivity後,我在onCreate()方法中綁定了服務。在onStart方法中,如果我檢查mBound變量,它總是爲false。我試過SystemClock.sleep()只是爲了看它是否會起作用,而不是。令我感到困惑的是,當我第一次寫這個Activity的時候,我使用了一個按鈕,當點擊這個按鈕時,會啓動該過程來填充列表。這工作完美。

    那麼我錯過了什麼?我顯然不希望用戶不得不按下按鈕來查看聯繫人,我想要在onStart()中填充列表。爲什麼當我試圖通過onClickListener訪問服務時會綁定服務,爲什麼它在onStart中不起作用?

    我猜它具有一切與綁定是異步的,但我試圖找出究竟是什麼。

MainActivity:

package com.example.smack_text; 

import android.app.Activity; 
import android.content.ComponentName; 
import android.content.Context; 
import android.content.Intent; 
import android.content.ServiceConnection; 
import android.os.Bundle; 
import android.os.IBinder; 
import android.util.Log; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.EditText; 
import android.widget.Toast; 

public class MainActivity extends Activity 
{ 
XMPPService mService; 
boolean mBound = false; 
Button logBtn; 
Button disBtn; 
EditText userTxt; 
EditText passTxt; 

@Override 
protected void onCreate(Bundle savedInstanceState) 
{ 
super.onCreate(savedInstanceState); 
setContentView(R.layout.activity_main); 
//  BIND SERVICE 
Intent intent = new Intent(getApplicationContext(), XMPPService.class); 
bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 
} 

@Override 
protected void onStart() 
{ 
super.onStart(); 

userTxt = (EditText) findViewById(R.id.userTxt); 
passTxt = (EditText) findViewById(R.id.passTxt); 

logBtn = (Button) findViewById(R.id.logBtn); 
disBtn = (Button) findViewById(R.id.disBtn); 
logBtn.setOnClickListener(new OnClickListener() 
    { 

    @Override 
    public void onClick(View v) 
     { 
     final String user = new String(userTxt.getText().toString()); 
     final String pass = new String(passTxt.getText().toString()); 

     if(user=="" || pass=="") 
      { 
      Toast.makeText(getApplicationContext(), "Enter name and pass",   
Toast.LENGTH_LONG).show(); 
      } 
     if(mBound) 
     { 
       mService.connect(user,pass); 
       Log.d("Alex","connected"); 
     } 
     else 
     { 
      Log.d("Alex","error in connecting"); 
     } 
     Intent roster = new Intent(); 
     roster.setClass(getApplicationContext(), RosterActivity.class); 
     startActivity(roster); 
    } 
}); 

disBtn.setOnClickListener(new OnClickListener() 
{ 
@Override 
public void onClick(View v) 
    { 
    if(mBound) 
     { 
     mService.disconnect(); 
     Log.d("Alex","disconnected"); 
     } 
    else 
     { 
     Log.d("Alex","error in disconnecting"); 
     } 
    } 
}); 


} 

@Override 
protected void onDestroy() 
{ 
// Unbind from the service 
if (mBound) 
    { 
    unbindService(mConnection); 
    mBound = false; 
    } 
super.onDestroy(); 
} 

private ServiceConnection mConnection = new ServiceConnection() 
{ 
@Override 
public void onServiceConnected(ComponentName name, IBinder service) 
    { 
    mService = ((XMPPService.LocalBinder)service).getService(); 
    mBound = true; 
    } 

@Override 
public void onServiceDisconnected(ComponentName name) 
    { 
    mBound = false; 
    } 
}; 
} 

RosterActivity:

package com.example.smack_text; 

import java.util.Collection; 
import org.jivesoftware.smack.Roster; 
import org.jivesoftware.smack.RosterEntry; 
import org.jivesoftware.smack.packet.Presence; 
import android.app.AlertDialog; 
import android.app.ListActivity; 
import android.content.ComponentName; 
import android.content.Context; 
import android.content.DialogInterface; 
import android.content.Intent; 
import android.content.ServiceConnection; 
import android.os.Bundle; 
import android.os.IBinder; 
import android.os.SystemClock; 
import android.util.Log; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.ArrayAdapter; 
import android.widget.Button; 
import android.widget.ListView; 
import android.widget.Toast; 

public class RosterActivity extends ListActivity{ 

boolean mBound = false; 
XMPPService mService; 
Button btn; 


@Override 
public void onCreate(Bundle savedInstanceState) 
    { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.roster); 

    Intent intent = new Intent(getApplicationContext(), XMPPService.class); 
    bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 
    } 

@Override 
public void onStart(){ 
    super.onStart(); 
//  btn = (Button) findViewById(R.id.button1); 
//  btn.setOnClickListener(new OnClickListener() { 

//   @Override 
//   public void onClick(View v) { 

      if(mBound){ 
       Log.d("Alex","roster connected"); 

       Roster roster = mService.connection.getRoster(); 
//     XWRIS TO RELOAD DN DOULEYEI 
       roster.reload(); 

       Integer length = roster.getEntryCount(); 
       String[] users = new String[length]; 
       String[] userPresence = new String[length]; 

       Integer i=0; 

       Collection<RosterEntry> entries = roster.getEntries(); 

       for(RosterEntry entry:entries){ 
        users[i] = entry.getName(); 


Presence tmpPres = roster.getPresence(entry.getUser()); 
        userPresence[i] = tmpPres.toString(); 
        Log.d("RosterActivity" , entry.getUser().toString()); 

        i++; 

       } 


ArrayAdapter<String> adapter = new ArrayAdapter<String> (RosterActivity.this, 

android.R.layout.simple_expandable_list_item_1,  users); 
        setListAdapter(adapter); 



      } 
      else{ 

Toast.makeText(getApplicationContext(), "service not bound yet", Toast.LENGTH_LONG).show(); 
      } 

     } 
//  }); 
// } 

@Override 
protected void onDestroy() { 

    // Unbind from the service 
    if (mBound) { 
     unbindService(mConnection); 
     mBound = false; 
    } 
    super.onDestroy(); 
} 

@Override 
protected void onListItemClick(ListView l, View v, int position, long id) { 


    // Creating the dialog 
    AlertDialog.Builder builder = new AlertDialog.Builder(this); 

    Object o = l.getItemAtPosition(position); 
    String str = o.toString(); 
    Log.d("Roster Activity",str); 

    builder.setTitle("Start Chat?"); 
    builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { 

     @Override 
     public void onClick(DialogInterface dialog, int which) { 
      Intent chat = new Intent(); 
      chat.setClass(getApplicationContext(), ChatActivity.class); 
      startActivity(chat); 
     } 
    }); 


    AlertDialog alert = builder.create(); 
    alert.show(); 
} 




private ServiceConnection mConnection = new ServiceConnection() { 


    @Override 
    public void onServiceConnected(ComponentName name, IBinder service) { 
     mService = ((XMPPService.LocalBinder)service).getService(); 

     mBound = true; 

    } 

    @Override 
    public void onServiceDisconnected(ComponentName name) { 
     mBound = false; 

    } 
    }; 

} 

XMPPService:

package com.example.smack_text; 

import java.io.File; 

import org.jivesoftware.smack.ConnectionConfiguration; 
import org.jivesoftware.smack.XMPPConnection; 
import org.jivesoftware.smack.XMPPException; 

import android.app.Service; 
import android.content.Intent; 
import android.os.Binder; 
import android.os.Build; 
import android.os.IBinder; 
import android.util.Log; 
import android.widget.Toast; 

public class XMPPService extends Service{ 

XMPPConnection connection; 
private final IBinder mBinder = new LocalBinder(); 


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

    /** 
    * Class used for the client Binder. Because we know this service always 
    * runs in the same process as its clients, we don't need to deal with IPC. 
    */ 
    public class LocalBinder extends Binder { 
     XMPPService getService() { 
      return XMPPService.this; 
     } 
    } 

    @Override 
    public IBinder onBind(Intent intent) { 
     return mBinder; 
    } 

    public void connect(final String user, final String pass) { 


     Log.d("Xmpp Alex","in service"); 


     ConnectionConfiguration config = new ConnectionConfiguration("10.0.2.2",5222); 

//   KEYSTORE SETTINGS 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 
      config.setTruststoreType("AndroidCAStore"); 
      config.setTruststorePassword(null); 
      config.setTruststorePath(null); 
     } else { 
      config.setTruststoreType("BKS"); 
      String path = System.getProperty("javax.net.ssl.trustStore"); 
      if (path == null) 
       path = System.getProperty("java.home") + File.separator + "etc" 
        + File.separator + "security" + File.separator 
        + "cacerts.bks"; 
      config.setTruststorePath(path); 
     } 

//   Create XMPP Connection 

     connection = new XMPPConnection(config); 

//   THELEI TO RUNNABLE ALLIWS DN TREXEI 

     new Thread(new Runnable() { 
      @Override 
      public void run() { 

       try { 
        connection.connect(); 
        connection.login(user, pass); 
        if(connection.isConnected()){ 
         Log.d("Alex", "connected biatch!"); 
//       try { 
//        Thread.sleep(5000); 
//       } catch (InterruptedException e) { 
//        e.printStackTrace(); 
//       } 
        } 
        else{ 
         Log.d("Alex","not connected"); 
        } 


       } catch (XMPPException e) { 
        e.printStackTrace(); 
       } 
      } 
     }).start(); 


    } 

    public void disconnect(){ 
     if(connection.isConnected()){ 
      connection.disconnect(); 
     } 

else{Toast.makeText(getApplicationContext(), "not  connected",Toast.LENGTH_LONG).show(); 
     } 
    } 

} 

而且佈局:

activity_main.xml中

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:background="#000000" 
tools:context=".MainActivity" > 

<EditText 
    android:id="@+id/userTxt" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_alignParentLeft="true" 
    android:layout_below="@+id/textView1" 
    android:layout_marginLeft="30dp" 
    android:layout_marginTop="27dp" 
    android:background="#FFFFFF" 
    android:ems="10" 
    android:inputType="textPersonName" > 

    <requestFocus /> 
</EditText> 

<TextView 
    android:id="@+id/textView1" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_alignLeft="@+id/userTxt" 
    android:layout_alignParentTop="true" 
    android:layout_marginLeft="14dp" 
    android:layout_marginTop="52dp" 
    android:background="#FFFFFF" 
    android:text="User Name :" /> 

<TextView 
    android:id="@+id/textView2" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_alignLeft="@+id/textView1" 
    android:layout_below="@+id/userTxt" 
    android:layout_marginTop="62dp" 
    android:background="#FFFFFF" 
    android:text="Password :" /> 

<EditText 
    android:id="@+id/passTxt" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_alignLeft="@+id/userTxt" 
    android:layout_below="@+id/textView2" 
    android:layout_marginTop="58dp" 
    android:background="#FFFFFF" 
    android:ems="10" 
    android:inputType="textPassword" /> 

<Button 
    android:id="@+id/logBtn" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_alignRight="@+id/textView2" 
    android:layout_below="@+id/passTxt" 
    android:layout_marginTop="66dp" 
    android:background="#FFFFFF" 
    android:text="Log In" /> 

<Button 
    android:id="@+id/disBtn" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_alignBottom="@+id/logBtn" 
    android:layout_alignRight="@+id/userTxt" 
    android:background="#FFFFFF" 
    android:text="disconnect" /> 

</RelativeLayout> 

roster.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:orientation="vertical" > 

<Button 
    android:id="@+id/button1" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="Button" /> 

<ListView 
    android:id="@android:id/list" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" > 
</ListView> 

</LinearLayout> 

回答

0

我一直認爲這樣的事情可能會有點混亂,所以我寫了中Vapor API使這個簡單的支持 - 設計的Android框架使應用程序開發更容易。

它隱式地爲你管理所有的綁定,並且允許你純粹由服務的類檢索一個綁定(你不需要在你的代碼中維護連接對象,它是爲你完成的)。

如果你想嘗試一下,你可以做你想做的是這樣的(使用VaporActivityVaporServiceBindable):

public class RosterActivity extends VaporActivity{ 

    public void create(VaporBundle bundle){ 

     $.srv(XMPPService.class); // optionally, first start the service 

     // set up the callback for when the service is bound 
     $.hook(SERVICE_BIND).hookIn(new $$hookee(){ 

      public void call(String hookName, VaporBundle args){ 

      // put your code here that depends on the binding... 

      } 

     }); 

     // bind to the service 
     $.bind(XMPPService.class); 


    } 

} 

這顯然你描述的那種問題的簡化骨架,基於這樣的事實綁定是異步的。

更重要的是,如果你想要再使用的方法從你的活動中您服務,您可以做到這一點很容易地從任何地方:

this.service(XMPPService.class).foo(); // some method in the service 

在引擎蓋這檢索時自動創建較早ServiceConnection爲您提供服務,併爲您提供訪問IBinder的服務。

如果您有興趣,您也應該查看VaporService。它在自己的線程中運行服務,您可以任意暫停,休眠和重新啓動,從而完全控制服務,而不用擔心血淋淋的細節。

希望有所幫助。

+0

非常感謝您的回覆,它看起來非常有用,但由於這是我的最終項目,我需要明確地做所有事情。 – countzero 2013-03-21 15:07:53