Android基于高德地图实现多人实时共享位置

自开发上一款智慧旅游产品后,发现一个很有意义而且很实用的功能,就是模仿微信的位置共享,可以看到对方的位置,一直想模仿做出这样的效果.最近闲下来之后终于实现了.下面就把我的实现过程和心得分享给大家.

步骤

1.基于高德地图定位

要实现实时位置共享,首先就要实现实时定位,高德官方api给的很详细,只要跟着步骤来,还是没什么难度的,下面直接上代码了

/**
 * 设置地图属性
 */
private void setUpMap(){
    aMap.setLocationSource(this);
    aMap.setMyLocationEnabled(true);// 设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是false
    aMap.setMyLocationType(AMap.LOCATION_TYPE_LOCATE);// 跟随模式
    aMap.getUiSettings().setMyLocationButtonEnabled(true);// 设置默认定位按钮是否显示
    aMap.setMyLocationEnabled(true);// 设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是false
}
//激活定位
@Override
public void activate(OnLocationChangedListener listener) {
    mListener = listener;
    if (mlocationClient == null){
        mlocationClient = new AMapLocationClient(MainActivity.this);
        mLocationOption = new AMapLocationClientOption();
        mlocationClient.setLocationListener(this);// 设置定位监听
        mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
        mlocationClient.setLocationOption(mLocationOption);// 设置为高精度定位模式
        mLocationOption.setInterval(1000);
        mlocationClient.startLocation();
    }
}

/**
*位置信息发生变化时
*/
@Override
public void onLocationChanged(AMapLocation aMapLocation) {
    if (mListener != null && aMapLocation != null){
        if (aMapLocation != null && aMapLocation.getErrorCode() == 0){
            mListener.onLocationChanged(aMapLocation);// 显示系统小蓝点
            if (isFirst){
                aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(aMapLocation.getLatitude(), aMapLocation.getLongitude()), 18));//定位成功移到当前定位点
                isFirst = false;
            }
        }else{
            Log.i("123",aMapLocation.getErrorCode()+"错误码"+aMapLocation.getErrorInfo()+"错误信息");
        }
    }
}

2.通过建立TCP实时传输交换数据

首先创建一个socket连接对象,文字不多说,直接上代码

private void startSocket(){
    try{
        socket = new Socket(Ip,port);
        outputStream = socket.getOutputStream();
        inputStream = new Send_InputStream(socket.getInputStream());
        inputStream.setMainActivity(mainActivity);
        new Thread(inputStream).start();
    }catch (Exception e){
    }

}
@Override
public void run() {
    try{
        startSocket();
        jsonObject = new JSONObject();
        while (socket != null){
            Thread.sleep(3000);
            jsonObject.put("lat",MainActivity.getLat());//连接成功后获取定位的经纬度通过json对象发送给服务器
            jsonObject.put("lng",MainActivity.getLng());
            outputStream.write(jsonObject.toString().getBytes("UTF-8"));
            outputStream.flush();
        }
    }catch (Exception e){
    }finally {
        if (outputStream != null){
            try {
                 outputStream.close();
            }catch (Exception e){
            }
        }
        if(socket != null){
            try{
                socket.close();
            }catch (Exception e){
            }
        }
    }
有发送就有接收的方法,下面来一段接收
@Override
public void run() {
     int len = 0;
    byte[] buff = new byte[1024];
    try{
        while ((len = inputStream.read(buff)) != -1){
            text = new String(buff,0,len);
            JSONObject jsonObject = new JSONObject(text);
            int errorCode = jsonObject.getInt("errorCode");
            if (errorCode == 200){
                JSONArray jsonArray = jsonObject.getJSONArray("date");//与服务器约定好的数据格式
                mainActivity.allLatLng(jsonArray);//调用首页地图的方法将接收的位置信息显示出来
            }
        }
    }catch (Exception e){
    }
}
到这里,前端的代码就差不多了,最主要的是TCP的建立以及数据的解析,把接收到的数据解析得到坐标位置以一个marker的形式添加在地图上
下面是我解析的代码

3.解析数据,在地图上添加marker


/**
 * 添加所接收到的共享位置信息
 * @param jsonArray
 */
public void allLatLng(JSONArray jsonArray){
    try{
        if (list.size() != 0){
            Remove(list);
        }
        for (int i = 0;inew JSONObject(jsonArray.get(i).toString());
            latitude = jsonObject.getDouble("lat");
            longitude = jsonObject.getDouble("lng");
            LatLng latLng = new LatLng(latitude,longitude);
            marker = (Marker) (aMap.addMarker(help_add_icon(latLng, R.mipmap.icon_tourist)));
            list.add(marker);
        }
    }catch (Exception e){
    }
}
这是在地图添加一个marke的方法
/**
 * 手机上显示共享位置的图标
 * @param latLng
 * @param id
 * @return
 */
public static MarkerOptions help_add_icon(LatLng latLng,int id){
    MarkerOptions markOptiopns = new MarkerOptions().position(latLng)
            .icon(BitmapDescriptorFactory.fromResource(id));
    return markOptiopns;
}
然后,这是调用方法
marker = (Marker) (aMap.addMarker(help_add_icon(latLng, R.mipmap.icon_tourist)));
最后说一下手机端这边的逻辑和原理
通过点击开启共享位置信息的按钮,与服务器建立TCP的链接,同时把自己的位置信息发送过去,当有第二个手机用户连进来的时候,服务器会把对方的位置信息发送出去,
然后手机用户通过接受服务器返回的数据显示对方的位置信息.这里注意一下,在接受到信息前要判断之前的信息是否为空,不为空就先把之前的marker移除再在地图上
添加,避免在接受的新的位置信息时,前面的还在地图上显示.

4.最后来看下服务器的业务逻辑代码

首先启动socket


public class ServiceTcp {
    private int port;

    public ServiceTcp() {

    }

    public ServiceTcp(int port) {
        this.port = port;
    }

    private List users = new ArrayList();// 用户集合

    public void start() {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(port);
            while (true) {
                Socket socket = serverSocket.accept();
                Client client = new Client(socket, users);
                Thread thread = new Thread(client);
                thread.start();
            }
        } catch (IOException e) {
            System.out.println("启动失败!!!");
            e.printStackTrace();
        }
    }
}

