0

像我之前的许多人一样,我正在使用 asmack 和 Openfire 编写一个聊天应用程序。它仍然很基本,但我已经成功地与一个用 Spark 登录的用户和另一个在模拟器上登录的用户发送和接收消息。

在阅读了一些 SO 之后,我决定为我绑定到每个 Activity 的 XMPP 连接创建一个服务。我目前有三个活动

  • MainActivity(用户登录并连接到 XMPPconnection)。
  • RosterActivity(包含用户联系人的列表视图)
  • 聊天活动

我的问题是双重的:

  1. 是否有必要将每个活动绑定到服务,或者是否可以将 MainActivity 绑定到它并将 XMPPConnection 作为额外的传递?如果是这样,如何通过?

  2. 登录并启动 RosterActivity 后,我在 onCreate() 方法中绑定服务。在 onStart 方法中,如果我检查 mBound 变量,它总是错误的。我已经尝试过 SystemClock.sleep() 只是为了看看它是否可以工作,但它没有。真正让我困惑的是,当我第一次编写这个 Activity 时,我使用了一个按钮,单击该按钮将启动填充列表的过程。那工作得很好。

    那么我错过了什么?我显然不希望用户必须按下按钮才能查看联系人,我希望在 onStart() 处填充列表。为什么当我尝试从 onClickListener 内部访问它时服务绑定,为什么它在 onStart 上根本不起作用。

    我猜它与异步绑定有关,但我正试图找出究竟是什么。

主要活动 :

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;
    }
};
} 

名册活动:

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;

    }
    };

}

XMPP服务:

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>

名册.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>
4

1 回答 1

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);


    }

}

基于绑定是异步的事实,这显然是您描述的那种问题的简化框架。

更重要的是,如果您想在 Activity 中使用 Service 中的方法,您可以在任何地方轻松地做到这一点:

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

这会在后台检索ServiceConnection自动为您创建的较早版本,并为您提供IBinder对服务的访问权限。

如果您有兴趣,也应该查看VaporService 。它在自己的线程中运行服务,您可以任意暂停、休眠和重新启动,让您完全控制您的服务,而不必担心血淋淋的细节。

希望有帮助。

于 2013-03-07T15:48:20.877 回答