Search code examples
c#xamarin.androidandroid-fileprovider

Xamarin Android FileProvider.GetUriForFile System.NullReferenceException


Hi for some time im trying install downloaded .apk file from my website but still get same error and after while still cant figure it out why it happens

 public void OpenFile(byte[] data, string name)
        {
            string directory = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
            string path = directory + name;
            foreach (string file in Directory.GetFiles(directory))
            {
                if (Path.GetExtension(file) == ".apk")
                    File.Delete(file);
            }

            File.WriteAllBytes(path, data);

            Intent intent = new Intent(Intent.ActionView);
            Android.Net.Uri fileUri = null;
            if ((int)Build.VERSION.SdkInt < 23)
                fileUri = Android.Net.Uri.FromFile(new Java.IO.File(path));
            else
                //fileUri = Uri.Parse("content://" + path);
//this is where error occures
                fileUri = FileProvider.GetUriForFile(this, "aaaa", new Java.IO.File(path));

            intent.SetDataAndType(fileUri, "application/vnd.android.package-archive");
            intent.SetFlags(ActivityFlags.GrantReadUriPermission);
            this.StartActivity(intent);
        }
public void InstallApk(string LatestVersion)
        {
            HttpClient client = new HttpClient();

            HttpResponseMessage response = client.GetAsync(BaseUrl + $"mobile/DownloadVersion?version={LatestVersion}").Result;
            
            var _stream = response.Content.ReadAsByteArrayAsync().Result;
            System.Diagnostics.Debug.WriteLine(_stream.Length);
            var fileName = response.Content.Headers.ContentDisposition.FileName;
            OpenFile(_stream, "/com.JakubStegienko.Mobile_app.apk");
        }

and here is how and where im trying to use it

 protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

            DependencyService.Register<IInstallerService, InstallerService>();

            LoadApplication(new App());
            Instance = this;

            InstallApk("1.1");
        }

AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.JakubStegienko.Mobile_app" android:installLocation="preferExternal">
    <uses-sdk android:minSdkVersion="26" android:targetSdkVersion="31" />
    
    <!--Permissions-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
  
  <application android:allowBackup="true" android:label="Mobile App.Android" android:theme="@style/MainTheme" android:icon="@mipmap/icon" android:supportsRtl="true">
        <provider android:name="androidx.core.content.FileProvider" android:authorities="aaaa" android:exported="false" android:grantUriPermissions="true">
        <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" />
    </provider>
    </application>
</manifest>

and provider_paths

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

  <external-path
      name="external_files"
      path="." />
</paths>

enter image description here

i know i miss some little thing but i cant figure it out

fileUri = Uri.Parse("content://" + path);

I also tried creating uri this way but when intent starts give error "while analizing package error occures"

Thanks for any help

Edit

[zygote64] Attempt to remove non-JNI local reference, dumping thread
[monodroid-debug] Trying to initialize the debugger with options: --debugger-agent=transport=dt_socket,loglevel=0,address=127.0.0.1:8880,server=y,embedding=1
[monodroid-gc] GREF GC Threshold: 46080
[zygote64] Attempt to remove non-JNI local reference, dumping thread
Resolved pending breakpoint for 'Xamarin.HotReload.HotReloadAgent.BreakpointCheckpoint()' to D:\a\_work\1\s\HotReload\Source\Xamarin.HotReload.Agent\HotReloadAgent.cs:414 [0x00000].
Loaded assembly: /data/data/com.JakubStegienko.Mobile_app/files/.__override__/System.Net.Http.dll [External]
Loaded assembly: /data/data/com.JakubStegienko.Mobile_app/files/.__override__/System.Xml.dll [External]
Thread started:  #2
Thread started:  #3
Thread started:  #4
Loaded assembly: /data/data/com.JakubStegienko.Mobile_app/files/.__override__/System.Runtime.Serialization.dll [External]
Loaded assembly: /data/data/com.JakubStegienko.Mobile_app/files/.__override__/System.ServiceModel.Internals.dll [External]
[monodroid-assembly] open_from_bundles: failed to load assembly Xamarin.Forms.Platform.Android.AppLinks.dll
Thread started:  #5
[NetworkSecurityConfig] No Network Security Config specified, using platform default
Thread started: <Thread Pool> #6
Thread started: <Thread Pool> #7
Thread started: <Thread Pool> #8
Thread started: <Thread Pool> #9
Thread started: <Thread Pool> #10
Thread started: <Thread Pool> #11
[System.out] (HTTPLog)-Static: isSBSettingEnabled false
[zygote64] Explicit concurrent copying GC freed 1396(211KB) AllocSpace objects, 0(0B) LOS objects, 68% free, 707KB/2MB, paused 461us total 14.112ms
Thread finished: <Thread Pool> #7
Wątek 0x7 zakończył działanie z kodem 0 (0x0).
Thread started: <Thread Pool> #12
Thread finished: <Thread Pool> #6
Thread started: <Thread Pool> #13
Wątek 0x6 zakończył działanie z kodem 0 (0x0).
[zygote64] Background concurrent copying GC freed 3(16KB) AllocSpace objects, 12(1584KB) LOS objects, 64% free, 836KB/2MB, paused 5.548ms total 42.550ms
[zygote64] Background concurrent copying GC freed 3(16KB) AllocSpace objects, 16(2MB) LOS objects, 50% free, 1628KB/3MB, paused 5.864ms total 49.653ms
[zygote64] Explicit concurrent copying GC freed 86(35KB) AllocSpace objects, 10(1320KB) LOS objects, 68% free, 700KB/2MB, paused 685us total 25.114ms
[0:] 20475508
[zygote64] Do partial code cache collection, code=30KB, data=25KB
[zygote64] After code cache collection, code=30KB, data=25KB
[zygote64] Increasing code cache capacity to 128KB
**System.NullReferenceException:** 'Object reference not set to an instance of an object.'

Debugging output while running app

Solution: So after i changed a bit how do i get path for a file it some how works

my quess is that now i get path to specific external path

 string directory = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
 string path = directory + name;

to this

 var path = Path.Combine(Android.App.Application.Context.GetExternalFilesDir("").AbsolutePath, "com.JakubStegienko.Mobile_app.apk");

Thanks to Jianwei Sun - MSFT


Solution

  • You can try to the following steps to install the apk:

    1.Add the provider in the AndroidManifest.xml, such as:

     <application android:allowBackup="true" android:icon="@drawable/androidicon" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme">
        <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>
    </application>
    

    2.Create the provider_paths.xml in the folder named xml below the values folder in the android part.

    <paths >
    
    <external-path
        name="external_files"
        path="." />
    </paths>
    

    3.Use the following code to install the apk:

    //When the apk file is downloaded, put it in the path you set
    var path = Path.Combine(Android.App.Application.Context.GetExternalFilesDir("").AbsolutePath, "com.MyCompany.clamexdroid.apk");
            Java.IO.File file = new Java.IO.File(path);
            Intent install = new Intent(Intent.ActionView);
            Android.Net.Uri apkURI = FileProvider.GetUriForFile(this, this.ApplicationContext.PackageName + ".provider", file);
            install.SetDataAndType(apkURI, "application/vnd.android.package-archive");
            install.AddFlags(ActivityFlags.NewTask);
            install.AddFlags(ActivityFlags.GrantReadUriPermission);
            StartActivity(install);