FileProvider:安装APK。解析包时出现错误。

3
我有一个APK文件在/storage/emulated/0/Download/app-debug.apk。我想使用FileProvider安装此文件,因为我正在使用Android N。
当我尝试启动意图时,logcat不会显示任何错误,但我在我的手机上收到以下消息:
“解析包时出现问题。”
我做错了什么?
MainActivity
public class MainActivity extends AppCompatActivity {

Button btnStartIntent;
String strRootPathInternalStorage = Environment.getExternalStorageDirectory().toString();       //-> /storage/emulated/0     //-> /storage/emulated/0/Download/
String strApkToInstall = "app-debug.apk";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

/*
        --> Booooh bad way! <--
        StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        StrictMode.setVmPolicy(builder.build());
*/

    btnStartIntent = (Button)findViewById(R.id.btnStartIntent);
    btnStartIntent.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            installApk(MainActivity.this, strRootPathInternalStorage+"/Download/"+strApkToInstall);
        }
    });
}

public static void installApk(Context context, String apkPath) {
    if (context == null || TextUtils.isEmpty(apkPath)) {
        return;
    }


    File file = new File(apkPath);
    Intent intent = new Intent(Intent.ACTION_VIEW);

    if (Build.VERSION.SDK_INT >= 24) {
        //provider authorities
        Uri apkUri = FileProvider.getUriForFile(context, "com.spicysoftware.test.provider", file);
        //Granting Temporary Permissions to a URI
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
    } else {
        intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
    }

    context.startActivity(intent);
}

}

清单

<?xml version="1.0" encoding="utf-8"?>

<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">


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

    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

provider_paths.xml

    <?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="Download" path="Download" />
</paths>

可能是因为你尝试安装的apk不适用于Android Nougat。请检查compileSDk、TargetSDK和minSDK以获取更多信息。 - Deepak kaku
感谢您的快速回复。compileSDK是24,TargetSDK是24,minSDK也是24。所以这不应该是问题。 - MSeiz5
尝试在您的设备上使用文件浏览器应用程序进行安装。 - greenapps
https://commonsware.com/blog/2017/10/31/android-studio-3p0-flag-test-only.html - greenapps
那不是我的问题的解决方案。如果我想使用文件浏览器应用程序,我不会尝试以编程方式执行它。因此,我认为唯一可接受的方法是通过Strictmode - VmPolicy来实现。 - MSeiz5
2个回答

9

对于遇到相同问题的人。

我不得不使用getExternalFilesDir()方法来精确指定文件夹。

MainActivity

public class MainActivity extends AppCompatActivity {

    Button btnStartIntentFileProvider;
    String strRootPathInternalStorage = Environment.getExternalStorageDirectory().toString();         
    String strApkToInstall = "app-debug.apk";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnStartIntentFileProvider = (Button)findViewById(R.id.btnFileProvider);
        btnStartIntentFileProvider.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {

                File fileApkToInstall = new File(getExternalFilesDir("Download"), strApkToInstall);  

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    Uri apkUri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", fileApkToInstall);
                    Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
                    intent.setData(apkUri);
                    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    MainActivity.this.startActivity(intent);
                } else {
                    Uri apkUri = Uri.fromFile(fileApkToInstall);
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    MainActivity.this.startActivity(intent);
                }
            }
        });
    }
}

清单文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.spicysoftware.test">
    <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">
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.spicysoftware.test.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

provider_paths (in XML Folder)

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="Download" path="Download/" />
</paths>

你具体改了什么? - Jacob Sánchez
1
这个解决方案对我没用,但当我在provider_paths.xml中将这个:<external-files-path name="Download" path="Download/" /> 改成了这个:<external-path name="external" path="." /> 它就已经起作用了! - Abigail La'Fay

0

经过研究和尝试,这是我在Android SDK 31上的工作代码:

1- 创建了FileProvider并将其放置在res/xml中,并命名为“provider_paths”:

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

2-清单:

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

在主活动中,我有一个从服务器下载应用的函数,会接收文件并启动安装意图来安装新版本:
void checkUpdate()
    {
        try{
            //check if file exists delete it
            File file=new File("/sdcard/Download/app-debug.apk");
            if(file.exists())
            {
                file.delete();
                Log.d(TAG,"Update file exists, Deleting it!");
            }
            int versionCode = BuildConfig.VERSION_CODE;
            String url="https://Website.com/index.php?version="+versionCode;
            Log.d(TAG,url);
            OkHttpClient client = new OkHttpClient.Builder()
                    .connectTimeout(20, TimeUnit.SECONDS)
                    .writeTimeout(20, TimeUnit.SECONDS)
                    .readTimeout(20, TimeUnit.SECONDS)
                    .build();
            Request request = new Request.Builder()
                    .url(url)
                    .build();
            Response response = null;
            response = client.newCall(request).execute();
            if(response.code()!=200)
            {
                runOnUiThread(() -> Toast.makeText(getApplicationContext(),"No update found",Toast.LENGTH_LONG).show());
                return;
            }
            //downloading the file

            InputStream is = response.body().byteStream();

            BufferedInputStream input = new BufferedInputStream(is);

            String strRootPathInternalStorage = Environment.getExternalStorageDirectory().toString();
            String path = strRootPathInternalStorage+"/"+Environment.DIRECTORY_DOWNLOADS+"/app-debug.apk";
            Log.d(TAG,path);
            ResponseBody body = response.body();
            long contentLength = body.contentLength();
            Log.d(TAG,""+contentLength);
            BufferedSource source = body.source();

            BufferedSink sink = null;
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                sink = Okio.buffer(Okio.sink(Paths.get(path)));
            }else
            {
                Log.e(TAG,"Check this");
            }
            Buffer sinkBuffer = sink.buffer();

            long totalBytesRead = 0;
            int bufferSize = 8 * 1024;
            for (long bytesRead; (bytesRead = source.read(sinkBuffer, bufferSize)) != -1; )
            {
                sink.emit();
                totalBytesRead += bytesRead;
            }
            sink.flush();
            sink.close();
            source.close();



            //finished downloading the file  start installing
            runOnUiThread(() -> {
                try {
                    File fileApkToInstall = new File("/sdcard/Download/app-debug.apk");

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                        Uri apkUri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", fileApkToInstall);
                        Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
                        intent.setData(apkUri);
                        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                        MainActivity.this.startActivity(intent);
                    } else {
                        Uri apkUri = Uri.fromFile(fileApkToInstall);
                        Intent intent = new Intent(Intent.ACTION_VIEW);
                        intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        MainActivity.this.startActivity(intent);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });



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

4- 网站更新逻辑是从应用程序获取当前版本,并在服务器上检查是否为最新版本,如果是,则返回404或类似的内容,如果不是,则返回apk文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接