使用百度地图API进行定位,当接收到定位信息后,为LocationClient对象注册的BDLocationListener会回调onReceiveLocation方法。在onReceiveLocation的BDLocation对象中获得地理信息后,将其设置到TextView上显示。程序并没有报错,但是显示发生了异常。
代码如下:
public void onReceiveLocation(BDLocation bdLocation) {
final StringBuilder sb=new StringBuilder();
sb.append("纬度: ").append(bdLocation.getLatitude()).append("\n");
sb.append("经度: ").append(bdLocation.getLongitude()).append("\n");
sb.append("定位方式: ");
if (bdLocation.getLocType()==BDLocation.TypeGpsLocation){
sb.append("GPS");
}else if (bdLocation.getLocType()==BDLocation.TypeNetWorkLocation){
sb.append("网络");
}
textView.setText(sb.toString());
}
在Fragment(活动中内嵌Fragment,而TextView是放在Fragment的布局文件中的)的onCreateView中添加如下代码:
long uId=Thread.currentThread().getId();
Log.d("Fragment", "onCreateView: current Thread id--"+uId);
long uId=Thread.currentThread().getId();
Log.d("MainActivity", "onCreate: current thread id-- "+uId);
long uId=Thread.currentThread().getId();
String tmpName=Thread.currentThread().getClass().getName();
Log.d(TAG, "onReceiveLocation: current thread id--"+uId);
Log.d(TAG, "onReceiveLocation: current thread name--"+tmpName);
D/MainActivity: onCreate: current thread id-- 1
D/PositionFragment: onCreateView: current Thread id--1
D/PositionFragment: onReceiveLocation: current thread id--93
D/PositionFragment: onReceiveLocation: current thread name--android.os.HandlerThread
可以看到,在onReceiveLocation中,执行线程并不是当前的主线程。因此,在该线程中更新UI当然会发生异常了。
为什么onReceiveLocation不执行在主线程当中,这里通过分析其调用过程和源码,得到答案。
当程序执行到onReceiveLocation时,其调用过程按如下的顺序:
1、HandlerThread.run()
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
即进入了HandlerThread这个线程中执行任务;
2、Looper.loop()
public static void loop() {
//…………..
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//…………………
其中,执行至msg.target.dispatchMessage(msg),其中target的值为Handler (com.baidu.location.LocationClient$a) {cc944e0};
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//LocationClient.a
}
}
4、LocationClient.a.handleMessage
public void handleMessage(Message var1) {
//……………..
case 21:
Bundle var2 = var1.getData();
var2.setClassLoader(BDLocation.class.getClassLoader());
BDLocation var3 = (BDLocation)var2.getParcelable("locStr");
if(LocationClient.this.serverFirst || !LocationClient.this.clientFirst || var3.getLocType() != 66) {
if(!LocationClient.this.serverFirst && LocationClient.this.clientFirst) {
LocationClient.this.serverFirst = true;
} else {
if(!LocationClient.this.serverFirst) {
LocationClient.this.serverFirst = true;
}
LocationClient.this.onNewLocation(var1, 21);
}
}
break;
//………………………..
}
5、之后,程序会执行至LocationClient.onNewLocation上,(中间有一步进行了跳跃,从LocationClient.a跳跃至LocationClient对象上,没搞懂,还要继续研究)
private void onNewLocation(Message var1, int var2) {
if(this.mIsStarted) {
try {
Bundle var3 = var1.getData();
var3.setClassLoader(BDLocation.class.getClassLoader());
this.mLastLocation = (BDLocation)var3.getParcelable("locStr");
if(this.mLastLocation.getLocType() == 61) {
this.lastReceiveGpsTime = System.currentTimeMillis();
}
this.callListeners(var2);
} catch (Exception var4) {
;
}
}
}
可以看到,这里调用了this.callListener。
6、LocationClient.callListener;
LocationClient. callListener
private void callListeners(int var1) {
//…………
//………….
if(this.isWaitingForLocation || this.mOption.location_change_notify && this.mLastLocation.getLocType() == 61 || this.mLastLocation.getLocType() == 66 || this.mLastLocation.getLocType() == 67 || this.inDoorState || this.mLastLocation.getLocType() == 161) {
if(this.mLocationListeners != null) {
Iterator var2 = this.mLocationListeners.iterator();
while(var2.hasNext()) {
BDLocationListener var3 = (BDLocationListener)var2.next();
var3.onReceiveLocation(this.mLastLocation);
}
}
//……………….
this.isWaitingForLocation = false;
this.lastReceiveLocationTime = System.currentTimeMillis();
}
}
综上,程序如何调用onReceiveLocation的流程已经展示完毕。
可以看到,消息循环是通过LocationClient.a对象(实现了Handler)来发出消息并实现了回调,并最终调用了onReceiveLocation方法。因此看一下LocationClinet.a,如下:
private class a extends Handler {
a(Looper var2) {
super(var2);
}
public void handleMessage(Message var1) {
switch(var1.what) {
//..................
//..................
}
}
}
该对象是LocationClient的内部类。那么看一下LocationClient是如何初始化该对象的。
//...........
//...........
private HandlerThread mThread;
private LocationClient.a mHandler;//...........//...........
public LocationClient(Context var1) {
this.mContext = var1;
this.mOption = new LocationClientOption();
this.mThread = new HandlerThread("LocationClient");
this.mThread.start();
this.mHandler = new LocationClient.a(this.mThread.getLooper());
this.mMessenger = new Messenger(this.mHandler);
}
//...................
//..................
public void onReceiveLocation(BDLocation bdLocation) {
final StringBuilder sb=new StringBuilder();
sb.append("纬度: ").append(bdLocation.getLatitude()).append("\n");
sb.append("经度: ").append(bdLocation.getLongitude()).append("\n");
sb.append("定位方式: ");
if (bdLocation.getLocType()==BDLocation.TypeGpsLocation){
sb.append("GPS");
}else if (bdLocation.getLocType()==BDLocation.TypeNetWorkLocation){
sb.append("网络");
}
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
TextView mTmp=(TextView) getActivity().findViewById(R.id.locatingresultview);
mTmp.setText(sb.toString());
}
});
}