联网有两种:
1,短连接(http连接):必须客户端发请求,服务器返回,连接断开,下一次再连接服务必须客户端先发请求。
2,长连接,实现服务器推送信息给客户端,客户端先连上服务器,连接不断开。客户端再发信息不用建立连接
1) Asmack 就是长连接框架,是做聊天应用的客户端的框架
2) Openfire服务器,类似于tomcat,tomcat是短连接,客户端请求,tomcat返回网页,openfire是长连接,把一个用户聊天说的话主动推送给所有人。
一,先将asmack-src源码文件里面的全部文件 拷贝到自己的项目 src里面,直接到工作空间里面覆盖
二,建聊天服务器 用openfire_3_8_1
因为连接是只要打开应用程序,建立连接之后,就会一直连着。所以放在Application 中 做成全局变量
public class TApplication extends Application {
static XMPPConnection xmppConnection;
public static MultiUserChat multiUserchat;
@Override
public void onCreate() {
super.onCreate();
int threadId = (int) Thread.currentThread().getId();
Log.i("openfire", Thread.currentThread().getId()
+ " application onCreate");
// 设置服务器信息
ConnectionConfiguration configuration = new ConnectionConfiguration(
"服务器IP地址", 服务器端口号, "服务器的域");
// 创立连接
xmppConnection = new XMPPConnection(configuration);
// 连接openfire 服务器
new Thread() {
public void run() {
try {
Log.i("openfire", "new XMPPConnection")
// 2.4 让框架中的接口指向实现类
MyPacketListener myPacketListener = new MyPacketListener();
xmppConnection.addPacketListener(myPacketListener, null);
//连上连接
xmppConnection.connect();
Log.i("openfire", Thread.currentThread().getId() + " 连接成功");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
synchronized (TApplication.class) {
TApplication.class.notify();
Log.i("openfire", Thread.currentThread().getId()
+ " 有人notify");
}
}
};
}.start();
}
// 2.3 写实现类
class MyPacketListener implements PacketListener {
@Override
public void processPacket(Packet packet) {
try {
// packet 数据包,服务器返回的数据
String objectInfo = packet.toString();
String data = packet.toXML();
Log.i("服务器发过来的数据", objectInfo);
Log.i("服务器发过来的数据", data);
// 判断数据是不是message
if (packet instanceof Message) {
Message msg = (Message) packet;
// 调用activity显示数据
MainActivity.instance.showMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
在Activity中
public class MainActivity extends Activity {
EditText etBody;
public static LinearLayout linearLayout;
public static MainActivity instance;
ScrollView scrollView;
Handler handler =new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance=this;
setContentView(R.layout.activity_main);
//初始化控件
etBody=(EditText) findViewById(R.id.etBody);
scrollView=(ScrollView) findViewById(R.id.scrollView1);
linearLayout=(LinearLayout) findViewById(R.id.linearLayout);
}
//发送图片
public void sendImage(View v){
try {
//读取sdcard卡跟目录
String sdcard_root=Environment.getExternalStorageDirectory().getAbsolutePath();
String filePath=sdcard_root+"/logo4.png";
FileInputStream fileInputStream=new FileInputStream(filePath);
//从文件中读到的是byte[]
//数据大小
int size=fileInputStream.available();
byte[] data=new byte[size];
fileInputStream.read(data);
//把byte[]变成Sing
//String string=new String(data);
//对方是android收到后 string.getBytes()-->bitmap
//对方是iphone,用base64
//base64在iphone上也能用
//把byte[]变成字符串
String string=Base64.encodeToString(data, Base64.DEFAULT);
//字符串前边加个标记 iamge
string="image"+string;
//发送
TApplication.multiUserchat.sendMessage(string);
} catch (Exception e) {
e.printStackTrace();
}
}
// 发送文本 文字
public void sendText(View v)
{
try {
String body=etBody.getText().toString();
//发内容前加标记tag,告诉收到的人这个信息是text
body="text"+body;
//发给openfire,openfire会把内容发给所有人
TApplication.multiUserchat.sendMessage(body);
//显示在我的屏幕上
//TextView tv=new TextView(this);
//tv.setText(body);
//linearLayout.addView(tv);
} catch (XMPPException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
// 把服务器发给我的信息 显示在屏幕上
public void showMessage(Message msg)
{
// 得到from ,body
//from 是昵称
final String from = msg.getFrom();
final String body = msg.getBody();
if (body == null) {
return;
}
if (MainActivity.instance != null) {
//在主线程更新UI
MainActivity.instance.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
// 运行在主线程
// textView
int threadId = (int) Thread.currentThread().getId();
// 判断收到的是文本还是图
if (body.startsWith("image")) {
// 收到的图,实际收到的是用base64编码出来的string
// 去掉前面image
String image = body.substring(5);
byte[] data = Base64.decode(image,Base64.DEFAULT);
// byte[]-->bitmap;
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,data.length);
ImageView imageView = new ImageView(MainActivity.instance);
imageView.setImageBitmap(bitmap);
MainActivity.instance.linearLayout.addView(imageView);
} else if (body.startsWith("text")) {
//判断是文字
TextView tv = new TextView(MainActivity.instance);
// 去掉前面加的text
tv.setText(from + ":"+ body.substring(4));
// linearLayout
MainActivity.linearLayout.addView(tv);
}
//自动向上移
//最后一行不显示,解决方案 sleep(延迟)之后再向上移
// 因为在主线程 需要把所有的消息都放到消息队列,这个过程需要点时间。
//所以需要延迟一些时间,然后让线程计算linearLayout高度,再移动到最后一条消息的位置
Log.i("执行顺序", Thread.currentThread().getId()+" 1");
handler.postDelayed(new Runnable() {
@Override
public void run() {
Log.i("执行顺序", Thread.currentThread().getId()+" 2");
//向上移动,得linearLayout高度
scrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
}, 0);//在samsung s4真机上,时间不能是0
Log.i("执行顺序", Thread.currentThread().getId()+" 3");
// 执行顺序是1 3 2
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
}
LogActivity 是负责登录的业务 ,在这个业务里面,TApplication 建立连接跟 登录业务不是一个线程
这就产生了 有先有后的次序,有可能登录的线程比建立连接的线程更快,就产生了登录失败的结果。
为了避免出现这种情况, 就需要用到 wait() 与 Notify() 等待 与通知方法。不能用延迟的方法
postDelayed(),因为用户可能要输入用户名与密码,这已经跟建立连接的线程产生时间差了,就不需要再延迟了。
所以现在用到了 如果建立连接不成功,就先等待,等通知的方法。
public class LoginActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
//登录需要启工作线程,
new Thread() {
public void run() {
try {
// 登录服务器
Log.i("openfire", Thread.currentThread().getId() + " 开始登录 "
+ TApplication.xmppConnection);
// 开始登录前要判断连接是否成功
if (TApplication.xmppConnection.isConnected() == false) {
// 不成功,等
synchronized (TApplication.class) {
Log.i("openfire", Thread.currentThread().getId()+ " 开始wait");
TApplication.class.wait();
}
}
Log.i("openfire", Thread.currentThread().getId()+ " 有人notify,继续执行login");
TApplication.xmppConnection.login("UserName", "pwd");
boolean isSuccess = TApplication.xmppConnection.isAuthenticated();
//如果打印下边的这句话 true 登录成功
Log.i("openfire", "登录结果" + isSuccess);
// 进到群Multi多
String room = "房间号@conference.服务器域";
multiUserchat = new MultiUserChat(TApplication.xmppConnection, room);
multiUserchat.join("昵称");
//测试
// multiUserchat.sendMessage("能看到我从android发的内容吗");
// 登录成功之后 需要从登录界面转到发送消息界面
LoginActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
startActivity(new Intent(LoginActivity.this,MainActivity.class));
}
});
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
}
}