简体   繁体   中英

NullPointerException when deleting file with Android FileProvider

I am having problems working properly with the Android File API.

Here is what I want to achieve, I am downloading videos from Amazon which should be saved locally after they have been fully downloaded. I am doing this in pretty straighforward manner (code has been shortened and I just show the essential lines):

inputStream = connection.getInputStream();
fileOutputStream = context.openFileOutput("my_video", Context.MODE_PRIVATE);
// Read bytes (and store them) until there is nothing more to read(-1)
do {
    int numread = inputStream.read(buffer);
    if (numread <= 0){
        break;                  
    }
    fileOutputStream.write(buffer, 0, numread);
} while (true);

where inputStream and fileOutputStream are instance variables.

This actually works pretty well if the video gets fully downloaded. In that case, everything is fine and I can access the video locally afterwards.

However, it can happen in the app that the video download gets interrupted and thus needs to be cancelled. If this is the case, I would like to delete the file which is there but obviously not complete.

The code to delete the file is as follows:

FileProvider fileProvider = new FileProvider();
File newFile = new File(context.getFilesDir(), "my_video");     
Uri contentUri = FileProvider.getUriForFile(context, FILE_PROVIDER, newFile);
fileProvider.delete(fileToDelete, null, null);

The last line fileProvider.delete(fileToDelete, null, null); is throwing a NullPointerException , I debugged it and saw that the fileProvider is initialized, so I strongly assume that there is an issue with the URI that I am using to call delete , but I don't know what's wrong with it. Does anyone know how to do a proper delete using the file provider?

Update: I hope this isn't too much of an overkill, I am now posting my whole VideoDownloader class:

public class VideoDownloader {

  private final int TIMEOUT_CONNECTION = 5000;//5sec
  private final int TIMEOUT_SOCKET = 30000;//30sec

  private static final String FILE_PROVIDER = "com.orangewise.fileprovider";

  private Context context;
  private String videoURL;
  private String targetFileName;

  private HttpURLConnection connection;
  private InputStream inputStream;
  private FileOutputStream fileOutputStream;

  private boolean downloadFinished;

  public VideoDownloader(Context context, String videoURL, String targetFileName) {
    this.context = context;
    this.videoURL = videoURL;
    this.targetFileName = targetFileName;
    this.downloadFinished = false;
  }

  public boolean isDownloadFinished(){
    return this.downloadFinished;
  }

  public void startDownload(){
    downloadVideoFile(this.context, this.videoURL, this.targetFileName);    
  }

  private void downloadVideoFile(Context context, String videoURL, String targetFileName) {
    URL url = null;
    try {
        url = new URL(videoURL);

        // Open a connection to that URL.
        connection = (HttpURLConnection) url.openConnection();

        connection.setReadTimeout(TIMEOUT_CONNECTION);
        connection.setConnectTimeout(TIMEOUT_SOCKET);r

        inputStream = connection.getInputStream();
        fileOutputStream = context.openFileOutput(targetFileName, Context.MODE_PRIVATE);

        byte[] buffer = new byte[3 * 1024];
        int counter = 0;

        // Read bytes (and store them) until there is nothing more to read(-1)
        do {
            int numread = inputStream.read(buffer);
            if (numread <= 0){
                break;                  
            }
            fileOutputStream.write(buffer, 0, numread);
        } while (true);
        downloadFinished = true;

        // Clean up
        closeStreams();
    } catch (Exception e) {
        Log.d(Constants.ERROR, "ERROR [" + getClass().getName() + "]: Caught exception (" + e + ") when trying to download video: " + e.getMessage());
    }
  }

  public Uri getUriForFile(){
    return getUriForFile(context, targetFileName);
  }

  private Uri getUriForFile(Context context, String fileName){

    File newFile = new File(context.getFilesDir(), fileName);       
    Uri contentUri = FileProvider.getUriForFile(context, FILE_PROVIDER, newFile);

    return contentUri;
  }

