|
|
@@ -0,0 +1,451 @@ |
|
|
|
package com.nsgk.ruralWeb.location; |
|
|
|
|
|
|
|
import android.annotation.SuppressLint; |
|
|
|
import android.content.Context; |
|
|
|
import android.os.HandlerThread; |
|
|
|
import android.os.Looper; |
|
|
|
import android.util.Log; |
|
|
|
|
|
|
|
import com.amap.api.location.AMapLocation; |
|
|
|
import com.amap.api.location.AMapLocationClient; |
|
|
|
import com.amap.api.location.AMapLocationClientOption; |
|
|
|
import com.amap.api.location.AMapLocationListener; |
|
|
|
import com.amap.api.location.CoordinateConverter; |
|
|
|
import com.amap.api.location.DPoint; |
|
|
|
|
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.concurrent.atomic.AtomicInteger; |
|
|
|
|
|
|
|
// 主线程调用, 无线程安全 |
|
|
|
public class NSAMapLocation implements AMapLocationListener |
|
|
|
{ |
|
|
|
// 定位场景: 默认无场景 |
|
|
|
public static final int FLAG_PURPOSE_SIGNIN = 1; // 签到 |
|
|
|
public static final int FLAG_PURPOSE_TRANSPORT = 1 << 1; // 出行 |
|
|
|
public static final int FLAG_PURPOSE_SPORT = 1 << 2; // 运动 |
|
|
|
|
|
|
|
// 定位模式 |
|
|
|
public static final int FLAG_MODE_HIGH = 1 << 3; // 高精度模式 |
|
|
|
public static final int FLAG_MODE_SAVING = 1 << 4; // 低功耗模式 |
|
|
|
public static final int FLAG_MODE_SENSORS = 1 << 5; // 仅设备模式 |
|
|
|
|
|
|
|
// 获取一次定位结果: 默认为false |
|
|
|
public static final int FLAG_ONCE = 1 << 6; // true |
|
|
|
|
|
|
|
// 获取最近3s内精度最高的一次定位结果: 默认为false |
|
|
|
public static final int FLAG_ONCE_LATEST = 1 << 7; // true |
|
|
|
|
|
|
|
// 设置是否返回地址信息: 默认返回地址信息 |
|
|
|
public static final int FLAG_NO_NEED_ADDRESS = 1 << 8; // false |
|
|
|
|
|
|
|
// 设置是否允许模拟位置: 默认为true |
|
|
|
public static final int FLAG_MOCK_DISABLE = 1 << 9; // false |
|
|
|
|
|
|
|
// 关闭缓存机制: 默认缓存 |
|
|
|
public static final int FLAG_CACHE_DISABLE = 1 << 10; // false |
|
|
|
|
|
|
|
// 线程 |
|
|
|
public static final int FLAG_BUILTIN_THREAD = 1 << 11; // 使用内建线程 |
|
|
|
public static final int FLAG_CUSTOM_THREAD = 1 << 12; // 使用外部线程 |
|
|
|
public static final int FLAG_MAIN_THREAD = 1 << 13; // 使用主线程 |
|
|
|
|
|
|
|
public static final int DEFAULT_FLAG = FLAG_MODE_HIGH | FLAG_NO_NEED_ADDRESS; |
|
|
|
|
|
|
|
|
|
|
|
public static final int OPTION_INTERVAL = 1; // 定位间隔(毫秒) 默认1000 |
|
|
|
public static final int OPTION_HTTP_TIMEOUT = 2; // 定位间隔(毫秒) 默认20000 |
|
|
|
|
|
|
|
private static final int DEFAULT_INTERVAL = 1000; |
|
|
|
private static final int DEFAULT_HTTP_TIMEOUT = 20000; |
|
|
|
|
|
|
|
private static final String ID_TAG = NSSystemLocation.class.getName(); |
|
|
|
|
|
|
|
private final Object m_lock = new Object(); |
|
|
|
|
|
|
|
//声明AMapLocationClient类对象 |
|
|
|
public AMapLocationClient mLocationClient = null; |
|
|
|
//声明定位回调监听器 |
|
|
|
public AMapLocationListener mLocationListener = this; |
|
|
|
//声明AMapLocationClientOption对象 |
|
|
|
public AMapLocationClientOption mLocationOption = null; |
|
|
|
private final Context m_context; |
|
|
|
private int m_flag = DEFAULT_FLAG; |
|
|
|
private boolean m_isPreInit = false; |
|
|
|
private NSLocationInfo m_lastLocation = null; |
|
|
|
private HandlerThread m_thread = null; |
|
|
|
private Looper m_customLooper = null; |
|
|
|
private AtomicInteger m_readOnce = new AtomicInteger(-1); |
|
|
|
private Map<Integer, Object> m_options = new HashMap<>(); |
|
|
|
|
|
|
|
public NSAMapLocation(Context context) |
|
|
|
{ |
|
|
|
m_context = context; |
|
|
|
} |
|
|
|
|
|
|
|
public boolean Init(int flag) |
|
|
|
{ |
|
|
|
if(IsInitialized()) |
|
|
|
{ |
|
|
|
Log.e(ID_TAG, "定位已初始化"); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
try |
|
|
|
{ |
|
|
|
m_readOnce.set(-1); |
|
|
|
m_flag = flag < 0 ? DEFAULT_FLAG : flag; |
|
|
|
|
|
|
|
// 在构造AMapLocationClient 之前必须进行合规检查,设置接口之前保证隐私政策合规 |
|
|
|
PreInit(); |
|
|
|
|
|
|
|
Context applicationContext = m_context.getApplicationContext(); |
|
|
|
//初始化定位 |
|
|
|
if(HasFlag(FLAG_BUILTIN_THREAD)) |
|
|
|
{ |
|
|
|
m_thread = new HandlerThread("AMap内建定位"); |
|
|
|
m_thread.start(); |
|
|
|
mLocationClient = new AMapLocationClient(m_thread.getLooper(), applicationContext); |
|
|
|
Log.d(ID_TAG, "定位使用内建线程: " + m_thread.getId()); |
|
|
|
} |
|
|
|
else if(HasFlag(FLAG_CUSTOM_THREAD)) |
|
|
|
{ |
|
|
|
if(null == m_customLooper) |
|
|
|
{ |
|
|
|
throw new RuntimeException("请先传入线程Looper"); |
|
|
|
} |
|
|
|
mLocationClient = new AMapLocationClient(m_customLooper, applicationContext); |
|
|
|
Log.d(ID_TAG, "定位使用用户指定线程: " + m_customLooper.getThread().getId()); |
|
|
|
} |
|
|
|
else if(HasFlag(FLAG_MAIN_THREAD)) |
|
|
|
{ |
|
|
|
Looper mainLooper = Looper.getMainLooper(); |
|
|
|
mLocationClient = new AMapLocationClient(mainLooper, applicationContext); |
|
|
|
Log.d(ID_TAG, "定位使用主线程: " + mainLooper.getThread().getId()); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
mLocationClient = new AMapLocationClient(applicationContext); |
|
|
|
Log.d(ID_TAG, "定位使用当前线程: " + Thread.currentThread().getId()); |
|
|
|
} |
|
|
|
|
|
|
|
//设置定位回调监听 |
|
|
|
mLocationClient.setLocationListener(mLocationListener); |
|
|
|
|
|
|
|
InitOptions(); |
|
|
|
|
|
|
|
mLocationClient.setLocationOption(mLocationOption); |
|
|
|
|
|
|
|
//V6.4.9版本起 client设置逆地理回调 |
|
|
|
/* mLocationClient.setReGeoLocationCallback(new IReGeoLocationCallback() |
|
|
|
{ |
|
|
|
@Override |
|
|
|
public void onReGeoLocation(AMapLocation reGeoLocation) |
|
|
|
{ |
|
|
|
Log.i(ID_TAG, reGeoLocation.toStr()); |
|
|
|
} |
|
|
|
});*/ |
|
|
|
|
|
|
|
Log.i(ID_TAG, "定位初始化完成"); |
|
|
|
return true; |
|
|
|
} |
|
|
|
catch(Exception e) |
|
|
|
{ |
|
|
|
e.printStackTrace(); |
|
|
|
mLocationClient = null; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void SetThread(Looper looper) |
|
|
|
{ |
|
|
|
CheckInitialization(false); |
|
|
|
m_customLooper = looper; |
|
|
|
} |
|
|
|
|
|
|
|
public void SetThread(HandlerThread thread) |
|
|
|
{ |
|
|
|
CheckInitialization(false); |
|
|
|
m_customLooper = thread.getLooper(); |
|
|
|
} |
|
|
|
|
|
|
|
public boolean IsRunning() |
|
|
|
{ |
|
|
|
return IsInitialized() && mLocationClient.isStarted(); |
|
|
|
} |
|
|
|
|
|
|
|
public void Start() |
|
|
|
{ |
|
|
|
Run(-1); |
|
|
|
} |
|
|
|
|
|
|
|
public void Stop() |
|
|
|
{ |
|
|
|
CheckInitialization(true); |
|
|
|
|
|
|
|
//设置场景模式后最好调用一次stop,再调用start以保证场景模式生效 |
|
|
|
mLocationClient.stopLocation(); |
|
|
|
|
|
|
|
Log.i(ID_TAG, "定位停止"); |
|
|
|
} |
|
|
|
|
|
|
|
public void Shutdown() |
|
|
|
{ |
|
|
|
if(!IsInitialized()) |
|
|
|
{ |
|
|
|
Log.e(ID_TAG, "请先初始化"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
Stop(); |
|
|
|
|
|
|
|
mLocationClient.onDestroy(); |
|
|
|
mLocationClient = null; |
|
|
|
if(null != m_thread) |
|
|
|
{ |
|
|
|
m_thread.quit(); |
|
|
|
m_thread = null; |
|
|
|
Log.d(ID_TAG, "内建线程结束"); |
|
|
|
} |
|
|
|
|
|
|
|
Log.i(ID_TAG, "定位销毁"); |
|
|
|
} |
|
|
|
|
|
|
|
public void PreInit() |
|
|
|
{ |
|
|
|
if(m_isPreInit) |
|
|
|
return; |
|
|
|
Context applicationContext = m_context.getApplicationContext(); |
|
|
|
/** 设置包含隐私政策,并展示用户授权弹窗 <b>必须在AmapLocationClient实例化之前调用</b> |
|
|
|
* |
|
|
|
* @param context |
|
|
|
* @param isContains: 是隐私权政策是否包含高德开平隐私权政策 true是包含 |
|
|
|
* @param isShow: 隐私权政策是否弹窗展示告知用户 true是展示 |
|
|
|
* @since 5.6.0 |
|
|
|
*/ |
|
|
|
AMapLocationClient.updatePrivacyShow(applicationContext, true, true); |
|
|
|
/** |
|
|
|
* 设置是否同意用户授权政策 <b>必须在AmapLocationClient实例化之前调用</b> |
|
|
|
* @param context |
|
|
|
* @param isAgree:隐私权政策是否取得用户同意 true是用户同意 |
|
|
|
* |
|
|
|
* @since 5.6.0 |
|
|
|
*/ |
|
|
|
AMapLocationClient.updatePrivacyAgree(applicationContext, true); |
|
|
|
Log.i(ID_TAG, "定位预初始化"); |
|
|
|
m_isPreInit = true; |
|
|
|
} |
|
|
|
|
|
|
|
public boolean IsInitialized() |
|
|
|
{ |
|
|
|
return null != mLocationClient; |
|
|
|
} |
|
|
|
|
|
|
|
private void CheckInitialization(boolean inited) |
|
|
|
{ |
|
|
|
if(IsInitialized() != inited) |
|
|
|
{ |
|
|
|
String msg = inited ? "定位未初始化" : "定位已初始化"; |
|
|
|
Log.e(ID_TAG, msg); |
|
|
|
throw new RuntimeException(msg); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private boolean HasFlag(int f) |
|
|
|
{ |
|
|
|
return (m_flag & f) != 0; |
|
|
|
} |
|
|
|
|
|
|
|
public void SetOption(int type, Object value) |
|
|
|
{ |
|
|
|
CheckInitialization(false); |
|
|
|
m_options.put(type, value); |
|
|
|
} |
|
|
|
|
|
|
|
@SuppressLint("unchecked") |
|
|
|
private <T> T GetOption(int type, T def) |
|
|
|
{ |
|
|
|
Object o = m_options.get(type); |
|
|
|
if(null == o) |
|
|
|
return def; |
|
|
|
return (T) o; |
|
|
|
} |
|
|
|
|
|
|
|
private void InitOptions() |
|
|
|
{ |
|
|
|
//初始化AMapLocationClientOption对象 |
|
|
|
mLocationOption = new AMapLocationClientOption(); |
|
|
|
|
|
|
|
/** |
|
|
|
* 设置定位场景,目前支持三种场景(签到、出行、运动,默认无场景) |
|
|
|
*/ |
|
|
|
if(HasFlag(FLAG_PURPOSE_SIGNIN)) |
|
|
|
mLocationOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.SignIn); |
|
|
|
else if(HasFlag(FLAG_PURPOSE_TRANSPORT)) |
|
|
|
mLocationOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.Transport); |
|
|
|
else if(HasFlag(FLAG_PURPOSE_SPORT)) |
|
|
|
mLocationOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.Sport); |
|
|
|
|
|
|
|
//设置定位模式为AMapLocationMode.Hight_Accuracy,高精度模式。 |
|
|
|
if(HasFlag(FLAG_MODE_HIGH)) |
|
|
|
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy); |
|
|
|
//设置定位模式为AMapLocationMode.Battery_Saving,低功耗模式。 |
|
|
|
else if(HasFlag(FLAG_MODE_SAVING)) |
|
|
|
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Battery_Saving); |
|
|
|
//设置定位模式为AMapLocationMode.Device_Sensors,仅设备模式。 |
|
|
|
else if(HasFlag(FLAG_MODE_SENSORS)) |
|
|
|
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Device_Sensors); |
|
|
|
|
|
|
|
//获取一次定位结果: |
|
|
|
//该方法默认为false。 |
|
|
|
if(HasFlag(FLAG_ONCE)) |
|
|
|
mLocationOption.setOnceLocation(true); |
|
|
|
|
|
|
|
//获取最近3s内精度最高的一次定位结果: |
|
|
|
//设置setOnceLocationLatest(boolean b)接口为true,启动定位时SDK会返回最近3s内精度最高的一次定位结果。如果设置其为true,setOnceLocation(boolean b)接口也会被设置为true,反之不会,默认为false。 |
|
|
|
if(HasFlag(FLAG_ONCE_LATEST)) |
|
|
|
mLocationOption.setOnceLocationLatest(true); |
|
|
|
|
|
|
|
//设置定位间隔,单位毫秒,默认为2000ms,最低1000ms。 |
|
|
|
int interval = GetOption(OPTION_INTERVAL, DEFAULT_INTERVAL); |
|
|
|
mLocationOption.setInterval(interval); |
|
|
|
|
|
|
|
//设置是否返回地址信息(默认返回地址信息) |
|
|
|
if(HasFlag(FLAG_NO_NEED_ADDRESS)) |
|
|
|
mLocationOption.setNeedAddress(false); |
|
|
|
|
|
|
|
//设置是否允许模拟位置,默认为true,允许模拟位置 |
|
|
|
if(HasFlag(FLAG_MOCK_DISABLE)) |
|
|
|
mLocationOption.setMockEnable(false); |
|
|
|
|
|
|
|
//单位是毫秒,默认30000毫秒,建议超时时间不要低于8000毫秒。 |
|
|
|
int timeout = GetOption(OPTION_HTTP_TIMEOUT, DEFAULT_HTTP_TIMEOUT); |
|
|
|
mLocationOption.setHttpTimeOut(timeout); |
|
|
|
|
|
|
|
//关闭缓存机制 |
|
|
|
if(HasFlag(FLAG_CACHE_DISABLE)) |
|
|
|
mLocationOption.setLocationCacheEnable(false); |
|
|
|
} |
|
|
|
|
|
|
|
private void Run(int num) |
|
|
|
{ |
|
|
|
CheckInitialization(true); |
|
|
|
|
|
|
|
m_readOnce.set(num); |
|
|
|
|
|
|
|
//设置场景模式后最好调用一次stop,再调用start以保证场景模式生效 |
|
|
|
mLocationClient.stopLocation(); |
|
|
|
mLocationClient.startLocation(); |
|
|
|
|
|
|
|
Log.i(ID_TAG, "定位开始"); |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
在调用线程中执行 |
|
|
|
*/ |
|
|
|
@Override |
|
|
|
public void onLocationChanged(AMapLocation amapLocation) |
|
|
|
{ |
|
|
|
Log.d(ID_TAG, "onLocationChanged线程: " + Thread.currentThread().getId()); |
|
|
|
System.err.println(amapLocation); |
|
|
|
SetLastLocation(amapLocation); |
|
|
|
if(null != amapLocation) |
|
|
|
{ |
|
|
|
if(m_readOnce.get() > 0) |
|
|
|
{ |
|
|
|
m_readOnce.decrementAndGet(); |
|
|
|
if(m_readOnce.get() == 0) |
|
|
|
{ |
|
|
|
synchronized(m_lock) |
|
|
|
{ |
|
|
|
m_lock.notifyAll(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private double[] Convert(double longitude, double latitude) |
|
|
|
{ |
|
|
|
try |
|
|
|
{ |
|
|
|
//初始化坐标转换类 |
|
|
|
CoordinateConverter converter = new CoordinateConverter(m_context.getApplicationContext()); |
|
|
|
converter.from(CoordinateConverter.CoordType.GPS); |
|
|
|
//设置需要转换的坐标 |
|
|
|
converter.coord(new DPoint(latitude, longitude)); |
|
|
|
//转换成高德坐标 |
|
|
|
DPoint destPoint = converter.convert(); |
|
|
|
double dx = destPoint.getLongitude() - longitude; |
|
|
|
double dy = destPoint.getLatitude() - latitude; |
|
|
|
longitude = longitude - dx; |
|
|
|
latitude = latitude - dy; |
|
|
|
return new double[]{ |
|
|
|
longitude, latitude |
|
|
|
}; |
|
|
|
} |
|
|
|
catch(Exception e) |
|
|
|
{ |
|
|
|
throw new RuntimeException(e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void SetLastLocation(AMapLocation amapLocation) |
|
|
|
{ |
|
|
|
if(null != amapLocation) |
|
|
|
{ |
|
|
|
double longitude = amapLocation.getLongitude(); |
|
|
|
double latitude = amapLocation.getLatitude(); |
|
|
|
double[] coord = Convert(longitude, latitude); |
|
|
|
longitude = coord[0]; |
|
|
|
latitude = coord[1]; |
|
|
|
m_lastLocation = new NSLocationInfo(amapLocation.getProvider(), longitude, latitude); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
m_lastLocation = null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 同步调用, 会锁住线程 |
|
|
|
*/ |
|
|
|
public NSLocationInfo Read(int count) |
|
|
|
{ |
|
|
|
CheckInitialization(true); |
|
|
|
/* if(IsRunning()) |
|
|
|
{ |
|
|
|
throw new RuntimeException("请先停止定位"); |
|
|
|
}*/ |
|
|
|
|
|
|
|
Log.d(ID_TAG, "定位线程: " + Thread.currentThread().getId()); |
|
|
|
synchronized(m_lock) |
|
|
|
{ |
|
|
|
try |
|
|
|
{ |
|
|
|
CleanLastLocation(); |
|
|
|
Run(count); |
|
|
|
m_lock.wait(); |
|
|
|
} |
|
|
|
catch(Exception e) |
|
|
|
{ |
|
|
|
throw new RuntimeException(e); |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
Stop(); |
|
|
|
} |
|
|
|
} |
|
|
|
return m_lastLocation; |
|
|
|
} |
|
|
|
|
|
|
|
public NSLocationInfo GetLastLocation() |
|
|
|
{ |
|
|
|
return m_lastLocation; |
|
|
|
} |
|
|
|
|
|
|
|
public void CleanLastLocation() |
|
|
|
{ |
|
|
|
m_lastLocation = null; |
|
|
|
} |
|
|
|
} |