【无人机学习之QGroundControl】android端App初解1

█ 【无人机学习之QGroundControl】android端 源码


█ 系列文章目录

提示:这里是收集了无人机的相关文章


█ 文章目录


█ 读前说明

  • 本文通过学习别人写demo,学习一些课件,参考一些博客,学习相关知识,如有涉及侵权请告知
  • 本文可能只简单罗列了一些相关的代码实现过程,复制了一些大神的高论,如内容有误请自行辨别
  • 涉及到的逻辑以及说明可能只做了简单的介绍,主要当做笔记,了解过程而已,如有不同看法,欢迎下方评论
  • 本文源码:https://github.com/mavlink/qgroundcontrol
  • 本文UI:https://github.com/mavlink/qgroundcontrol/blob/master/src/ui
  • QGC中UI设计的主要模式是用QML编写的UI页面,多次与用C ++编写的定制“控制器”进行通信。类似MVC的设计模式。

提示:QGroundControl是使用QT & c++ 编写的


█ android端

1.android\AndroidManifest.xml

此文件在qgroundcontrol-master\android\AndroidManifest.xml

重点字段
org.qtproject.qt5.android.bindings.QtApplication
org.mavlink.qgroundcontrol.QGCActivity
<?xml version="1.0"?>
<manifest package="org.mavlink.qgroundcontrol" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="3.0.0-243-gd759437" android:versionCode="300243" android:installLocation="auto">
    <application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:icon="@drawable/icon">
        <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.mavlink.qgroundcontrol.QGCActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="sensorLandscape" android:launchMode="singleTask" android:keepScreenOn="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
                <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
                <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED"/>
                <action android:name="android.bluetooth.device.action.ACL_CONNECTED"/>
                <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED"/>
                <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/>
            </intent-filter>
        </activity>
    </application>
    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
	。。。。。。。。
</manifest>

2.android\AndroidManifest.xml.in

此文件在qgroundcontrol-master\android\AndroidManifest.xml.inl

重点字段
org.qtproject.qt5.android.bindings.QtApplication
org.qtproject.qt5.android.bindings.QtActivity
<?xml version="1.0"?>
<manifest android:versionName="@QT_ANDROID_APP_VERSION@" package="@QT_ANDROID_APP_PACKAGE_NAME@" android:installLocation="auto" xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="@QT_ANDROID_APP_VERSION_CODE@">
    <application android:label="@QT_ANDROID_APP_NAME@" android:name="org.qtproject.qt5.android.bindings.QtApplication">
		<activity android:label="@QT_ANDROID_APP_NAME@" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:screenOrientation="unspecified" android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation">
			<intent-filter>
				<action android:name="android.intent.action.MAIN"/>
				<category android:name="android.intent.category.LAUNCHER"/>
			</intent-filter>
			。。。。。。。。
		</activity>
	</application>
	<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="19"/>
</manifest>

3.QGCActivity.java

此文件在qgroundcontrol-master\android\src\org\mavlink\qgroundcontrol

重点字段
public class QGCActivity extends org.qtproject.qt5.android.bindings.QtActivity
public void onCreate(Bundle savedInstanceState) -> public native void nativeInit()

界面初始化都是在QT中处理,包含界面

import org.qtproject.qt5.android.bindings.QtActivity;
import org.qtproject.qt5.android.bindings.QtApplication;

public class QGCActivity extends QtActivity
{
    public  static int                                  BAD_DEVICE_ID = 0;
    private static QGCActivity                          _instance = null;
    private static final String                         TAG = "QGC_QGCActivity";
    private static final String                         ACTION_USB_PERMISSION = "org.mavlink.qgroundcontrol.action.USB_PERMISSION";
    
    public static Context m_context;

    private final static UsbIoManager.Listener m_Listener =
            new UsbIoManager.Listener()
            {
                @Override
                public void onRunError(Exception eA, long userData)
                {
                    Log.e(TAG, "onRunError Exception");
                    nativeDeviceException(userData, eA.getMessage());
                }

                @Override
                public void onNewData(final byte[] dataA, long userData)
                {
                    nativeDeviceNewData(userData, dataA);
                }
            };

