安卓手机定位研究

Android 基站定位源代码

经过几天的调研以及测试,终于解决了联通2G、移动2G、电信3G的基站定位代码。团队里面只有这些机器的制式了。下面就由我来做一个详细的讲解吧。

1 相关技术内容

Google Android Api里面的TelephonyManager的管理。

联通、移动、电信不同制式在获取基站位置的代码区别。

通过基站的基本信息,通过Google Gears获取对应的GPS经纬度。

通过Google Map API根据GPS经纬度获取当前位置。

2 目前存在的几个问题

由于得到的GPS经纬度在Google Map上面显示需要偏移,这块暂时没有进行处理。

没有使用PhoneStateListener来对状态实时进行更新。

没有使用线程异步获取数据

没有使用服务的方式来实时获取数据

所以如果是商业使用的话,还需进一步修改。

3
当然本部分代码已经移植到我们的家庭卫士的项目中了,2提到的问题全部解决了。

下面我针对第一部分的四大内容进行代码注解。

1
Google Android Api里面的TelephonyManager的管理。

TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 

通过这个方式就可以得到TelephonyManager接口。

这个接口的源代码可以通过设置在项目里面查看,这里不具体附上了。

得到TelephonyManager后,由于针对不同的运营商,代码有所不同,所以需要判断getNetworkType()

在源代码里面有如下的类型定义

01 /**
Network type is unknown */
02     public static final int NETWORK_TYPE_UNKNOWN
0;
03     /**
Current network is GPRS */
04     public static final int NETWORK_TYPE_GPRS
1;
05     /**
Current network is EDGE */
06     public static final int NETWORK_TYPE_EDGE
2;
07     /**
Current network is UMTS */
08     public static final int NETWORK_TYPE_UMTS
3;
09     /**
Current network is CDMA: Either IS95A or IS95B*/
10     public static final int NETWORK_TYPE_CDMA
4;
11     /**
Current network is EVDO revision 0*/
12     public static final int NETWORK_TYPE_EVDO_0
5;
13     /**
Current network is EVDO revision A*/
14     public static final int NETWORK_TYPE_EVDO_A
6;
15     /**
Current network is 1xRTT*/
16     public static final int NETWORK_TYPE_1xRTT
7;
17     /**
Current network is HSDPA */
18     public static final int NETWORK_TYPE_HSDPA
8;
19     /**
Current network is HSUPA */
20     public static final int NETWORK_TYPE_HSUPA
9;
21     /**
Current network is HSPA */
22     public static final int NETWORK_TYPE_HSPA
10;

2 联通、移动、电信不同制式在获取基站位置的代码区别。

这部分是我实际测试出来的,经过无数次的拆机,放卡,才实现了不同制式的完美实现。

代码如下:

01 TelephonyManager
tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
02 int type
= tm.getNetworkType();
03 //中国电信为CTC
04 //NETWORK_TYPE_EVDO_A是中国电信3G的getNetworkType
05 //NETWORK_TYPE_CDMA电信2G是CDMA
06 if(type
== TelephonyManager.NETWORK_TYPE_EVDO_A || type == TelephonyManager.NETWORK_TYPE_CDMA || type ==TelephonyManager.NETWORK_TYPE_1xRTT)
07 {
08 }
09 //移动2G卡
+ CMCC + 2
10 //type
= NETWORK_TYPE_EDGE
11 else if(type
== TelephonyManager.NETWORK_TYPE_EDGE)
12 {
13 }
14 //联通的2G经过测试
China Unicom   1 NETWORK_TYPE_GPRS
15 else if(type
== TelephonyManager.NETWORK_TYPE_GPRS)
16 {
17   
18 }
19 else
20 {
21 tv.setText("Current
Not Support This Type."
);
22 }

3 通过基站的基本信息,通过Google
Gears获取对应的GPS经纬度。

这部分前面的两篇文章都有提到,代码参考了网友们的代码,感谢感谢。

