如何在 Android 10 中以编程方式安装任何 Android 应用

Posted

技术标签:

【中文标题】如何在 Android 10 中以编程方式安装任何 Android 应用【英文标题】:How to install any Android app programmatically in Android 10 【发布时间】:2021-12-01 13:57:01 【问题描述】:

在 Android 9 和 10 中 我在使用文件路径 apk 文件以编程方式在 android Studio 中安装应用程序时遇到问题。 下面显示我尝试过的内容..

Intent intent = new Intent("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.setDataAndType(Uri.parse("content://"+Environment.getExternalStorageDirectory() + "/download/" + "app-release.apk"), "application/vnd.android.package-archive");
startActivity(intent);

我还在清单文件中添加了所需的权限。

当我运行它时,它给了我解析包时出现问题错误。

【问题讨论】:

请edit您的问题并发布整个堆栈跟踪。 我在 logcat 中没有收到任何异常或错误。 【参考方案1】:

如果您想在 Android 10 中以编程方式安装应用程序,您需要授予为您的应用程序安装应用程序的权限

第 1 步:在 Manifest 中授予权限

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

第 2 步:在 Android Manifest 中编写提供程序

 <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/provider_paths" />
    </provider>

步骤 4:在名为 provider_paths.xml 的 XML 文件夹中创建文件并写入

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="files_root"
        path="Android/data/$applicationId" />
    <external-path
        name="external_files"
        path="." />
</paths>

第 4 步:为 apk 安装和存储权限授予运行时权限

    //installtion permission

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 
                if (!getPackageManager().canRequestPackageInstalls()) 
                    startActivityForResult(new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(Uri.parse(String.format("package:%s", getPackageName()))), 1234);
                 else 
                
    

    //Storage Permission

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) 
                ActivityCompat.requestPermissions(this, new String[]Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE, 1);
            

            if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) 
                ActivityCompat.requestPermissions(this, new String[]Manifest.permission.WRITE_EXTERNAL_STORAGE, 1);
    

第五步:调用安装apk函数

 void installAPK()

    String PATH = Environment.getExternalStorageDirectory() + "/" + "apkname.apk";
    File file = new File(PATH);
    if(file.exists()) 
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(uriFromFile(getApplicationContext(), new File(PATH)), "application/vnd.android.package-archive");
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        try 
            getApplicationContext().startActivity(intent);
         catch (ActivityNotFoundException e) 
            e.printStackTrace();
            Log.e("TAG", "Error in opening the file!");
        
    else
        Toast.makeText(getApplicationContext(),"installing",Toast.LENGTH_LONG).show();
    

Uri uriFromFile(Context context, File file) 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) 
        return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file);
     else 
        return Uri.fromFile(file);
    

【讨论】:

正是我需要这个 ACTION_VIEW 在 android 10 中已弃用【参考方案2】:

首先,您的Uri 在每个 Android 版本上都无效,除了设备制造商为您入侵某些设备的少数设备。

其次,默认情况下,您和软件包安装程序进程都无法访问 Android 10 上的外部存储。

第三,通过ACTION_VIEWACTION_INSTALL_PACKAGE 安装应用程序在Android 10 上已弃用。

适用于 Android 10 及更高版本(也适用于 Android 5.0 及更高版本)的解决方案是use PackageInstaller

This sample app 演示了如何使用它。关键部分在MainMotor

/*
  Copyright (c) 2019 CommonsWare, LLC

  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  use this file except in compliance with the License. You may obtain   a copy
  of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
  by applicable law or agreed to in writing, software distributed under the
  License is distributed on an "AS IS" BASIS,   WITHOUT WARRANTIES OR CONDITIONS
  OF ANY KIND, either express or implied. See the License for the specific
  language governing permissions and limitations under the License.

  Covered in detail in the book _Elements of Android Q

  https://commonsware.com/AndroidQ
*/

package com.commonsware.q.appinstaller

import android.app.Application
import android.app.PendingIntent
import android.content.Intent
import android.content.pm.PackageInstaller
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

private const val NAME = "mostly-unused"
private const val PI_INSTALL = 3439

class MainMotor(app: Application) : AndroidViewModel(app) 
  private val installer = app.packageManager.packageInstaller
  private val resolver = app.contentResolver

  fun install(apkUri: Uri) 
    viewModelScope.launch(Dispatchers.Main) 
      installCoroutine(apkUri)
    
  

  private suspend fun installCoroutine(apkUri: Uri) =
    withContext(Dispatchers.IO) 
      resolver.openInputStream(apkUri)?.use  apkStream ->
        val length =
          DocumentFile.fromSingleUri(getApplication(), apkUri)?.length() ?: -1
        val params =
          PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
        val sessionId = installer.createSession(params)
        val session = installer.openSession(sessionId)

        session.openWrite(NAME, 0, length).use  sessionStream ->
          apkStream.copyTo(sessionStream)
          session.fsync(sessionStream)
        

        val intent = Intent(getApplication(), InstallReceiver::class.java)
        val pi = PendingIntent.getBroadcast(
          getApplication(),
          PI_INSTALL,
          intent,
          PendingIntent.FLAG_UPDATE_CURRENT
        )

        session.commit(pi.intentSender)
        session.close()
      
    

在这里,install() 被赋予了来自ACTION_OPEN_DOCUMENTUri。在协程内部,我创建了一个 PackageInstaller 会话,打开会话,将 APK 内容复制到会话提供的流中,然后提交并关闭会话。

【讨论】:

@Ak23:抱歉,我没有可用的 PackageInstaller 示例 Java 代码。 安装完成后自动启动应用有什么想法吗? @Ak23 有很多方法可以解决,具体取决于您的需要。您可以使用PackageManagergetLaunchIntentForPackage(),或者如果您已经知道参数,您只需定义一个新意图。我建议将其作为一个单独的问题查找。 @AbandonedCart 你能解释一下 getLaunchIntentForPackage() 我和@Ak23 有同样的问题吗? @shakil.k 在 Google 上快速搜索 getLaunchIntentForPackage 会找到 Android documentation。这将需要您进行一些投资。

以上是关于如何在 Android 10 中以编程方式安装任何 Android 应用的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android Studio 中以编程方式上传和下载 Google Drive 上的任何文件

如何在 Android 中以编程方式更改按钮大小?

如何在android中以编程方式设置ImageView的高度宽度?

如何在 Android 中以编程方式启用禁用 GPS? [复制]

如何在android中以编程方式启用位置访问?

如何在android中以编程方式创建多个列表