安卓socket实现客户端间即时通讯(二)

客户端部分

接着上篇来讲解客户端部分,下面给出代码。
Message和User类的创建很简单,就不贴出代码。
其中用到了EventBus,很好的解耦合工具,github上开源哦!

public class ChatActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private EditText input;
    private Button btn_send;
    private List<Session> msgList = new ArrayList<>();
    private MsgAdapter msgAdapter;
    private Intent intent;
    private TextView nick;
    private TextView sign;
    MyApplication继承Application类
    private MyApplication application=MyApplication.getApplication();
    private Long receiveId;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);
        initView();
        initNet();
        btn_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String content = input.getText().toString();
//                如果内容不为空,则发送消息
                updateMessage(content);
                sendMessage(content);
            }
        });
    }
    public void initView(){
        recyclerView =findViewById(R.id.recyclerView);
        input =findViewById(R.id.et_content);
        btn_send =findViewById(R.id.btn_send);
        recyclerView.setLayoutManager(new ChatLayoutManager(this));
        msgAdapter=new MsgAdapter(msgList);
        recyclerView.setAdapter(msgAdapter);
        nick=findViewById(R.id.friend_nick);
        sign=findViewById(R.id.friend_sign);
    }
    @SuppressLint("StaticFieldLeak")
    public void sendMessage(final String content){
        new AsyncTask<String,Void,Void>(){
            @Override
            protected Void doInBackground(String... strings) {
//                发送信息
                DataOutputStream out=application.getOut();
                if (content!=null){
         
                    Message message=new Message();
                    message.setReceiveId(receiveId);
                    message.setNick(User.getInstance().getNick());
                    message.setType(MessageType.MSG_TYPE_CHAT_P2P);
                    message.setSenderId(User.getInstance().getId());
                    message.setContent(content);
                    message.setSign(User.getInstance().getSign());
                    try {
                        out.writeUTF(message.toXML());
                        out.flush();
                        Log.i("chat--->",message.toXML());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }catch (NullPointerException e){
                    }
                }
                return null;
            }
        }.execute();
    }
    更新消息
    public void updateMessage(String content){
        if (!"".equals(content)){
            Session msg = new Session(MessageType.MSG_TYPE_SEND,User.getInstance().getId(),User.getInstance().getNick(),content);
            msgList.add(msg);
            msgAdapter.notifyItemRangeInserted(msgList.size() - 1,1);// 当有新消息时,刷新适配器
            recyclerView.scrollToPosition(msgList.size() - 1);// 将circleRecycle 定位到最后一行
            Log.i("chat.send",String.valueOf(msgAdapter.getItemCount()));
            input.setText("");// 清空输入框
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
        Log.i("chat","destroy");
    }
    @Override
    protected void onStart() {
        super.onStart();
        Log.i("chat","start");
    }
    public void initNet(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    application.init();
                    Socket socket=application.getSocket();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void receiveMessage(Message message) {
        if (message.getSenderId().equals(receiveId) &&message.getType()== MessageType.MSG_TYPE_CHAT_P2P){
            Session session=new Session(MessageType.MSG_TYPE_RECEIVE,message.getSenderId(),message.getNick(),message.getContent(),message.getSign());
            msgList.add(session);
            msgAdapter.notifyItemRangeInserted(msgList.size() - 1,1);// 当有新消息时,刷新适配器
            recyclerView.scrollToPosition(msgList.size() - 1);// 将circleRecycle 定位到最后一行
            Log.i("chat.receive",String.valueOf(msgAdapter.getItemCount()));
        }
    }
//    获取bundle信息
    public void getBundle(){
        EventBus.getDefault().register(this);
        Session message=(Session)intent.getSerializableExtra("session");
        if (message!=null){
            receiveId=message.getSenderId();
            nick.setText(message.getNick());
            sign.setText(message.getSign());
        }
    }
    @Override
    protected void onStop() {
        super.onStop();
        Log.i("chat","stop");
    }
}

这里是专门接收信息的服务。

public class MyService extends Service {
//    后台服务类,不断接受服务器数据,并转发给activity
    private Socket socket;
    private DataOutputStream out;
    private DataInputStream in;
    private MyApplication application;
    private static int Foreground=11101;
    private MyBinder myBinder=new MyBinder();
    public MyService() {
    }
    @Override
    public IBinder onBind(Intent intent) {
        return myBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        application=MyApplication.getApplication();
        socket=application.getSocket();
        Log.i("myService","create");
    }
//运行时调用
    @Override
    public int onStartCommand(final Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                in = application.getIn();
                String xml = null;
                Message message=null;
                try{
                阻塞读取接收消息
                    while (null!=(xml=in.readUTF())) {
                        message=new Message();
                        message = (Message) message.fromXML(xml);
                        Log.i("mySerivce", xml);
                        EventBus.getDefault().postSticky(message);
                    }
                }catch (IOException | NullPointerException E){}
            }
        }).start();
        Log.i("myserivce","服务运行!");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("myService.destroy","destroy");
    }
    public class MyBinder extends Binder{

    }

}

接下来是适配器,用到了Session实体类,很简单,不贴了。

