确保绑定服务

时间:2013-03-07 15:23:35

标签: android android-service

像我之前的许多人一样,我正在使用asmack和Openfire编写聊天应用程序。 它仍然非常基本,但我已经设法发送和接收用户Loged In with Spark和另一个模拟器的消息。

经过一些SO读取后,我决定为我的XMPP连接创建一个服务,并将其绑定到每个Activity。我目前有三项活动

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

我的问题有两个:

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

  2. 登录并启动RosterActivity后,我在onCreate()方法中绑定服务。在onStart方法中,如果我检查mBound变量,它总是假的。我试过SystemClock.sleep()只是为了看看它是否会起作用而它没有。让我感到困惑的是,当我第一次写这个Activity时,我使用了一个按钮,当点击它时,它将启动填充列表的过程。这非常有效。

    那么我错过了什么?我显然不希望用户只需按一个按钮来查看联系人,我希望列表在onStart()中填充。当我尝试从onClickListener内部访问服务时为什么绑定服务,为什么它不能在onStart上工作。

    我猜它与异步绑定有关,但我正在试图找出确切的内容。

  3. 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>
    

1 个答案:

答案 0 :(得分:0)

我一直认为这样的事情可能有点令人困惑,所以我写了一些支持,让这更简单到Vapor API - 一个Android框架,旨在让app dev变得更容易。

它隐式管理你的所有绑定,并且还允许你完全由服务类检索绑定(你不需要在你的代码中维护连接对象,它就是为你完成的)。

如果你想尝试一下,你可以按照你想要的那样做(使用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。它在自己的线程中运行服务,您可以随意暂停,休眠和重启,让您完全控制您的服务,而无需担心血腥的细节。

希望有所帮助。