1、安卓端代码

package cn.net.xuefei.schemedemo;

import android.net.Uri;
import android.os.Bundle;
import android.util.Log;

import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;
 
public class MainActivity extends UnityPlayerActivity {

    private String launchInfo="";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        launchInfo = onLaunchInfo();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        launchInfo = onLaunchInfo();
    }

    private String onLaunchInfo()
    {
        String info="";
        Uri uri = getIntent().getData();
        if (uri != null) {
            // 完整的url信息
            String url = uri.toString();

            info = url;
            Log.e("Unity", "url: " + url);
        }
        return info;

    }

    public void getLaunchInfo()
    {
        UnityPlayer.UnitySendMessage("Main Camera", "OnLaunchInfo", launchInfo);
        launchInfo="";
    }
}

2、Unity中 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package = "cn.net.xuefei.schemedemo"
    android:versionCode="1"
    android:versionName="1.0">

  <application
      android:allowBackup="true"
      android:icon="@drawable/app_icon"
      android:label="@string/app_name"
      android:supportsRtl="true">

    <activity android:name=".MainActivity" >
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.BROWSABLE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="scheme"
              android:host="host"
              android:path="/path"
              android:port="8888"/>
      </intent-filter>
      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    </activity>
  </application> 
</manifest>

3、Unity中代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

#if UNITY_IOS
using System.Runtime.InteropServices;
#endif

public class SchemeDemo : MonoBehaviour
{
    public Text text;

    // Use this for initialization
    void Start()
    {
        GetInfo();
    }

    // Update is called once per frame
    void Update()
    {

    }

    public void OnLaunchInfo(string launchInfo)
    {
        Debug.LogError("launchInfo:" + launchInfo);
        text.text = launchInfo;
    }

    public void GetInfo()
    {
        
#if UNITY_ANDROID
        AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
        jo.Call("getLaunchInfo");
#elif UNITY_IOS
        _GetLaunchInfo();
#endif
    }

    private void OnApplicationFocus(bool focus)
    {
        Debug.LogError("focus:" + focus);
        if (true)
        {
            GetInfo();
        }
    }

#if UNITY_IOS
    [DllImport("__Internal")]
    private static extern void _GetLaunchInfo();
#endif
}

4、设置Unity iOS URL Schemes,修改导出的Xcode工程中的UnityAppController.mm

#import "UnityAppController.h"

NSString *URLString = @"";

// 向Unity传递参数;
extern void UnitySendMessage(const char *, const char *, const char *);

//添加的代码
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options
{
    URLString = [url absoluteString]; 
    return YES;
}