接收手机用户的线程类

@Override
    public void run() {
        try {
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();

            User user = new User(socket, this);
            users.add(user);

            // 发送线程
            Sender sender = new Sender(user, users);
            Thread thread = new Thread(sender);
            thread.start();

            byte[] bytes = new byte[1024];
            Integer length = null;
            String inString = null;
            while ((length = inputStream.read(bytes)) != -1) {
                inString = new String(bytes, 0, length, encoder);
                try {
                    if (user.isOnLine() == false) {
                        this.close();
                        break;
                    }
                    if (end.equals(inString)) {
                        this.close();
                        break;
                    }
                    JSONObject reJson = JSONObject.fromObject(inString);
                    Double lat = reJson.getDouble("lat");
                    Double lng = reJson.getDouble("lng");
                    user.setLat(lat);
                    user.setLng(lng);
                } catch (JSONException e) {
                    JSONObject jso = (ErrorCode.e_201).toJson();
                    this.send(jso.toString());
                } catch (Exception e) {
                    user.setOnLine(false);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

发送在线用户的方法

@Override
    public void run() {
        while (user.isOnLine()) {
            try {
                Thread.sleep(3 * 1000);// 休眠3秒
                user.getClient().send(ret().toString());
            } catch (Exception e) {
                user.setOnLine(false);// 设置离线
            }
        }
    }

    private JSONObject ret() {
        JSONObject jsonObject = null;
        JSONArray array = new JSONArray();
        for (User user : users) {
            if (user.getLat() != null && user.getLng() != null) {
                if (user.getUuid() != this.user.getUuid()) {
                    JSONObject us = new JSONObject();
                    us.put("uuid", user.getUuid());
                    us.put("lat", user.getLat());
                    us.put("lng", user.getLng());
                    array.add(us);
                }
            }
        }
        jsonObject = ErrorCode.s_200.toJson(array);
        return jsonObject;
    }

附带一下服务器返回的数据格式

{"errorCode":200,"message":"成功","date":[{"uuid":"e4bd7c4a-9d39-45a2-9378-b833d62e0538","lat":22.684095,"lng":114.23056}]}

就是这么简单粗暴直接,服务器这边的逻辑就是随机生成一个uuid最为手机用户的唯一标识,避免向用户发送自己的位置信息

最后来看下整体的效果图

Android基于高德地图实现多人实时共享位置_第1张图片

这是一个魅族pro6手机上的效果,游客代表另一个手机用户

Android基于高德地图实现多人实时共享位置_第2张图片

这是三星A5100上的效果,游客表示另一个手机用户

本次博客就这么多了,tcp的连接在断开方面没来得及多做处理,可能会出现多个用户在共享位置信息,这是由于tcp没有正常退出的原因,服务器没把这个用户的信息清空,所以才有这个小bug,需要大家在服务器端的代码手动清空一下.这是本人第一次写的技术博客,在很多方面并不完善,希望大家多多支持,后续附上程序的源码.

Android代码下载:https://github.com/WengYihao/GdDisplayMap

服务器代码下载:https://github.com/WengYihao/Server



你可能感兴趣的:(Android基于高德地图实现多人实时共享位置)