首页 > Android > 正文

Android Location在GPS中的应用(二)

2014-08-27 Android 3046 ℃ 0 评论



这一篇其实跟GPS 毫无关系。 继续上一篇的内容,讲GPS以外的东西,比如说Service的使用。比如说gps监控,它并不需要任何UI,在后台默默地运行就行。为什么不做成 Service呢?悄悄地向服务器发送用户的位置坐标是一个不错的想法,因为它完全不需要用户的干预。当然为了保留用户权利,我们应当留一个地方让用户把 服务关掉。

继续前一篇的工程,如果你没有保留前面的工作也没有关系,从头来就是了。

一、AndroidManifext.xml

新建Google Project ,注意选择Google APIs。

编辑AndroidManifest.xml,加入相应的权限:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> 
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

 

其次,由于我们使用了服务,需要在<application></application>标签加入一个<service>标签,以注册自己的服务:

<service android:label="@string/app_name"android:name=".GpsService"> 
<intent-filter> 
<action android:name="start_gps_service"></action> 
<category android:name="android.intent.category.DEFAULT"></category> 
</intent-filter> 
</service>

这个.GpsService是我们自定义的service类,它可以相应一个”start_aps_servicee”的Action。

二、main.java

我们这个应用就一个Activity。main.xml不用做任何修改。打开main.java,以下代码在菜单中添加了2个菜单项,以便让用户开启和关闭gps:

// 创建菜单 
public booleanonCreateOptionsMenu(Menu menu) { 
// TODO Auto-generatedmethod stub 
super.onCreateOptionsMenu(menu); 
menu.add(0, Menu.FIRST + 1, 1, "打开GPS监控"); 
menu.add(0, Menu.FIRST + 2, 2, "关闭GPS监控"); 
return true; 
} 
  
// 菜单项监听 
public booleanonOptionsItemSelected(MenuItem item) { 
Intent i; 
// TODO Auto-generatedmethod stub 
super.onOptionsItemSelected(item); 
switch (item.getItemId()) { 
case Menu.FIRST + 1:// 打开GPS监控 
this.setTitle("GPS Service Started"); 
i = new Intent(this, GpsService.class); 
this.bindService(i, connection, Context.BIND_AUTO_CREATE); 
break; 
case Menu.FIRST + 2:// 关闭GPS监控 
this.setTitle("GPS Service Stopped"); 
// 通过检测mBinder是否为空来判断服务是否已绑定,从而避免service not registered错误 
if(mBinder!=null){ 
i = new Intent(this, GpsService.class); 
this.unbindService(connection); 
mBinder=null;//释放mBinder,防止重复解绑定
} 
break; 
} 
return true; 
}

this.bindService 和this.unbindService 和分别启动和关闭服务。这里的服务是GpsService类,呆会再介绍。

Android 的Service有两种使用方式。一种比较简单,使用Activity的startService/stopService方法启动关闭服务。但这种方式 不能满足我们的需要,因为无法向Service类传递参数。因此我们决定使用第2种方式,即IBinder的方式。其实第2种方式跟第1种方式的区别只在 于,service实现了onBind方法,在onBind方法中返回了IBinder对象,你可以把更多的代码实现在IBinder中。同时 service类会调用IBinder中的方法,于是我们可以通过该方法向IBinder传递参数。

而且在调用方法上,bindService和unbindService都需要一个 ServiceConnection 类的参数。因此我们在main.java中申明了这个ServiceConnection:

ServiceConnection connection = newServiceConnection() { 
@Override 
public void onServiceConnected(ComponentName arg0, IBinder arg1) { 
// TODO Auto-generated method stub 
mBinder = (IGpsBinder)arg1; 
if (mBinder != null) { 
mBinder.bindService(main.this); 
} 
  
} 
@Override 
public void onServiceDisconnected(ComponentName name) { 
// TODO Auto-generated method stub 
  
} 
};

在这个 ServiceConnection 示例中,有两个方法需要实现。我们关注的只有onServiceConnected方法。

这个方法在连接服务后调用。我们在这个方法中使用了一个接口对象IGpsBinder:

IGpsBinder mBinder = null;

IGpsBinder是一个接口,它对应的实现是GpsBinder类。这两个东西后面会说。

先看服务的绑定,代码:

i = new Intent(this, GpsService.class);

this.bindService(i, connection, Context.BIND_AUTO_CREATE);

 

首先,Intent中已经包含了服务的实现类GpsService,这样在bindService时就可以把服务类传递进去。其次,ServiceConnection也被传递进去了。

这样,当bindService 时,会先调用服务类GpsService的onBind方法,这个方法会返回一个IBinder对象(其实是IGpsBinder)。在后面 GpsService的代码中可以看到,其实onBinder方法返回了一个IGpsBinder的实现类GpsBinder。