01 private Location
callGear(ArrayList cellID) {
02         if (cellID
== 
nullreturn null;
03         DefaultHttpClient
client = 
new DefaultHttpClient();
04         HttpPost
post = 
new HttpPost(
05                 "http://www.google.com/loc/json");
06         JSONObject
holder = 
new JSONObject();
07         try {
08             holder.put("version""1.1.0");
09             holder.put("host""maps.google.com");
10             holder.put("home_mobile_country_code",
cellID.get(
0).mobileCountryCode);
11             holder.put("home_mobile_network_code",
cellID.get(
0).mobileNetworkCode);
12             holder.put("radio_type",
cellID.get(
0).radioType);
13             holder.put("request_address"true);
14             if ("460".equals(cellID.get(0).mobileCountryCode))
15                 holder.put("address_language""zh_CN");
16             else
17                 holder.put("address_language""en_US");
18             JSONObject
data,current_data;
19             JSONArray
array = 
new JSONArray();
20             current_data
new JSONObject();
21             current_data.put("cell_id",
cellID.get(
0).cellId);
22             current_data.put("location_area_code",
cellID.get(
0).locationAreaCode);
23             current_data.put("mobile_country_code",
cellID.get(
0).mobileCountryCode);
24             current_data.put("mobile_network_code",
cellID.get(
0).mobileNetworkCode);
25             current_data.put("age"0);
26             array.put(current_data);
27             if (cellID.size()
2)
{
28                 for (int i
1;
i < cellID.size(); i++) {
29                     data
new JSONObject();
30                     data.put("cell_id",
cellID.get(i).cellId);
31                     data.put("location_area_code",
cellID.get(i).locationAreaCode);
32                     data.put("mobile_country_code",
cellID.get(i).mobileCountryCode);
33                     data.put("mobile_network_code",
cellID.get(i).mobileNetworkCode);
34                     data.put("age"0);
35                     array.put(data);
36                 }
37             }
38             holder.put("cell_towers",
array);
39             StringEntity
se = 
new StringEntity(holder.toString());
40             Log.e("Location
send"
,
holder.toString());
41             post.setEntity(se);
42             HttpResponse
resp = client.execute(post);
43             HttpEntity
entity = resp.getEntity();
44   
45             BufferedReader
br = 
new BufferedReader(
46                     new InputStreamReader(entity.getContent()));
47             StringBuffer
sb = 
new StringBuffer();
48             String
result = br.readLine();
49             while (result
!= 
null)
{
50                 Log.e("Locaiton
receive"
,
result);
51                 sb.append(result);
52                 result
= br.readLine();
53             }
54             if(sb.length()              return null;
55             data
new JSONObject(sb.toString());
56             data
= (JSONObject) data.get(
"location");
57   
58             Location
loc = 
new Location(LocationManager.NETWORK_PROVIDER);
59             loc.setLatitude((Double)
data.get(
"latitude"));
60             loc.setLongitude((Double)
data.get(
"longitude"));
61             loc.setAccuracy(Float.parseFloat(data.get("accuracy").toString()));
62             loc.setTime(GetUTCTime());
63             return loc;
64         catch (JSONException
e) {
65             return null;
66         catch (UnsupportedEncodingException
e) {
67             e.printStackTrace();
68         catch (ClientProtocolException
e) {
69             e.printStackTrace();
70         catch (IOException
e) {
71             e.printStackTrace();
72         }
73         return null;
74     }

4 通过Google Map API根据GPS经纬度获取当前位置。

本部分代码参考了 简单基站定位程序 ,感谢雷一兄这么好的文章。同时雷一兄的排版真的非常好看,清晰明了。

01 private String
getLocation(Location itude) 
throws Exception
{
02         String
resultString = 
"";
03   
04         /**
这里采用get方法,直接将参数加到URL上 */
05         String
urlString = String.format(
"http://maps.google.cn/maps/geo?key=abcdefg&q=%s,%s",
itude.getLatitude(), itude.getLongitude());
06         Log.i("URL",
urlString);
07   
08         /**
新建HttpClient */
09         HttpClient
client = 
new DefaultHttpClient();
10         /**
采用GET方法 */
11         HttpGet
get = 
new HttpGet(urlString);
12         try {
13             /**
发起GET请求并获得返回数据 */
14             HttpResponse
response = client.execute(get);
15             HttpEntity
entity = response.getEntity();
16             BufferedReader
buffReader = 
new BufferedReader(newInputStreamReader(entity.getContent()));
17             StringBuffer
strBuff = 
new StringBuffer();
18             String
result = 
null;
19             while ((result
= buffReader.readLine()) != 
null)
{
20                 strBuff.append(result);
21             }
22             resultString
= strBuff.toString();
23   
24             /**
解析JSON数据,获得物理地址 */
25             if (resultString
!= 
null &&
resultString.length() > 
0)
{
26                 JSONObject
jsonobject = 
new JSONObject(resultString);
27                 JSONArray
jsonArray = 
newJSONArray(jsonobject.get("Placemark").toString());
28                 resultString
"";
29                 for (int i
0;
i < jsonArray.length(); i++) {
30                     resultString
= jsonArray.getJSONObject(i).getString(
"address");
31                 }
32             }
33         catch (Exception
e) {
34             throw new Exception("获取物理位置出现错误:" +
e.getMessage());
35         finally {
36             get.abort();
37             client
null;
38         }
39   
40         return resultString;
41     }

5
最关键的出来了,附上代码吧。

AndroidPosition

补充一下:

在AndroidMenifest.xml里面需要加上

android.permission.INTERNET、android.permission.ACCESS_COARSE_LOCATION、android.permission.READ_PHONE_STATE权限,否则会出错。

放在Application包前面。

6
图片看一下效果吧。

7
另外在提交数据到Google Gears的时候,格式如下

发送到Google的数据格式:

02-24 18:08:20.550: E/Location send(12892): {“address_language”:”zh_CN”,”host”:”maps.google.com”,”radio_type”:”cdma”,”home_mobile_country_code”:”460″,”home_mobile_network_code”:”13965″,”cell_towers”:[{"mobile_network_code":"13965","location_area_code":11,"cell_id":1985,"age":0,"mobile_country_code":"460"}],”request_address”:true,”version”:”1.1.0″}

接收到Google的数据格式:

02-24 18:08:22.975: E/Locaiton receive(12892): {“location”:{“latitude”:43.8595097,”longitude”:125.3355736,”address”: {“country”:”中国”,”country_code”:”CN”,”region”:”吉林省”,”city”:”长春市”,”street”:”文昌路”,”street_number”:”"},”accuracy”:1815.0},”access_token”:”2:_Kpk9mOFMgyWgLai:8iWlDpBYZsp4_VxO”}

-End-

前言

经过前面几节的学习,我们已经对Android程序的开发流程有了个大体的了解,为了提高我们的学习兴趣,在这一节我们将编写一个简单的基站定位程序。现在LBS(Location Based Service,基于位置的服务)移动应用相当流行(如:微信,切客,嘀咕,街旁等),基站定位是这类程序用到的关键性技术之一,我们来揭开它的神秘面纱吧。

在这一节里,我们会接触到事件、TelephonyManager、HTTP通信、JSON的使用等知识点。

声明

本系列文章不是教程,仅为笔记,如有不当之处请指正。

欢迎转载,转载请保留原出处:http://www.cnblogs.com/rayee

目录

一、设置界面

二、为按钮绑定事件

三、获取基站信息

四、获取经纬度

五、获取物理位置

六、显示结果

七、运行程序

八、总结

九、程序代码

正文

在Android操作系统下,基站定位其实很简单,先说一下实现流程:

调用SDK中的API(TelephonyManager)获得MCC、MNC、LAC、CID等信息,然后通过google的API获得所在位置的经纬度,最后再通过google map的API获得实际的地理位置。(google真牛!)

有同学会问:MNC、MCC、LAC、CID都是些什么东西?google又怎么通过这些东西就获得经纬度了呢?

我们一起来学习一下:

MCC,Mobile Country Code,移动国家代码(中国的为460);

MNC,Mobile Network Code,移动网络号码(中国移动为00,中国联通为01);

LAC,Location Area Code,位置区域码;

CID,Cell Identity,基站编号,是个16位的数据(范围是0到65535)。

了解了这几个名词的意思,相信有些朋友已经知道后面的事了:google存储了这些信息,直接查询就能得到经纬度了。(至于google怎么得到移动、联通的基站信息,这就不得而知了,反正google免费提供接口,直接调用就是)

下面开始动手。

一、设置界面

我们在上一节的程序的基础上进行开发,在DemoActivity的界面上实现这个功能。(没有代码的同学可点击这里下载,感谢yuejianjun同学的建议,以后我会在每一节的最后把例子打包提供下载)

首先我们将DemoActivity使用的布局修改一下:

第1行为TextView,显示提示文字;第2行为一个Button,触发事件;第3行、第4行分别显示基站信息和地理位置(现在为空,看不到)。

layout/main.xml文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Please click the button below to get your location" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me" />

    <TextView
        android:id="@+id/cellText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="" />

    <TextView
        android:id="@+id/lacationText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="" />

</LinearLayout>

接下来我们打开DemoActivity.java编写代码。

二、为按钮绑定事件

我们在Activity创建时绑定事件,将以下代码添加到setContentView(R.layout.main);后:

        /** 为按钮绑定事件 */
        Button btnGetLocation = (Button)findViewById(R.id.button1);
        btnGetLocation.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				onBtnClick();
			}
		});

同时还需要在头部import相关组件:

import android.view.View;
import android.widget.Button;
import android.view.View.OnClickListener;

我们来分析一下这段代码:

首先我们通过findViewById(R.id.button1)找到按钮这个对象,前面加(Button)表示显示的转换为Button对象;

然后设置按钮点击事件的监听器,参数为OnClickListener对象,再重载这个类的onClick方法,调用onBtnClick方法(这个方法得由我们自己去写,他在点击按钮时被调用)。

好了,调用方法写好了,我们来写实现(调用后需要做什么事)。动手编码之前先在脑中整理好思路,养成好习惯。

我们需要在DemoActivty类中添加如下私有方法:

  1. 我们需要刚刚提到的onBtnClick回调方法,被调用时实现取得基站信息、获取经纬度、获取地理位置、显示的功能。但是很显然,全部揉到一个方法里面并不是个好主意,我们将它分割为几个方法;
  2. 添加获取基站信息的方法getCellInfo,返回基站信息;
  3. 添加获取经纬度的方法getItude,传入基站信息,返回经纬度;
  4. 添加获取地理位置的方法getLocation,传入经纬度,返回地理位置;
  5. 添加显示结果的方法showResult,传入得到的信息在界面上显示出来。

好了,先将方法添上,完整代码如下:

package com.android.demo;

import android.R.bool;
import android.R.integer;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.view.View.OnClickListener;

public class DemoActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        /** 为按钮绑定事件 */
        Button btnGetLocation = (Button)findViewById(R.id.button1);
        btnGetLocation.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				onBtnClick();
			}
		});
    }

    /** 基站信息结构体 */
    public class SCell{
    	public int MCC;
    	public int MNC;
    	public int LAC;
    	public int CID;
    }

    /** 经纬度信息结构体 */
    public class SItude{
    	public String latitude;
    	public String longitude;
    }

    /** 按钮点击回调函数 */
    private void onBtnClick(){

    }

    /** 获取基站信息 */
    private SCell getCellInfo(){

    }

    /** 获取经纬度 */
    private SItude getItude(SCell cell){

    }

    /** 获取地理位置 */
    private String getLocation(SItude itude){

    }

    /** 显示结果 */
    private void showResult(SCell cell, String location){

    }
}

现在在onBtnClick方法中编码,依次调用后面几个方法,代码如下:

    /** 按钮点击回调函数 */
    private void onBtnClick(){
    	/** 弹出一个等待状态的框 */
    	ProgressDialog mProgressDialog = new ProgressDialog(this);
    	mProgressDialog.setMessage("正在获取中...");
		mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
		mProgressDialog.show();

    	try {
    		/** 获取基站数据 */
    		SCell cell = getCellInfo();

    		/** 根据基站数据获取经纬度 */
    		SItude itude = getItude(cell);

    		/** 获取地理位置 */
    		String location = getLocation(itude);

    		/** 显示结果 */
    		showResult(cell, location);

    		/** 关闭对话框 */
    		mProgressDialog.dismiss();
    	}catch (Exception e) {
    		/** 关闭对话框 */
    		mProgressDialog.dismiss();
    		/** 显示错误 */
    		TextView cellText = (TextView)findViewById(R.id.cellText);
    		cellText.setText(e.getMessage());
		}
    }

按钮相关的工作就完成了,接下来编写获取基站信息的方法。

三、获取基站信息

获取基站信息我们需要调用SDK提供的API中的TelephonyManager,需要在文件头部引入:

import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;

完整代码为:

	/**
	 * 获取基站信息
	 *
	 * @throws Exception
	 */
	private SCell getCellInfo() throws Exception {
		SCell cell = new SCell();

		/** 调用API获取基站信息 */
		TelephonyManager mTelNet = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
		GsmCellLocation location = (GsmCellLocation) mTelNet.getCellLocation();
		if (location == null)
			throw new Exception("获取基站信息失败");

		String operator = mTelNet.getNetworkOperator();
		int mcc = Integer.parseInt(operator.substring(0, 3));
		int mnc = Integer.parseInt(operator.substring(3));
		int cid = location.getCid();
		int lac = location.getLac();

		/** 将获得的数据放到结构体中 */
		cell.MCC = mcc;
		cell.MNC = mnc;
		cell.LAC = lac;
		cell.CID = cid;

		return cell;
	}

如果获得的位置信息为null将抛出错误,不再继续执行。最后将获取的基站信息封装为结构体返回。

四、获取经纬度

在这一步,我们需要采用HTTP调用google的API以获取基站所在的经纬度。

Android作为一款互联网手机,联网的功能必不可少。Android提供了多个接口供我们使用,这里我们使用DefaultHttpClient。

完整的方法代码如下:

	/**
	 * 获取经纬度
	 *
	 * @throws Exception
	 */
	private SItude getItude(SCell cell) throws Exception {
		SItude itude = new SItude();

		/** 采用Android默认的HttpClient */
		HttpClient client = new DefaultHttpClient();
		/** 采用POST方法 */
		HttpPost post = new HttpPost("http://www.google.com/loc/json");
		try {
			/** 构造POST的JSON数据 */
			JSONObject holder = new JSONObject();
			holder.put("version", "1.1.0");
			holder.put("host", "maps.google.com");
			holder.put("address_language", "zh_CN");
			holder.put("request_address", true);
			holder.put("radio_type", "gsm");
			holder.put("carrier", "HTC");

			JSONObject tower = new JSONObject();
			tower.put("mobile_country_code", cell.MCC);
			tower.put("mobile_network_code", cell.MNC);
			tower.put("cell_id", cell.CID);
			tower.put("location_area_code", cell.LAC);

			JSONArray towerarray = new JSONArray();
			towerarray.put(tower);
			holder.put("cell_towers", towerarray);

			StringEntity query = new StringEntity(holder.toString());
			post.setEntity(query);

			/** 发出POST数据并获取返回数据 */
			HttpResponse response = client.execute(post);
			HttpEntity entity = response.getEntity();
			BufferedReader buffReader = new BufferedReader(new InputStreamReader(entity.getContent()));
			StringBuffer strBuff = new StringBuffer();
			String result = null;
			while ((result = buffReader.readLine()) != null) {
				strBuff.append(result);
			}

			/** 解析返回的JSON数据获得经纬度 */
			JSONObject json = new JSONObject(strBuff.toString());
			JSONObject subjosn = new JSONObject(json.getString("location"));

			itude.latitude = subjosn.getString("latitude");
			itude.longitude = subjosn.getString("longitude");

			Log.i("Itude", itude.latitude + itude.longitude);

		} catch (Exception e) {
			Log.e(e.getMessage(), e.toString());
			throw new Exception("获取经纬度出现错误:"+e.getMessage());
		} finally{
			post.abort();
			client = null;
		}

    	return itude;
    }

代笔中关键的地方都作了注释,同学们还有不理解的举手哈。

在这里采用POST方法将JSON数据发送到googleAPI,google返回JSON数据,我们得到数据后解析,得到经纬度信息。

关于google 基站信息API的官方说明>>请到这里查看

五、获取物理位置

得到经纬度后,我们将之转换为物理地址。

我们仍然使用DefaultHttpClient来调用google地图的API,获得物理信息,不过在这里我们使用GET方法。

完整的方法代码如下:

	/**
	 * 获取地理位置
	 *
	 * @throws Exception
	 */
	private String getLocation(SItude itude) throws Exception {
		String resultString = "";

		/** 这里采用get方法,直接将参数加到URL上 */
		String urlString = String.format("http://maps.google.cn/maps/geo?key=abcdefg&q=%s,%s", itude.latitude, itude.longitude);
		Log.i("URL", urlString);

		/** 新建HttpClient */
		HttpClient client = new DefaultHttpClient();
		/** 采用GET方法 */
		HttpGet get = new HttpGet(urlString);
		try {
			/** 发起GET请求并获得返回数据 */
			HttpResponse response = client.execute(get);
			HttpEntity entity = response.getEntity();
			BufferedReader buffReader = new BufferedReader(new InputStreamReader(entity.getContent()));
			StringBuffer strBuff = new StringBuffer();
			String result = null;
			while ((result = buffReader.readLine()) != null) {
				strBuff.append(result);
			}
			resultString = strBuff.toString();

			/** 解析JSON数据,获得物理地址 */
			if (resultString != null && resultString.length() > 0) {
				JSONObject jsonobject = new JSONObject(resultString);
				JSONArray jsonArray = new JSONArray(jsonobject.get("Placemark").toString());
				resultString = "";
				for (int i = 0; i < jsonArray.length(); i++) {
					resultString = jsonArray.getJSONObject(i).getString("address");
				}
			}
		} catch (Exception e) {
			throw new Exception("获取物理位置出现错误:" + e.getMessage());
		} finally {
			get.abort();
			client = null;
		}

		return resultString;
	}

GET方法就比POST方法简单多了,得到的数据同样为JSON格式,解析一下得到物理地址。

六、显示结果

好了,我们已经得到我们想要的信息了,我们把它显示出来,方法代码如下:

	/** 显示结果 */
	private void showResult(SCell cell, String location) {
		TextView cellText = (TextView) findViewById(R.id.cellText);
		cellText.setText(String.format("基站信息:mcc:%d, mnc:%d, lac:%d, cid:%d",
				cell.MCC, cell.MNC, cell.LAC, cell.CID));

		TextView locationText = (TextView) findViewById(R.id.lacationText);
		locationText.setText("物理位置:" + location);
	}

七、运行程序

我们的编码工作已经完成了。在上面的代码中有些地方需要的引入代码没有提到,下面把完整的代码贴出来:

package com.android.demo;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;

import org.json.JSONArray;
import org.json.JSONObject;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.view.View.OnClickListener;

public class DemoActivity extends Activity {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		/** 为按钮绑定事件 */
		Button btnGetLocation = (Button) findViewById(R.id.button1);
		btnGetLocation.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				onBtnClick();
			}
		});
	}

    /** 基站信息结构体 */
    public class SCell{
    	public int MCC;
    	public int MNC;
    	public int LAC;
    	public int CID;
    }

    /** 经纬度信息结构体 */
    public class SItude{
    	public String latitude;
    	public String longitude;
    }

	/** 按钮点击回调函数 */
	private void onBtnClick() {
		/** 弹出一个等待状态的框 */
		ProgressDialog mProgressDialog = new ProgressDialog(this);
		mProgressDialog.setMessage("正在获取中...");
		mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
		mProgressDialog.show();

		try {
			/** 获取基站数据 */
			SCell cell = getCellInfo();

			/** 根据基站数据获取经纬度 */
			SItude itude = getItude(cell);

			/** 获取地理位置 */
			String location = getLocation(itude);

			/** 显示结果 */
			showResult(cell, location);

			/** 关闭对话框 */
			mProgressDialog.dismiss();
		} catch (Exception e) {
			/** 关闭对话框 */
			mProgressDialog.dismiss();
			/** 显示错误 */
			TextView cellText = (TextView) findViewById(R.id.cellText);
			cellText.setText(e.getMessage());
			Log.e("Error", e.getMessage());
		}
	}

	/**
	 * 获取基站信息
	 *
	 * @throws Exception
	 */
	private SCell getCellInfo() throws Exception {
		SCell cell = new SCell();

		/** 调用API获取基站信息 */
		TelephonyManager mTelNet = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
		GsmCellLocation location = (GsmCellLocation) mTelNet.getCellLocation();
		if (location == null)
			throw new Exception("获取基站信息失败");

		String operator = mTelNet.getNetworkOperator();
		int mcc = Integer.parseInt(operator.substring(0, 3));
		int mnc = Integer.parseInt(operator.substring(3));
		int cid = location.getCid();
		int lac = location.getLac();

		/** 将获得的数据放到结构体中 */
		cell.MCC = mcc;
		cell.MNC = mnc;
		cell.LAC = lac;
		cell.CID = cid;

		return cell;
	}

	/**
	 * 获取经纬度
	 *
	 * @throws Exception
	 */
	private SItude getItude(SCell cell) throws Exception {
		SItude itude = new SItude();

		/** 采用Android默认的HttpClient */
		HttpClient client = new DefaultHttpClient();
		/** 采用POST方法 */
		HttpPost post = new HttpPost("http://www.google.com/loc/json");
		try {
			/** 构造POST的JSON数据 */
			JSONObject holder = new JSONObject();
			holder.put("version", "1.1.0");
			holder.put("host", "maps.google.com");
			holder.put("address_language", "zh_CN");
			holder.put("request_address", true);
			holder.put("radio_type", "gsm");
			holder.put("carrier", "HTC");

			JSONObject tower = new JSONObject();
			tower.put("mobile_country_code", cell.MCC);
			tower.put("mobile_network_code", cell.MNC);
			tower.put("cell_id", cell.CID);
			tower.put("location_area_code", cell.LAC);

			JSONArray towerarray = new JSONArray();
			towerarray.put(tower);
			holder.put("cell_towers", towerarray);

			StringEntity query = new StringEntity(holder.toString());
			post.setEntity(query);

			/** 发出POST数据并获取返回数据 */
			HttpResponse response = client.execute(post);
			HttpEntity entity = response.getEntity();
			BufferedReader buffReader = new BufferedReader(new InputStreamReader(entity.getContent()));
			StringBuffer strBuff = new StringBuffer();
			String result = null;
			while ((result = buffReader.readLine()) != null) {
				strBuff.append(result);
			}

			/** 解析返回的JSON数据获得经纬度 */
			JSONObject json = new JSONObject(strBuff.toString());
			JSONObject subjosn = new JSONObject(json.getString("location"));

			itude.latitude = subjosn.getString("latitude");
			itude.longitude = subjosn.getString("longitude");

			Log.i("Itude", itude.latitude + itude.longitude);

		} catch (Exception e) {
			Log.e(e.getMessage(), e.toString());
			throw new Exception("获取经纬度出现错误:"+e.getMessage());
		} finally{
			post.abort();
			client = null;
		}

    	return itude;
    }

	/**
	 * 获取地理位置
	 *
	 * @throws Exception
	 */
	private String getLocation(SItude itude) throws Exception {
		String resultString = "";

		/** 这里采用get方法,直接将参数加到URL上 */
		String urlString = String.format("http://maps.google.cn/maps/geo?key=abcdefg&q=%s,%s", itude.latitude, itude.longitude);
		Log.i("URL", urlString);

		/** 新建HttpClient */
		HttpClient client = new DefaultHttpClient();
		/** 采用GET方法 */
		HttpGet get = new HttpGet(urlString);
		try {
			/** 发起GET请求并获得返回数据 */
			HttpResponse response = client.execute(get);
			HttpEntity entity = response.getEntity();
			BufferedReader buffReader = new BufferedReader(new InputStreamReader(entity.getContent()));
			StringBuffer strBuff = new StringBuffer();
			String result = null;
			while ((result = buffReader.readLine()) != null) {
				strBuff.append(result);
			}
			resultString = strBuff.toString();

			/** 解析JSON数据,获得物理地址 */
			if (resultString != null && resultString.length() > 0) {
				JSONObject jsonobject = new JSONObject(resultString);
				JSONArray jsonArray = new JSONArray(jsonobject.get("Placemark").toString());
				resultString = "";
				for (int i = 0; i < jsonArray.length(); i++) {
					resultString = jsonArray.getJSONObject(i).getString("address");
				}
			}
		} catch (Exception e) {
			throw new Exception("获取物理位置出现错误:" + e.getMessage());
		} finally {
			get.abort();
			client = null;
		}

		return resultString;
	}

	/** 显示结果 */
	private void showResult(SCell cell, String location) {
		TextView cellText = (TextView) findViewById(R.id.cellText);
		cellText.setText(String.format("基站信息:mcc:%d, mnc:%d, lac:%d, cid:%d",
				cell.MCC, cell.MNC, cell.LAC, cell.CID));

		TextView locationText = (TextView) findViewById(R.id.lacationText);
		locationText.setText("物理位置:" + location);
	}
}

