qt on android之GPS信號的獲取


0.      寫在最前面

本人參考安曉輝大俠的一篇博文后。做了Qt on android的GSP相關的實驗。為了后面不時之需。故而記錄下來。


1.      Qt on Android GPS系統流程


圖1. 系統流程圖

如圖1所看到的,系統含兩個層面:其一為基於QT的UI。提供啟動GPS的button(QPushButton)。以及顯示GPS信號的文本域(QTextBrowser)。其二為基於Activity的GPS服務,提供GPS的啟動,GPS信號上報等服務。兩個層面的交互及C++與Java的交互通過JNI來實現。

系統的總體線索如箭頭方式所看到的:在QT層,button被點擊后。槽函數startGps被觸發。該函數調用Activity層的方法calledByCpp,而calledByCpp方法發送消息到mes handle,消息類型為MSG_STR_GPS_LOC。handle收到消息后。調用函數startAmap啟動GPS的定位。GPS啟動后,位置的實時信息在函數onLocationChanged()函數中公布,該函數把位置信息通過消息發送給msg handle,當中消息類型為MSG_RPT_GSP_INF。handle再調用native方法reportGpsInfo,就能夠把消息發送給QT層。

2.      創建Androidproject

圖2 創建project

project命名為QtAndroidGps,且其路徑為E:\work\c++\qt。project創建之后。文件夾結構例如以下:

圖3. project初始文件

在上述自己主動生成文件夾下增加文件夾android:


圖4. 增加目錄android后

目錄android里面包括例如以下內容:


圖5. 目錄android的內容

能夠看出,文件夾結構與通過eclipse生成的android工程類似,少了assets。bin,gen,res等文件夾,以及project.properties等文件。

本project可採用百度與高德的庫來定位。其相關的jar包分別為BaiduLBS_Android.jar與Android_Location_V1.1.2.jar。這兩個包均放入到e:\work\c++\qt\QtAndroid\android\libs。

同一時候。在構建projectQtAndroidGps之后,qt會在路徑e:\work\c++\qt\之下自己主動生成文件夾:

E:\work\c++\qt\build-QtAndroidGps-Android_for_armeabi_v7a_GCC_4_8_Qt_5_4_0-Debug,該文件夾包括下面文件:


圖6. qt構建project后生成的project文件夾

當中,android-build文件夾包括qt構建出來的androidproject,該project即為完整的androidproject:


圖7. qt構建project后生成的androidproject文件夾

能夠發現,之前我們人為創建的projecte:\work\c++\qt\QtAndroid\android\里面的文件除了AnroidManifest.xml被整合外,其它文件包含jar包和java文件都被拷貝入:

build-QtAndroidGps-Android_for_armeabi_v7a_GCC_4_8_Qt_5_4_0-Debug。

3.      project文件清單

3.1 E:\work\c++\qt\QtAndroidGps\android\AndroidManifest.xml


......


圖8. AndroidManifest.xml

該文件有如上圖標注的4個重要點:

①  ->與E:\work\c++\qt\QtAndroidGps\android\src中java文件的包路徑相應

②  ->要把百度與高德的包用起來。必須增加Api-key

③  ->相應Activity的類名

④  ->須要開啟GPS定位相關的權限

3.2.1 E:\work\c++\qt\QtAndroidGps\mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPushButton>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

protected:
    bool event(QEvent *e);

private:
    Ui::MainWindow *ui;
    QPushButton *btn; // 點擊啟動GPS

private slots:
    void startGps(); // btn相應的點擊槽函數

};