这样就获得了一个GpsBinder,然后把这个GpsBinder传递给connection的 onServiceConnected 方法。通过 onServiceConnected 代码,最终调用的是GpsBinder的接口方法bindService(这个方法中包含了一个参数,我们通过它把main这个Activity传递进去)。

对于服务的解绑定,先调用GpsService的unOnbind方法。

了解了服务绑定的大致工作过程。接下来就是服务类GpsService了。

三、GpsService.java

public class GpsService extends Service { 
private GpsBinder binder=new GpsBinder(); 
@Override 
public void onCreate() { 
super.onCreate(); 
} 
  
@Override 
public void onDestroy() { 
// TODO Auto-generatedmethod stub 
super.onDestroy(); 
} 
  
@Override 
public void onStart(Intentintent, int startId) { 
// TODO Auto-generatedmethod stub 
super.onStart(intent, startId); 
} 
  
@Override 
public boolean onUnbind(Intentintent) { 
// TODO Auto-generatedmethod stub 
binder.unbindService(); 
return true; 
} 
  
@Override 
public IBinderonBind(Intent intent) { 
// TODO Auto-generatedmethod stub 
return binder; 
} 
}

可以看到,GpsService 的代码很简单,继承了Service,覆盖Service的5个生命周期方法。但除了onUnbind和onBinder方法外,我们都使用了默认的实 现。在onUnbind方法中,我们调用binder的unbindService方法进行解绑定。其中,binder是一个GpsBinder,后面会 介绍。在onBind方法中,我们返回了GpsBinder对象。这个GpsBinder对象能为我们做些什么呢?

四、GpsBinder.java

public class GpsBinder extends Binder implements IGpsBinder{ 
private LocationManager locationManager; 
private Location location; 
private String provider; 
private Context mContext = null; 
public GpsBinder(){ 
  
} 
@Override 
// 接口暴露方法 
public voidbindService(Context ctx){ 
mContext=ctx; 
// 获取LocationManager服务 
locationManager = (LocationManager) mContext 
.getSystemService(Context.LOCATION_SERVICE); 
  
// 如果未设置位置源,打开GPS设置界面 
openGPS(); 
// 获取Location Provider 
getProvider(); 
// 获取位置 
location = locationManager.getLastKnownLocation(provider); 
// 显示位置信息到文字标签 
updateWithNewLocation(location); 
// 注册监听器locationListener,第2、3个参数可以控制接收gps消息的频度以节省电力。第2个参数为毫秒, 
// 表示调用listener的周期,第3个参数为米,表示位置移动指定距离后就调用listener 
locationManager.requestLocationUpdates(provider, 2000, 10, 
locationListener); 
} 
public void unbindService(){ 
// 注销location监听器 
locationManager.removeUpdates(locationListener); 
} 
// 获取Location Provider 
private void getProvider() { 
// 构建位置查询条件 
Criteria criteria = new Criteria(); 
// 查询精度:高 
criteria.setAccuracy(Criteria.ACCURACY_FINE); 
// 是否查询海拨:否 
criteria.setAltitudeRequired(false); 
// 是否查询方位角:否 
criteria.setBearingRequired(false); 
// 是否允许付费:是 
criteria.setCostAllowed(true); 
// 电量要求:低 
criteria.setPowerRequirement(Criteria.POWER_LOW); 
// 返回最合适的符合条件的provider,第2个参数为true说明,如果只有一个provider是有效的,则返回当前provider 
provider = locationManager.getBestProvider(criteria, true); 
} 
  
// Gps消息监听器 
private final LocationListener locationListener = new LocationListener(){ 
// 位置发生改变后调用 
public void onLocationChanged(Location location) { 
updateWithNewLocation(location); 
} 
  
// provider被用户关闭后调用 
public void onProviderDisabled(String provider) { 
updateWithNewLocation(null); 
} 
  
// provider被用户开启后调用 
public void onProviderEnabled(String provider) { 
} 
  
// provider状态变化时调用 
public void onStatusChanged(String provider, int status, Bundleextras) { 
} 
}; 
  
// Gps监听器调用,处理位置信息 
private voidupdateWithNewLocation(Location location) { 
// 利用反射机制调用mContext的locationChanged方法 
Class<?>[] types = new Class[] {Location.class}; //这个方法有1个参数 
try{ 
Method m = mContext.getClass().getMethod("locationChanged", types); 
if (m != null) m.invoke(mContext, location); 
} catch (Exception e) { 
Log.e(GpsBinder.class.getName(),e.toString()); 
} 
Log.i(GpsBinder.class.getName(),"location ischanged:"+location); 
} 
// 判断是否开启GPS,若未开启,打开GPS设置界面 
private void openGPS() { 
boolean bGPS = locationManager 
.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER); 
boolean bNetwork = locationManager 
.isProviderEnabled(android.location.LocationManager.NETWORK_PROVIDER); 
Log.e(":::", "bGPS=" + bGPS + "bNetwork=" + bNetwork); 
if (bGPS || bNetwork) { 
Toast.makeText(mContext, "位置源已设置!", Toast.LENGTH_SHORT).show(); 
return; 
} 
// Toast.makeText(this, "位置源未设置!", Toast.LENGTH_SHORT).show(); 
((Activity)mContext).showDialog(0); 
} 
}