我们连上手机在手机上运行程序看看。

不出意外的话程序运行起来了,自动跳转到了主界面。点击“Click Me”,出错了!

详细的错误信息为:Neither user 10078 nor current process has android.permission.ACCESS_COARSE_LOCATION.

原来是没有权限,经过前面的学习,我们知道Android在应用的安全上下了一番功夫,要用一些特殊功能必须先报告,安装应用的时候列给用户看,必须要得到用户的允许。这里我们用了获取基站信息的功能,涉及到用户的隐私了,所以我们必须申明一下。

打开AndroidManifest.xml配置文件,在里面添加相应的配置信息:

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

我们继续把网络连接的权限申明也加上:

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

再编译运行看看(点击“Click Me”后程序会卡住,等待一段时间才有反应,取决于网络情况):

成功啦!

可能有的同学还是出现错误,没有成功:

█ 提示“www.google.com…”什么的错误

请确认你的手机能访问互联网,调用google的API是必须联网的。

█ 提示获取不到基站信息

你确定你是在手机上测试的吗?模拟器可不行哦。或者你的手机使用的CMDA网络?这个例子只支持GSM网络…

█ 获取不到经纬度

很有可能你中奖了,你所在的基站还没纳入google的数据库…(话说我之前也遇到过,怎么查就是查不出经纬度来,返回数据为空)

