新手, 花了一周时间做出来的, 遇到很多坑, 如有不足请多指教.
项目地址: github
一. 移植华为给的java工程
在华为的OceanConnect上下载java的Demo, 待会我们把工程文件直接复制到我们新建的AS工程下面去. (我只用到了一个接口, 但是每个接口修改调用都是一样的)
复制所需文件:
Huawei_IoT_Platform_Demo_North_Lite\LiteNAdemo_https\src\com\huawei 目录下的所有文件夹
Huawei_IoT_Platform_Demo_North_Lite\LiteNAdemo_https\Open source components 目录下的jar包
Huawei_IoT_Platform_Demo_North_Lite\LiteNAdemo_https\src\resource 目录下的证书文件
工程配置:
右击lib目录下的jar包 -> add as library
gridle配置:
src目录下的build.grdle:
出现问题如下:
/*
Error:duplicate files during packaging of APK E:\AndroidStudioProjects\huawei\NADemo\app\build\outputs\apk\app-debug-unaligned.apk
Path in archive: META-INF/DEPENDENCIES
Origin 1: E:\AndroidStudioProjects\huawei\NADemo\app\libs\httpmime-4.5.2.jar
Origin 2: E:\AndroidStudioProjects\huawei\NADemo\app\libs\httpcore-4.4.4.jar
You can ignore those files in your build.gradle:
android {
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
}
}
*/
解决方法:
//兼容一些版本较旧的包
packagingOptions {
exclude 'META-INF/DEPENDENCIES.txt'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/notice.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/dependencies.txt'
exclude 'META-INF/LGPL2.1'
}
修改Constant类:
这里就是OceanConnect应用的一些配置修改为自己应用的参数.
主要是:
//please replace the IP and Port, when you use the demo.
public static final String BASE_URL = "https://ip:8743";
//please replace the appId and secret, when you use the demo.
public static final String APPID = "'''''''''''";
public static final String SECRET = "''''''''''";
然后我开始编译这个工程, 发现在eclipse下运行可以获取到数据, 但是AS下试报错的. 报错有两方面.
- 一方面是原本在eclipse下可以用的方法, 在AS里却标红(不知道是什么原因), 我在网上查了一些资料最终还是解决了, 具体解决可以参考我的代码
- 另一方面是非继承Activity的类是不可以去访问AS工程目录下的资源文件的, 所以我想了办法先在Activity中把文件复制到手机的指定位置, 然后非继承Activity类就去访问这个位置就好了
修改HttpUtil类
修改如下: sunx509改为x509, SSLSocketFactory方法的调用, httpclient的register, 不再使用 URIBuilder
//TODO : url替换
/**
* Two-Way Authentication In the two-way authentication, the client needs: 1
* Import your own certificate for server verification; 2 Import the CA
* certificate of the server, and use the CA certificate to verify the
* certificate sent by the server; 3 Set the domain name to not verify
* (Non-commercial IoT platform, no use domain name access.)
* */
public void initSSLConfigForTwoWay() throws Exception {
// 1 Import your own certificate
String demo_base_Path = System.getProperty("user.dir");
String selfcertpath = Constant.SELFCERTPATH; //demo_base_Path + Constant.SELFCERTPATH; // 69-73
String trustcapath = Constant.TRUSTCAPATH; // demo_base_Path + Constant.TRUSTCAPATH; //
KeyStore selfCert = KeyStore.getInstance("pkcs12");
String sdCardDir = Environment.getExternalStorageDirectory().getAbsolutePath(); //把文件放在SD根目录
//File file = new File(sdCardDir + "/a.txt");
selfCert.load(new FileInputStream(sdCardDir+"/outgoing.CertwithKey.pkcs12"),//-------wenjian
Constant.SELFCERTPWD.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509"); //sunx509
kmf.init(selfCert, Constant.SELFCERTPWD.toCharArray());
// 2 Import the CA certificate of the server,
KeyStore caCert = KeyStore.getInstance("bks"); //jks
caCert.load(new FileInputStream(sdCardDir+"/ca.bks"), Constant.TRUSTCAPWD.toCharArray());//----------wenjian
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(caCert);
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
// 3 Set the domain name to not verify
// (Non-commercial IoT platform, no use domain name access generally.)
//SSLSocketFactory ssf = new SSLSocketFactory(selfCert,Constant.SELFCERTPWD, caCert);
SSLSocketFactory ssl = new SSLSocketFactory(selfCert, Constant.SELFCERTPWD, caCert);
ssl.setHostnameVerifier(new AllowAllHostnameVerifier());
//SSLConnectionSocketFactory scsf=new SSLConnectionSocketFactory(sc, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
// If the platform has already applied for a domain name which matches
// the domain name in the certificate information, the certificate
// domain name check can be enabled (open by default)
//SSLSocketFactory ssf = new SSLSocketFactory(sc);
//ClientConnectionManager ccm = this.getConnectionManager();
//SchemeRegistry sr = ccm.getSchemeRegistry();
//sr.register(new Scheme("https", ssl, 8743));//有修改
httpClient = new DefaultHttpClient();
if (ssl != null) {
Scheme sch = new Scheme("https", ssl, 8743);
httpClient.getConnectionManager().getSchemeRegistry().register(sch);
}
}
调用接口:
最后一步就是调用接口获取数据了, 在java的工程里每一个接口都是static main方法, 我们把他改成普通方法并调用就可以了
调用示例:
QueryDeviceData qd = new QueryDeviceData();
String msg = qd.hello(Constant.xxx);
二. 使用高德地图
调用高德地图主要是定位, 标记点 (附近派单功能开发过程中调用失败)
高德地图调用并不难, 文档说的也很清楚: https://lbs.amap.com/api/android-sdk/guide/create-map/show-map
这里也贴出我调用的代码:
显示基本地图:
/**
* 显示基本地图
*/
mapView = (MapView) findViewById(R.id.map);
mapView.onCreate(savedInstanceState);// 此方法须覆写,虚拟机需要在很多情况下保存地图绘制的当前状态。The specified child already has a parent. You must call removeView() on the child's parent first
//初始化地图控制器对象
showMap();
定位的实现:
/**
* 不显示缩放浮标, 定位按钮显示
*/
mUiSettings = aMap.getUiSettings();//实例化UiSettings类对象
mUiSettings.setZoomControlsEnabled(false);
//aMap.setLocationSource(this);//通过aMap对象设置定位数据源的监听
mUiSettings.setMyLocationButtonEnabled(true); //显示默认的定位按钮
mUiSettings.setLogoPosition(AMapOptions.LOGO_POSITION_BOTTOM_CENTER);//底部居中
aMap.setMyLocationEnabled(true);// 可触发定位并显示当前位置
定位蓝点
/**
* 定位蓝点Style的实现
*/
MyLocationStyle myLocationStyle;
myLocationStyle = new MyLocationStyle();//初始化定位蓝点样式类myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);//连续定位、且将视角移动到地图中心点,定位点依照设备方向旋转,并且会跟随设备移动。(1秒1次定位)如果不设置myLocationType,默认也会执行此种模式。
myLocationStyle.strokeColor(Color.BLACK);//设置定位蓝点精度圆圈的边框颜色的方法。
myLocationStyle.radiusFillColor(Color.argb(100, 0, 0, 180));//设置定位蓝点精度圆圈的填充颜色的方法。
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_FOLLOW_NO_CENTER);//连续定位、蓝点不会移动到地图中心点,定位点依照设备方向旋转,并且蓝点会跟随设备移动。
myLocationStyle.interval(2000); //设置连续定位模式下的定位间隔,只在连续定位模式下生效,单次定位模式下不会生效。单位为毫秒。
aMap.setMyLocationStyle(myLocationStyle);//设置定位蓝点的Style
aMap.getUiSettings().setMyLocationButtonEnabled(true);//设置默认定位按钮是否显示,非必需设置。
aMap.getMinZoomLevel();
aMap.setMyLocationEnabled(true);// 设置为true表示启动显示定位蓝点,false表示隐藏定位蓝点并不进行定位,默认是false。
打点方法:
/**
* 打点
* tip: LatLan 在 Constant类
*/
public void addMarker(double lng,double lat, String title,String context,int status){ //通用类
if(status==0) {
aMap.addMarker(new MarkerOptions().position(new LatLng(lng, lat))
.title(title).icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory
.decodeResource(getResources(), R.drawable.trash_unfull))).snippet(context));
}
else if(status==1){
aMap.addMarker(new MarkerOptions().position(new LatLng(lng, lat))
.title(title).icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory
.decodeResource(getResources(), R.drawable.trash_full))).snippet(context));
}
}
消除点方法:
/**
* --清除所有点
* --使用需小心 !!! ---- 触发使用, 用完刷新 !!!
* --UI线程
*/
public void clearAllMarker(){
runOnUiThread(new Runnable() {
@Override
public void run() {
aMap.clear();
}
});
Log.d("111","clearAllMarker over");
}
地图回调方法
@Override
protected void onDestroy(){
super.onDestroy();//在activity执行onDestroy时执行mMapView.onDestroy(),销毁地图
mMapView.onDestroy();
}
@Override
protected void onResume(){
super.onResume();//在activity执行onResume时执行mMapView.onResume (),重新绘制加载地图
mMapView.onResume();
}
@Override
protected void onPause(){
super.onPause();//在activity执行onPause时执行mMapView.onPause (),暂停地图的绘制
mMapView.onPause();
}
@Override
protected void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);//在activity执行onSaveInstanceState时执行 mMapView.onSaveInstanceState (outState),保存地图当前的状态
mMapView.onSaveInstanceState(outState);
}
这边都仅仅是局部代码, 具体的参考工程
三. UI界面设计
UI界面分为五个部分
登录部分
我使用的就是AS自带的登录Activity, 这里不需要过多说明, 设置好登录正确进入主页就好了
地图主页
这里调用高德地图, 通过Marker显示信息
信息发送
这里也是小儿科的电话, 短信, qq, 微信的启动
任务列表
通过OceanConnect接口查询, 显示一个自定义的Listview, 这里太长就不贴代码.
个人中心
参照CSDN某篇博客摸索出来的, 效果还不错
四. 可能遇到的问题
ui线程 : 这里是我掉进去最多的坑, 更新Ui请务必在主线程中进行 !!!! !!!!!!! !!!!!!! !!!!!!!
调用方法:
1. runOnUiThread()
runOnUiThread(new Runnable() {
@Override
public void run() {
aMap.clear();
}
});
2. handler.post ()
handler.post(new Runable()){
@Override
public void run() {
aMap.clear();
}
});
线程死循环
这应该是基础了吧, 我还是掉进来了, 花了好长时间才恍然大悟, 我忘记了while还想让他轮询
new Thread(new Runnable(){
@Override
public void run() {
while(true) { //竟然忘了这个!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//休眠线程
try {
//do something
Thread.sleep(500);
} catch (InterruptedException e) {
Log.d("111", "线程意外失效! ");
}
}
}
}).start();
网络线程
还有一个就是我们调用OceanConnect的接口获取数据是一个网络请求, 最好单独开一个线程, 以防阻塞. 不建议用别的方法.
作为菜鸟分享一个安卓开发的好方法: 在出现错误调试的时候, 在每个节点加上Log.d(); 这样问题出现在什么地方会更容易发现, 是不是还要debug方法我不知道. 我目前用的都是Log排错.
五. 实现效果