安卓即时通讯项目--xmpp实现方法

来源:互联网 发布:论文代发机构 知乎 编辑:程序博客网 时间:2024/05/20 21:18

 

1.Sock实现方法


Socket:自主研发

知识点/功能

概念

注意事项

消息对象

内容+附加的信息 

sendtime from to content type

IM接口文档:字段+格式

ProtocalObj

“Http的表单

 

Xstream

xmljava 对象互转

fromXml()解析 返回对象

toXml()  生成xml 

GSON

jsonjava 对象互转

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_NAMEpwdString);

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_URInull);

 

 <!-- 联系人 -->

        <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_contactnull);

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_contactnull);

//游标:多条数据记录的集合

Cursor cursor=context.getContentResolver().query(MyContactProvider.CONTACT_URInullnullnull, 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_URInullnullnull, 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_URInullnullnull, 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_URItrueobserver);

View view = View.inflate(getActivity(), R.layout.fragment_contactnull);

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(toChatAccountnull);

// 创建消息对象

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. ChatMessageListener

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. 消息推送

 

1 0
原创粉丝点击