经过前面几节的学习,我们已经对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文件内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<? xml version = "1.0" encoding = "utf-8" ?>
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);后:
1
2
3
4
5
6
7
8
9
10
|
/** 为按钮绑定事件 */ Button btnGetLocation = (Button)findViewById(R.id.button1); btnGetLocation.setOnClickListener( new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
onBtnClick();
} }); |
同时还需要在头部import相关组件:
1
2
3
|
import
import
import
|
我们来分析一下这段代码:
首先我们通过findViewById(R.id.button1)找到按钮这个对象,前面加(Button)表示显示的转换为Button对象;
然后设置按钮点击事件的监听器,参数为OnClickListener对象,再重载这个类的onClick方法,调用onBtnClick方法(这个方法得由我们自己去写,他在点击按钮时被调用)。
好了,调用方法写好了,我们来写实现(调用后需要做什么事)。动手编码之前先在脑中整理好思路,养成好习惯。
我们需要在DemoActivty类中添加如下私有方法:
- 我们需要刚刚提到的onBtnClick回调方法,被调用时实现取得基站信息、获取经纬度、获取地理位置、显示的功能。但是很显然,全部揉到一个方法里面并不是个好主意,我们将它分割为几个方法;
- 添加获取基站信息的方法getCellInfo,返回基站信息;
- 添加获取经纬度的方法getItude,传入基站信息,返回经纬度;
- 添加获取地理位置的方法getLocation,传入经纬度,返回地理位置;
- 添加显示结果的方法showResult,传入得到的信息在界面上显示出来。
好了,先将方法添上,完整代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
package
import
import
import
import
import
import
import
public 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方法中编码,依次调用后面几个方法,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
/** 按钮点击回调函数 */ private 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,需要在文件头部引入:
1
2
|
import
import
|
完整代码为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
/**
* 获取基站信息
*
* @throws Exception
*/ private
throws
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。
完整的方法代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
/**
* 获取经纬度
*
* @throws Exception
*/ private
throws
SItude itude = new SItude();
/** 采用Android默认的HttpClient */
HttpClient client = new DefaultHttpClient();
/** 采用POST方法 */
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方法。
完整的方法代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
/**
* 获取地理位置
*
* @throws Exception
*/ private
throws
String resultString = "" ;
/** 这里采用get方法,直接将参数加到URL上 */
String urlString = String.format( "http://maps.google.cn/maps/geo?key=abcdefg&q=%s,%s" ,
Log.i( "URL" , urlString);
/** 新建HttpClient */
HttpClient client = new DefaultHttpClient();
/** 采用GET方法 */
HttpGet get = new HttpGet(urlString); <
|