Ionic百度原生定位插件开发

一、背景啰唆

  这两天被临时要求帮忙解决一个安卓App上的定位问题。其实我是有点抵触心理的,因为我最近刚入手了Koa2和Vue,玩得不亦乐乎,不太想在这个时候被打断。但是作为一个有良好职业素养的搬砖工,同时也本着“自己也是一块砖,哪里需要哪里搬”的精神,同时感受到“这个问题好像只我能解决”的特殊使命感,我愉快地(不得不)接受了这个任务。经过分析,App是用Ionic开发的,用的是百度的Js定位api,多数机型,多数时候定位准确,但是出现不准的概率还是比较多,于是我决定给它做个原生插件,用百度的Android sdk重做定位(说得云淡风清,其实,过程让我好蛋疼)。
  下图是过程总结,先看一下,做到心理有个大概:


Ionic-Android原生插件开发流程.png

一、环境准备

1.1 Android开发环境准备

1.1.1 JDK、Android-sdk、gradle、android-studio(不用as的,也可以用其它IDE)

  以上三件套准备好,安装过程也不再赘述,有不清楚的地方可以百度,最终结果要达下图所示(相关版本是我本机的,不一定要一样):

环境变量

image.png

JDK版本

image.png

JDK下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

Gradle环境

image.png

Gradle下载地址:http://www.androiddevtools.cn/

Android SDK

  下图是我本机的情况,实际当然不需要这么多版本的sdk,只需要一个你期望的编译版本就好。

image.png

这里有你想要的关于Android的一切
http://www.androiddevtools.cn/

1.2 Ionic开发环境准备

nodejs、ionic相关、cordova、vsCode/webstorm(由你喜欢)

nodejs环境

装完nodejs后,验证一下:

image.png

从这里下载nodejs
https://nodejs.org/en/

Ionic

通过指令安装:

npm install -g ionic

结果:

image.png

  这个版本只是当时装的时候的最新稳定版本,每个人可能不一样,如果要装指定版本,请用类似“npm install -g [email protected]”的指令,其中3.0.5就是你要指定的版本号。

三、Ionic项目准备

3.1 生成官方标准项目

这里就用官方的标准项目为例,在cmd定位到你的项目目录,执行以下指令:

ionic start myApp tabs
image.png

  这里选择y,同意给你的Ionic应用添加iOS和Android的cordova依赖平台。如果你是用ionic来做类似微信公众号那样的应用就可以不加这个依赖。
  一堆日志后,会面临如图的第二个要选择的处理:


image.png

  这个就是问你要不要用ionic的在线平台,这里可以选择n,不要。最后跑完之后,生成的项目目录是这样的:


image.png

cmd进入到该目录下:
执行:
ionic server

把项目跑一下,起来后效果如下图:


image.png

image.png

3.2 增加Android编译平台

给你的ionic增加cordova cli

在你的项目目录下,执行指令:

npm install --save -D cordova

安装cordovacli,
还是在你的项目目录下,执行指令:

ionic cordova platform add [email protected]

这里要下的东西比较多,时间也比较久。

这里一定要加一个android的版本号指明你要依赖哪个android版本的sdk来编译

成功后,你的项目目录下会多出这两个目录:


image.png

完成!

四、Android插件开发

  我要开发的插件是百度地图的Android原生定位插件。因此要到百度平台去申请账号和创建App应用,到得相关的key,下载相关的so库和jar包
这个过程就不赘述了,百度的文档很清楚:http://lbsyun.baidu.com/

4.1 在Android原生应用上开发功能和自测

  这里啰唆一下,可以先在Androidd原生应用上写一遍你的逻辑,并且自测一下功能,保证你理解了你要做的功能是什么,用原生代码逻辑上怎么实现,自己测试通过,然后再搬到插件项目中去,因为插件项目调试比较麻烦,不要把bug留到那个时候去调试。

4.2 创建cordova插件项目:

安装plugman

在cmd执行以下命令:

npm install plugman -g
image.png

找一个你准备放插件项目的目录:
在该目录下执行:

plugman -create --name BDLocation --plugin_id com.test.bdlocation --plugin_version 1.0.0

  其中,BDLocation是我的插件项目名,com.test.bdlocation是我的主包名,作为插件的唯一标识。生成如下目录结构的项目:

image.png

在该目录下,执行:

plugman platform add --platform_name android

  在项目的src下会创建android文件夹,下面会生成一个文件BDLocation.java,这就是我们cordova插件的入口文件。另外在项目的www文件夹下还有一个js的调用例子。
  这里不详细讨论cordova的api,只作简单的例子介绍,希望后面有时间再详细讨论cordova的api。这是BDLocation.java里的内容:

  package com.test.bdlocation;