#endif // MAINWINDOW_H
3.2.2  E:\work\c++\qt\QtAndroidGps\ mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QAndroidJniObject>
#include "CustomEvent.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    btn = new QPushButton("start", this);
    btn->setGeometry(QRect(10, 10, 100, 50));
    connect(btn, SIGNAL(clicked()), this, SLOT(startGps()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::startGps()
{
    //QAndroidJniObject javaAction = QAndroidJniObject::fromString(url);
    QAndroidJniObject::callStaticMethod<void>( /* 調用java方法,具體情況請參考qt的幫助文檔 */
        "com/mtn/mes/GpsService",
        "calledByCpp",
        "()V");

}

bool MainWindow::event(QEvent *e)
{
    if(e->type() == CustomEvent::eventType()){ /* 匹配上自己定義事件類型 */
        e->accept();
        CustomEvent *sce = (CustomEvent*)e;
       //_resultView->setText(sce->m_arg2);
        ui->textBrowser->setText(sce->m_arg2); /* 顯示GPS信息 */
        return true;
    }
    return QWidget::event(e);
}

3.3.1  E:\work\c++\qt\QtAndroidGps\ CustomEvent.h

#ifndef CUSTOMEVENT_H
#define CUSTOMEVENT_H
#include <QEvent>
#include <QString>

class CustomEvent : public QEvent
{
public:
    CustomEvent(int arg1 = 0, const QString &arg2 = QString());
    ~CustomEvent();

    static Type eventType();

    int m_arg1;
    QString m_arg2;

private:
    static Type m_evType;
};

#endif // CUSTOMEVENT_H
3.3.2  E:\work\c++\qt\QtAndroidGps\ CustomEvent.cpp

#include "CustomEvent.h"


QEvent::Type CustomEvent::m_evType = (QEvent::Type)QEvent::None;

CustomEvent::CustomEvent(int arg1, const QString &arg2)
    : QEvent(eventType()), m_arg1(arg1), m_arg2(arg2)
{}

CustomEvent::~CustomEvent()
{

}

QEvent::Type CustomEvent::eventType()
{
    if(m_evType == QEvent::None)
    {
        m_evType = (QEvent::Type)registerEventType(); /* 注冊自己定義事件,返回m_evType值為qt分配的自己定義事件類型 */
    }
    return m_evType;
}
3.4  E:\work\c++\qt\QtAndroidGps\ CustomEvent.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QDebug>
#include <jni.h>
#include <QAndroidJniEnvironment>
#include <QAndroidJniObject>
#include "CustomEvent.h"

QObject *main_window = 0;
/* 該函數被java的本地方法調用 */
static void reportGpsInfo(JNIEnv *env, jobject thiz,int result, jstring data)
{
    QString qstrData;
    const char *nativeString = env->GetStringUTFChars(data, 0);
    qstrData = nativeString;
    env->ReleaseStringUTFChars(data, nativeString);
    QCoreApplication::postEvent(main_window, new CustomEvent(result, qstrData)); /* 發送事件(攜帶GPS信息)給主窗體。自己定義事件類型 */
    qDebug() << qstrData;
}

bool registerNativeMethods()
{
    JNINativeMethod methods[] {
        {"reportGpsInfo", "(ILjava/lang/String;)V", (void*)reportGpsInfo} /* 注冊本地方法,java方可調用,具體信息見qt幫助文檔 */
    };

    const char *classname = "com/mtn/mes/ExtendsQtNative";
    jclass clazz;
    QAndroidJniEnvironment env;
    QAndroidJniObject javaClass(classname);
    clazz = env->GetObjectClass(javaClass.object<jobject>());
    qDebug() << "find ExtendsQtNative - " << clazz;

    bool result = false;
    if(clazz) {
        jint ret = env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0]));
        env->DeleteLocalRef(clazz);
        qDebug() << "RegisterNatives return - " << ret;
        result = ret >= 0;
    }
    if(env->ExceptionCheck()) env->ExceptionClear();
    return result;
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    CustomEvent::eventType(); // 注冊自己定義事件,生成自己定義事件類型
    registerNativeMethods();  // 注冊本地方法
    w.show();
    main_window = qobject_cast<QObject*>(&w);
    return a.exec();
}
3.5  E:\work\c++\qt\QtAndroidGps\android\src\com\mtn\mes\ExtendsQtNative.java

package com.mtn.mes;
import java.lang.String;

public class ExtendsQtNative
{
    public native void reportGpsInfo(int result, String content);
}
3.6  E:\work\c++\qt\QtAndroidGps\android\src\com\mtn\mes\ GpsService.java

package com.mtn.mes;
import java.lang.String;
import android.content.Context;
import android.content.Intent;
import android.app.PendingIntent;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.location.Criteria;
import android.provider.Settings;
import android.os.Bundle;
import android.os.Environment;
import java.io.File;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import android.widget.Toast;
import java.util.Date;

