让聊天显示在屏幕上
过程分析
获取聊天内容
MyApplication
public class MyApplication extends Application {
......
@Override
public void onCreate() {
super.onCreate();
//连接服务器,起一个线程
new Thread() {
@Override
public void run() {
try {
......
//让框架的接口指向实现类
MyPacketListener listener = new MyPacketListener();
xmppConnection.addPacketListener(listener,null);
} catch (XMPPException e) {
e.printStackTrace();
}
}
}.start();
}
class MyPacketListener implements PacketListener{
@Override
public void processPacket(Packet packet) {
//packet包,openfire发过来的数据包
//判断是不是聊天内容
if(packet instanceof Message){
Message msg = (Message)packet;
String from = msg.getFrom();
String body = msg.getBody();
Log.i("MyPacketListener","from:"+from+",body:"+body);
}
}
}
}
当在屏幕上发送
查看打印日志
显示在屏幕
MainActivity中写一个接收器,当收到广播后,LinearLayout将动态添加内容
public class MainActivity extends AppCompatActivity {
......
LinearLayout linearLayout;
ShowMessageReceiver showMessageReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
......
scrollView = findViewById(R.id.scroll_view);
linearLayout = findViewById(R.id.ll_content);
//注册接收器
showMessageReceiver = new ShowMessageReceiver();
registerReceiver(showMessageReceiver,new IntentFilter("showMessage"));
}
@Override
protected void onDestroy() {
//注销接收器
this.unregisterReceiver(showMessageReceiver);
super.onDestroy();
}
......
class ShowMessageReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String from = intent.getStringExtra("from");
String body = intent.getStringExtra("body");
TextView textView = new TextView(context);
textView.setText(from+":"+body);
linearLayout.addView(textView);
}
}
}
MyApplication中获取内容后,发送广播
class MyPacketListener implements PacketListener{
@Override
public void processPacket(Packet packet) {
if(packet instanceof Message){
......
//发广播给接收器
Intent intent = new Intent("showMessage");
intent.putExtra("from",from);
intent.putExtra("body",body);
sendBroadcast(intent);
}
}
}
发送图片
我们向服务器发的信息,实际上是xml
在MyApplication中的MyPacketListener里打印看一下
Log.i("MyPacketListener",packet.toXML());
<message
id="J06zI-5"
to="spark@127.0.0.1/Smack"
from="729@conference.127.0.0.1/Errol_King"
type="groupchat">
<body>少用塑料袋</body>
<stanza-id xmlns="urn:xmpp:sid:0"></stanza-id>
</message>
我们在assets文件夹放一个“study.jpg”图片
在布局上加一个发送图片的按钮,点击执行sendImage()来发送图片
MainActivity中主要代码:
public class MainActivity extends AppCompatActivity {
......
Button buttonImg;
......
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
......
buttonImg = findViewById(R.id.send_img);
buttonImg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendImg();
}
});
}
......
private void sendImg() {
try {
//读取assets下的图片
AssetManager manager = getAssets();
InputStream inputStream = manager.open("study.jpg");
int size = inputStream.available();
byte[] data = new byte[size];
inputStream.read(data);
//图片转成文字
//base64:跨语言的编码方式
String str = Base64.encodeToString(data,Base64.DEFAULT);
//发送时加tag标记表示数据类型
str = "<image>"+str;
//发送
MyApplication.multiUserChat.sendMessage(str);
} catch (IOException e) {
e.printStackTrace();
} catch (XMPPException e) {
e.printStackTrace();
}
}
class ShowMessageReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String from = intent.getStringExtra("from");
String body = intent.getStringExtra("body");
//根据tag判断数据的类型
if(body != null && body.startsWith("<text>")){
body = body.substring("<text>".length());
TextView textView = new TextView(context);
textView.setText(from+":"+body);
linearLayout.addView(textView);
}
if(body != null && body.startsWith("<image>")){
//把前边的<Image>去掉
body = body.substring("<image>".length());
//把字符串转为byte[]
byte[] data = Base64.decode(body,Base64.DEFAULT);
//创建bitmap
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
//创建ImageView
ImageView imageView = new ImageView(context);
imageView.setImageBitmap(bitmap);
//把ImageView放到LinearLayout
linearLayout.addView(imageView);
}
}
}
}
ScrollView向上移动
MainActivity关键代码
class ShowMessageReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
......
//scrollview向上移动
//最后的内容得不到,解决方法是,等一会儿再取值
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
//因为ScrollView已经显示出来了,所以调用getHeight()来直接获取高度
int scrollViewHeight = scrollView.getHeight();
int linearLayoutHeight = linearLayout.getHeight();
Log.d("向上移","scrollViewHeight:"+scrollViewHeight+",linearLayoutHeight"+linearLayoutHeight);
if(linearLayoutHeight > scrollViewHeight){
int y = linearLayoutHeight - scrollViewHeight;
scrollView.scrollTo(0,y);
}
}
},10);
}
}
多线程
创建LoginBiz,用来处理登录,把MyApplication中的3、4步拿过来
public class LoginBiz {
public void login(final LoginActivity activity){
new Thread(){
@Override
public void run() {
try {
//3.登录
MyApplication.xmppConnection.login("spark","123456");
//4.加入群聊
String room = "729@conference.127.0.0.1";
MyApplication.multiUserChat = new MultiUserChat(MyApplication.xmppConnection,room);
MyApplication.multiUserChat.join("Errol_King");
} catch (XMPPException e) {
e.printStackTrace();
}
}
}.start();
};
}
创建LoginActivity
public class LoginActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LoginBiz loginBiz = new LoginBiz();
loginBiz.login(this);
}
}
布局activity_login.xml 暂不写
修改AndroidManifest,修改启动页,同时注册MainActivity
<activity android:name=".LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity"/>
同时MyApplication中,要把创建ConnectionConfiguration拿到new Thread上面,由于之前创建和LoginBiz是两个线程,防止使用时为null
public class MyApplication extends Application {
......
@Override
public void onCreate() {
super.onCreate();
//由于MyApplication和LoginBiz是两个线程,为了防止LoginBiz使用xmppConnection时为null,把初始化拿到创建线程外
//1.设置服务器信息
ConnectionConfiguration configuration = new ConnectionConfiguration("10.0.2.2", 5222, "Spark");
//2.连接上服务器
xmppConnection = new XMPPConnection(configuration);
//连接服务器,起一个线程
new Thread() {
@Override
public void run() {
try {
xmppConnection.connect();
Log.d("多线程","");
//让框架的接口指向实现类
MyPacketListener listener = new MyPacketListener();
xmppConnection.addPacketListener(listener,null);
} catch (XMPPException e) {
e.printStackTrace();
}
}
}.start();
}
}
还有一个问题就是
当LoginBiz中的login执行时,另一个线程的connect可能还没成功(服务器还没连接成功),为了解决这个问题,加一个锁
public class MyApplication extends Application {
......
//对象锁
public static Object objLock = new Object();
@Override
public void onCreate() {
super.onCreate();
......
//连接服务器,起一个线程
new Thread() {
@Override
public void run() {
try {
xmppConnection.connect();
//让框架的接口指向实现类
MyPacketListener listener = new MyPacketListener();
xmppConnection.addPacketListener(listener,null);
} catch (XMPPException e) {
e.printStackTrace();
} finally {
//连上后,notify
synchronized (objLock){
objLock.notify();
Log.d("多线程","连接成功,"+getId()+",发出了notify");
}
}
}
}.start();
}
......
}
LoginBiz
public class LoginBiz {
public void login(final LoginActivity activity){
new Thread(){
@Override
public void run() {
try {
//登录的时候判断连接线程是否连接成功
if(!MyApplication.xmppConnection.isConnected()){
//没连上
synchronized (MyApplication.objLock){
Log.d("多线程",getId()+"没连上,开始等待");
//等
MyApplication.objLock.wait();
}
}
//3.登录
Log.d("多线程",getId()+"已经有人notify,不再等,可以执行登录了");
MyApplication.xmppConnection.login("spark","123456");
......
} catch (XMPPException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
};
}
值得注意的是wait()后的代码是不执行的
登录成功后跳转
LoginActivity中增加跳转方法
public class LoginActivity extends Activity {
......
public void handleResult(){
startActivity(new Intent(this,MainActivity.class));
}
}
LoginBiz中
public class LoginBiz {
public void login(final LoginActivity activity){
new Thread(){
@Override
public void run() {
try {
......
} catch (XMPPException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
activity.handleResult();
}
}
}.start();
};
}