█ 获取到的地理地址不正确

这个可能程序出错了,可能google出错了?

其实google map API返回的数据中还包含了很多其他信息,我们可以用来开发一些更有趣的功能,如制作我们专属的地图软件、足迹记录软件等,充分发挥你的创造力:)

八、总结

这个程序基本实现了基站定位功能,但还有很多问题,如:点击了按钮后界面会卡住(访问网络时阻塞了进程)、未对异常进一步处理、不兼容CMDA网络等。

另外这个程序的精度也不够,获得的位置实际上是基站的物理位置,与人所在的位置还有一定差距。在城市里面,一般采用密集型的小功率基站,精度一般在几百米范围内,而在郊区常为大功率基站,密度很小,精度一般在几千米以上。

想要取得更高的精度需要通过一些其他的算法来实现,如果大家有兴趣的话我们可以一起来研究一下,再专门写篇笔记。

可见写一段程序和做一个实际的产品是有很大差别的。

九、程序代码

这一节完整程序的请点击这里下载

结尾

这一节基本实现了最简单的基站定位,只是作为学习的例子,远远达不到产品的要求,请大家见谅。

我们进一步熟悉了JAVA编码,之前没怎么接触JAVA看起来有点吃力的同学建议找点JAVA基础的书来看看。

话说我这段代码也是写得乱七八糟,JAVA没系统学习过,不知道JAVA编程的习惯,命名规则等,大家见笑了。

