【问题标题】:Auto download and install apk in android O在android O中自动下载并安装apk
【发布时间】:2018-03-23 09:28:52
【问题描述】:

我正在尝试使用文件提供程序在我的应用程序中实现自动下载和安装 apk。对于低于 Oreo 的 android 版本,它可以正常工作,但在 android 8.0 及更高版本的设备上运行应用程序时出现问题。在牛轧糖之前它可以正常工作,但不适用于奥利奥。从服务器下载 apk 工作正常,但无法安装新的 apk。 这是我的代码。

File file, folder;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);
    Utility.checkPermissionCamera(Main2Activity.this);
    Utility.checkPermissionInstall(Main2Activity.this);
        String fileName = "AutoDownloadApplication.apk";
        folder = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString());
        Logger.e("FOLDER", " " + folder);
        file = new File(folder.getAbsolutePath(), fileName);
        Log.e("File ", "" + file);

        final Uri uri = FileProvider.getUriForFile(Main2Activity.this, BuildConfig.APPLICATION_ID + ".provider", file);
        Logger.e("Check URI ", "" + uri);
    if (Utility.checkPermissionCamera(Main2Activity.this)) {
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(StaticConstant.DOWNLOADAPK));
        request.setDescription("Downloading New Apk");
        request.setTitle(Main2Activity.this.getString(R.string.app_name));
        //set destination
        request.setDestinationInExternalFilesDir(Main2Activity.this, BuildConfig.APPLICATION_ID + ".provider", fileName);

        // get download service and enqueue file

            final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
            final long downloadId = manager.enqueue(request);
            //set BroadcastReceiver to install app when .apk is downloaded
            BroadcastReceiver onComplete = new BroadcastReceiver() {
                public void onReceive(Context ctxt, Intent intent) {

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
                    intent.setData(uri);
                    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    startActivity(intent);
                } else {
                    Intent install = new Intent(Intent.ACTION_VIEW);
                    install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                    install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    install.setDataAndType(uri,
                            manager.getMimeTypeForDownloadedFile(downloadId));
                    startActivity(install);
                }
                    unregisterReceiver(this);
                }
            };
            //register receiver for when .apk download is compete
            registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
        }
}

清单文件:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="MyPackageName">

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

<permission
    android:name="android.permission.REQUEST_INSTALL_PACKAGES"
    android:protectionLevel="normal" />

<uses-permission
    android:name="android.permission.REQUEST_DELETE_PACKAGES"
    tools:ignore="ProtectedPermissions" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="MyPackageName.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>

</application>

这里是 file_paths xml:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="Download" path="Android/data/MyPackageName/files/Download"/>
</paths>

【问题讨论】:

    标签: android android-8.0-oreo


    【解决方案1】:

    请先在Manifest.xml中添加&lt;uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/&gt;权限。

    如果SDK版本等于或大于26,我们应该检查getPackageManager().canRequestPackageInstalls()的结果。

    代码如下:

    private void checkIsAndroidO() {  
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {  
        boolean result = getPackageManager().canRequestPackageInstalls();  
        if (result) {  
          installApk();
        } else {  
          // request the permission 
          ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.REQUEST_INSTALL_PACKAGES}, INSTALL_PACKAGES_REQUESTCODE);  
        }  
      } else {
        installApk();  
      }  
    }
    
    @Override  
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {  
      super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    
      switch (requestCode) {  
        case INSTALL_PACKAGES_REQUESTCODE:  
          if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {  
            installApk();  
          } else {  
            Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);  
            startActivityForResult(intent, GET_UNKNOWN_APP_SOURCES);  
          }  
        break;  
      }  
    }
    
    @Override  
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
      super.onActivityResult(requestCode, resultCode, data); 
    
      switch (requestCode) {  
        case GET_UNKNOWN_APP_SOURCES:  
          checkIsAndroidO();  
          break;  
    
        default:  
          break;  
      }  
    }
    

    希望对你有帮助。

    【讨论】:

      【解决方案2】:

      您可以从以下文章中详细了解 FileProvider 实现的工作原理:

      https://inthecheesefactory.com/blog/how-to-share-access-to-file-with-fileprovider-on-android-nougat/en

      【讨论】:

        【解决方案3】:

        JohnWatsonDev 的解决方案完美运行! 只需要做一件事,直接调用您的应用即可:

        intent.setData(Uri.parse("package:" + getPackageName()));
        

        通话前

        startActivityForResult(intent, GET_UNKNOWN_APP_SOURCES);
        

        【讨论】: