Android之手机定位方式(GPS定位,网络定位,基站定位)
从前天学习GPS定位开始,这两天断断续续都在学习Android的三种基本定位方式。
1.GPS定位(基本Android机上都会有,缺点是必须在空旷的地方才有用)
2.网络定位(NetWork,这个很多手机上没有这个功能,比如我的联想机就没有这个功能)
4.基站定位(获取基站信息,然后向谷歌发送解析要求,解析地址)
在编写代码时,遇到了很多问题,大多数时间都是花费在一些不太注意的小问题上了,而且最后基站定位也还是有BUG(Http post不会执行)
遇到的问题:
1.权限没有添加,定位时需要的权限
1.1:<uses-permission android:name="android.permission.INTERNET" />(网络连接)
1.2:<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />(基站定位时需要的权限)
1.3:<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> (网络定位需要的权限)
1.4<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>(GPS精确定位时需要的权限)
2.GPS定位和网络定位时,没有添加监听函数:requestLocationUpdates().
正确的做法应该是在 onResume里面注册监听函数GPS位置监听和GPS状态监听(NetWork位置监听),然后在onPause()里面取消对应的监听
3.抛出错误:ERROR/LocationManagerService(1236): java.lang.IllegalArgumentException: provider=network(这是由于手机上不支持NetWork定位功能,没办法)
4.基站定位:程序执行到HttpResponse resp = client.execute(post)时会自动抛出错误,而且Catch捕获的错误是null.
解决方法1:在主线程里面使用了耗时操作,Android4.0以后不被允许,加上一下代码即可解决:
(但是这个方法会将程序一直卡着,虽然没出错,但是我的手机运行这个程序后直接就死掉了)
/**解决4.0中不能进行网络操作要求* 暴力方法* 其实最好是另外单独使用一个线程,完了后告诉主线程if (Build.VERSION.SDK_INT >= 11) {StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads ().detectDiskWrites().detectNetwork().penaltyLog().build());StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects().detectLeakedClosableObjects().penaltyLog().penaltyDeath().build());}*/
解决方法2(Thread+Handler):重新开一个线程MyThread,然后在MyThread里面执行耗时操作,将结果通知MyHandler,其中创建MyHandler时,将MyHandler与主线程的Looper绑定,所以在子线程MyThread里面通过MyHandler即可发送消息给主线程,主线程只需要重写handleMessage(Message msg)函数即可接收相应的消息并进行处理。这是异步线程交互的一种最常用的方式。
***但是问题来了:在我用了(Thread+Handler)的方式后,程序执行到HttpResponse resp = client.execute(post)时还是会自动抛出错误,而且捕获不到错误(如果尝试输出错误信息又会抛出新的错误(java.lang.NullPointerException:println needs a message),因为捕获到的是null)。目前还不知道为什么会这样。
一些小结:
1.在是线程间传送消息时,用到了bundle,关于bundle的使用
1.1bundle可以直接设置一些常用的类型,String,int,boolean等
1.2当需要自定义类型Object时,需要将Object类序列化(可以Serializable也可以Parcelable,其中实现Parcelable时需要重写几个方法),需要注意的时,父类序列化时,子类也是默认序列化的。
2.关于Toast的显示,Toast是依附于Activity的,所以在Activity中的子线程里面最好不要使用Toast,如果需要显示,则有下列几种解决方法
2.1:Looper.prepare(); Toast显示内容; Looper.loop();(注意,Toast显示一定要用 .show())
2.2:使用线程见的通信方式,子线程通过MyHandle发送消息给主线程,然后在主线程里面选择对应的消息类型然后进行相应的处理。
3.子线程访问主线程中的UI界面,这里介绍两种方法:
3.1:通过MyHandler.post(new Thread())其实是只调用线程的run()方法,然不是调用start()方法来启动一个新线程,其本质是在主线程里面调用了另外一个线程的run()方法,而不是新开一个线程,所以还是不能做耗时操作。
3.2new Thread(new Runnable()).start(),新开一个线程后,通过MyHandler来给主线程发送消息,主线程发送消息后在进行相应的处理,新开了一个线程,异步执行,所以可以做耗时操作。
4.在创建Activity对应的Handler时,可以使用弱引用,避免Handler内存泄漏(OOM):
如MyHandler(Activity activity) this.activity=new WeakReference<Activity>(activity).get();
为什么会内存泄漏:当创建一个Handler对象时,Handler对象会隐式的持有一个外部对象(通常是一个Activity的引用),而Handler通常会伴随着一个耗时的后台线程(这个后台线程在任务执行完毕,之后,通过消息机制通知Handler,然后Handler把图片更新到界面。)然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用,这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到线程结束。(如果打开一个Activiity,关闭它内存泄漏,然后继续打开,关闭。。。那么内存占用就很夸张了)
什么是WeakReference:它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向,该对象就会在被GC检查到时回收掉。比
如用户在关闭Activity之后,就算后台线程还没结束,但由于仅有一条来自Handler的弱引用指向Activity,所以GC仍然会在检查的时候把Activity回收掉。
用到了判断用户网络状态,根据不同网络状态做不同操作,比如WIFI时加载广告,GPRS时没有广告:
代码:
/**检查网络连接状态*/private void checkNetworkState(){if(LocationUtils.isNetworkAvaliable(mConnecManager)){//网络连接正常,判断网络状态judgeNetworkState();}else//网络没有连接,进行网络连接设置{setNetwork();}}/**网络未连接状态下,调用设置网络连接*/private void setNetwork(){Toast.makeText(this, "网络连接不可用!", Toast.LENGTH_SHORT).show(); //下面调用设置DialogAlertDialog.Builder builder=new AlertDialog.Builder(this);builder.setIcon(R.drawable.ic_notify);builder.setTitle("网络提示信息!");builder.setMessage("网络不可用,如果继续,请先设置网络!");builder.setPositiveButton("设置", new DialogInterface.OnClickListener() { @Overridepublic void onClick(DialogInterface dialog, int which) { Intent intent = null; /** * 判断手机系统的版本!如果API大于10 就是3.0+ * 因为3.0以上的版本的设置和3.0以下的设置不一样,调用的方法不同 */ if (android.os.Build.VERSION.SDK_INT > 10) { intent = new Intent(android.provider.Settings.ACTION_WIFI_SETTINGS); } else { intent = new Intent(); ComponentName component = new ComponentName( "com.android.settings", "com.android.settings.WirelessSettings"); intent.setComponent(component); intent.setAction("android.intent.action.VIEW"); } startActivity(intent); }});builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); builder.create();builder.show();}/**网络已经连接,判断是wifi还是GPRS* 设置一些自己的逻辑调用* */private void judgeNetworkState(){State gprs = mConnecManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState(); State wifi = mConnecManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState(); //如果是gprs连接if(gprs==State.CONNECTED||gprs==State.CONNECTING){Toast.makeText(this, "网络连接可用! GPRS模式!", Toast.LENGTH_SHORT).show();}//如果是wifi状态,一般wifi状态可以做一些其它的事情(如加载广告)if(wifi==State.CONNECTED|| wifi == State.CONNECTING){Toast.makeText(this, "网络连接可用! WIFI模式!", Toast.LENGTH_SHORT).show();//加载广告loadADmod();}}<pre name="code" class="java">/**得到基站位置的函数*/private void onGetTelephonyPosition(){Log.i(TAG,"进行基站定位!");txt_TelephonyPosition.setText("正在通过基站获取定位!"); //开启一个获取基站定位的线程,获取成功后会自动通知Activity//启动runnable的线程必须这样//因为实现Runnable接口没有start方法MyThread myTelephony=new MyThread();new Thread(myTelephony).start();//updateUIToNewLocation(currentLocation,myLocation,currentCellInfo);}
其中LocationUtils里面的isNetworkAvaliable函数:
/**判断网络是否连接诶*/public static boolean isNetworkAvaliable(ConnectivityManager mConnecManager){boolean flag = false; //去进行判断网络是否连接 if (mConnecManager.getActiveNetworkInfo() != null) { flag = mConnecManager.getActiveNetworkInfo().isAvailable(); } return flag;}
GPS连接NetWork连接以及基站连接就不具体贴代码了,最后上传完整工程。(由于我机子上没有NetWork连接,这个Demo里面GPS连接和NetWork连接放一块,优先使用NetWork连接,如果没有NetWork连接在使用GPS连接)
目前没解决的问题,代码如下:(程序执行到HttpResponse resp = client.execute(post)时还是会自动抛出错误)
线程开启方式,TelephonyDemoActivity里面的一个按钮监听函数onGetTelephonyPosition()
/**得到基站位置的函数*/private void onGetTelephonyPosition(){Log.i(TAG,"进行基站定位!");txt_TelephonyPosition.setText("正在通过基站获取定位!"); //开启一个获取基站定位的线程,获取成功后会自动通知Activity//启动runnable的线程必须这样//因为实现Runnable接口没有start方法MyThread myTelephony=new MyThread();new Thread(myTelephony).start(); }
线程类:
private class MyThread implements Runnable{@Overridepublic void run() {//从消息池中取出一个messageMessage msg = myHandler.obtainMessage(); //Bundle是message中的数据Bundle b = new Bundle();//设置一些数据,如Location,等等CellIdInfoManager cellIdInfoManager; cellIdInfoManager=new CellIdInfoManager(getBaseContext());List<CellInfo> currentCellInfo=cellIdInfoManager.getCellInfo();Location currentLocation = null;String myLocation="";try {currentLocation=TelePhonyNetWorkLocationManager.getBaseStationLocation(currentCellInfo); myLocation=TelePhonyNetWorkLocationManager.getLocation(currentLocation);msg.arg1=1;} catch (Exception e) {//设置msg为解析地址错误Log.e(TAG,"地址解析错误!");msg.arg1=0; } //设置数据b.putString("PhysicLocation", myLocation);b.putSerializable("Location", (Serializable) currentLocation);b.putSerializable("CellInfo", (Serializable) currentCellInfo);msg.setData(b);Log.i(TAG,"基站获取地址完毕,向主线程发送对应消息!");myHandler.sendMessage(msg); // 向Handler发送消息,更新UI}}
TelePhonyNetWorkLocationManager .getBaseStationLocation(currentCellInfo)函数完整代码:功能是将基站信息解析成为地址信息:
/*** 通过得到的基站信息,发送给谷歌服务器,解析并取得结果(经纬度)*/public static Location getBaseStationLocation(List<CellInfo> cellID) { if (cellID == null) { Log.i("TAG", "cellId is null."); return null; } Log.i("TAG","基站数量为:"+cellID.size());Log.i("TAG","向google服务器解析基站地址!");//默认的heep客户端DefaultHttpClient client = new DefaultHttpClient(); //提出请求HttpPost post = new HttpPost(""); //得到JSONObject 对象,并设置好各种参数JSONObject holder = new JSONObject(); try { Log.i("TAG","得到第一个基站!");CellInfo info = cellID.get(0); holder.put("version", "1.1.0"); holder.put("host", "maps.google.com"); holder.put("home_mobile_country_code", info.getMobileCountryCode()); holder.put("home_mobile_network_code", info.getMobileNetworkCode()); holder.put("request_address", true); holder.put("radio_type", info.getRadio_type()); if ("460".equals(info.getMobileCountryCode())) { holder.put("address_language", "zh_CN"); } else { holder.put("address_language", "en_US"); } //设置数据JSONObject data, current_data; JSONArray array = new JSONArray(); current_data = new JSONObject(); Log.i("TAG","设置第一个基站的数据!");current_data.put("cell_id", info.getCellId()); current_data.put("location_area_code", info.getLocationAreaCode()); current_data.put("mobile_country_code", info.getMobileCountryCode()); current_data.put("mobile_network_code", info.getMobileNetworkCode()); current_data.put("age", 0); array.put(current_data); Log.i("TAG","第一个基站的数据设置完毕!");if (cellID.size() > 2) { for (int i = 1; i < cellID.size(); i++) { Log.i("TAG","设置第"+i+1+"个基站的数据!");data = new JSONObject(); data.put("cell_id", info.getCellId()); data.put("location_area_code", info.getLocationAreaCode()); data.put("mobile_country_code", info.getMobileCountryCode()); data.put("mobile_network_code", info.getMobileNetworkCode()); data.put("age", 0); array.put(data); } } //放置数据holder.put("cell_towers", array); Log.i("TAG","将基站信息放到解析对象中!");StringEntity se = new StringEntity(holder.toString()); Log.i("TAG","得到holder的信息!"+se);post.setEntity(se); Log.i("TAG","设置post请求内容!"+se);/** 发出POST数据并获取返回数据 * HttpResponse resp = client.execute(post); 这句话出问题了* 因为Android4.0中,网络操作不能在UI线程中* 解决方法-开启新的线程,用来处理网络连接* 也就是说,这个函数实在新的线程里面运行* 但是,在新的Thread里面执行这个函数,这句话也会出错。。。Why?* */HttpResponse resp = client.execute(post); int state = resp.getStatusLine().getStatusCode(); Log.i("TAG","得到系统响应的状态!"+state);if (state == HttpStatus.SC_OK) { HttpEntity entity = resp.getEntity(); if (entity != null) { BufferedReader br = new BufferedReader( new InputStreamReader(entity.getContent())); //存放结果的字符串,sbStringBuffer sb = new StringBuffer(); String resute = ""; while ((resute = br.readLine()) != null) { sb.append(resute); } br.close(); data = new JSONObject(sb.toString()); data = (JSONObject) data.get("location"); Location loc = new Location( android.location.LocationManager.NETWORK_PROVIDER); loc.setLatitude((Double) data.get("latitude")); loc.setLongitude((Double) data.get("longitude")); loc.setAccuracy(Float.parseFloat(data.get("accuracy") .toString())); loc.setTime(System.currentTimeMillis()); return loc; } else { return null; } }//正常的http相应处理完毕 else { Log.v("TAG", state + ""); return null; } } catch (Exception e) { // Log.e("TAG", e.getMessage()); Log.e("TAG", "未知错误"); return null; } }
最后,MyHandler类代码(继承Handler):在Activity的onCreate里面调用 myHandler=new MyHandler(TelephonyDemoActivity.this);
/**myHandler类,继承Handler,重写了handleMessage方法*/private class MyHandler extends Handler{private Activity activity;public MyHandler(Activity activity){this.activity=new WeakReference<Activity>(activity).get();//使用弱引用避免Handler内存泄露}//重写handleMessage,用于处理message@Overridepublic void handleMessage(Message msg) {Log.i(TAG,"主线程接收到消息!");//选择不同的口令类型//0-获取地址失败 1-获取地址成功switch(msg.arg1){case 0:Toast.makeText(activity.getApplicationContext(), "解析地址出错,不能将经纬度解析为正确地址!", Toast.LENGTH_SHORT).show();Update(msg);break;case 1:Toast.makeText(activity.getApplicationContext(), "地址获取成功!", Toast.LENGTH_SHORT).show();Update(msg);break; default:Toast.makeText(activity.getApplicationContext(), "解析地址出错,未知错误!", Toast.LENGTH_SHORT).show();Update(msg);break;} }/**更新UI方法*/ private void Update(Message msg){//这里用于更新网络信息---更新LocationBundle b=msg.getData();//记得设置bundle时,将Location 键的值设为对应的locationLocation currentLocation=(Location) b.getSerializable("Location");String myLocation=b.getString("PhysicLocation");List<CellInfo> currentCellInfo=(List<CellInfo>) b.getSerializable("CellInfo");TelephonyDemoActivity.this.updateUIToNewLocation(currentLocation, myLocation, currentCellInfo);}}
那位大侠能解决这个问题。。。感激不尽!
完整工程路径:http://download.csdn.net/detail/u010979495/8106861
Android之手机定位方式(GPS定位,网络定位,基站定位)相关推荐
- Android定位--GPS定位与LBS基站定位
虽然现在第三方的定位非常强大,非常方便,但是我们在只需要很简单的定位,且不想用第三方的时候,我们可以自己动动手,基于GPS卫星定位和LBS基站定位的方式获取当前位置. GPS定位 1.权限申请 首先我 ...
- 室内定位:基于LoRa网络的蓝牙定位 BLE-2
①适用说明 基于LoRa网络的蓝牙定位是依赖LoRa作为数据回传网络,通过在场所内部署安装蓝牙信标设备作为定位依据,移动定位终端设备如定位胸牌或定位手表主动扫描环境中的iBeacon信号并通过LoRa ...
- 微信定位精灵服务器或网络异常,微信定位精灵系统界面无法更新怎么办
我们在前几篇微博中讲到了如何开始使用微信定位精灵,点击这里传送门,如果需要序列号,可以参考这篇文章<微信定位精灵免费序列号赠送> 而我们看到很多用户在使用的过程中遇到了一些这样或者那样的问 ...
- Android获取当前位置(GPS和网络定位)
1.添加定位权限 <!--定位权限--> <uses-permission android:name="android.permission.ACCESS_FINE_LOC ...
- Android常用的蓝牙,GPS,网络等状态检测方法汇总
序言 记录Android的一些判断网络,蓝牙,GPS,等设备状态的方法. 1.判断网络是否可用 // 是否有可用网络private boolean isNetworkConnected() {Conn ...
- android 获取手机GSM/CDMA信号信息,并获得基站信息
本文转自:http://software.intel.com/zh-cn/blogs/2011/12/16/android-gsmcdma/ 在Android中我们常用的轻松获取WIFI信号列表,那如 ...
- Android定位方式和测试方法,定位方式(d16)
一,回顾, ①selector和xpath的区别,selector是解析的html,xpth是解析的xml,所以使用selector比使用xpath快, ②定位元素的单数和复数,当使用单数定位不到元素 ...
- Android中手机定位相关知识点汇总
1.手机定位分类 根据不同的定位方式,手机定位又分为卫星定位和网络定位两大类. 2.卫星定位服务提供系统 卫星定位服务由几个全球卫星导航系统提供,主要包括美国GPS,俄罗斯格洛纳斯,中国北斗. 3.卫 ...
- 安卓定位方式全总结-gps定位,network定位,ip定位,基站定位
一般我们使用的是第三方定位,因为第三方定位比较成熟,有些场景我们不需要或者不可以使用第三方定位的时候我们就需要自己去获取定位了. 文章目录 定位方式 1.gps和network(时效性差) 1.1 使 ...
最新文章
- Web字体库下载及转换工具
- TLS回调函数(2)
- ios CGRec用法
- 【随笔】About QWERTY
- 到底该不该上马Vista 中小企业升级全攻略(上)
- 通过流程构建组织的【个人能力】和【团队能力】
- 复旦sakai安装指南
- 【贪玩巴斯】无线传感器网络(三)「Mac协议讲解」——2021-10-08
- 多源信息融合研究综述
- 计算机错误代码0x 00000006,什么原因造成了蓝屏 电脑蓝屏错误代码介绍
- 计算机网络(5.13)运输层- TCP的拥塞控制方法
- 三国志战略版:三势法正出奇效,藤甲张角新篇章
- 易语言-post登陆百度
- 深度学习-胶囊网络学习
- Windows快捷键大全(2020年版)
- CreateProcess 的正确关闭
- javascript--DOM事件流
- 那些有趣又实用的开源人工智能项目 Top 10
- ubuntu系统下C语言编写简单程序
- 什么是I/O寄存器的边际效应