import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * This class echoes a string called from JavaScript.
 */
public class BDLocation extends CordovaPlugin {
    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        if (action.equals("coolMethod")) {
            String message = args.getString(0);
            this.coolMethod(message, callbackContext);
            return true;
        }
        return false;
    }

    private void coolMethod(String message, CallbackContext callbackContext) {
        if (message != null && message.length() > 0) {
            callbackContext.success(message);
        } else {
            callbackContext.error("Expected one non-empty string argument.");
        }
    }
}
  下面来简单说一下execute方法:

  这个方法是插件的入口方法,js通过传入类名来让cordova找到这个插件,找到这个插件后,默认执行入口方法execute。

  参数action:通过js调用传进来,代表你要这个插件干什么,通常代表一个行为,这里默认的例子是调用了一个方法
  参数args:通过js调用传进来,类似java main方法传参,在js中就是传对象数组进来
  参数callbackContext:通过它来把结果回调给js,在js中会传来成功和失败的两个回调函数,对应callbackContext的success和error方法,如这里的coolMethod示例方法所示。
  返回boolean值,根据英文理解是,如果你收到的action是你想要的,就返回true,否则返回false。(这里还可以测试一下,如果首次不处理这个action,并且返回false,下次js调用如果还是这个类的这个action,有可能cordova不帮你调插件了,这一点我还没验证,回头再补)

  下面是wwww/BDLocation.js里调用示例:

var exec = require('cordova/exec');

exports.coolMethod = function (arg0, success, error) {
    exec(success, error, 'BDLocation', 'coolMethod', [arg0]);
};

  这里主要看exec函数的传参。一目了然,sucess和error分别对应BDLocation.java中的callbackContext成功与失败的回调,也就是说callbackContext中调用success方法,那么js中这个success方法就会被回调,error也是同理。
  BDLocation是代表了java中插件的类名(也是插件名,一般保持同名,不同会怎样呢?可以再试。),arg0当然就是你要传给插件的业务参数了。

4.3 编写插件代码

  现在我们回到前面在AndroidStudio上的应用:

  我们把插件项目的BDLocation.java文件直接copy到应用中:

image.png

  我们发现项目里根据还没有Cordova相关的依赖,代码会报错。于是我们还要添加一个模块:
  回到我们之前创建的ionic项目,在项目目录下,进入路径:platforms\android,找到该路径下的CordovaLib目录,记下该目录。
  现在再回到AndroidStudio,我把CordovaLib以模块的形式加入到应用项目依赖中,成功后如下图效果,BDLocation中的报错会消失。


image.png

  根据之前在应用中写的百度地图的定位功能,在BDLocation上编写相应功能:

package com.test.bdlocation;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;

import com.baidu.location.Address;
import com.baidu.location.BDAbstractLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2018/8/8.
 */