- (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation
{
    //添加的代码
    URLString = [url absoluteString]; 
    return YES;
}

extern "C"  
{  
    void _GetLaunchInfo();  
}  
  
void _GetLaunchInfo()  
{    
    UnitySendMessage( "Main Camera", [@"OnLaunchInfo" UTF8String], [URLString UTF8String] );  
    // 清空,防止造成干扰;  
    URLString = @"";  
}  

5、网页

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input  id="btn" type="button" value="唤起SchemeDemo">
    <script>
        var btn = document.getElementById('btn');
        
        btn.onclick = function () {
            jump('scheme://host:8888/path?ps=123456');
        };
        
        function  GetMobelType()  {                
            var  browser  =   {                    
                versions:   function()  {                        
                    var  u  =  window.navigator.userAgent;    
                    console.log(u);  //Safari浏览器 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.1 Safari/605.1.15                  
                    return  {                            
                        trident:  u.indexOf('Trident')  >  -1, //IE内核
                        presto:  u.indexOf('Presto')  >  -1, //opera内核
                        Alipay:  u.indexOf('Alipay')  >  -1, //支付宝
                        webKit:  u.indexOf('AppleWebKit')  >  -1, //苹果、谷歌内核
                        gecko:  u.indexOf('Gecko')  >  -1  &&  u.indexOf('KHTML')  ==  -1, //火狐内核
                        mobile:  !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
                        ios:  !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
                        android:  u.indexOf('Android')  >  -1  ||  u.indexOf('Linux')  >  -1, //android终端或者uc浏览器
                        iPhone:  u.indexOf('iPhone')  >  -1  ||  u.indexOf('Mac')  >  -1, //是否为iPhone或者安卓QQ浏览器
                        //iPhone: u.match(/iphone|ipod|ipad/),//
                        iPad:  u.indexOf('iPad')  >  -1, //是否为iPad
                        webApp:  u.indexOf('Safari')  ==  -1, //是否为web应用程序,没有头部与底部
                        weixin:  u.indexOf('MicroMessenger')  >  -1, //是否为微信浏览器
                        qq: u.match(/\sQQ/i) !== null, //是否QQ
                        Safari:  u.indexOf('Safari')  >  -1,
                          ///Safari浏览器,
                    };                    
                }()                
            };                
            return  browser.versions;            
        }
        
        
        function jump(myurl) { 
            var timeout = 2300, timer = null;
            if(GetMobelType().weixin) {
                // 微信浏览器不支持跳转
                // 可以显示提示在其他浏览器打开
            } else {
                var startTime = Date.now();
                if(GetMobelType().android) {
                    var ifr = document.createElement('iframe');
                    ifr.src = myurl;//这里是唤起App的协议,有Android可爱的同事提供
                    ifr.style.display = 'none';
                    document.body.appendChild(ifr);
                    timer = setTimeout(function() {
                        var endTime = Date.now();
                        if(!startTime || endTime - startTime < timeout + 300) {
                            document.body.removeChild(ifr);
                            //window.open("唤起失败跳转的链接");
                            window.open("https://www.ganghood.net.cn/SchemeDemo.apk");
                        }
                    }, timeout);
                }
                if(GetMobelType().ios || GetMobelType().iPhone || GetMobelType().iPad) {
                    if(GetMobelType.qq) { 
                    // ios的苹果浏览器
                    // 提示在浏览器打开的蒙板
                    } else {
                        /*var ifr = document.createElement("iframe");
                        ifr.src = myurl;
                        ifr.style.display = "none";*/ // iOS9+不支持iframe唤起app
                        window.location.href = myurl; //唤起协议,由iOS小哥哥提供
                        //document.body.appendChild(ifr);
                        
                        timer = setTimeout(function() {
                            // window.location.href = "ios下载的链接";
                            window.location.href = "https://www.ganghood.net.cn/没有该文件.ipa";
                        }, timeout);
                    };
                }
            }
        }
    </script>
</body>
</html>

TIM图片20190315140409.jpg

工程地址https://gitee.com/awnuxcvbn/SchemeDemo

整个过程:
1、Unity获取GPS信息
2、GPS坐标转高德坐标
3、高德逆地理编码

步骤1见文章 https://www.xuefei.net.cn/index.php/archives/35/

步骤2代码

    /// <summary>
    /// GPS转高德坐标
    /// </summary>
    /// <returns></returns>
    IEnumerator GPS2GD(string gps)
    {
        lbs.text = "定位中……";
        string api = "https://restapi.amap.com/v3/assistant/coordinate/convert?locations=";
        string pas = "&coordsys=gps&output=json&key=";
        string key = "这里是key";
        Log.Debug(api + gps + pas + key);
        WWW www = new WWW(api + gps + pas + key);
        yield return www;
        Log.Debug(www.text);
        if (www.isDone && www.error == null)
        {
            Log.Debug(www.text);
            GDPOS pos = new GDPOS();
            JsonUtility.FromJsonOverwrite(www.text, pos);
            StartCoroutine(GetGDPos(pos.locations));
        }
        else
        {
            lbs.text = "定位失败";
            yield break;
        }
    }

[System.Serializable]
/// <summary>
/// GPS转高德坐标返回的信息
/// </summary>
public class GDPOS
{
    public int status;
    public string info;
    public int infocode;
    public string locations;
}

步骤3代码

    /// <summary>
    /// 获取反查位置
    /// </summary>
    /// <param name="gps"></param>
    /// <returns></returns>
    IEnumerator GetGDPos(string gdPos)
    { 
        string api = "https://restapi.amap.com/v3/geocode/regeo?output=json&location=";
        string key = "&key=这里是key&radius=500";
        Log.Debug(api + gdPos + key);
        WWW www = new WWW(api + gdPos + key);
        yield return www;
        Log.Debug(www.text);
        if (www.isDone && www.error == null)
        {
            Log.Debug(www.text);
            LBPOS pos = new LBPOS();
            JsonUtility.FromJsonOverwrite(www.text, pos);
            lbs.text = pos.regeocode.formatted_address;
        }
        else
        {
            lbs.text = "定位失败";
            yield break;
        }
    }

[System.Serializable]
public class LBPOS
{
    public int status;
    public Regeocode regeocode = new Regeocode();
    public string info;
    public int infocode;
}

[System.Serializable]
public class Regeocode
{
    public AddressComponent addressComponent = new AddressComponent();
    public string formatted_address;
}

[System.Serializable]
public class AddressComponent
{
    public string city;
    public string province;
    public string adcode;
    public string district;
    public string towncode;
    public StreetNumber streetNumber;
    public string country;
    public string township;
    public List<BusinessArea> businessAreas = new List<BusinessArea>();
    public Building building = new Building();
    public Neighborhood neighborhood = new Neighborhood();
    public string citycode;
}

[System.Serializable]
public class StreetNumber
{
    public string number;
    public string location;
    public string direction;
    public string distance;
    public string street;
}

[System.Serializable]
public class BusinessArea
{
    public string location;
    public string name;
    public string id;
}
[System.Serializable]
public class Building
{
    public List<string> name = new List<string>();
    public List<string> type = new List<string>();
}
[System.Serializable]
public class Neighborhood
{
    public List<string> name = new List<string>();
    public List<string> type = new List<string>();
}

task makeJar(type: Copy) {
    //删除存在的
    delete 'build/libs/plugin.jar'
    //设置拷贝的文件
    from('build/intermediates/packaged-classes/release/')
    //打进jar包后的文件目录
    into('build/libs/')
    //将classes.jar放入build/libs/目录下
    //include ,exclude参数来设置过滤
    //(我们只关心classes.jar这个文件)
    include('classes.jar')
    //重命名
    rename('classes.jar', 'plugin.jar')
}
makeJar.dependsOn(build)

Termina 输入命令 gradlew makeJar 打包jar

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class TestLocation : MonoBehaviour
{
    public InputField inputField;

    // Use this for initialization
    void Start()
    {
        StartCoroutine(Location());
        //Debug.Log(WGS2BD(31, 118));
    }

    // Update is called once per frame
    void Update()
    {

    }

    /// <summary>
    /// 定位
    /// </summary>
    /// <returns></returns>
    IEnumerator Location()
    {
        // First, check if user has location service enabled
        if (!Input.location.isEnabledByUser)
        {
            inputField.text = "未获取GPS定位权限,定位失败";
            yield break;
        }

        // Start service before querying location
        Input.location.Start();

        // Wait until service initializes
        int maxWait = 20;
        while (Input.location.status == LocationServiceStatus.Initializing && maxWait > 0)
        {
            yield return new WaitForSeconds(1);
            maxWait--;
        }

        // Service didn't initialize in 20 seconds
        if (maxWait < 1)
        {
            print("Timed out");
            inputField.text = "定位超时";
            yield break;
        }

        // Connection has failed
        if (Input.location.status == LocationServiceStatus.Failed)
        {
            print("Unable to determine device location");
            inputField.text = "定位失败";
            yield break;
        }
        else
        {
            // Access granted and location value could be retrieved
            string location = "latitude:" + Input.location.lastData.latitude
                + " longitude:" + Input.location.lastData.longitude
                + " altitude:" + Input.location.lastData.altitude
                + " horizontalAccuracy:" + Input.location.lastData.horizontalAccuracy
                + " timestamp:" + Input.location.lastData.timestamp;
            inputField.text = location;
            print("Location: " + location);
        }

        // Stop service if there is no need to query location updates continuously
        Input.location.Stop();
    }

    //系数常量
    readonly double a = 6378245.0;
    readonly double ee = 0.00669342162296594323;
    readonly double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
    
    //转换经度
    double transformLat(double lat, double lon)
    {
        double ret = -100.0 + 2.0 * lat + 3.0 * lon + 0.2 * lon * lon + 0.1 * lat * lon + 0.2 * Math.Sqrt(Math.Abs(lat));
        ret += (20.0 * Math.Sin(6.0 * lat * Math.PI) + 20.0 * Math.Sin(2.0 * lat * Math.PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.Sin(lon * Math.PI) + 40.0 * Math.Sin(lon / 3.0 * Math.PI)) * 2.0 / 3.0;
        ret += (160.0 * Math.Sin(lon / 12.0 * Math.PI) + 320 * Math.Sin(lon * Math.PI / 30.0)) * 2.0 / 3.0;
        return ret;
    }


    //转换纬度
    double transformLon(double lat, double lon)
    {
        double ret = 300.0 + lat + 2.0 * lon + 0.1 * lat * lat + 0.1 * lat * lon + 0.1 * Math.Sqrt(Math.Abs(lat));
        ret += (20.0 * Math.Sin(6.0 * lat * Math.PI) + 20.0 * Math.Sin(2.0 * lat * Math.PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.Sin(lat * Math.PI) + 40.0 * Math.Sin(lat / 3.0 * Math.PI)) * 2.0 / 3.0;
        ret += (150.0 * Math.Sin(lat / 12.0 * Math.PI) + 300.0 * Math.Sin(lat / 30.0 * Math.PI)) * 2.0 / 3.0;
        return ret;
    }
     
    /// <summary>
    /// WGS transform to GCJ
    /// </summary>
    /// <param name="lat">维度</param>
    /// <param name="lon">经度</param>
    /// <returns></returns>
    POS WGS2GCJ(double lat, double lon)
    {
        double dLat = transformLat(lon - 105.0, lat - 35.0);
        double dLon = transformLon(lon - 105.0, lat - 35.0);
        double radLat = lat / 180.0 * Math.PI;
        double magic = Math.Sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.Sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * Math.PI);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.Cos(radLat) * Math.PI);
        double mgLat = lat + dLat;
        double mgLon = lon + dLon;
        POS loc = new POS(mgLat, mgLon);
        return loc;
    }

    /// <summary>
    /// GCJ transform to BD2
    /// </summary>
    /// <param name="lat"></param>
    /// <param name="lon"></param>
    /// <returns></returns>
    POS GCJ2BD2(double lat, double lon)
    {
        double x = lon;
        double y = lat;
        double z = Math.Sqrt(x * x + y * y) + 0.00002 * Math.Sin(y * x_pi);
        double theta = Math.Atan2(y, x) + 0.000003 * Math.Cos(x * x_pi);
        double bd_lon = z * Math.Cos(theta) + 0.0065;
        double bd_lat = z * Math.Sin(theta) + 0.006;
        POS bdpoint = new POS(bd_lat, bd_lon);
        return bdpoint;
    }

    // wgs transform to bd
    POS WGS2BD(double lat, double lon)
    {
        POS wgs_to_gcj = WGS2GCJ(lat, lon);
        POS gcj_to_bd = GCJ2BD2(wgs_to_gcj.Lat, wgs_to_gcj.Lon);
        return gcj_to_bd;
    }

    /// <summary>
    /// 位置
    /// </summary>
    struct POS
    {
        /// <summary>
        /// 维度
        /// </summary>
        public double Lat;
        /// <summary>
        /// 经度
        /// </summary>
        public double Lon;

        public POS(double La, double Lo)
        {
            Lat = La;
            Lon = Lo;
        }

        public override string ToString()
        {
            return "维度:" + Lat + " 经度:" + Lon;
        }
    }
}