像我之前的许多人一样,我正在使用asmack和Openfire编写聊天应用程序。 它仍然非常基本,但我已经设法发送和接收用户Loged In with Spark和另一个模拟器的消息。
经过一些SO读取后,我决定为我的XMPP连接创建一个服务,并将其绑定到每个Activity。我目前有三项活动
我的问题有两个:
是否有必要将每个活动绑定到服务,或者是否可以将MainActivity绑定到它并将XMPPConnection作为额外传递?如果是这样,如何完成传递?
登录并启动RosterActivity后,我在onCreate()方法中绑定服务。在onStart方法中,如果我检查mBound变量,它总是假的。我试过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 :(得分:0)
我一直认为这样的事情可能有点令人困惑,所以我写了一些支持,让这更简单到Vapor API - 一个Android框架,旨在让app dev变得更容易。
它隐式管理你的所有绑定,并且还允许你完全由服务类检索绑定(你不需要在你的代码中维护连接对象,它就是为你完成的)。
如果你想尝试一下,你可以按照你想要的那样做(使用VaporActivity和VaporServiceBindable):
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。它在自己的线程中运行服务,您可以随意暂停,休眠和重启,让您完全控制您的服务,而无需担心血腥的细节。
希望有所帮助。