public class BDLocation extends CordovaPlugin{
    private LocationClientOption option=new LocationClientOption();
    private LocationClient ml=null;
    private CallbackContext callbackContext;
    private List permissionNeedCheck=null;
    public BDAbstractLocationListener myL=new BDAbstractLocationListener() {
        @Override
        public void onReceiveLocation(com.baidu.location.BDLocation bdLocation) {
            if(bdLocation!=null){
                JSONObject data=new JSONObject();
                try {
                    JSONObject object=new JSONObject();
                    object.put("type", bdLocation.getLocType());
                    object.put("latitude",bdLocation.getLatitude());
                    object.put("longitude",bdLocation.getLongitude());
                    JSONObject address=new JSONObject();
                    address.put("adcode",bdLocation.getAddress().adcode);
                    address.put("address",bdLocation.getAddress().address);
                    address.put("city",bdLocation.getAddress().city);
                    address.put("cityCode",bdLocation.getAddress().cityCode);
                    address.put("country",bdLocation.getAddress().country);
                    address.put("countryCode",bdLocation.getAddress().countryCode);
                    address.put("district",bdLocation.getAddress().district);
                    address.put("province",bdLocation.getAddress().province);
                    address.put("street",bdLocation.getAddress().street);
                    address.put("streetNumber",bdLocation.getAddress().streetNumber);
                    object.put("address",address);
                    object.put("city",bdLocation.getCity());
                    object.put("cityCode",bdLocation.getCityCode());
                    data.put("status","success");
                    data.put("location",object);
                    data.put("code","000");
                    data.put("errMsg","");
                    PluginResult pluginResult=new PluginResult(PluginResult.Status.OK,data);
                    callbackContext.sendPluginResult(pluginResult);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                ml.stop();
            }
        }
    };
    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        if(Build.VERSION.SDK_INT>=23){
            permissionNeedCheck=new ArrayList();
            permissionNeedCheck.add(Manifest.permission.ACCESS_COARSE_LOCATION);
            permissionNeedCheck.add(Manifest.permission.ACCESS_FINE_LOCATION);
            this.callbackContext=callbackContext;
            if (action.equals("location")) {
                this.location();
                return true;
            }
            return false;
        }else{
            if(action.equals("location")){
                this.startLocate();
                return true;
            }
            return false;
        }
    }
    private boolean permissionCheck(){
        List unGrantedPermission=new ArrayList();
        for(String permission:permissionNeedCheck){
            if(PackageManager.PERMISSION_GRANTED!=ContextCompat.checkSelfPermission(cordova.getActivity(),permission)){
                unGrantedPermission.add(permission);
            }
        }
        this.permissionNeedCheck=unGrantedPermission;
        return unGrantedPermission.size() > 0 ? false : true;
    }
    private void askPermission(){
        String[] unGrantedPer=new String[permissionNeedCheck.size()];
        for(int i=0;i

  这里不再详细说明里面的代码了,主要是定位和权限相关的内容。

  这里唯一可以说明一下的就是PluginResult,当你要从插件返回一个复杂结果给js的时候,就用它。

五、打包与调试

  最后一个大环节就是打包与调试

5.1首先要进行文件准备

  到目前为止我们只写了代码,我们在应用中调试了相关的逻辑,我们so库,jar包,等相关的东西都还没有放进插件相关的目录。

  现在我们回到插件项目的目录

把在应用中写好的BDLocation.java文件Copy到插件项目的src/android文件夹,把初始生成的BDLocation.java覆盖掉:

image.png

库处理——在项目目录的src/android下新建libs文件夹,把相关的so库,jar包放到libs下:

image.png

plugin.xml文件处理:

默认生成的plugin.xml文件内容如下:



        BDLocation
        
            
        
        
            
                
                
            
            
                
            
            
        

配置完成后,如下:



        BDLocation
        
            
        
        
            
                
                
                
            
            
            
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
            
            
            
                
                 
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
        

配置的注释我都尽量写详细了,我这里主要就是相关权限、地图的配置、对应jar、so、java文件的对位copy。

插件package.json文件生成与配置

在我们的插件主目录下,执行cmd指令:

npm init

生成初始package.json文件:


image.png

package.json初始内容如下:

{
  "name": "bdlocation",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

配置完成后:

{
  "name": "bdlocation",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "cordova": {
    "id": "com.test.bdlocation",
    "platforms": [
      "android"
    ]
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

  主要是加了cordova插件相关配置。
  其中id是指你给你的插件自定义的唯一id,platform就是你的插件的运行平台,如果支持ios,你可以加上ios。
  到此文件准备就完成了!!!

5.2给Ionic项目添加插件

  现在回到我们的Ionic项目目录

添加自定义插件到Ionic项目

  在ionic项目目录下,执行cmd指令:

ionic cordova plugin add ../BDLocation

  其中,../BDLocation是自定义插件的项目的相对目录,完成后,Ionic项目目录下,会产生一个plugins文件夹,里面有我们安装好的插件:

image.png
image.png

5.3打包debug apk装备进行真机调试

  在Ionic项目目录下,执行cmd指令:

ionic cordova build android --debug

  这个过程相对比较久,第一次的时候还要联网下载不少东西,完成后在Ionic项目的platforms\android\build\outputs\apk的目录下,生成了相应的android-debug.apk文件就是了。

5.4真机调试

  把上面的apk copy到手机上安装,或者通过adb指令安装都可以。这里我们用chrome的DevTools进行真机调试。在Chrome地址中输入chrome://inspect/#devices,用USB接上你的Android手机,打开调试:

image.png

如上图,我的pro5就被找到了。
运行你的app,你们看到多了一个Inspect标签,可以点击:
image.png

点击进去后,发现:
image.png

  有日志输出了!!!!
  当然你完全也可以用Android Studio来看日志,还可以在cmd用adb相关命令来看日志。

5.4打生产包与签名

  在Ionic项目目录下,执行cmd指令:

ionic cordova build android --release

得到未签名的apk文件:android-unsign.apk(根据打包的输出日志,可以看到apk包被打出到哪个目录,叫什么名字)
  把未签名apk,keystore文件放到同一个目录,在该目录下,执行cmd指令:

jarsigner -verbose -keystore your.keystore -signedjar complete.apk android-unsign.apk your.keystore

得到签名好的apk,complete.apk

  虽然,还有不少是我还想写的详细一点的内容,还有一些是我原本计划要写但是又被我砍掉的内容,但是就写到这里吧,等下还要再校正一遍,我的原力快用完了!!!

  有错误的地方欢迎指正。

最后我的环境如下:


QQ图片20181008111658.png

你可能感兴趣的:(Ionic百度原生定位插件开发)