public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.ViewHolder>{
//    会话界面适配器,加载数据
    private List<Session> msgList;
    public MsgAdapter(List<Session> msgList) {
        this.msgList = msgList;
    }
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.items,parent,false);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Session msg = msgList.get(position);
        if (msg.getType()== MessageType.MSG_TYPE_RECEIVE){
            // 如果收到消息,则显示在左边的消息布局,将右边的隐藏
            holder.leftLayout.setVisibility(View.VISIBLE);
            holder.rightLayout.setVisibility(View.GONE);
            holder.leftMsg.setText(msg.getContent());
            Log.i("msgAdapter",msg.getContent());
        }else if (msg.getType()==MessageType.MSG_TYPE_SEND){
            // 如果发送消息,则显示在右边的消息布局,将左边的隐藏
            holder.rightLayout.setVisibility(View.VISIBLE);
            holder.leftLayout.setVisibility(View.GONE);
            holder.rightMsg.setText(msg.getContent());
        }
    }
    @Override
    public int getItemCount() {
        return msgList.size();
    }
    public class ViewHolder extends RecyclerView.ViewHolder {
        LinearLayout leftLayout,rightLayout;
        TextView leftMsg,rightMsg;
        public ViewHolder(View itemView) {
            super(itemView);
            leftLayout = itemView.findViewById(R.id.left_layout);
            rightLayout = itemView.findViewById(R.id.right_layout);
            leftMsg = itemView.findViewById(R.id.left_msg);
            rightMsg = itemView.findViewById(R.id.right_msg);
        }
    }
}

最后是布局文件。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#f0fcff"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!--android.support.v7.widget.RecyclerView-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <RelativeLayout
            android:background="#ffffff"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
            <TextView
                android:textColor="#000000"
                android:textSize="20sp"
                android:id="@+id/friend_nick"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center_horizontal"
                android:text="" />
            <TextView
                android:textColor="#000000"
                android:id="@+id/friend_sign"
                android:layout_below="@id/friend_nick"
                android:gravity="center_horizontal"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
        </RelativeLayout>
    </LinearLayout>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:textColor="#000000"
            android:id="@+id/et_content"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="请输入内容"/>
        <Button
            android:background="@null"
            android:id="@+id/btn_send"
            android:text="发送"
            android:textColor="#000000"
            android:textSize="20sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

</LinearLayout>
这个是信息条目的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#f0fcff"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <LinearLayout
        android:background="#f0fcff"
        android:id="@+id/left_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="start">
        <TextView
            android:background="@drawable/chat_left"
            android:id="@+id/left_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="10dp"
            android:textColor="#000000" />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/right_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end"
        android:background="#f0fcff">
        <TextView
            android:background="@drawable/chat_right"
            android:id="@+id/right_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="10dp"
            android:textColor="#000000"/>
    </LinearLayout>
</LinearLayout>

这是Message类和ProtocalObj类,MessageType类可以自己设计哦。
public class Message extends ProtocalObj implements Serializable {
    private int sex;
    private String sign;
    public String getSign() {
        return sign;
    }

    public void setSign(String sign) {
        this.sign = sign;
    }

    public int getSex() {
        return sex;
    }
    public void setSex(int sex) {
        this.sex = sex;
    }
    private Long senderId;
    private Long receiveId;
    private String name;
    public Message(Long senderId, String name, String content, int type) {
        this.senderId = senderId;
        this.name = name;
        this.content = content;
        this.type = type;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public Message() {
    }

    public Message(Long senderId) {
        this.senderId = senderId;
    }

    public Message(Long senderId, int sex,String nick, double lat, double lng, int type,String content) {
        this.senderId = senderId;
        this.lat = lat;
        this.lng = lng;
        this.type = type;
        this.nick=nick;
        this.sex=sex;
        this.sign=content;
    }

    public Long getSenderId() {
        return senderId;
    }

    public void setSenderId(Long senderTd) {
        this.senderId = senderTd;
    }

    public Long getReceiveId() {
        return receiveId;
    }

    public void setReceiveId(Long receiveId) {
        this.receiveId = receiveId;
    }

    private String content;

    public String getNick() {
        return nick;
    }

    public void setNick(String nick) {
        this.nick = nick;
    }

    private String nick;
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public double getLat() {
        return lat;
    }
    public void setLat(double lat) {
        this.lat = lat;
    }
    public double getLng() {
        return lng;
    }
    public void setLng(double lng) {
        this.lng = lng;
    }
    private double lat;
    private double lng;
    public int getType() {
        return type;
    }
    public void setType(int type) {
        this.type = type;
    }
    private int type;
}
---------------------------------------
public class ProtocalObj {
    /**
     * 生成xml
     *
     * @return
     */
    public String toXML() {
        XStream x = new XStream();
        // 设置别名,默认是类的全路径名
        x.alias(getClass().getSimpleName(), getClass());
        String xml = x.toXML(this);// 将本类转换成xml返回
        return xml;

    }

    /**
     * xml-->实体类
     *
     * @param xml
     * @return
     */
    public Object fromXML(String xml) {
        XStream x = new XStream();
        x.alias(getClass().getSimpleName(), getClass());
        return x.fromXML(xml);

    }
}

最后,我来讲解一下思路,首先呢。接收服务端信息部分,需要用到多线程,并且阻塞读取。我这里是个大点的项目,所以采用Service读取信息,其实完全可以Activity中写。然后,我这里继承了Application类实现自定义的应用程序类,主要是比较方便,因为我将Socket保存到应用程序类。最后,就是Eventbus解耦合通信了,在服务中实例化,需要接受的就注册事件就行了,更多的大家可以搜索一下哈。
还有就是布局文件,我相信大家都能看懂,信息左右分发,主要是通过设置了信息的属性。左就是left,右就是right,在适配器的代码中可以看到。
最后是XstreamJAR包
链接:https://pan.baidu.com/s/1PNF5nyBvvXv9zCQfxXJDNw
提取码:rabh
复制这段内容后打开百度网盘手机App,操作更方便哦

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值