这个代码不解释了,完全就是把我们在上一篇中关于Location的代码搬到这里了。需要注意的是unbindServicebindServiceupdateWithNewLocation 方法。

unbindService方法前面已提过,调用main对象的unbindService方法时被调用,它会把location监听器注销,于是位置变化不再通知监听器。

bindService 方法是IGpsBinder中的接口方法,它注册了location监听器,当Gps芯片检测到位置发生改变时通知locationListener监听 器。值得注意的是这个方法中的ctx参数,实际上是把main这个Activity传递进来了,并且保存在mContext变量中,这样在 updateWithNewLocation方法中可以使用main。

updateWithNewLocation 方法是监听器里的主要方法,它使用了一个技巧:利用java反射机制调用mContext的 locationChanged 方法。如果main没有实现 locationChanged 方法,则什么也不会做。这个技巧用来更新UI是很实用的。

当然,我们在main.java中也实现了这个 locationChanged 方法:

public voidlocationChanged(Location loc) { 
updateWithNewLocation(loc); 
} 
// Gps监听器调用,处理位置信息 
private voidupdateWithNewLocation(Location location) { 
String latLongString; 
TextView myLocationText = (TextView) findViewById(R.id.text); 
if (location != null) { 
double lat =location.getLatitude(); 
double lng =location.getLongitude(); 
latLongString = "纬度:" + lat + "/n经度:" + lng; 
} else { 
latLongString = "无法获取地理信息"; 
} 
myLocationText.setText("您当前的位置是:/n" + latLongString + "/n地址:" 
+ getAddressString(getAddressbyGeoPoint(location))); 
} 
// 获取地址信息 
privateList<Address> getAddressbyGeoPoint(Location location) { 
List<Address> result = null; 
// 先将Location转换为GeoPoint 
// GeoPoint gp=getGeoByLocation(location); 
try { 
if (location != null) { 
// 获取Geocoder,通过Geocoder就可以拿到地址信息 
Geocoder gc = new Geocoder(this, Locale.getDefault()); 
// double geoLatitude = (int)gp.getLatitudeE6() / 1E6; 
// double geoLongitude = (int)gp.getLongitudeE6() / 1E6; 
result = gc.getFromLocation(location.getLatitude(), 
location.getLongitude(), 1); 
} 
} catch (Exception e) { 
e.printStackTrace(); 
} 
return result; 
} 
  
// 把地址信息转换为一定格式的字符串 
private StringgetAddressString(List<Address> list) { 
if (list != null && list.size() > 0) { 
Address add = list.get(0); 
return String.format("%s%s%s%s", add.getCountryName(), 
add.getAdminArea(),add.getLocality(), 
add.getThoroughfare()); 
} else 
return ""; 
}

内容很多,但大部分仍然是上一篇中实现过的内容。

此外,在main.java中还要实现托管对话框:

protected DialogonCreateDialog(int id) { 
Log.e("::::", "ddsdg"); 
return newAlertDialog.Builder(this) 
.setMessage("位置源未设置!是否现住设置位置源?") 
.setPositiveButton("是", newDialogInterface.OnClickListener() { 
public void onClick(DialogInterface dialog, int which) { 
// 转至GPS设置界面 
Intent intent = new Intent( 
Settings.ACTION_SECURITY_SETTINGS); 
startActivityForResult(intent, 0); 
} 
}) 
.setNegativeButton("不", newDialogInterface.OnClickListener() { 
public voidonClick(DialogInterface dialog, int which) { 
dialog.dismiss();//removeDialog(0);移除对话框 
} 
}).create(); 
}

因为在GpsBinder的openGps方法中调用了mContext.showDialog方法。

五、IGpsBinder.java

IGpsBinder中只定义了1个接口方法,这也是在ServiceConnection唯一能识别和调用的IBinder方法:

public interface IGpsBinder{ 
// 接口暴露方法 
public voidbindService(Context ctx); 
}

方法本身含有一个参数,这个就是把main对象传递给GpsBinder的地方。

 

六、测试

可以在模拟器中进行测试了,别忘记前面说的利用DDMS 的Location Controls来修改Gps坐标。点击“打开GPS监控”菜单,可以看到模拟器从Gps获取到当前位置信息,修改坐标经纬度后点”send”,新坐标立 即被捕获并显示在窗口里;点“关闭GPS监控”菜单后,再修改坐标就没用了,还是显示原来的坐标。

 


日历
«    2024年3月    »
123
45678910
11121314151617
18192021222324
25262728293031
标签列表
最近发表
友情链接