    private final BroadcastReceiver mOpenAccessoryReceiver =
        new BroadcastReceiver()
        {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (ACTION_USB_PERMISSION.equals(action)) {
                    // usb 权限处理
                } else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
                    // usb 插入处理
                }
            }
        };

    private final static BroadcastReceiver _usbReceiver = new BroadcastReceiver() {
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                Log.i(TAG, "BroadcastReceiver USB action " + action);

                if (ACTION_USB_PERMISSION.equals(action)) {
                    // usb 权限处理
                } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
                    // usb 插入处理
                }

                try {
                    nativeUpdateAvailableJoysticks();
                } catch(Exception e) {
                    Log.e(TAG, "Exception nativeUpdateAvailableJoysticks()");
                }
            }
        };

    // Native C++ functions which connect back to QSerialPort code
    private static native void nativeDeviceHasDisconnected(long userData);
    private static native void nativeDeviceException(long userData, String messageA);
    private static native void nativeDeviceNewData(long userData, byte[] dataA);
    private static native void nativeUpdateAvailableJoysticks();

    // Native C++ functions called to log output
    public static native void qgcLogDebug(String message);
    public static native void qgcLogWarning(String message);

    public native void nativeInit();

    // QGCActivity singleton
    public QGCActivity()
    {
        _instance =                 this;
        _drivers =                  new ArrayList<UsbSerialDriver>();
        _userDataHashByDeviceId =   new HashMap<Integer, Long>();
        m_ioManager =               new HashMap<Integer, UsbIoManager>();
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        nativeInit();
        PowerManager pm = (PowerManager)_instance.getSystemService(Context.POWER_SERVICE);
        _wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "QGroundControl");
        if(_wakeLock != null) {
            _wakeLock.acquire();
        } else {
            Log.i(TAG, "SCREEN_BRIGHT_WAKE_LOCK not acquired!!!");
        }
        _instance.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        _usbManager = (UsbManager)_instance.getSystemService(Context.USB_SERVICE);

        // Register for USB Detach and USB Permission intent
        IntentFilter filter = new IntentFilter();
        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        filter.addAction(ACTION_USB_PERMISSION);
        filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
        _instance.registerReceiver(_instance._usbReceiver, filter);

        // Create intent for usb permission request
        _usbPermissionIntent = PendingIntent.getBroadcast(_instance, 0, new Intent(ACTION_USB_PERMISSION), 0);

	// Workaround for QTBUG-73138
	if (_wifiMulticastLock == null)
            {
                WifiManager wifi = (WifiManager) _instance.getSystemService(Context.WIFI_SERVICE);
                _wifiMulticastLock = wifi.createMulticastLock("QGroundControl");
                _wifiMulticastLock.setReferenceCounted(true);
            }

	_wifiMulticastLock.acquire();
	Log.d(TAG, "Multicast lock: " + _wifiMulticastLock.toString());


        try {
            taiSync = new TaiSync();

            IntentFilter accessoryFilter = new IntentFilter(ACTION_USB_PERMISSION);
            filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
            registerReceiver(mOpenAccessoryReceiver, accessoryFilter);

            probeAccessoriesTimer = new Timer();
            probeAccessoriesTimer.schedule(new TimerTask() {
                @Override
                public void run()
                {
                    probeAccessories();
                }
            }, 0, 3000);
        } catch(Exception e) {
           Log.e(TAG, "Exception: " + e);
        }
    }

█ C++端

1.main.cc

此文件在qgroundcontrol-master\src\main.cc

重点字段
“nativeInit” -> gst_android_init
public void onCreate(Bundle savedInstanceState) -> public native void nativeInit()
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QGCApplication* app = new QGCApplication(argc, argv, runUnitTests);
app->_initCommon();
getQGCMapEngine()->init();//-- Initialize Cache System
void setNativeMethods(void)
{
    JNINativeMethod javaMethods[] {// reinterpret_cast运算符是用来处理无关类型之间的转换;它会产生一个新的值,这个值会有与原始参数(expression)有完全相同的比特位。
        {"nativeInit", "()V", reinterpret_cast<void *>(gst_android_init)}
    };
    。。。。。。 
}
//-----------------------------------------------------------------------------
static void gst_android_init(JNIEnv* env, jobject context)
{
    jobject class_loader = nullptr;

    jclass context_cls = env->GetObjectClass(context);
    if (!context_cls) {
        return;
    }
	
    jmethodID get_class_loader_id = env->GetMethodID(context_cls, "getClassLoader", "()Ljava/lang/ClassLoader;");
    /*如:当异常发生时,清理异常*/
    if (env->ExceptionCheck()) {
        env->ExceptionDescribe();
        env->ExceptionClear();//清除异常
        return;
    }
	
    class_loader = env->CallObjectMethod(context, get_class_loader_id);
    if (env->ExceptionCheck()) {
        env->ExceptionDescribe();
        env->ExceptionClear();
        return;
    }
	// 创建全局变量的引用
    _context = env->NewGlobalRef(context);//context为jni方法的参数,表示java层的类对象
    _class_loader = env->NewGlobalRef (class_loader);
}
//-----------------------------------------------------------------------------
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    Q_UNUSED(reserved);

    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }

    setNativeMethods();