相对前面几节来说这一节没那么详细了,我也很困惑:详细点呢进度很慢,进度提上去了又怕不够详细,看看大家的意见,请留言哈。

下一节我们回归理论知识,一起深入学习Android的Activity组件。

如果你喜欢看到这些文字,请点击右下角的“推荐”支持我一下,谢谢!

安卓手机定位研究,布布扣,bubuko.com

时间: 2024-09-30 09:57:26

安卓手机定位研究的相关文章

手机自带的显示基站命令(android手机定位,iphone基站定位)

手机自带的显示基站命令(安卓手机定位,苹果手机基站定位) 分类: 通信和网络2012-02-07 17:48 1734人阅读 评论(0) 收藏 举报 手机htciphone中兴三星网络 安卓手机自带快捷指令显示当前的网络信息,不过手机厂商不同,命令也不一样.每次要用时总想不起来命令,记下备用.(1) HTC 安卓手机在拨号键盘输入 *#*#4636#*#* , 选择 “手机信息” (2)三星安卓手机 *#*#4636#*#* , 选择 “手机信息” (3)摩托罗拉安卓手机 *#*#4636#*#

手机Web定位 解决国行安卓手机没有GMS包

最近在做手机打开一个网址,然后定位具体的位置,本来觉得很简单的,HTML5就能解决这问题了,后来发现问题还是很多. 首先是很多安卓手机不支持HTML5,判断navigator.geolocation是true,但是就是不返回经纬度信息,查资料发现很多国行很多手机都是这样,GMS(Google Mobile Service)被阉割了.解决这种方法可以恢复网络定位.但是咱们这手机网页是对所有用户开放的,显然不合理. 后来查资料发现安卓浏览器有些是可以的,然后下载了安卓所有常见的浏览器测试,确定四款浏

