外业数据采集平台(GPS+Android Studio+Arcgis for android 100.2.1)
外业数据采集平台
1. 综述
在室外,通过平板或者手机接收GPS坐标,实时绘制点、线、面数据,以便为后续进行海域监测、土地确权、地图绘图提供有效数据和依据。
2. 技术路线
Android studio3.0.1+Arcgis for android 100.2.1+GPS
2.1 Android studio工具:
2.2 Android studio工具下载地址:
http://www.android-studio.org/index.php/download/hisversion
2.2.1 Android studio安装与配置
参考博客地址:https://www.cnblogs.com/xiadewang/p/7820377.html
2.3 Arcgis for android
Arcgis for android是ESRI公司专门为Android手机开发GIS地图软件的一套API,整合广泛的地图和GIS能力在线或离线,包括编辑,分析,地理编码,路由,网络地图管理,数据可视化,移动地图包和矢量平铺层。
2.3.1 Arcgis for android SDK
https://developers.arcgis.com/android/latest/
2.3.2 Arcgis for android配置
http://www.cnblogs.com/gis-luq/p/4760370.html
2.4 技术架构
外业数据采集平台采用的是Android最原生也是最基础的架构,可以理解为MVC,Controller即是Activity和Fragment,但是这两者掌握了Android系统中绝大多数的资源,并且在内部直接控制View,因此传统的Android App一般是以Activity和Fragment为核心,将网络模块,数据库管理模块,文件管理模块,常用工具类等分离成若干工具类包,供Activity和Fragment使用。由于项目不是很大,最后决定采用Android默认的架构,而没有采用MVP或者MVVM架构。
3. 平台展示与实例代码
3.1 登录与主界面
3.1.1 登录
3.1.2 主界面
包含最基本的:放大、缩小、旋转、指北针、比例尺等基本功能。
3.2 基础功能
3.2.1 导入矢量
支持导入内置SD卡和外置SD卡中.shp格式的数据。
private ShapefileFeatureTable featureLayerShapefile(String filePath, String fileType, boolean
isFullExtent, String dapFilePath, boolean isVisible, float opacity, boolean isCheckLayerName) {
if (!check(filePath, "SHP", isCheckLayerName)) {
return null;
}
ShapefileFeatureTable shapefileFeatureTable = new ShapefileFeatureTable(filePath);
shapefileFeatureTable.loadAsync();
ShapefileFeatureTable finalShapefileFeatureTable = shapefileFeatureTable;
shapefileFeatureTable.addDoneLoadingListener(() -> {
if (finalShapefileFeatureTable.getLoadStatus() == LoadStatus.LOADED) {
// create a feature layer to display the shapefile
FeatureLayer featureLayer = new FeatureLayer(finalShapefileFeatureTable);
if ("".equals(dapFilePath)) {
featureLayer.setDescription(filePath);
} else {
featureLayer.setDescription(dapFilePath);
}
featureLayer.setVisible(isVisible);
mMapView.getMap().getOperationalLayers().add(featureLayer);
if (isFullExtent) {
Envelope envelope = LayerUtil.GetLayerFullExtend(featureLayer);
if (envelope != null && !envelope.isEmpty()) {
mMapView.setViewpointGeometryAsync(envelope
, LayerUtil.FullExtendPadding);
}
}
} else {
String error = "Shapefile feature table failed to load: " +
finalShapefileFeatureTable
.getLoadError().toString();
Log.e(TAG, error);
}
}); return shapefileFeatureTable;
}
3.2.2 导入影像
支持导入内置SD卡和外置SD卡中.tif影像格式的数据。
private Layer loadLocalTif(ArcGISMap arcGISMap, String filePath, String extendName, boolean
isVisible, float opacity, boolean isCheckLayerName) {
if (!check(filePath, extendName, isCheckLayerName)) {
return null;
}
Raster raster = new Raster(filePath);
if (raster == null)
return null; final RasterLayer rasterLayer = new RasterLayer(raster);
rasterLayer.setName("基础底图");
rasterLayer.setDescription(filePath);
rasterLayer.setVisible(isVisible);
rasterLayer.setOpacity(opacity);
Basemap basemap = new Basemap(rasterLayer);
mMapView.getMap().setBasemap(basemap);
rasterLayer.addDoneLoadingListener(new Runnable() {
@Override
public void run() {
mMapView.setViewpointGeometryAsync(rasterLayer.getFullExtent(), LayerUtil
.FullExtendPadding);
mMapView.setViewpointScaleAsync(MyConfig.initialScale);
}
});
mMapView.setViewpointScaleAsync(MyConfig.initialScale); return rasterLayer;
}
3.2.3 全图
将所有图层中所有要素范围作为地图的显示范围。
Envelope envelope = LayerUtil.GetFullExtend(mMapView);
if (envelope != null) {
mMapView.setViewpointGeometryAsync(envelope, LayerUtil.FullExtendPadding);
}
3.2.4 属性识别
识别最上层的点、线、面要素。
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
android.graphics.Point screenPoint = new android.graphics.Point((int) e.getX(), (int) e.getY());
final ListenableFuture<List<IdentifyLayerResult>> identifyFuture = mMapView
.identifyLayersAsync(
screenPoint, 20, false, 25);
identifyFuture.addDoneListener(new Runnable() {
@Override
public void run() {
try {
List<IdentifyLayerResult> identifyLayersResults = identifyFuture.get();
for (IdentifyLayerResult identifyLayerResult : identifyLayersResults) {
if (identifyLayerResult.getElements().size() > 0) {
GeoElement topmostElement = identifyLayerResult.getElements().get(0);
if (topmostElement instanceof Feature) {
Feature identifiedFeature = (Feature) topmostElement;
LayerContent layerContent = identifyLayerResult.getLayerContent();
String layerName = "";
if (layerContent != null)
layerName = layerContent.getName();
Map<String, Object> attrs = identifiedFeature.getAttributes();
String[] itemArr = new String[items.size()];
itemArr = items.toArray(itemArr);
showIdentifyInfo(itemArr, layerName);
break;
}
}
}
} catch (InterruptedException | ExecutionException ex) {
}
}
}); return true;
}
3.2.5 图层管理
包括图层顺序(置顶、置底、上移、下移)、图层标注(能标注该图层中多个字段)、图层样式(能改变图层中要素的颜色、宽度等信息)、图层移除、图层缩放到(缩放到选择图层的所有要素的范围)等功能;
private void loadLayers(boolean isToast,List<String> listChecked) {
if (mapView == null) {
return;
}
linearLayoutContent.removeAllViews();
LayerList layers = mapView.getMap().getOperationalLayers();
boolean layersLoaded = initialLayers(layers, 2,listChecked);
if(layersLoaded)
{
linearLayoutContent.addView(ViewUtil.AddDividerView(this));
}
Basemap basemap = mapView.getMap().getBasemap();
LayerList baseLayers = basemap.getBaseLayers();
boolean baseLayersLoaded = initialLayers(baseLayers, 1,listChecked);
} /**
* @param layers
* @param layerType //1:底图 2:操作类
* @return
*/
private boolean initialLayers(LayerList layers, int layerType,List<String> listChecked) {
if (layers == null || layers.size() == 0) {
return false;
}
for (int j = layers.size() - 1; j > -1; j--) {
Layer layer = layers.get(j);
boolean isChecked=false;
if(listChecked!=null) {
isChecked= listChecked.contains(layer.getName());
}
addItem(layer, layerType,isChecked);
}
return true;
}
private void initialLineSymbol(SimpleLineSymbol lineSymbol)
{
rowFillColor.setVisibility(View.INVISIBLE);
rowIsFill.setVisibility(View.INVISIBLE);
txtSymbolColor.setText("线颜色:");
txtSymbolSize.setText("线宽度:");
txtType.setText("线样式:"); btnSymbolColor.setBackgroundColor(lineSymbol.getColor());
seekBarSymbolSize.seekBar.setProgress((int)lineSymbol.getWidth());
initialColorPick(lineSymbol.getColor(),btnSymbolColor);
} private SimpleLineSymbol setLineSymbol(SimpleLineSymbol lineSymbol)
{
lineSymbol.setColor(ViewUtil.GetButtonBackgoundColor(btnSymbolColor));
lineSymbol.setWidth(seekBarSymbolSize.seekBar.getProgress());
return lineSymbol;
}
private void initialListView()
{
FeatureLayer featureLayer=(FeatureLayer)layer;
List<Field> fieldList= featureLayer.getFeatureTable().getFields();
List<LabelDefinition> labelDefinitionList= featureLayer.getLabelDefinitions();
boolean isLabel=featureLayer.isLabelsEnabled();
for (Field field :fieldList)
{
CheckBox checkBox=new CheckBox(this);
String fileName=field.getAlias();
if(TextUtils.isEmpty(fileName))
{
fileName=field.getName();
}
checkBox.setText(fileName);
checkBox.setTag(field);
if(isLabel)
{
for (LabelDefinition labelDefinition:labelDefinitionList)
{
String jsonString =labelDefinition.toJson();
try {
JSONObject jObject = new JSONObject(jsonString);
if(jObject!=null)
{
JSONObject jObjectItem= jObject.getJSONObject("labelExpressionInfo");
if(jObjectItem!=null)
{
String expression=jObjectItem.getString("expression");
String[] expressionContent=expression.split("\\.");
if(expressionContent.length>1)
{
String[] expressionField= expressionContent[1].split(";");
if(expressionField.length>0)
{
if( expressionField[0].equals(field.getName()))
{
checkBox.setChecked(true);
}
}
}
}
}
} catch (JSONException e) { }
}
} listViewLabel.addView(checkBox);
}
} public void btnLabelOk_Click(View view)
{
FeatureLayer featureLayer = (FeatureLayer) layer;
List<Field> checkedFieldList = getCheckedFields();
featureLayer.getLabelDefinitions().clear();
if(checkedFieldList!=null&&checkedFieldList.size()>0) {
for (Field field : checkedFieldList) {
LabelDefinition labelDefinition = LayerUtil.CreateFillLabelDefinition(field.getName());
featureLayer.getLabelDefinitions().add(labelDefinition);
}
featureLayer.setLabelsEnabled(true);
}
else
{
featureLayer.setLabelsEnabled(false);
}
finish();
}
3.2.6 距离测量
通过手在屏幕中点击开始,双击停止,支持动态显示每段线的距离(以米为单位)。
public boolean onDoubleTap(MotionEvent point) { if (geoType == GeometryType.POLYLINE)//绘制线
{
if (!lineGeometry.isSketchValid()) {
removeGraphic(pCurrGraphic);
} else {
updateGraphic(lineGeometry.toGeometry());
}
PartCollection partCollection = lineGeometry.getParts();
if (partCollection.size() == 0) {
return false;
}
Part part = partCollection.get(partCollection.size() - 1);
int count = part.getPointCount();
if (count <= 2) {
return false;
}
double length = GeometryEngine.lengthGeodetic(lineGeometry.toGeometry(), new
LinearUnit(LinearUnitId.METERS), GeodeticCurveType.GEODESIC);
Graphic lengthGriphic = new Graphic(ptCurrent, getTextSymbol(getFormatString(length,
2, "米"), TextSymbol.HorizontalAlignment.RIGHT, TextSymbol.VerticalAlignment
.TOP));
drawLayer.getGraphics().add(lengthGriphic); } }
3.2.7 面积测量
通过手在屏幕中点击开始,双击停止,会形成一个面要素,并显示长度和面积(以米为单位)。
public boolean onDoubleTap(MotionEvent point) { if (!polygonGeometry.isSketchValid()) {
removeGraphic(pCurrGraphic);
return true;
} else {
updateGraphic(polygonGeometry.toGeometry());
} double area = GeometryEngine.areaGeodetic(polygonGeometry.toGeometry(), new AreaUnit
(AreaUnitId.SQUARE_METERS), GeodeticCurveType.GEODESIC);
Graphic areaGriphic = new Graphic(ptCurrent, getTextSymbol(getFormatString(area, 2,
"平方米"), TextSymbol.HorizontalAlignment.LEFT, TextSymbol.VerticalAlignment
.BOTTOM));
drawLayer.getGraphics().add(areaGriphic); double length = GeometryEngine.lengthGeodetic(polygonGeometry.toPolyline(), new
LinearUnit(LinearUnitId.METERS), GeodeticCurveType.GEODESIC);
Point ptShift = mapView.screenToLocation(new android.graphics.Point(Math.round(point
.getX()), Math.round(point.getY()) + 30));
Graphic lengthGriphic = new Graphic(ptShift, getTextSymbol(getFormatString(length,
2, "米"), TextSymbol.HorizontalAlignment.RIGHT, TextSymbol.VerticalAlignment
.TOP));
drawLayer.getGraphics().add(lengthGriphic); }
3.2.8 清除覆盖物
清除上面距离测量或者面积测量的图形要素。
if (mearsureGraphicsOveray != null) {
mearsureGraphicsOveray.getGraphics().clear();
}
3.2.9 当前位置
点击此按钮,将GPS最近一次接受点的位置置于地图中心位置,并增加图标。
public boolean startGPSCurrent(boolean isRequestUpdate) {
if (GPSLocationManager.gpsLocationManager == null) {
GPSLocationManager.gpsLocationManager = GPSLocationManager.getInstances
(MainActivity
.this);
}
if (currentLocationListener == null) {
currentLocationListener = new MyGPSCurrentLocationListener();
}
if (isRequestUpdate)
GPSLocationManager.gpsLocationManager.stop(2);
//开启定位
GPSLocationManager.gpsLocationManager.start(currentLocationListener,
isRequestUpdate, 100, 2); return true;
} class MyGPSCurrentLocationListener implements GPSLocationListener {
@Override
public void UpdateLocation(Location location) {
if (location != null) {
LocationUtil.AddOrUpdateLocation(MainActivity.this, location,
locationGraphicsOveray,
spatialReference, mMapView);
if (GPSLocationManager.gpsLocationManager != null) {
GPSLocationManager.gpsLocationManager.stop(2);
}
}
} }
3.2.10 数据导出
将采集到的点、线、面数据导出成.shp格式数据,并支持删除要素功能。
public void initialGridData(ShapefileFeatureTable shapefileFeatureTable, GridView gridView,
int gridType, Context context, boolean isDisplayToastInf) {
List<Field> fileds = shapefileFeatureTable.getFields();
Map<String, Object> gridTitle = new HashMap<String, Object>(); for (String field : LayerUtil.mFields) {
String key = field;
keysList.add(key);
String alias = LayerUtil.GetAliasNameByFieldName(key);
gridTitle.put(key, alias);
}
gridDataList.add(gridTitle);
QueryParameters qParameters = new QueryParameters();
String whereClause = "1=1";
qParameters.setWhereClause(whereClause);
final ListenableFuture<FeatureQueryResult> future = shapefileFeatureTable
.queryFeaturesAsync(qParameters);
FeatureQueryResult result = null;
try {
result = future.get();
Iterator<Feature> features = result.iterator();
while (features.hasNext()) {
Feature feature = features.next();
Map<String, Object> attrs = feature.getAttributes(); Map<String, Object> dd = new HashMap<String, Object>();
for (String field : LayerUtil.mFields) {
dd.put(field, String.valueOf(attrs.get(field)));
}
gridDataList.add(dd);
}
} catch { }
String[] keys = keysList.toArray(new String[keysList.size()]);
GridViewAdapter adapter = new GridViewAdapter(context, gridDataList);
gridView.setAdapter(adapter);
}
3.2.11 系统设置
设置GPS接收坐标间隔以及地图经纬度显示格式。
@Override
public boolean onPreferenceChange(Preference preference, Object value) {
String stringValue = value.toString();
if (preference instanceof ListPreference) {
ListPreference listPreference = (ListPreference) preference;
if (listPreference.getKey().equals(updateMapFormatKey)) {
PreferencesUtil.setPrefString(this.getActivity(), updateMapFormatKey, String.valueOf(stringValue));
} else if (listPreference.getKey().equals(updateCollectIntervalKey)) {
PreferencesUtil.setPrefString(this.getActivity(), updateCollectIntervalKey, String.valueOf(stringValue));
MyConfig.mMinTime=Long.parseLong(stringValue);
}
} return true;
}
3.3 高级功能
3.3.1 拍照
拍取外业测量点的实况,并在照片上增加 经纬度、备注信息,信息录入完毕后,保存即可,为后续数据处理增加依据。
public void takePhoto(Activity activity) {
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (hasSdcard()) {
SimpleDateFormat timeStampFormat = new SimpleDateFormat(
"yyyy_MM_dd_HH_mm_ss");
String filename = timeStampFormat.format(new Date());
File tempFile = new File(FileUtils.GetDefaultPath(this) + "/" + MyConfig.OutCameraDir,
filename + ".jpg");
imageView.setTag(tempFile);
if (currentapiVersion < 24) {
imageUri = Uri.fromFile(tempFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
} else {
ContentValues contentValues = new ContentValues(1);
contentValues.put(MediaStore.Images.Media.DATA, tempFile.getAbsolutePath());
imageUri = activity.getContentResolver().insert(MediaStore.Images.Media
.EXTERNAL_CONTENT_URI, contentValues);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
}
}
}
3.3.2 点采集
根据GPS坐标,实时生成点要素,并随位置变化而变化地图中心点,点击 “结束”会弹出属性保存界面;
3.3.3 线采集
根据GPS坐标,实时生成线要素,并随位置变化而变化地图中心点,点击 “结束”会弹出属性保存界面;
3.3.4 面采集
根据GPS坐标,实时生成面要素,并随位置变化而变化地图中心点,点击 “结束”会弹出属性保存界面;
3.3.5 停止编辑
private void addOrUpdateGeometry(Location point ) {
if (point == null || point.isEmpty() || point.getX() == 0 || point.getY() == 0)
return; switch (collectGeometryType) {
case POINT:
if (collectPointTable == null) {
return;
}
collectFeatures.add(pointF);
toastGpsInfo("点采集数据", "", collectFeatures.size());
break;
case POLYLINE:
if (collectLineTable == null) {
return;
}
lineGeometry.addPoint(point);
collectPoints.add(point);
if (lineGeometry.isSketchValid()) {
if (polylineFeature == null) {
polylineFeature = collectLineTable.createFeature();
polylineFeature.setGeometry(lineGeometry.toGeometry());
collectLineTable.addFeatureAsync(polylineFeature);
} else {
polylineFeature.setGeometry(lineGeometry.toGeometry());
collectLineTable.updateFeatureAsync(polylineFeature);
}
}
toastGpsInfo("线采集数据", "", collectPoints.size());
break;
case POLYGON:
if (collectAreaTable == null) {
return;
}
polygonGeometry.addPoint(point);
collectPoints.add(point);
if (polygonGeometry.isSketchValid()) {
if (polygonFeature == null) {
polygonFeature = collectAreaTable.createFeature();
polygonFeature.setGeometry(polygonGeometry.toGeometry());
collectAreaTable.addFeatureAsync(polygonFeature);
} else {
polygonFeature.setGeometry(polygonGeometry.toGeometry());
collectAreaTable.updateFeatureAsync(polygonFeature);
}
}
toastGpsInfo("面采集数据", "", collectPoints.size());
break;
}
}
3.3.6 撤销
若有GPS坐标发生偏移,可以根据此功能进行撤销,一直可以撤销到第一个采集到的点。
private boolean deleteFirstFeature(ShapefileFeatureTable shapefileFeatureTable, Feature
defaultFeature) {
if (shapefileFeatureTable == null)
return false;
if (shapefileFeatureTable.getTotalFeatureCount() == 1) {
if (defaultFeature == null)
return false;
String delFid = "";
Map<String, Object> item = defaultFeature.getAttributes();
if (item != null) {
delFid = String.valueOf(item.get(CollectEidtActivity.attrFID));
}
QueryParameters qParameters = new QueryParameters();
String whereClause = CollectEidtActivity.attrFID + "=" + delFid;
qParameters.setReturnGeometry(true);
qParameters.setWhereClause(whereClause); final ListenableFuture<FeatureQueryResult> featuresResult = shapefileFeatureTable
.queryFeaturesAsync(qParameters); FeatureQueryResult features;
try {
features = featuresResult.get();
if (!features.iterator().hasNext()) {
return false;
}
shapefileFeatureTable
.deleteFeaturesAsync(features).get();
return true;
} catch (Exception ex) {
return false;
}
} return false;
}
外业数据采集平台(GPS+Android Studio+Arcgis for android 100.2.1)的更多相关文章
- android studio :com.android.support:appcompat-v7:21.+ 报错
android studio :com.android.support:appcompat-v7:21.+ 报错: 在project——>app——>build.gradle修改: app ...
- 【Android Studio】为Android Studio设置HTTP代理
[Android Studio]为Android Studio设置HTTP代理 大陆的墙很厚很高,初次安装Android Studio下载SDK等必定失败,设置代理方法如下: 1. 到androi ...
- 在Android Studio中打开Android Device Monitor时报错的解决方法
在Android Studio中打开Android Device Monitor时报以下错误时(Android-SDK\tools\lib\monitor-x86_64\configuration\1 ...
- 【转】在Android Studio中下载Android SDK的两种方式(Android Studio3.0、windows)
在Android Studio中下载Android SDK的两种方式(Android Studio3.0.windows) 方式一.设置HTTP Proxy1. 打开Settings2. 点击HTTP ...
- Android Studio快捷键【Android学习入门】
Studio快捷键[Android学习入门]" title="Android Studio快捷键[Android学习入门]"> 提示 Ctrl+P方法参数提示 Ct ...
- Android studio怎么创建Android虚拟机?
进行Android studio中进行开发app应用的情况,如果在进行调式app的应用的情况下,没有真机手机机器是没有办法调式的,那么只能通过Android studio中sdk提供虚拟机进行调式ap ...
- Android Studio(八):Android Studio设置教程
Android Studio相关博客: Android Studio(一):介绍.安装.配置 Android Studio(二):快捷键设置.插件安装 Android Studio(三):设置Andr ...
- Android Studio(六):Android Studio添加注释模板
Android Studio相关博客: Android Studio(一):介绍.安装.配置 Android Studio(二):快捷键设置.插件安装 Android Studio(三):设置Andr ...
- Android Studio(四):Android Studio集成Genymotion
Android Studio相关博客: Android Studio(一):介绍.安装.配置 Android Studio(二):快捷键设置.插件安装 Android Studio(三):设置Andr ...
随机推荐
- Ejb3 + Jboss8 出现Session id hasn't been set for stateful component
Ejb 3 + JBoss 8 在使用客户端远程访问有状态的Ejb对象时,出现ERROR: Session id hasn't been set for stateful component 出现该 ...
- SELECT列表中的标量子查询
发现了一种表连接新的写法,以前还没有这样写过或者见别人写过.跟同学聊天他们公司却很多人这样写,看来真的要学学sql了 表 CREATE TABLE `t_book` ( `FId` ) NOT NUL ...
- RSNAKE 的 Slowloris DOS攻击工具初试
Slowloris 号称低带宽对服务器进行DDOS攻击 原理就是对WEB服务器发送 不完整的包并且以 单一 \r\n结尾,并不是 完整的HTTP包.造成WEB服务器堵塞达到最大连接数. 官网给出介绍 ...
- AndroidStudio报错Software caused connection abort: recv failed
Software caused connection abort: recv failed 这个问题网上有一种说法 已知会导致这种异常的一个场景如下: 客户端和服务端建立tcp的短连接,每次客户端发送 ...
- Golang gRPC 使用
一.概念 1.gRPC默认使用protocol buffers,这是google开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如JSON),可以用proto files创建gRPC服务 ...
- springboot-4-整合fastjson
使用fastjson作为springboot的默认json解析, 原来使用的是jackson 1, 引入依赖 <dependencies> <dependency> <g ...
- 解决php中文乱码
在文件的第一行,加入下面这一句: header("Content-Type: text/html; charset=utf8"); 然后在把文件以utf-8的格式保存起来就行了
- c#调用webservices
有两种方式,静态调用(添加web服务的暂且这样定义)和动态调用: 静态调用: 使用添加web服务的方式支持各种参数,由于vs2010会自动转换,会生成一个特定的Reference.cs类文件 动态 ...
- sqlserver把任意一列放到第一列并顺序排列
请用一句sql写出将id为1234放到表的第一列,其他紧随其后并以正序排列的查询语句. 答案: select * from table where ID=2union all select * fro ...
- 解决Tomcat出现内存溢出的问题
Tomcat服务器出现java.lang.OutOfMemoryError:Java heap space异常 1.可能是程序错误,比如:程序陷入死循环 2.堆内存太小 一般情况下,java创建的对象 ...