#if defined(QGC_GST_STREAMING)
    // Tell the androidmedia plugin about the Java VM
    gst_amc_jni_set_java_vm(vm);
#endif

 #if !defined(NO_SERIAL_LINK)
    QSerialPort::setNativeMethods();
 #endif

    JoystickAndroid::setNativeMethods();

#if defined(QGC_ENABLE_PAIRING)
    PairingManager::setNativeMethods();
#endif

    return JNI_VERSION_1_6;
}
//-----------------------------------------------------------------------------
/**
 * @brief Starts the application
 * 启动应用程序
 * @return exit code, 0 for normal exit and !=0 for error cases 
 * 0表示正常退出!=0表示错误情况
 */

int main(int argc, char *argv[])
{

    //-- Record boot time
    QGC::initTimer();
    // install the message handler
    AppMessages::installHandler();
	// qRegisterMetaType 调试的警告为静音输出
    qRegisterMetaType<QSerialPort::SerialPortError>();
    qRegisterMetaType<QBluetoothSocket::SocketError>();
    qRegisterMetaType<QBluetoothServiceInfo>();
    qRegisterMetaType<QAbstractSocket::SocketError>();
    qRegisterMetaType<QGCSerialPortInfo>();

    QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
    QGCApplication* app = new QGCApplication(argc, argv, runUnitTests);// 下一步需要查看的信息
    Q_CHECK_PTR(app);
    if(app->isErrorState()) {
        app->exec();
        return -1;
    }
	// 设置图标
    QApplication::setWindowIcon(QIcon(":/res/resources/icons/qgroundcontrol.ico"));
    app->_initCommon();// 下一步需要查看的信息
    //-- Initialize Cache System
    getQGCMapEngine()->init();// 下一步需要查看的信息
    if (!app->_initForNormalAppBoot()) {
        return -1;
    }
	。。。。。。
    return exitCode;
}

2.QGCApplication:app->_initCommon()

QGCApplication.cpp:此文件在qgroundcontrol-master\src\QGCApplication.cc

重点字段
_toolbox = new QGCToolbox(this); -> _toolbox->setChildToolboxes();
qmlRegisterUncreatableType (kQGCVehicle, 1, 0, “Vehicle”, kRefOnly);

初始化,全局方法,语言设置

QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
    : QApplication          (argc, argv)
    , _runningUnitTests     (unitTesting)
{
    _app = this;
    _msecsElapsedTime.start();
	。。。。。。
    // Set application information
    setApplicationName(applicationName);
    setOrganizationName(QGC_ORG_NAME);
    setOrganizationDomain(QGC_ORG_DOMAIN);

    this->setApplicationVersion(QString(GIT_VERSION));

    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
    QSettings settings;
    qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();

    settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);

    // We need to set language as early as possible prior to loading on JSON files.
    setLanguage();

    _toolbox = new QGCToolbox(this);
    _toolbox->setChildToolboxes();
    。。。。。。
    _checkForNewVersion();
}
void QGCApplication::_initCommon()
{
    static const char* kRefOnly         = "Reference only";
    static const char* kQGroundControl  = "QGroundControl";
    static const char* kQGCControllers  = "QGroundControl.Controllers";
    static const char* kQGCVehicle      = "QGroundControl.Vehicle";
    static const char* kQGCTemplates    = "QGroundControl.Templates";

    QSettings settings;

    // Register our Qml objects

    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");

    qmlRegisterUncreatableType<Vehicle>                 (kQGCVehicle,                       1, 0, "Vehicle",                    kRefOnly);
    qmlRegisterUncreatableType<MissionManager>          (kQGCVehicle,                       1, 0, "MissionManager",             kRefOnly);
    qmlRegisterUncreatableType<ParameterManager>        (kQGCVehicle,                       1, 0, "ParameterManager",           kRefOnly);
	。。。。。。
}
bool QGCApplication::_initForNormalAppBoot()
{
    QSettings settings;

    _qmlAppEngine = toolbox()->corePlugin()->createQmlApplicationEngine(this);
    toolbox()->corePlugin()->createRootWindow(_qmlAppEngine);

    // Image provider for PX4 Flow
    QQuickImageProvider* pImgProvider = dynamic_cast<QQuickImageProvider*>(qgcApp()->toolbox()->imageProvider());
    _qmlAppEngine->addImageProvider(QStringLiteral("QGCImages"), pImgProvider);

    QQuickWindow* rootWindow = (QQuickWindow*)qgcApp()->mainRootWindow();

    if (rootWindow) {
        rootWindow->scheduleRenderJob (new FinishVideoInitialization (toolbox()->videoManager()),
                QQuickWindow::BeforeSynchronizingStage);
    }

    // Safe to show popup error messages now that main window is created
    UASMessageHandler* msgHandler = qgcApp()->toolbox()->uasMessageHandler();
    if (msgHandler) {
        msgHandler->showErrorsInToolbar();
    }

    // Now that main window is up check for lost log files
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
    emit checkForLostLogFiles();

    // Load known link configurations
    toolbox()->linkManager()->loadLinkConfigurationList();

    // Probe for joysticks
    toolbox()->joystickManager()->init();

    if (_settingsUpgraded) {
        showAppMessage(QString(tr("The format for %1 saved settings has been modified. "
                    "Your saved settings have been reset to defaults.")).arg(applicationName()));
    }

    // Connect links with flag AutoconnectLink
    toolbox()->linkManager()->startAutoConnectedLinks();

    if (getQGCMapEngine()->wasCacheReset()) {
        showAppMessage(tr("The Offline Map Cache database has been upgraded. "
                    "Your old map cache sets have been reset."));
    }

    settings.sync();
    return true;
}