  public void cancel(){
    // 1. cancel the connection
    Log.d(Constants.LOG, "DEBUG [" + getClass().getName() + "]: Cancel connection");

    try {
        connection.disconnect();
        closeStreams();

        if(!isDownloadFinished()){              
            // Remove the file if it has not been fully downloaded
            FileProvider fileProvider = new FileProvider();
            Uri fileToDeleteUri = getUriForFile();
            fileProvider.delete(fileToDeleteUri, null, null); // returns 1 if the delete succeeds; otherwise, 0.
        }
        else{
            Log.d(Constants.LOG, "DEBUG [" + getClass().getName() + "]: Leave the file, it has been completely download");
        }
    } 
    catch (Exception e) {
        Log.d(Constants.ERROR, "Exception ( " + e + " ) caught: " +  e.getMessage() + "; ");
    }
}

private void closeStreams() throws IOException{     
    // Close the streams
    try {           
        fileOutputStream.flush();
        fileOutputStream.close();
        inputStream.close();
    } catch (NullPointerException e) {
        Log.d(Constants.ERROR, "Null pointer exception caught: " +  e.getMessage());
    }
    Log.d(Constants.LOG, "DEBUG [" + getClass().getName() + "]: Clean up performed");
  }
} 

Another update: Here is my stack trace:

07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152): java.lang.NullPointerException
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at android.support.v4.content.FileProvider.delete(FileProvider.java:497)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at com.orangewise.just4kidstv.util.VideoDownloader.cancel(VideoDownloader.java:134)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at com.orangewise.just4kidstv.util.VideoDownloadTask.cancel(VideoDownloadTask.java:20)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at com.orangewise.just4kidstv.activities.VideoPlayerActivity.onStop(VideoPlayerActivity.java:64)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at android.app.Instrumentation.callActivityOnStop(Instrumentation.java:1170)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at android.app.Activity.performStop(Activity.java:3873)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:2623)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:2694)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at android.app.ActivityThread.access$2100(ActivityThread.java:117)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:968)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at android.os.Handler.dispatchMessage(Handler.java:99)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at android.os.Looper.loop(Looper.java:130)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at android.app.ActivityThread.main(ActivityThread.java:3687)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at java.lang.reflect.Method.invokeNative(Native Method)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at java.lang.reflect.Method.invoke(Method.java:507)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
07-23 17:31:29.114: E/com.organgewise.just4kidstv.LOG(6152):    at dalvik.system.NativeStart.main(Native Method)

Examining the source of FileProvider.java of the v4 support library shipped in the sdk extras, we find:

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
   // ContentProvider has already checked granted permissions
   final File file = mStrategy.getFileForUri(uri);   /* line 497 */
   return file.delete() ? 1 : 0;
}

So mStrategy is null.

Searching further, we find that is initialized in only one place:

/**
 * After the FileProvider is instantiated, this method is called to provide the system with
 * information about the provider.
 *
 * @param context A {@link Context} for the current component.
 * @param info A {@link ProviderInfo} for the new provider.
 */
@Override
public void attachInfo(Context context, ProviderInfo info) {
    super.attachInfo(context, info);

    // Sanity check our security
    if (info.exported) {
        throw new SecurityException("Provider must not be exported");
    }
    if (!info.grantUriPermissions) {
        throw new SecurityException("Provider must grant uri permissions");
    }

    mStrategy = getPathStrategy(context, info.authority);
}

So clearly your FileProvider has not been properly set up with a call to this method.

The FileProvider documentation is not entirely clear, but it appears as if you should not simply do "new FileProvider()" but rather should be doing some related setup in your manifest.

@Chris Stratton is right. You should not instantiate it with that constructor. FileProvider is a subclass of ContentProvider. Since you declared it in your AndroidManifest.xml, you can get a handle on it using:

context.getContentResolver()

So you can fix your NullPointerException by just doing this:

context.getContentResolver().delete(contentUri, null, null);

You can check that it returns 1 to confirm it worked.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM