安卓即时通讯项目--xmpp实现方法
来源:互联网 发布:论文代发机构 知乎 编辑:程序博客网 时间:2024/05/20 21:18
1.Sock实现方法
Socket:自主研发
知识点/功能
概念
注意事项
消息对象
内容+附加的信息
sendtime from to content type
IM接口文档:字段+格式
ProtocalObj
“Http的表单”
Xstream
xml与java 对象互转
fromXml()解析 返回对象
toXml() 生成xml
GSON
json与java 对象互转
fromJson 解析 返回对象
toJson 生 json
消息通道
传输消息.1.发送 2接收
I/O
InputStream
|--DataInputStream readUTF
OutputStream
|--DataOutputStream writeUTF
IM
void sendMessage
监听器: 接收消息的接收器
HTTP
String json=get/post
json 解析
断开连接对象
消息转发
发给服务,根据对方在线与否,根据 to 写到对方
服务端实现,客户端不需掌握
启动
布局:拆分法
1.Thread
2.AsyncTask
3.handler.post
4.Animation setDuration(3000)
登录
TableLayout:表格布局 。每一个标签 代表一行
TableRow:代表一行 形成多列
sendMesage
1.type 成功/失败
2.Connection 保持 长连接
3.登录成功的连接代表一个在线用户
4.添加监听器/反之移除
联系人
1.解析json
2.生成List<JavaBean>
3.创建适配器
4.设置给控件
上线/下线
1.编写setAdapterOrNitify
2.注意监听器 在主线程调用刷新
聊天
消息的来回传输
参考QQ:退出所有的界面还能接收到消息
1. 静态UI 假数据
2. getViewTypeCount返回行视图的种类 2
3. getItemViewType返回指定下标数据使用的视图类型
0 ,1
4. 给getView使用
5. 假数据换真数据 发送/接收
sendMessage 注册监听器
Activity/Fragment
返回键 聊天程序也被关闭
Service
没有界面 隐蔽 长期运行到后台
如果想要service运行于另外进程 process:
<service
android:enabled="true"
android:process=":qqchat"
Command :Listener
Observer
Adapter
CallBack
两种 机制 监听器 回调
SingleTon Fractory
1个月 2周 基础 2项目 两个
2. Xmpp基础
xml protocal
xmpp是一种协议,规定 通讯 字段+格式,im接口文档。行业标准。
2.1. Spark安装配置
http://asmack.freakempire.de/
绑定源代码
① Android Private Lib
② 重新绑定 Reference lib
③ 编辑source属性
3. 项目实战
3.1. 模块:登录
创建消息通道
if (conn == null) {
// 192.168.15.97 5222
// ConnectionConfiguration cfg=new
// ConnectionConfiguration(ip, port);
ConnectionConfiguration cfg = new ConnectionConfiguration("192.168.15.97", 5222);
// debug
cfg.setDebuggerEnabled(true);// 可以查看底层的数据
// 是否加密 服务默认不加密
cfg.setSecurityMode(SecurityMode.disabled);// 传明文
// 创建消息通道
conn = new XMPPConnection(cfg);
// 打开连接
try {
conn.connect();
} catch (XMPPException e) {
e.printStackTrace();
}
}
登录
// conn.login(账号, 密码);
try {
conn.login(usernameString+"@"+MyApp.SERVICE_NAME, pwdString);
flag = true;
} catch (XMPPException e) {
e.printStackTrace();
flag = false;
}
//创建主线程
ThreadUtils.runUIThread(new Runnable() {
@Override
public void run() {
if (flag) {
// 要求:http本质区别
MyApp.conn = conn;
// 保存账号
MyApp.username = usernameString;
startActivity(new Intent(getBaseContext(), MainActivity.class));
Toast.makeText(getBaseContext(), "登录成功!!", 0).show();
finish();
} else {
Toast.makeText(getBaseContext(), "账号或者密码不对!!", 0).show();
}
}
});
XUtils
底层
开发库/框架
代码比较大
3~5代码实现了原有的功能
3.2. 模块:主页
3.2.1. 页面搭建ViewPager
① 创建Fragment 两个
② 创建FragmetnPagerAdapter
③ 创建FragmentActivity 布局ViewPager
④ 设置给ViewPager
public class MainActivity extends FragmentActivity{
@InjectView(R.id.viewpager)
ViewPager viewpager;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
//① 创建Fragment 两个
//② 创建FragmetnPagerAdapter
//③ 创建FragmentActivity 布局ViewPager
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
//④ 设置给ViewPager Support-->default
MyFragmentAdapter adapter=new MyFragmentAdapter(getSupportFragmentManager());
viewpager.setAdapter(adapter);
}
}
//适配器
public class MyFragmentAdapter extends FragmentPagerAdapter{
private List<Fragment> pages=new ArrayList<Fragment>();
public MyFragmentAdapter(FragmentManager fm) {
super(fm);
pages.add(new SessionFragment());
pages.add(new ContactFragment());
}
//返回页面总数
@Override
public int getCount() {
return pages.size();
}
//返回指定下标的片段
@Override
public Fragment getItem(int position) {
return pages.get(position);
}
}
3.3. 页面指示器
① 开源 ViewPagerIndicator指示器
② RadioGroup RadioButton 风格
③ TabWidget 集成TabHost
④ 两个TextView 写事件
选择器:裁判
管理素材(图片/颜色)的对象,根据不同的状态显示不同的素材
//
viewpager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
// 切换完成
@Override
public void onPageSelected(int position) {
if (position == 0) {
title.setText("会话");
session.setEnabled(false);
contact.setEnabled(true);
} else if (position == 1) {
title.setText("好友");
session.setEnabled(true);
contact.setEnabled(false);
}
}
3.3.1. Fix Fragment的BUG
丢失数据
//显示会话的片段
public abstract class BaseFragment extends Fragment {
protected View view;
protected Context context;
// 在Fragment显示时返回视图内容
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// 同一个View不能添加到布局两次
if (view == null) {
context = getActivity();
view =createView(inflater,container,savedInstanceState);
}
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null)// 当前控件被加入到布局
{
parent.removeView(view);
}
return view;
}
protected abstract View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) ;
}
Roster
所有联系人的封装对象
RosterGroup
一个好友分组 集合
RosterEntry
一个联系人 头像 账号 状态 昵称
QQ:好友联系同步时 界面不是全部清空 而是变化的数据重新载入可以。
好友同步原理
3.5. 模块:联系人--Roster
开发步骤
1.继承Provider
2.重写
3.注册
4.开启
getContext().getContentResolver().insert(MyContactProvider.CONTACT_URI, null);
<!-- 联系人 -->
<provider
android:name="com.itheima.im.xmpp.provider.MyContactProvider"
android:authorities="com.itheima.im.xmpp.provider.MyContactProvider" >
public class MyContactProvider extends ContentProvider{
private static final String authority="com.itheima.im.xmpp.provider.MyContactProvider";
public static final Uri CONTACT_URI=Uri.parse("content://"+authority+"/contacts");
//成功创建
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
System.out.println("---query---");
return null;
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
System.out.println("---insert---");
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
System.out.println("---delete---");
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
System.out.println("---update---");
return 0;
}
}
① 创建Service 同步 Roster数据到Provider
② 实现Provider query方法
③ Activity/Fragment 调用contentResolver 获取Cursor
//BaseAdapter
//|--CursorAdapter 游标适配器
public class MyContactCursorAdapter extends CursorAdapter {
private Context context;
public MyContactCursorAdapter(Context context, Cursor c) {
super(context, c);
this.context = context;
}
// 返回视图 显示了指定下标的数据 List get()
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ContactViewHolder holder = null;
if (convertView == null) {
convertView = View.inflate(this.context, R.layout.item_contact, null);
holder = new ContactViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ContactViewHolder) convertView.getTag();
}
// 设置数据
Cursor c = getCursor();
// 选 中指定下标的数据
c.moveToPosition(position);
// String account=c.getString(列号)
String account = c.getString(c.getColumnIndex(MyContactProvider.CONTACT.ACCOUNT));
String nick = c.getString(c.getColumnIndex(MyContactProvider.CONTACT.NICK));
holder.title.setText(nick);
holder.desc.setText(account);
return convertView;
}
//显示联系人的片段
public class ContactFragment extends BaseFragment {
private ListView contactlistview;
// 在Fragment显示时返回视图内容
public View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = View.inflate(getActivity(), R.layout.fragment_contact, null);
//游标:多条数据记录的集合
Cursor cursor=context.getContentResolver().query(MyContactProvider.CONTACT_URI, null, null, null, MyContactProvider.CONTACT.SORT+" ASC");
MyContactCursorAdapter adapter=new MyContactCursorAdapter(context,cursor);
contactlistview=(ListView) view.findViewById(R.id.contactlistview);
contactlistview.setAdapter(adapter);
return view;
}
}
联系人 受 其他终上修改名册影响
RosterListener
监听 终端对象Roster上的修改
1.重命名 2.删除3.添加
类似 OnClickListener
① Provider数据
roster.addRosterListener(listener);// 添加监听器
@Override
public void onDestroy() {
super.onDestroy();
roster.removeRosterListener(listener);
}
// OnClickListener是对控件的点击事件的监听
// RosterListener是对Roster的修改事件的监听
private RosterListener listener = new RosterListener() {
// 添加
@Override
public void entriesAdded(Collection<String> accounts) {
System.out.println("---entriesAdded--");
for (String account : accounts) {
// System.out.println(account);
// RosterEntry:Xmpp对一个联系记录的封装
RosterEntry person = roster.getEntry(account);
dao.saveOrUpdate(person);
}
// println(accounts);
}
private void println(Collection<String> accounts) {
for (String account : accounts) {
System.out.println(account);
}
}
// 重命名
@Override
public void entriesUpdated(Collection<String> accounts) {
System.out.println("---entriesUpdated--");
// println(accounts);
for (String account : accounts) {
// System.out.println(account);
// RosterEntry:Xmpp对一个联系记录的封装
RosterEntry person = roster.getEntry(account);
dao.saveOrUpdate(person);
}
}
// 删除
@Override
public void entriesDeleted(Collection<String> accounts) {
System.out.println("--entriesDeleted---");
for (String account : accounts) {
dao.delete(account);
}
}
// QQ 微信
@Override
public void presenceChanged(Presence p) {
}
};
② 页面自动刷新
private ContentObserver observer = new ContentObserver(new Handler()) {
// 2.2
public void onChange(boolean selfChange) {
// 刷新
System.out.println("onChange----低--");
setAdapterOrNotify();
};
// 4.0
public void onChange(boolean selfChange, android.net.Uri uri) {
// 刷新
System.out.println("onChange----高--");
setAdapterOrNotify();
};
};
public void onDestroy() {
super.onDestroy();
context.getContentResolver().unregisterContentObserver(observer);
};
private void setAdapterOrNotify() {
if (adapter == null) {
Cursor cursor = context.getContentResolver().query(MyContactProvider.CONTACT_URI, null, null, null, MyContactProvider.CONTACT.SORT + " ASC");
if (cursor.getCount() < 1) {
return;
}
adapter = new MyContactCursorAdapter(context, cursor);
contactlistview.setAdapter(adapter);
} else {
Cursor newCursor = context.getContentResolver().query(MyContactProvider.CONTACT_URI, null, null, null, MyContactProvider.CONTACT.SORT + " ASC");
Cursor olderCursor = adapter.swapCursor(newCursor);
olderCursor.close();
}
// 游标:多条数据记录的集合
}
MyContactCursorAdapter adapter;
// 在Fragment显示时返回视图内容
public View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// content://contacts/contact/
// content://contacts/contact/1
// context.getContentResolver().registerContentObserver(路径, 是否监听子路径,
// 观察者);
context.getContentResolver().registerContentObserver(MyContactProvider.CONTACT_URI, true, observer);
View view = View.inflate(getActivity(), R.layout.fragment_contact, null);
contactlistview = (ListView) view.findViewById(R.id.contactlistview);
setAdapterOrNotify();
return view;
}
4. 模块:聊天
Message
xmpp实现的消息对象
Chat
xmpp实现的消息发送工具 void sendMessage
ChatManager
获取工具管理者
MessageListener
发送消息
// 1.获取工具Chat
ChatManager cm = MyApp.conn.getChatManager();
// currchat=cm.createChat(对方账号 老王@itheima.com, null);
currchat = cm.createChat(toChatAccount, null);
// 创建消息对象
Message msg = new Message();
msg.setBody(inputMessage);// 内容
msg.setFrom(MyApp.username + "@" + MyApp.SERVICE_NAME);
msg.setTo(toChatAccount);
msg.setType(Message.Type.chat);
if (currchat != null) {
try {
currchat.sendMessage(msg);
} catch (XMPPException e) {
e.printStackTrace();
}
}
接收消息
// 接收消息的监听器
private MessageListener listener = new MessageListener() {
@Override
public void processMessage(Chat arg0, final Message msg) {
ThreadUtils.runUIThread(new Runnable() {
@Override
public void run() {
System.out.println("---接收消息--" + msg.toXML());
Toast.makeText(getBaseContext(), "好友消息:" + msg.getBody(), 0).show();
}
});
}
};
Service实现消息监听
Activity/Fragment 显示界面
Service
返回键 杀死聊天程序
无视图 退出所有的Activity 不会关闭服务
ChatManagerListener
监听器 监听 Chat被创建
//被动接收消息的处理
//管理工具
ChatManager cm=MyApp.conn.getChatManager();
//ChatManagerListener:添加 Chat创建监听器 1.true 我找别人聊天的工具 2.别人找我聊天 使用的Chat false
cm.addChatListener(new ChatManagerListener() {
//获取聊天工具创建 实例
@Override
public void chatCreated(Chat chat, boolean isLocal) {
System.out.println("主动还是被动"+isLocal);
chat.addMessageListener(listener);
}
});
//监听消息的接收
// 接收消息的监听器
private MessageListener listener = new MessageListener() {
@Override
public void processMessage(Chat arg0, final Message msg) {
ThreadUtils.runUIThread(new Runnable() {
@Override
public void run() {
System.out.println("---接收消息--" + msg.toXML());
Toast.makeText(getBaseContext(), "好友消息:" + msg.getBody(), 0).show();
}
});
}
};
聊天 消息中保存时 增加一个session_id 标识 与某个人的聊天记录
3.6.1. Chat与MessageListener
3.6.2. ChatManagerListener监听Chat创建
3.6.3. Provider保存消息
3.6.4. 使用CusorAdapter显示聊天内容
3.6.5. 通过注册ContnetObserver自动更新UI
3.7. 模块:会话列表
3.7.1. CursorAdapter显示会话列表
3.7.2. 注册ContentObserver自动更新UI
3.7.3. 进入ChatActivity
3.8. 消息推送
- 安卓即时通讯项目--xmpp实现方法
- 安卓--即时通讯项目
- XMPP(三)-安卓即时通讯客户端
- XMPP(三)-安卓即时通讯客户端
- openfire+XMPP实现即时通讯
- 即时通讯:XMPP项目实践-微聊
- XMPP - 实现即时通讯相关库
- 安卓蓝牙实现即时通讯功能
- 基于xmpp实现android端实现即时通讯---asmack基本方法(一)
- 基于xmpp实现android端实现即时通讯---asmack基本方法(二)
- 基于xmpp实现android端实现即时通讯---asmack基本方法(三)
- 论XMPP与其他即时通讯方法比较
- xmpp即时通讯
- xmpp 即时通讯
- XMPP 即时通讯
- XMPP即时通讯
- 即时通讯Xmpp
- IOS开发,XMPP实现聊天,即时通讯(一)
- Java第一天
- foreach包简介
- hdoj.1052 Tian Ji -- The Horse Racing【贪心】 2015/04/20
- 关于const的一些知识点小结
- 安卓去掉应用默认的名字
- 安卓即时通讯项目--xmpp实现方法
- java中时间的基本使用即转换
- 输入和输出运算符
- [leetcode题后感]first missing positive
- C++动态存储的应用
- MySQL数据库大数据处理
- NoSQL 在腾讯应用实践
- C++内存
- jquery mobile跳转到指定id时怎样传递参数