3. QGCMapEngine.cpp:getQGCMapEngine()->init()

此文件在qgroundcontrol-master\src\QtLocationPlugin\QGCMapEngine.cpp

单例模式

//-----------------------------------------------------------------------------
// Singleton
static QGCMapEngine* kMapEngine = nullptr;
QGCMapEngine*
getQGCMapEngine()
{
    if(!kMapEngine)
        kMapEngine = new QGCMapEngine();
    return kMapEngine;
}

//-----------------------------------------------------------------------------
QGCMapEngine::~QGCMapEngine()
{
    _worker.quit();
    _worker.wait();
    delete _urlFactory;
    _urlFactory = nullptr;
}

//-----------------------------------------------------------------------------
void
QGCMapEngine::init()
{
    //-- Delete old style caches (if present)
    _wipeOldCaches();
    //-- Figure out cache path
    QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)      + QLatin1String("/QGCMapCache" CACHE_PATH_VERSION);
    if(!QDir::root().mkpath(cacheDir)) {
        qWarning() << "Could not create mapping disk cache directory: " << cacheDir;
        cacheDir = QDir::homePath() + QStringLiteral("/.qgcmapscache/");
        if(!QDir::root().mkpath(cacheDir)) {
            qWarning() << "Could not create mapping disk cache directory: " << cacheDir;
            cacheDir.clear();
        }
    }
    _cachePath = cacheDir;
    if(!_cachePath.isEmpty()) {
        _cacheFile = kDbFileName;
        _worker.setDatabaseFile(_cachePath + "/" + _cacheFile);
        qDebug() << "Map Cache in:" << _cachePath << "/" << _cacheFile;
    } else {
        qCritical() << "Could not find suitable map cache directory.";
    }
    QGCMapTask* task = new QGCMapTask(QGCMapTask::taskInit);
    _worker.enqueueTask(task);
}

4. QGCApplication:qmlRegisterUncreatableType()

QGCApplication.cpp:此文件在qgroundcontrol-master\src\QGCApplication.cc
qmlRegisterUncreatableType()将c++类(派生自QObject)注册到QML系统(注册为非实例化类型)。但是我们不能在qml文件(ui界面文件)中为类创建对象,我们需要在qml文件中使用属性枚举方式来调用。


█ 相关资料

提示:这里是参考的相关文章

  1. 2018-03-08 QGC 连接功能 底层执行逻辑_/* */-CSDN博客
  2. 2019-01-15 QT Qml 的qmlRegisterUncreatableType()函数_小马哔哔-CSDN博客
  3. 2018-03-04 QGroundControl 开发人员指南_/* */-CSDN博客_qgroundcontrol
  4. 2019-05-11 Qt学习笔记:多语言文件.qm的生成和使用_chase_hung的博客-CSDN博客:生成ts文件,修改ts文件,生成qm文件,加载qm语言包

█ 免责声明

博主分享的所有文章内容,部分参考网上教程,引用大神高论,部分亲身实践,记下笔录,内容可能存在诸多不实之处,还望海涵,本内容仅供学习研究使用,切勿用于商业用途,若您是部分内容的作者,不喜欢此内容被分享出来,可联系博主说明相关情况通知删除,感谢您的理解与支持!

提示:转载请注明出处:
https://blog.csdn.net/ljb568838953/article/details/112860105

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值