Qt / C ++ / Android - 如何以编程方式安装.APK文件?

时间:2016-02-02 22:59:09

标签: android c++ qt java-native-interface apk

我正在我的应用程序中实现自己的自动更新程序。我能够成功地将新版本的.apk文件下载到SD卡上的 / Download 文件夹中,但我无法弄清楚如何打开/运行该文件,以便用户随新安装对话框一起提供。

我唯一想出的就是:

QString downloadedAPK = "/storage/emulated/0/Download/latest.apk"; // Not hardcoded, but wrote it here this way for simplicity
QDesktopServices::openUrl(QUrl(downloadedAPK));

调试器输出:

D/Instrumentation(26418): checkStartActivityResult  :Intent { act=android.intent.action.VIEW dat=file:///storage/emulated/0/Download/latest.apk }
D/Instrumentation(26418): checkStartActivityResult  inent is instance of inent:
W/System.err(26418): android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=file:///storage/emulated/0/Download/latest.apk }
W/System.err(26418):    at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1660)
W/System.err(26418):    at android.app.Instrumentation.execStartActivity(Instrumentation.java:1430)
W/System.err(26418):    at android.app.Activity.startActivityForResult(Activity.java:3532)
W/System.err(26418):    at android.app.Activity.startActivityForResult(Activity.java:3493)
W/System.err(26418):    at android.app.Activity.startActivity(Activity.java:3735)
W/System.err(26418):    at android.app.Activity.startActivity(Activity.java:3703)
W/System.err(26418):    at org.qtproject.qt5.android.QtNative.openURL(QtNative.java:110)
W/System.err(26418):    at dalvik.system.NativeStart.run(Native Method)

我到处寻找但从未找到任何关于从Qt打开APK的内容。我发现的唯一一个是使用JNI的solutoin(我不想使用它,因为用C ++做它更简单,因为我对整个C ++ / JNI事物没有经验)而且它没有很好的记录,所以我不知道如何使它工作。

那么,打开下载的apk最简单的方法是什么?

修改

我已经按照Tumbus的回答,但由于一些编译错误,我不得不对他的JNI代码进行一些修改,如下所示:

void Updater::InstallApp(const QString &appPackageName)
{
    qDebug() << "[+] APP: " << appPackageName; // Which is the string ("/storage/emulated/0/Download/latest.apk")
    QAndroidJniObject app = QAndroidJniObject::fromString(appPackageName);
    QAndroidJniObject::callStaticMethod<jint>("AndroidIntentLauncher",
                                       "installApp",
                                       "(Ljava/lang/String;)I",
                                       app.object<jstring>());
}

当我在我的Android设备上运行我的应用程序时,它从我的服务器中提取最新的.apk文件,然后没有任何反应。为什么? (到目前为止,我还没有在AndroidManifest.xml上做过任何更改。)

1 个答案:

答案 0 :(得分:3)

您必须制作自定义意图才能安装APK。有关详细信息,请参阅this question

我担心这种特定于平台的想法必须要求调用特定于平台的API。好消息是Qt框架简化了JNI的总结,你可以在Android项目中包含一个Java类。因此,我将创建自己的从Qt调用的静态java函数。

实施例

Java类

package io.novikov.androidintentlauncher;

import org.qtproject.qt5.android.QtNative;

import java.lang.String;
import java.io.File;
import android.content.Intent;
import android.util.Log;
import android.net.Uri;
import android.content.ContentValues;
import android.content.Context;

public class AndroidIntentLauncher
{
    protected AndroidIntentLauncher()
    {
    }

    public static int installApp(String appPackageName) {
        if (QtNative.activity() == null)
            return -1;
        try {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.fromFile(new File(appPackageName)), 
                                               "application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            QtNative.activity().startActivity(intent);
            return 0;
        } catch (android.content.ActivityNotFoundException anfe) {
            return -3;
        }
    }

}

请注意, startActivity()应该从* QtNative.activity()调用为方法。我们必须根据传统规则维护java的特殊目录结构。该示例位于下面的 Makefile 部分。

JNI

调用此方法的C ++代码有点棘手。

const static char* MY_JAVA_CLASS = "io/novikov/androidintentlauncher/AndroidIntentLauncher";


static void InstallApp(const QString &appPackageName) {
        QAndroidJniObject jsText = QAndroidJniObject::fromString(appPackageName);
        QAndroidJniObject::callStaticMethod<jint>(MY_JAVA_CLASS,
                                           "installApp",
                                           "(Ljava/lang/String;)I",
                                           jsText.object<jstring>());
   }

字符串文字&#34;(Ljava / lang / String;)I&#34;是java方法的签名。 Java类的名称必须是完整的表单&#34; my / domain / my_app / MyClass&#34;

生成文件

最后一个挑战是将java代码正确包含在项目中。在 .pro 文件的相应片段下面。

android {
    QT += androidextras
    OTHER_FILES += android_src/src/io/novikov/androidintentlauncher/AndroidIntentLauncher.java
    ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android_src
}

QMake为此作业提供了一个特殊变量 ANDROID_PACKAGE_SOURCE_DIR 。 Java源必须位于 ANDROID_PACKAGE_SOURCE_DIR / src / my / domain 目录中。 另外,不要忘记将java源添加到OTHER_FILES并包含 androidextras QT选项。