解决安卓手机键盘弹出时会把背景或百分比定位的布局压缩的问题

做移动端页面时经常遇到以下案例,在有背景的页面上写表单,而且底部为了适应不同手机还得运用绝对定位,因为通常是把容器高度设为了100%,这时在安卓手机上弹窗软键盘时就会把背景图片及其他东西挤压上去,解决方法如下: 在css样式中把大容器定义为fixed布局 .wrap{ position:fixed;left:0;top:0; } 在js中强制把页面的高度覆给他,就相当于自动撑开 var x =document.body.clientWidth; //查询设备的宽度 var y =document

强大的安卓手机远程管理工具 – Droidjack

免责声明:本站提供安全工具.程序(方法)可能带有攻击性,仅供安全研究与教学之用,风险自负! Droidjack是一款针对安卓手机远程管理工具,你可以利用它在PC上对手机进行远程操控,不仅功能强大,使用也很方便. 功能特性 1.可以生成一个APK,绑定在被控手机的任何APP上 2.可在电脑端控制手机,包括浏览.传输.删除文件等 3.可进行SMS短信收发和查看功能 4.可以控制手机的电话功能 5.联系人管理 6.麦克风监听 7.GPS定位 8.APP管理 测试截图 再次提醒大家,不用滥用安全工具的功

如何正确的对安卓手机进行数据恢复?

摘要: 很多人觉得数据恢复就是拿工具扫一扫,这种想法是非常错误的.想干好一件事,绝不是仅仅机械性的重复固有动作,必须要加入个人的思考.比如对安卓手机的数据恢复,你真的会吗? 0×00 背景介绍 很多人都有在使用手机时误删数据的经历,比方说和女朋友分手后把之前一起玩耍的影像资料删除了,结果没过几天又复合了,某天女朋友想和你一起回忆某个温馨时刻,这时候拿不出照片或视频来会非常尴尬. 很多人觉得数据恢复就是拿工具扫一扫,这种想法是非常错误的.想干好一件事,绝不是仅仅机械性的重复固有动作,必须要加入个人

安卓手机屏幕投射到电脑以及一台电脑控制多台手机技术原理浅析

奥创软件研究院推出的奥创微群控让越来越多的人了解到了电脑控制手机的操作.自奥创软件研究院首家发布电脑批量控制手机的解决方案以来,有很多人开始探讨电脑控制手机技术在实际工作中的应用. 由于市场太大,仅靠奥创软件研究院一家也是做不过来的,现在将手机屏幕投射到到电脑的技术原理,以及一台电脑批量控制多台手机的技术(即所谓的手机反响控制)简单的给大家介绍下,在此抛砖引玉,希望能给大家一些思路上的指导. 说到安卓手机的屏幕投射,就不得不说安卓的adb,ADB的全称为Android Debug Bridge这