import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationListener;
import com.amap.api.location.LocationManagerProxy;
import com.amap.api.location.LocationProviderProxy;
import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.location.LocationClientOption.LocationMode;

public class GpsService extends org.qtproject.qt5.android.bindings.QtActivity
{
    private static GpsService m_instance;
    private final static String TAG = "GpsService";
    private final static int MSG_STR_GPS_LOC = 0;
    private final static int MSG_RPT_GPS_INF = 1;

    ////////////////////////////////////////////////////////////////
    //                        定位相關
    ////////////////////////////////////////////////////////////////
    private LocationManagerProxy aMapManager;

    public GpsService(){
        m_instance = this;
    }

    public static void calledByCpp() {
        System.out.println("[0]hello world!");
        Message msg = new Message();
        msg.what = MSG_STR_GPS_LOC;
        m_instance.handler.sendMessage(msg); // 消息觸發,啟動GPS定位
        //m_instance.handler.sendEmptyMessage(0); // 消息觸發。啟動GPS定位
    }

    public static void calledByCpp(int arg0) {
         System.out.println("[1]hello world!");
    }

    private void startAmap() {
        aMapManager = LocationManagerProxy.getInstance(this);
        /*
         * mAMapLocManager.setGpsEnable(false);
         * 1.0.2版本號新增方法,設置true表示混合定位中包括gps定位,false表示純網絡定位,默認是true Location
         * API定位採用GPS和網絡混合定位方式
         * ,第一個參數是定位provider,第二個參數時間最短是2000毫秒,第三個參數距離間隔單位是米,第四個參數是定位監聽者
         */
        aMapManager.requestLocationUpdates(LocationProviderProxy.AMapNetwork, 2000, 10, mAMapLocationListener);
    }

    private AMapLocationListener mAMapLocationListener = new AMapLocationListener() {

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        @Override
        public void onProviderEnabled(String provider) {

        }

        @Override
        public void onProviderDisabled(String provider) {

        }

        @Override
        public void onLocationChanged(Location location) {

        }

        @Override
        public void onLocationChanged(AMapLocation location) {
            if (location != null) {
                Double geoLat = location.getLatitude();
                Double geoLng = location.getLongitude();
                String cityCode = "";
                String desc = "";
                Bundle locBundle = location.getExtras();
                if (locBundle != null) {
                    cityCode = locBundle.getString("citycode");
                    desc = locBundle.getString("desc");
                }

                String str = (
                "location ok:(" + geoLng + "," + geoLat + ")"+
                "\nAccuracy   :" + location.getAccuracy() + "Meter" +
                "\nPositioning:" + location.getProvider() +
                "\nPositioning time:" + new Date(location.getTime()).toLocaleString() +
                "\nCity coding :" + cityCode +
                "\nLocation Description:" + desc +
                "\nProvince:" + location.getProvince() +
                "\nCity:" + location.getCity() +
                "\nDistrict (county):" + location.getDistrict() +
                "\nRegional Coding:" + location.getAdCode());

                //Toast.makeText(getApplicationContext(), "高德定位\n" + str, Toast.LENGTH_SHORT).show();

                // 發送位置信息到handler, hander處再轉發給Qt
                Message msg = new Message();
                Bundle data = new Bundle();
                data.putString("value", str);
                msg.setData(data);
                msg.what = MSG_RPT_GPS_INF;
                handler.sendMessage(msg);
            }
        }
    };

    private Handler handler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch(msg.what) {
               case MSG_STR_GPS_LOC: // 消息類型為啟動GPS定位
                   m_instance.startAmap();
               break;

               case MSG_RPT_GPS_INF: // 消息類型為上送GSP信息
                   ExtendsQtNative  m_nativeNotify = new ExtendsQtNative();
                   Bundle data = msg.getData();
                   System.out.println(data.getString("value"));
                   m_nativeNotify.reportGpsInfo(0, data.getString("value")); // 掉用c++方法
               break;

               default:
                   System.out.println("msg type error!");
               break;
            }
        }
    };
}

點擊下載源代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM