Android 解析包時出現問題 的解決方案(應用檢查更新)

山海自有歸期發表於2022-05-14

問題描述
我們在進行Android開發的時候,一般都會在應用裡檢測有沒有更新,並且從網上下載最新的版本包,覆蓋本地的舊版本。在我的專案中,出現了一個問題,就是當安裝包下載到本地的時候,產生了“解析包時出現問題”這樣的錯誤,導致無法安裝相應的新版本。同時,值得一提的是,這個問題只有在我們用程式碼呼叫去進行安裝的時候才會發生,我們手動去用安裝包覆蓋本地的舊包則沒有出現問題。經過反覆的測試,發現這個問題只會發生在Android 7.0版本的裝置上,在較低版本的裝置上則無這個問題。


這也是在網上比較通用的方案,就是在打包的時候我們使用了V2簽名導致無法進行安裝。
Android 7.0 引入一項新的應用簽名方案 APK Signature Scheme v2,它能提供更快的應用安裝時間和更多針對未授權 APK 檔案更改的保護。在預設情況下,Android Studio 2.2 和 Android Plugin for Gradle 2.2 會使用 APK Signature Scheme v2 和傳統簽名方案來簽署應用。
這項新方案並非強制性的,如果應用在使用 APK Signature Scheme v2 時不能正確開發,可以停用這項新方案。禁用過程會導致 Android Studio 2.2 和 Android Plugin for Gradle 2.2 僅使用傳統簽名方案來簽署應用。要僅用傳統方案簽署,開啟模組級 build.gradle 檔案,然後將行 v2SigningEnabled false 新增到版本簽名配置中:

android {
…
defaultConfig { … }
signingConfigs {
release {
storeFile file(“myreleasekey.keystore”)
storePassword “password”
keyAlias “MyReleaseKey”
keyPassword “password”
v2SigningEnabled false
}
}
}

  

根據官方文件,就是在我們的gradle檔案裡的相應位置新增這行程式碼

v2SigningEnabled false

解決方案2
但是,在我的情境中,即使使用了方案一,仍然在更新包下載完成後顯示了“解析包時出現問題”這樣的字樣,導致下載更新流程出錯。
這個時候,就有可能是另外一種情況:當我們在7.0上使用系統服務去下載apk並且試圖安裝時,有可能由於許可權問題導致安裝失敗。
每個Android版本的釋出,對於安全性問題的要求越來越高,也為Android程式設計師增加了額外的工作量。Android6.0引入動態許可權控制(Runtime Permissions),Android7.0引入私有目錄被限制訪問和StrictMode API 。私有目錄被限制訪問是指在Android7.0中為了提高應用的安全性,在7.0上應用私有目錄將被限制訪問,這與iOS的沙盒機制類似。StrictMode API是指禁止向你的應用外公開 file:// URI。 如果一項包含檔案 file:// URI型別 的 Intent 離開你的應用,則會報出異常。
以下是我原來在7.0上出問題的程式碼:

Intent install = new Intent(Intent.ACTION_VIEW);
install.setDataAndType(Uri.fromFile(new File(fileName)), "application/vnd.android.package-archive");
install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(install);

從程式碼中可以看出,Uri.fromFile導致我們在7.0上出現了問題,它其實就是生成一個file://URL。這就是為什麼在下載完成後,呼叫這段程式碼去安裝的時候出錯,因為一旦我們通過這種辦法開啟系統安裝器,就認為file:// URI型別的 Intent 離開我的應用,這樣程式就會發生異常;而我們手動去把安裝包覆蓋原來的舊包則沒有問題。

解決方案將使用FileProvider,它的步驟是:

第一步:
在AndroidManifest.xml中註冊provider,provider可以嚮應用外提供資料。

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

  

其中com.dafangya.app.pro是我的應用包名。

第二步:

  • 在res/xml/file_paths.xml建立檔案。 內容為:
  • <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <paths>
            <external-path path="" name="download"/>
        </paths>
    </resources>
    
  • 第三步:
  • String fileName = cursor.getString(fileNameIdx);//承接我的程式碼,filename指獲取到了我的檔案相應路徑
    if (fileName != null) {
        if (fileName.endsWith(".apk")) {
            if(Build.VERSION.SDK_INT>=24) {//判讀版本是否在7.0以上
                File file= new File(fileName);
                Uri apkUri = FileProvider.getUriForFile(context, "com.dafangya.app.pro.fileprovider", file);//在AndroidManifest中的android:authorities值
                Intent install = new Intent(Intent.ACTION_VIEW);
                install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//新增這一句表示對目標應用臨時授權該Uri所代表的檔案
                install.setDataAndType(apkUri, "application/vnd.android.package-archive");
                context.startActivity(install);
            } else{
                Intent install = new Intent(Intent.ACTION_VIEW);
                install.setDataAndType(Uri.fromFile(new File(fileName)), "application/vnd.android.package-archive");
                install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(install);
            }
        }
    }
    

      

    因為對於7.0以下的裝置,還是走以前的邏輯,所以上面的程式碼進行分情況討論,6.0及其以下的裝置還是走以前的邏輯。

      • 第四步
        按照大部分教程來說,第四步其實是不存在的,但是在我的專案中,執行到第三步中的程式碼時,還是報錯了,錯誤如下:
      • java.lang.RuntimeException: Error receiving broadcast Intent { act=android.intent.action.DOWNLOAD_COMPLETE flg=0x10 pkg=com.dafangya.app.pro (has extras) } in com.b.b.a.a.e$1@457cfd2
            at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:1229)
            at android.os.Handler.handleCallback(Handler.java:755)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:156)
            at android.app.ActivityThread.main(ActivityThread.java:6524)
            at java.lang.reflect.Method.invoke(Method.java)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:941)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:831)
        Caused by: java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/com.android.providers.downloads/cache/product_other_V1.4.8ceshi.apk
            at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:678)
            at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:377)
            at com.example.xh.toolsdk.umeng.Downloads$1.onReceive(Downloads.java:95)
            at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:1219)
            ... 7 more
        

          

相關文章