(1)
一般来讲,通过gps获取到经纬度坐标以后,要继续深入的获取该经纬度坐标的城市、街道与精度(误差)等信息。
private String getAddressbyGeoPoint() {
// 自经纬度取得地址
StringBuilder sb = new StringBuilder();
Geocoder gc = new Geocoder(getBaseContext(), Locale.getDefault());
List<Address> lstAddr = null;
try {
lstAddr = gc.getFromLocation(location.getLatitude(), location.getLongitude(), 1);
} catch (IOException e) {
Log.e("HangzhouBike", e.getMessage());
}
if (lstAddr != null && lstAddr.size() > 0) {
Address addr = lstAddr.get(0);
if (addr.getAddressLine(1) != null) {
sb.append(addr.getAddressLine(1)).append(" ");
}
if (addr.getAddressLine(2) != null){
sb.append(addr.getAddressLine(2)).append(" ");
sb.append(" ±" + location.getAccuracy() + "米");
}、
}
return sb.toString();
}
(2)
Android中LocationManager的提供了一系列方法来地理位置相关的问题,包括查询上一个已知位置;注册/注销来自某个 LocationProvider的周期性的位置更新;以及注册/注销接近某个坐标时对一个已定义Intent的触发等。今天我们就来看看Android 中LocatinManager的简单使用,以获取当前所在的位置为例。
首先,我们需要获取LocationManager的一个实例,这里需要注意的是他的实例只能通过下面这种方式来获取,直接实例化LocationManager是不被允许的。
Java代码
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
得到了LocationManager的实例locatonManager以后,我们通过下面的语句来注册一个周期性的位置更新。
Java代码
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
1000, 0, locationListener);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
1000, 0, locationListener);
这句代码告诉系统,我们需要从GPS获取位置信息,并且是每隔1000ms更新一次,并且不考虑位置的变化。最后一个参数是LocationListener的一个引用,我们必须要实现这个类。
Java代码
private final LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) { //当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发
// log it when the location changes
if (location != null) {
Log.i("SuperMap", "Location changed : Lat: "
+ location.getLatitude() + " Lng: "
+ location.getLongitude());
}
}
public void onProviderDisabled(String provider) {
// Provider被disable时触发此函数,比如GPS被关闭
}
public void onProviderEnabled(String provider) {
// Provider被enable时触发此函数,比如GPS被打开
}
public void onStatusChanged(String provider, int status, Bundle extras) {
// Provider的转态在可用、暂时不可用和无服务三个状态直接切换时触发此函数
}
};
private final LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) { //当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发
// log it when the location changes
if (location != null) {
Log.i("SuperMap", "Location changed : Lat: "
+ location.getLatitude() + " Lng: "
+ location.getLongitude());
}
}
public void onProviderDisabled(String provider) {
// Provider被disable时触发此函数,比如GPS被关闭
}
public void onProviderEnabled(String provider) {
// Provider被enable时触发此函数,比如GPS被打开
}
public void onStatusChanged(String provider, int status, Bundle extras) {
// Provider的转态在可用、暂时不可用和无服务三个状态直接切换时触发此函数
}
};
以上的这些步骤一般应当在Activity的onCreate()阶段完成。
在成功注册了一个周期性坐标更新以后,我们就随时可以通过下面的方法来取得当前的坐标了。
Java代码
Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
double latitude = location.getLatitude(); //经度
double longitude = location.getLongitude(); //纬度
double altitude = location.getAltitude(); //海拔
Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
double latitude = location.getLatitude(); //经度
double longitude = location.getLongitude(); //纬度
double altitude = location.getAltitude(); //海拔
不过这时候,如果你尝试去运行这个LocationSample的话程序启动时多半就会报错,因为我们没有设置GPS相关的权限,解决方法也相当简单,在AndroidManifest.xml中的block里添加下面这句即可解决权限的问题。详细的权限设置,请参考官方文档docs/reference/android/Manifest.permission.html
Java代码
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
(3)
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setAltitudeRequired(false);
criteria.setBearingRequired(false);
criteria.setCostAllowed(true);
criteria.setPowerRequirement(Criteria.POWER_LOW);
provider = locationManager.getBestProvider(criteria,true);
if (provider != null) {
location = locationManager.getLastKnownLocation(provider);
/* TODO - adjust update times to minimize battery use. Can be changed as needed.
*
*/
locationManager.requestLocationUpdates(provider,
60000, // 1min
100, // 100m
locationListener);
}
Obviously you don't need to request location updates, but if you do then below is some code I use....
private final LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location newloc) {
// something to do here when location changes
// TODO get other datq (accuracy, velocity, altitude, etc...)
// write data to database
// add markers
location = newloc;
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
};
and you need to remember to turn off/on the updating at onStart and onStop
@Override
public void onStop() {
super.onStop();
if (provider != null) {
locationManager.removeUpdates(locationListener);
}
@Override
public void onStart() {
super.onStart();
/* recheck which provider to use */
provider = locationManager.getBestProvider(criteria,true);
if (provider != null) {
location = locationManager.getLastKnownLocation(provider);
locationManager.requestLocationUpdates(provider,
60000, // 1min
100, // 100m
locationListener);
}
}
曾经遇到过一个面试题,让你写出横屏切换竖屏Activity的生命周期。现在给大家分析一下他切换时具体的生命周期是怎么样的:
1、新建一个Activity,并把各个生命周期打印出来
2、运行Activity,得到如下信息
onCreate-->
onStart-->
onResume-->
3、按crtl+f12切换成横屏时
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
4、再按crtl+f12切换成竖屏时,发现打印了两次相同的log
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
5、修改AndroidManifest.xml,把该Activity添加 android:configChanges="orientation",执行步骤3
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
6、再执行步骤4,发现不会再打印相同信息,但多打印了一行onConfigChanged
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
onConfigurationChanged-->
7、把步骤5的android:configChanges="orientation" 改成 android:configChanges="orientation|keyboardHidden",执行步骤3,就只打印onConfigChanged
onConfigurationChanged-->
8、执行步骤4
onConfigurationChanged-->
onConfigurationChanged-->
总结:
1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
3、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
总结一下整个Activity的生命周期
补充一点,当前Activity产生事件弹出Toast和AlertDialog的时候Activity的生命周期不会有改变
Activity运行时按下HOME键(跟被完全覆盖是一样的):onSaveInstanceState --> onPause --> onStop onRestart -->onStart--->onResume
Activity未被完全覆盖只是失去焦点:onPause--->onResume
一、首先要明白,ContentProvider(以下简称为CP)是什么。
1,是一套数据存储和获取的统一接口。
2,最大的特点是,可以在不同的应用程序间共享数据。
3,Android系统自身已经提供数个CP,包括音频、视频、通讯录的数据。
4,如第一条所说,CP是一套数据存储和获取的接口。所以有不同的实现,其中最常见的是使用SQLite的实现,数据被保存在一张表中。
5,CP需要向外部提供一个唯一的Uri(统一资源标识符),来标明资源的位置。该Uri的格式为:
content://xxx.yyy.zzz/aaa其中红色部分为统一的,表示协议、
xxx.yyy.zzz被称为authority,是一个唯一的字符串,按doc推荐,通常采用项目的包名、
蓝色部分/aaa被称为PathSegments,表示该资源更确切的位置,是一个目录结构。某些时候,可以将其理解为一张表。
二、自定义CP不是很有必要,除非应用需要向外部共享数据。一般都是使用Android系统提供的CP。
三、自定义CP的实现方法。
此处采用常见的SQLite方式实现。
1,书写一个类,继承android.content.ContentProvider,该类是抽象类,需要实现以下的方法:
从以上方法的参数可见,确实用SQLite的相关API来实现CP是很合适的。
2,自定义CP必要的内容。
public static final String AUTHORITY = "org.ashtray.todolist";
public static finalUri URI_1 = Uri.parse("content://" + AUTORITY + "/location_1");
最后的字符串location_1即为PathSegment。
public static final UriMatcher matcher = null; public static final int TODOS = 0; public static final int TODO = 1; static{ matcher = new UriMatcher(UriMatcher.NO_MATCH); //为matcher添加映射,用于过滤uri, //todolist,todolist/#为uri的形式, //TODOS,TODO未定义的两个整型常量,用于标识该类型的uri matcher.addURI(AUTHORTY, "todolist", TODOS); matcher.addURI(AUTHORTY, "todolist/#", TODO); }
public static final String CONTENT_ITEMS_TYPE= "vnd.android.cursor.dir/vnd.ashtray.todolist"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.ashtray.todolist";
vnd.android.cursor.item:固定的,表示单条数据。
vnd.android.cursor.dir:固定的,表示复数数据。
“/”之后为自定义,可随意。
public static final Map<String,String> projectionMap = null; static { projectionMap = new HashMap<String,String>(); projectionMap.put("col_1","col_1"); projectionMap.put("col_2","col_2"); projectionMap.put("col_3","col_3"); }
3,几个重点方法的实现。
@Override public String getType(Uri uri) { //UriMatcher对象的match方法,获取uri对应的int型标识 //该映射关系需要用首先用matcher.addURI进行添加 switch (matcher.match(uri)) { case Const.TODO: //返回uri对应的MIME类型 return Const.CONTENT_ITEM_TYPE; case Const.TODOS: return Const.CONTENT_ITEMS_TYPE; default: return null; } }
个人认为,该方法的作用在于,在查询方法中判断是查询单条数据还是多条数据以作不同处理。如果是查询单条数据,即可从uri中取出所要查询数据的id
//以List<String>的形式返回authority之后的内容,以“/”隔开 String id = uri.getPathSegments().get(1)
@Override public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) { //通过SQLiteOpenHelper的实现类,获取数据库对象 SQLiteDatabase db = helper.getReadableDatabase(); //根据uri取得查询内容的MIME类型 String mime = this.getType(uri); //创建查询 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); //设置描述数据结构的Map qb.setProjectionMap(getProjection(projection)); //如果MIME类型为查询单条数据,则从uri中取出数据的id,并添 加为查询的条件 if(Const.CONTENT_ITEM_TYPE.equalsIgnoreCase(mime)){ qb.appendWhere(Const.COL_ID + "=" + uri.getPathSegments().get(1)); } //设置查询的表 qb.setTables(Const.TABLE_NAME); //执行查询,并返回Cursor对象 Cursor cursor = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder); //设置更新通知,以获取最新的数据 cursor.setNotificationUri(this.getContext().getContentResolver(), uri); return cursor; }
SQLiteDatabase db = this.helper.getWritableDatabase(); long id = db.insert(Const.TABLE_NAME, null, values); if(id < 0){ throw new SQLException("insert field"); } //将插入数据的id添加到Uri并返回 Uri curr = ContentUris.withAppendedId(Const.TODO_LIST_URI, id); //将新插入的数据uri进行通知,以便获取最新的数据 this.getContext().getContentResolver().notifyChange(curr, null); return curr;
4,Activity中使用CP。
首先需要在AndroidManifest.xml中对自定义CP进行注册,
eg:
<provider <!--自定义CP的类全名--> android:name="org.ashtray.cp.TodoListContentProvider" <!--authority,自定义CP的唯一标识,与代码中定义的字符串相同--> android:authorities="org.ashtray.todolist"/>
自定义CP在应用启动时,由系统实例化(调用CP的onCreate方法,并且会实例化所有注册了的CP),在Activity中通过ContentResolver来操作自定义CP。
而对于查询,则调用Activty的mangedQuery方法。
或者ContentResolver的query方法。