IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]莫名Crash---目睹Baidu地图API之怪现象一

    lincyang发表于 2013-12-06 11:12:38
    love 0

    这是地图API系列的第一弹,那么首先从我经历的地图API说起。

    几年前有过一次探索Google Map的经历,但是并没有深入和坚持下去。记得当时只是做一个可以定位和显示地图的Demo。

    记得常用的几个类分别是MapView, MapController, Overlay, GeoPoint...


    几年之后,国内的Android地图API也雨后春笋一样,遍地开花。

    我心里觉得两个不错的代表就是Baidu地图和高德地图。

    用朋友的话说,Baidu是家大公司,它出的API一定差不了。确实,Android版的百度地图,使用起来确实很棒,而且功能也越来越多,用起来也很容易上手。

    而高德一直就是做导航的,也是Google的地图提供商之一。所以一直对其有着好感。


    这一次,是要快速做一个原型Demo,提供的功能是在地图中显示当前的位置。说白了,就是和google地图那个demo类似,定位和显示地图。

    首先用Baidu地图API打头阵。Baidu将Map和Location分开成两个SDK,这样每个都可以单独使用。后来发现,高德地图也是如此。

    看了一阵API文档, 感到不明觉厉啊。

    整体上看,共8个包。而其中最重要的(我按照自己需求而定)是:

    com.baidu.mapapi.map        地图图层相关,包括地图控制,图层上自定义图标、图形等
    com.baidu.mapapi.search    信息查询和路径规划相关

    而常见的那些类(上述google地图中的)在这里都可以看到。这反映了同一领域的知识是相通的,看了高德的api更是如此感慨。


    为了实现我想要的功能,只需将BaiduMapsApiDemo中的LocationOverlayDemo单独抽出来就可以。但我还是喜欢一切自己从头开始。

    按照开发指南 中的Hello world开始,一步一步加入MyLocationOverlay等内容。其实一切是非常简单的,代码我就不列出了。

    但是编译、手机中运行。App 死掉了!很干脆的死掉了。我从log里根本找不到一丝线索。

    也许是我在加载地图和定位so和jar时出错了?

    再按照文档一步一步重来,还是挂掉。浪费几个小时无果后,决定不能这样了。快速建立原型的宗旨是,要快速要出一个可以运行的程序。

    好吧,直接在官方demo上修改吧。

    一路重构下来,最后demo中只剩下LocationOverlayDemo和DemoApplication了。

    我发现LocationOverlayDemo类中还有可重构的余地,比如这个类我没有必要使用的:

    class MyLocationMapView extends MapView{
    	static PopupOverlay   pop  = null;//弹出泡泡图层,点击图标使用
    	public MyLocationMapView(Context context) {
    		super(context);
    		// TODO Auto-generated constructor stub
    	}
    	public MyLocationMapView(Context context, AttributeSet attrs){
    		super(context,attrs);
    	}
    	public MyLocationMapView(Context context, AttributeSet attrs, int defStyle){
    		super(context, attrs, defStyle);
    	}
    	@Override
        public boolean onTouchEvent(MotionEvent event){
    		if (!super.onTouchEvent(event)){
    			//消隐泡泡
    			if (pop != null && event.getAction() == MotionEvent.ACTION_UP)
    				pop.hidePop();
    		}
    		return true;
    	}
    }
    因为现在没有对pop使用的需求,那么直接使用MapView就可以的,没有必要自己写这个类。

    将程序中的MyLocationMapView替换为MapView,从常识上讲,这是可行的。但是编译、运行后,程序又挂了,跟之前的现象一样。

    这次我没有着急,肯定是重构出问题了,而问题的root cause就是这个MyLocationMapView。

    反复调试,我发现这么个怪现象:

    只要我的地图包下有这样一个类继承自MapView,那么程序就不会Crash。下面是修改字官方Demo的源码,十分奇怪的源码:

    package baidumapsdk.demo;
    
    import android.app.Activity;
    import android.content.Context;
    import android.graphics.drawable.Drawable;
    import android.os.Bundle;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.Menu;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.RadioGroup;
    import android.widget.RadioGroup.OnCheckedChangeListener;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.baidu.location.BDLocation;
    import com.baidu.location.BDLocationListener;
    import com.baidu.location.LocationClient;
    import com.baidu.location.LocationClientOption;
    import com.baidu.mapapi.map.LocationData;
    import com.baidu.mapapi.map.MapController;
    import com.baidu.mapapi.map.MapView;
    import com.baidu.mapapi.map.MyLocationOverlay;
    import com.baidu.mapapi.map.MyLocationOverlay.LocationMode;
    import com.baidu.mapapi.map.PopupClickListener;
    import com.baidu.mapapi.map.PopupOverlay;
    import com.baidu.platform.comapi.basestruct.GeoPoint;
    /**
     * 此demo用来展示如何结合定位SDK实现定位,并使用MyLocationOverlay绘制定位位置
     * 同时展示如何使用自定义图标绘制并点击时弹出泡泡
     *
     */
    public class LocationOverlayDemo extends Activity {
    	private enum E_BUTTON_TYPE {
    		LOC,
    		COMPASS,
    		FOLLOW
    	}
    	
    	private E_BUTTON_TYPE mCurBtnType;
    
    	// 定位相关
    	LocationClient mLocClient;
    	LocationData locData = null;
    	public MyLocationListenner myListener = new MyLocationListenner();
    	
    	//定位图层
    	locationOverlay myLocationOverlay = null;
    	//弹出泡泡图层
    	private PopupOverlay   pop  = null;//弹出泡泡图层,浏览节点时使用
    	private TextView  popupText = null;//泡泡view
    	private View viewCache = null;
    	
    	//地图相关,使用继承MapView的MyLocationMapView目的是重写touch事件实现泡泡处理
    	//如果不处理touch事件,则无需继承,直接使用MapView即可
    //	MyLocationMapView mMapView = null;	// 地图View
    	MapView mMapView = null;	// 地图View
    	private MapController mMapController = null;
    
    	//UI相关
    	OnCheckedChangeListener radioButtonListener = null;
    	Button requestLocButton = null;
    	boolean isRequest = false;//是否手动触发请求定位
    	boolean isFirstLoc = true;//是否首次定位
    	
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_locationoverlay);
            CharSequence titleLable="定位功能";
            setTitle(titleLable);
            requestLocButton = (Button)findViewById(R.id.button1);
            mCurBtnType = E_BUTTON_TYPE.LOC;
            OnClickListener btnClickListener = new OnClickListener() {
            	public void onClick(View v) {
    				switch (mCurBtnType) {
    				case LOC:
    					//手动定位请求
    					requestLocClick();
    					break;
    				case COMPASS:
    					myLocationOverlay.setLocationMode(LocationMode.NORMAL);
    					requestLocButton.setText("定位");
    					mCurBtnType = E_BUTTON_TYPE.LOC;
    					break;
    				case FOLLOW:
    					myLocationOverlay.setLocationMode(LocationMode.COMPASS);
    					requestLocButton.setText("罗盘");
    					mCurBtnType = E_BUTTON_TYPE.COMPASS;
    					break;
    				}
    			}
    		};
    	    requestLocButton.setOnClickListener(btnClickListener);
    	    
            RadioGroup group = (RadioGroup)this.findViewById(R.id.radioGroup);
            radioButtonListener = new OnCheckedChangeListener() {
    			
    			@Override
    			public void onCheckedChanged(RadioGroup group, int checkedId) {
    				if (checkedId == R.id.defaulticon){
    					//传入null则,恢复默认图标
    					modifyLocationOverlayIcon(null);
    				}
    				if (checkedId == R.id.customicon){
    					//修改为自定义marker
    					modifyLocationOverlayIcon(getResources().getDrawable(R.drawable.icon_geo));
    				}
    			}
    		};
    		group.setOnCheckedChangeListener(radioButtonListener);
            
    		//地图初始化
    //        mMapView = (MyLocationMapView)findViewById(R.id.bmapView);
    		mMapView = (MapView)findViewById(R.id.bmapView);
            mMapController = mMapView.getController();
            mMapView.getController().setZoom(14);
            mMapView.getController().enableClick(true);
            mMapView.setBuiltInZoomControls(true);
          //创建 弹出泡泡图层
            createPaopao();
            
            //定位初始化
            mLocClient = new LocationClient( this );
            locData = new LocationData();
            mLocClient.registerLocationListener( myListener );
            LocationClientOption option = new LocationClientOption();
            option.setOpenGps(true);//打开gps
            option.setCoorType("bd09ll");     //设置坐标类型
            option.setScanSpan(1000);
            mLocClient.setLocOption(option);
            mLocClient.start();
           
            //定位图层初始化
    		myLocationOverlay = new locationOverlay(mMapView);
    		//设置定位数据
    	    myLocationOverlay.setData(locData);
    	    //添加定位图层
    		mMapView.getOverlays().add(myLocationOverlay);
    		myLocationOverlay.enableCompass();
    		//修改定位数据后刷新图层生效
    		mMapView.refresh();
    		
        }
        /**
         * 手动触发一次定位请求
         */
        public void requestLocClick(){
        	isRequest = true;
            mLocClient.requestLocation();
            Toast.makeText(LocationOverlayDemo.this, "正在定位……", Toast.LENGTH_SHORT).show();
        }
        /**
         * 修改位置图标
         * @param marker
         */
        public void modifyLocationOverlayIcon(Drawable marker){
        	//当传入marker为null时,使用默认图标绘制
        	myLocationOverlay.setMarker(marker);
        	//修改图层,需要刷新MapView生效
        	mMapView.refresh();
        }
        /**
    	 * 创建弹出泡泡图层
    	 */
    	public void createPaopao(){
    		viewCache = getLayoutInflater().inflate(R.layout.custom_text_view, null);
            popupText =(TextView) viewCache.findViewById(R.id.textcache);
            //泡泡点击响应回调
            PopupClickListener popListener = new PopupClickListener(){
    			@Override
    			public void onClickedPopup(int index) {
    				Log.v("click", "clickapoapo");
    			}
            };
            pop = new PopupOverlay(mMapView,popListener);
    //        MyLocationMapView.pop = pop;
    	}
    	/**
         * 定位SDK监听函数
         */
        public class MyLocationListenner implements BDLocationListener {
        	
            @Override
            public void onReceiveLocation(BDLocation location) {
                if (location == null)
                    return ;
                
                locData.latitude = location.getLatitude();
                locData.longitude = location.getLongitude();
                //如果不显示定位精度圈,将accuracy赋值为0即可
                locData.accuracy = location.getRadius();
                // 此处可以设置 locData的方向信息, 如果定位 SDK 未返回方向信息,用户可以自己实现罗盘功能添加方向信息。
                locData.direction = location.getDerect();
                //更新定位数据
                myLocationOverlay.setData(locData);
                //更新图层数据执行刷新后生效
                mMapView.refresh();
                //是手动触发请求或首次定位时,移动到定位点
                if (isRequest || isFirstLoc){
                	//移动地图到定位点
                	Log.d("LocationOverlay", "receive location, animate to it");
                    mMapController.animateTo(new GeoPoint((int)(locData.latitude* 1e6), (int)(locData.longitude *  1e6)));
                    isRequest = false;
                    myLocationOverlay.setLocationMode(LocationMode.FOLLOWING);
    				requestLocButton.setText("跟随");
                    mCurBtnType = E_BUTTON_TYPE.FOLLOW;
                }
                //首次定位完成
                isFirstLoc = false;
            }
            
            public void onReceivePoi(BDLocation poiLocation) {
                if (poiLocation == null){
                    return ;
                }
            }
        }
        
        //继承MyLocationOverlay重写dispatchTap实现点击处理
      	public class locationOverlay extends MyLocationOverlay{
    
      		public locationOverlay(MapView mapView) {
      			super(mapView);
      			// TODO Auto-generated constructor stub
      		}
      		@Override
      		protected boolean dispatchTap() {
      			// TODO Auto-generated method stub
      			//处理点击事件,弹出泡泡
      			popupText.setBackgroundResource(R.drawable.popup);
    			popupText.setText("我的位置");
    			pop.showPopup(BMapUtil.getBitmapFromView(popupText),
    					new GeoPoint((int)(locData.latitude*1e6), (int)(locData.longitude*1e6)),
    					8);
      			return true;
      		}
      		
      	}
    
        @Override
        protected void onPause() {
            mMapView.onPause();
            super.onPause();
        }
        
        @Override
        protected void onResume() {
            mMapView.onResume();
            super.onResume();
        }
        
        @Override
        protected void onDestroy() {
        	//退出时销毁定位
            if (mLocClient != null)
                mLocClient.stop();
            mMapView.destroy();
            super.onDestroy();
        }
        
        @Override
        protected void onSaveInstanceState(Bundle outState) {
        	super.onSaveInstanceState(outState);
        	mMapView.onSaveInstanceState(outState);
        	
        }
        
        @Override
        protected void onRestoreInstanceState(Bundle savedInstanceState) {
        	super.onRestoreInstanceState(savedInstanceState);
        	mMapView.onRestoreInstanceState(savedInstanceState);
        }
        
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
    //        getMenuInflater().inflate(R.menu.activity_main, menu);
            return true;
        }
    
    }
    
    class TestMapView extends MapView{
    public TestMapView(Context context) {
    	super(context);
    	// TODO Auto-generated constructor stub
    }
    public TestMapView(Context context, AttributeSet attrs){
    	super(context,attrs);
    }
    }
    

    从代码中可以看出,TestMapView这个类我是根本没有用的,但是我删掉它,程序就会Crash。

    好吧,我现在还是没有找到root cause,只能这样先用着。

    大家也可以体验一样,并帮我找找原因。




沪ICP备19023445号-2号
友情链接