【Android】第18章 位置服务和手机定位&mdash;本章示例主界面

分类:C#.Android.VS2015: 创建日期:2016-03-04 一.简介 目前,基于位置的服务发展迅速,已涉及到商务.医疗.定位.追踪.敏感区域警告.工作和生活等各个方面.定位服务融合了GPS定位.移动通信.导航等多种技术,从而获取用户终端设备的位置信息,为移动用户提供了与空间位置相关的综合应用服务. 这一章我们主要以安卓内置的定位服务和百度定位服务为例,演示定位服务(Location Service)的基本用法. 二.本章示例主界面 1.运行截图 2.MainActivity.cs

安卓手机的后门控制工具SPADE

SPADE,一款安卓手机的后门控制工具,安全研究人员可以以此了解和研究安卓后门原理. 首先,我们从网站www.apk4fun.com下载apk文件,如ccleaner.然后,我们安装spade git clone https://github.com/suraj-root/spade.git 之后,利用以下命令开始捆绑恶意载体 ./spade.py /root/Desktop/ccleaner.apk 有6种不同payload可供选择,设置完lhost和lport,将会生成恶意payload 选

[安卓] 13、安卓蓝牙定位(一)——如何周期性获得蓝牙节点信号强度?

前言 本文将用一个简单的例子介绍如何用安卓手机的蓝牙功能来周期性搜索周边蓝牙节点的信号强度,为有意玩一下蓝牙室内定位的爱好者提供一点小的思路.(如果是想用安卓手机和蓝牙模块通信来实现遥控功能的话,可以参考:http://www.cnblogs.com/zjutlitao/p/4231635.html:想用笔记本和蓝牙模块通信来实现遥控功能的话可以参考:http://www.cnblogs.com/zjutlitao/p/3886826.html:如果是想使用笔记本的蓝牙来做蓝牙定位的话,哈哈,建