这几天由于要做一个解决方案,才来学XMPP方案,Android+Smack+Openfire方案,其实这已经是很成熟的方案了。但因为没做过,所以有必要总结一下:
第一,PC的问题,很容易,也很快。只要解决防火墙的问题就可以解决了。示例代码如下:
1、GlobalHelper.java类
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Registration;
public class GlobalHelper {
private static Connection con;
private static String Service_Name="www.example.com";
public static Connection CreateConnection()
{
// Create the configuration for this new connection
XMPPConnection.DEBUG_ENABLED=false;
ConnectionConfiguration config = new ConnectionConfiguration("172.168.1.100", 5222);
config.setCompressionEnabled(false);
config.setSASLAuthenticationEnabled(false);
System.out.println("T1");
if(con==null||con.isConnected()==false)
{
try
{
con=new XMPPConnection(config);
System.out.println("T2");
con.connect();
System.out.println("T3");
return con;
}
catch(XMPPException e)
{
System.out.println("T4");
System.out.println(e.getMessage());
e.printStackTrace();
return null;
}
}
return con;
}
}
2、testopenfire.java类
import java.io.File;
import java.util.Collection;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smackx.filetransfer.FileTransfer;
import org.jivesoftware.smackx.filetransfer.FileTransferManager;
import org.jivesoftware.smackx.filetransfer.OutgoingFileTransfer;
public class testopenfire {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String account="test";
String password="test";
String retStr=Register(account, password);
if(retStr=="0")
System.out.println("fail");
else
System.out.println("success");
getFriends(GlobalHelper.CreateConnection());
sendMessage("test1@example.com","我的测试数据",GlobalHelper.CreateConnection());
SendFile(GlobalHelper.CreateConnection(),"test1@example.com");
}
public static String Register(String account,String password)
{
Connection conn=GlobalHelper.CreateConnection();
if(conn==null)
return "0";
if(conn.isConnected()==false)
return "0";
try
{
System.out.println( "连接成功");
conn.login(account, password);
System.out.println("登录成功");
return "1";
}
catch(XMPPException e)
{
System.out.println("登录失败:"+e.getMessage());
return "0";
}
}
public static boolean sendMessage(String JID,String body,Connection connection)
{
System.out.println("发送消息开始。。。。。");
Message newmsg=new Message();
newmsg.setTo(JID);
newmsg.setBody(body);
newmsg.setType(Message.Type.normal);
try
{
connection.sendPacket(newmsg);
System.out.println("发送消息结束。。。。。成功!");
return true;
}
catch(Exception e)
{
System.out.println("发送消息结束。。。。。失败!");
return false;
}
}
public static void getFriends(Connection connection)
{
System.out.println("获取好友列表");
Collection<RosterEntry> rosters=connection.getRoster().getEntries();
for(RosterEntry rosterEntry:rosters)
{
System.out.println("name:"+rosterEntry.getName()+",jid:"+rosterEntry.getUser());
}
}
public static void SendFile(Connection connection,String User) throws Exception
{
System.out.println("进入发送文件方法。。。"+User);
Presence pre=connection.getRoster().getPresence(User);
System.out.println(pre);
if(pre.getType()!=Presence.Type.unavailable)
{
FileTransferManager manager=new FileTransferManager(connection);
OutgoingFileTransfer transfer=manager.createOutgoingFileTransfer(pre.getFrom());
transfer.sendFile(new File("test.png"), "图片");
while(!transfer.isDone())
{
if(transfer.getStatus()==FileTransfer.Status.in_progress)
{
System.out.println(transfer.getStatus());
System.out.println(transfer.getProgress());
System.out.println(transfer.isDone());
}
}
}
}
}
以上是PC上的问题,如果连不上,基本都是防火墙的事。只要连不上,把防火墙关了就行了。
第二,就是Android手机或模拟器的问题了。这个问题经常出现如下三类错误,但网上又找不到,问又没解决方案。下面就我碰到的提出解决方案:
前提条件:
PC(或笔记本,无线或有线都可以,全试过)->路由<-手机(无线)
具体方案:
1、PC防火墙的问题,关闭就行了。
2、解决网络是否通的问题。在手机或模拟器上安装FPing ,解决Ping我PC地址是否通的问题,下载Easy Telnet解决端口5222(默认)是否通的问题。如果不通就得检查一下哪里出了问题。另外得从PC解决Ping我的手机的问题。获取手机IP的问题有如下几种方法:
1)自己写一个程序,获取本地IP。
2)网上随便下载一个获取本地IP的软件,N多,非常容易。
3)从路由上获取IP
以上这些都比较容易从PC Ping手机IP。通过以上的方法解决手机和电脑网络没问题。
3、以上确认完后,然后就是Android的问题了。
1)确认你的程序没问题,不同Smack版本,写法都是对应版本的写法,最简单的办法都是下载版本文档中都有一个对应的示例代码,各种参数设置是正确的。
2)第1)种情况都没问题的情况下,出现如下错误:
XMPPError connecting to 172.168.1.100:5222.: remote-server-error(502) XMPPError connecting to 172.168.1.100:5222.
-- caused by: java.net.SocketException: socket failed: EACCES (Permission denied)
这个问题的解决方案,在AndroidManifest.xml中加入如下代码就行了:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
前两条语句最重要。
3)就是解决Android的Bug问题(这是我个人的理解,已经N年了Google还是没解决这个问题),出现的问题如下:
java.lang.IllegalArgumentException: Can't initialize the configured debugger!
这个问题之前我有关在讲网络编程时好象讲过。只要将如下的语句加进来就可以了。具体如下:
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects().detectLeakedClosableObjects() .penaltyLog().penaltyDeath().build());
这个问题已经不是问题了,做Android网络编程的人(游戏、网购等的)非常熟悉。但对于初学者,没有愿意帮介绍。我今天总结一下,给大家发出来。我查过一天,翻墙等都没解决这个问题。没办法,一直在试,当快抢劫信心时,发现了问题所在。
全部原码如下,不通再找我:
package com.example.xmppclient;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.StrictMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class LoginActivity extends Activity {
private Button btnLogin;
private Button btnExit;
private EditText txtUserID;
private EditText txtPWD;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork()
.penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects().detectLeakedClosableObjects()
.penaltyLog().penaltyDeath().build());
setContentView(R.layout.login);
btnLogin=(Button)findViewById(R.id.btnLogin);
btnExit=(Button)findViewById(R.id.btnExit);
txtUserID=(EditText)findViewById(R.id.txtUserID);
txtPWD=(EditText)findViewById(R.id.txtPWD);
btnLogin.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
//XMPPConnection.DEBUG_ENABLED=true;
try
{
ConnectionConfiguration config = new ConnectionConfiguration("172.168.1.100", 5222);
config.setCompressionEnabled(false);
config.setSASLAuthenticationEnabled(false);
Connection conn2 = new XMPPConnection(config);
conn2.connect();
String account=txtUserID.getText().toString();
String password=txtPWD.getText().toString();
conn2.login(account, password);
//Toast.makeText(getApplicationContext(),"登录成功", 5000).show();
Intent intent=new Intent(LoginActivity.this,MainActivity.class);
startActivity(intent);
}
catch(XMPPException e)
{
Toast.makeText(getApplicationContext(), e.getMessage(), 5000).show();
}
catch(Exception ex)
{
Toast.makeText(getApplicationContext(), ex.getMessage(), 5000).show();
}
}});
btnExit.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
LoginActivity.this.finish();
}});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.login, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
XML文件如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.xmppclient.LoginActivity" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
<EditText
android:id="@+id/txtUserID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="test"
android:ems="10" >
<requestFocus />
</EditText>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
<EditText
android:id="@+id/txtPWD"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:text="test"
android:inputType="textPassword" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/btnLogin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Login" />
<Button
android:id="@+id/btnExit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Exit" />
</LinearLayout>
</LinearLayout>