我如何在我的应用程序中共享 apk 文件(发送应用程序本身)

How can i share apk file in my app (send app itself)

我正在尝试使用此代码将我的应用程序 apk 文件发送到另一台设备:

public static void sendAppItself(Activity paramActivity) throws IOException {
    PackageManager pm = paramActivity.getPackageManager();
    ApplicationInfo appInfo;
    try {
        appInfo = pm.getApplicationInfo(paramActivity.getPackageName(),
                PackageManager.GET_META_DATA);
        Intent sendBt = new Intent(Intent.ACTION_SEND);
        sendBt.setType("*/*");
        sendBt.putExtra(Intent.EXTRA_STREAM,
                Uri.parse("file://" + appInfo.publicSourceDir));

        paramActivity.startActivity(Intent.createChooser(sendBt,
                "Share it using"));
    } catch (PackageManager.NameNotFoundException e1) {
        e1.printStackTrace();
    }
}

此代码运行良好。

但是用这段代码共享的apk文件的名字是base.apk

如何更改?

这只是因为它是由 base.apk 名称保存的。 要根据您的需要共享它,您只需将此文件复制到另一个目录路径并在那里重命名。然后使用新文件进行共享。

数据文件夹中的这个文件路径[file:///data/app/com.yourapppackagename/base.apk]只有读取权限,所以你不能重命名那里的.apk文件。

将文件从源目录复制到新目录。 复制时重命名文件并共享复制的文件。 共享完成后删除临时文件。

 private void shareApplication() {
    ApplicationInfo app = getApplicationContext().getApplicationInfo();
    String filePath = app.sourceDir;

    Intent intent = new Intent(Intent.ACTION_SEND);

    // MIME of .apk is "application/vnd.android.package-archive".
    // but Bluetooth does not accept this. Let's use "*/*" instead.
    intent.setType("*/*");

    // Append file and send Intent
    File originalApk = new File(filePath);

    try {
        //Make new directory in new location
        File tempFile = new File(getExternalCacheDir() + "/ExtractedApk");
        //If directory doesn't exists create new
        if (!tempFile.isDirectory())
            if (!tempFile.mkdirs())
                return;
        //Get application's name and convert to lowercase
        tempFile = new File(tempFile.getPath() + "/" + getString(app.labelRes).replace(" ","").toLowerCase() + ".apk");
        //If file doesn't exists create new
        if (!tempFile.exists()) {
            if (!tempFile.createNewFile()) {
                return;
            }
        }
        //Copy file to new location
        InputStream in = new FileInputStream(originalApk);
        OutputStream out = new FileOutputStream(tempFile);

        byte[] buf = new byte[1024];
        int len;
        while ((len = in.read(buf)) > 0) {
            out.write(buf, 0, len);
        }
        in.close();
        out.close();
        System.out.println("File copied.");
        //Open share dialog
        intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(tempFile));
        startActivity(Intent.createChooser(intent, "Share app via"));

    } catch (IOException e) {
        e.printStackTrace();
    }
}

Update:此方法不再有效,如果您实施它会抛出异常。由于 android N,如果我们想访问内存中的文件(如 apk 文件),我们应该使用内容提供程序。如需更多信息,请访问此 Guide。尽管复制、重命名和共享复制版本的整个想法仍然有效。

可以使用这个功能,测试api 22和27

    private void shareApplication() {
        ApplicationInfo app = getApplicationContext().getApplicationInfo();
        String filePath = app.sourceDir;

        Intent intent = new Intent(Intent.ACTION_SEND);

        // MIME of .apk is "application/vnd.android.package-archive".
        // but Bluetooth does not accept this. Let's use "*/*" instead.
        intent.setType("*/*");

        // Append file and send Intent
        File originalApk = new File(filePath);

        try {
            //Make new directory in new location=
            File tempFile = new File(getExternalCacheDir() + "/ExtractedApk");
            //If directory doesn't exists create new
            if (!tempFile.isDirectory())
                if (!tempFile.mkdirs())
                    return;
            //Get application's name and convert to lowercase
            tempFile = new File(tempFile.getPath() + "/" + getString(app.labelRes).replace(" ","").toLowerCase() + ".apk");
            //If file doesn't exists create new
            if (!tempFile.exists()) {
                if (!tempFile.createNewFile()) {
                    return;
                }
            }
            //Copy file to new location
            InputStream in = new FileInputStream(originalApk);
            OutputStream out = new FileOutputStream(tempFile);

            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            in.close();
            out.close();
            System.out.println("File copied.");
            //Open share dialog
//          intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(tempFile));
            Uri photoURI = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", tempFile);
//          intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(tempFile));
            intent.putExtra(Intent.EXTRA_STREAM, photoURI);
            startActivity(Intent.createChooser(intent, "Share app via"));

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

如果有人试图从片段生成 apk,他们可能需要更改@sajad 的回答中的几行,如下所示

  1. 替换

    File tempFile = new File(getExternalCacheDir() + "/ExtractedApk");

File tempFile = new File(getActivity().getExternalCacheDir() + "/ExtractedApk");

2.while 正在为以下行导入 BuildConfig

import androidx.multidex.BuildConfig // 不要这样做!!! ,使用您的应用程序 BuildConfig。

并且如果您低于异常

Couldn't find meta-data for provider with authority

  1. 在清单文件中查找提供商信息

然后在清单文件中查找“提供者”的名称和权限,如果是 androidx.core.content.FileProvider,则 替换

Uri photoURI = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", tempFile);

Uri photoURI = FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID + ".fileprovider", tempFile);

2021 Kotlin 之路

首先我们需要设置一个文件提供者 在 AndroidManifest.xml 中创建文件提供程序

        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true"
            >
        <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"
                />
    </provider>

如果您没有 file_path.xml,请在 res/xml 中创建一个(如果不存在,请创建 xml 文件夹) 并在 file_path.xml 添加

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-files-path
            name="apk"
            path="cache/ExtractedApk/" />
</paths>

现在添加分享apk的代码

private fun shareAppAsAPK(context: Context) {
    val app: ApplicationInfo = context.applicationInfo
    val originalApk = app.publicSourceDir
    try {
        //Make new directory in new location
        var tempFile: File = File(App.instance.getExternalCacheDir().toString() + "/ExtractedApk")
        //If directory doesn't exists create new
        if (!tempFile.isDirectory) if (!tempFile.mkdirs()) return
        //rename apk file to app name
        tempFile = File(tempFile.path + "/" + getString(app.labelRes).replace(" ", "") + ".apk")
        //If file doesn't exists create new
        if (!tempFile.exists()) {
            if (!tempFile.createNewFile()) {
                return
            }
        }
        //Copy file to new location
        val inp: InputStream = FileInputStream(originalApk)
        val out: OutputStream = FileOutputStream(tempFile)
        val buf = ByteArray(1024)
        var len: Int
        while (inp.read(buf).also { len = it } > 0) {
            out.write(buf, 0, len)
        }
        inp.close()
        out.close()
        //Open share dialog
        val intent = Intent(Intent.ACTION_SEND)
//MIME type for apk, might not work in bluetooth sahre as it doesn't support apk MIME type

        intent.type = "application/vnd.android.package-archive"
        intent.putExtra(
            Intent.EXTRA_STREAM, FileProvider.getUriForFile(
                context, BuildConfig.APPLICATION_ID + ".fileprovider", File(tempFile.path)
            )
        )
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
        startActivity(intent)
    } catch (e: IOException) {
        e.printStackTrace()
    }
}