NullPointerException at openFileOutput in Activity

2019-01-15 20:55发布

问题:

I have an Activity with an ImageView, which, if it is empty and clicked, should load an image from Internet and save it to internal storage. So the activity looks like this:

public class PlaceCreate extends Activity {
Context context;

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.create);
  context = this;

  ImageView img = (ImageView) findViewById(R.id.imageView);
  img.setOnClickListener(new OnClickListener() {
    public void onClick(View v) {
        EditText edit = (EditText)findViewById(R.id.editTextName);
        Download(edit.getText().toString());
    }
  });
}

private boolean Download(String Name)
{
  try
  {
    URL u = new URL("http://example.com/img.png");
    HttpURLConnection c = (HttpURLConnection)u.openConnection();
    c.setRequestMethod("GET");
    c.setDoOutput(true);
    c.connect();
    FileOutputStream f = context.openFileOutput(Name, Context.MODE_PRIVATE);
    InputStream in = c.getInputStream();
    byte[] buffer = new byte[1024];
    int len1 = 0;
    while((len1 = in.read(buffer)) > 0 )
    {
      f.write(buffer, 0, len1);
    }
    f.close();

    FileInputStream is = openFileInput(Name);
    Bitmap bitmap = BitmapFactory.decodeStream(is);
    is.close();
    ImageView img = (ImageView) findViewById(R.id.imageViewPlan);
    img.setImageBitmap(bitmap);
  }
  catch(IOException e)
  {
    return false;
  }
  return true;
}}

The problem is that I get NullPointerException on the line with openFileOutput. I found a couple of similar questions, but all answers imply that null is a missing context. As it's an activity in this case, it obviously has a context (this), and it is not null. I tried to call openFileOutput (with this implied) and via the context member. Either approach fails. Also I tried to pass v.getContext() from onClick into Download as an additional parameter, and this value was not null, yet produced the same exception as well. (If it is important, the name of the file requested is a valid filename, for example "abc".)

Could someone shed some light on this?

Here is an example of the stack:

04-22 00:45:10.659: W/dalvikvm(330): threadid=1: thread exiting with uncaught exception (group=0x40015560)
04-22 00:45:10.909: E/AndroidRuntime(330): FATAL EXCEPTION: main
04-22 00:45:10.909: E/AndroidRuntime(330): java.lang.NullPointerException
04-22 00:45:10.909: E/AndroidRuntime(330):  at android.app.ContextImpl.openFileOutput(ContextImpl.java:420)
04-22 00:45:10.909: E/AndroidRuntime(330):  at android.content.ContextWrapper.openFileOutput(ContextWrapper.java:158)
04-22 00:45:10.909: E/AndroidRuntime(330):  at com.example.scanner.PlaceCreate.Download(PlaceCreate.java:93)
04-22 00:45:10.909: E/AndroidRuntime(330):  at com.example.scanner.PlaceCreate.access$0(PlaceCreate.java:84)
04-22 00:45:10.909: E/AndroidRuntime(330):  at com.example.scanner.PlaceCreate$3.onClick(PlaceCreate.java:67)
04-22 00:45:10.909: E/AndroidRuntime(330):  at android.view.View.performClick(View.java:2485)
04-22 00:45:10.909: E/AndroidRuntime(330):  at android.view.View$PerformClick.run(View.java:9080)
04-22 00:45:10.909: E/AndroidRuntime(330):  at android.os.Handler.handleCallback(Handler.java:587)
04-22 00:45:10.909: E/AndroidRuntime(330):  at android.os.Handler.dispatchMessage(Handler.java:92)
04-22 00:45:10.909: E/AndroidRuntime(330):  at android.os.Looper.loop(Looper.java:123)
04-22 00:45:10.909: E/AndroidRuntime(330):  at android.app.ActivityThread.main(ActivityThread.java:3683)
04-22 00:45:10.909: E/AndroidRuntime(330):  at java.lang.reflect.Method.invokeNative(Native Method)
04-22 00:45:10.909: E/AndroidRuntime(330):  at java.lang.reflect.Method.invoke(Method.java:507)
04-22 00:45:10.909: E/AndroidRuntime(330):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
04-22 00:45:10.909: E/AndroidRuntime(330):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
04-22 00:45:10.909: E/AndroidRuntime(330):  at dalvik.system.NativeStart.main(Native Method)

And here is some more findings. According to the stack, the error occures inside ContextImpl file, and I see that parent local variable is null inside this method. Here is the code:

public FileOutputStream openFileOutput(String name, int mode)
  throws FileNotFoundException {
  final boolean append = (mode&MODE_APPEND) != 0;
  File f = makeFilename(getFilesDir(), name);
  try {
     FileOutputStream fos = new FileOutputStream(f, append);
     setFilePermissionsFromMode(f.getPath(), mode, 0);
     return fos;
  } catch (FileNotFoundException e) {
 }

 File parent = f.getParentFile();
 parent.mkdir();
 FileUtils.setPermissions(
     parent.getPath(),
     FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
     -1, -1);
 FileOutputStream fos = new FileOutputStream(f, append);
 setFilePermissionsFromMode(f.getPath(), mode, 0);
 return fos;
}

So now the question is how can I make sure that the call to f.getParentFile() will not return null, and why does it work from time to time?

回答1:

The problem is that I get NullPointerException on the line with openFileOutput.

You are getting a NullPointerException inside the implementation of openFileOutput(). This is why you need to supply SO with a stack trace, rather than just supplying exception names. An exception on a line is different than an exception triggered by a line, particularly with NullPointerException.

Based on my reading of openFileOutput() on ContextImpl, either:

  • Name is null, or
  • you have an exceedingly strange context, or
  • there is some other possible problem on the version of Android you are running that is not visible in the current implementation of openFileOutput()

I would start by deleting context entirely, as you are inside an Activity and do not need it. Then, put in some Log statements or set breakpoints to see what Name is.



回答2:

This is proved to be a feature of Android 2.3.3 emulator installed here as a part of SDK. The problem does not occur if I use a real device with Android 2.3.3. Also the code works ok if I use Android 4.0.3 emulator. So, if some weird things happen, tests in another environment can shed some light. I hope this can help someone else in similar problem solving.



回答3:

I had this same problem on my Asus TF101 running ICS - i.e. a real device rather than an emulator. It was vexing since it turned out that the problem was related to the Linux write permissions on the underlying folder.

I had been playing with using shared resources with the android:sharedUserId tag in my manifest file. Since the UID had changed, the activity no longer had write permission in the data files directory.

so anything that might change the uid will potentially cause this problem.

To solve the problem I:

  1. Removed the sharedUserID tag from the manifest (possibly optional);
  2. Manually uninstalled the app from my device.
  3. Re-installed the app.

It then ran perfectly.



回答4:

By reading the code for openFileOutput() I can see one case where f.getParentFile() may return null that has not been mentioned in any other answer or comment here.

In the line

File f = makeFilename(getFilesDir(), name);

getFilesDir() can return null if the dataDir does not exist and if getFilesDir() fails to create the dataDir. If this happens the file f will only have a parent if the name is a path (such as "directory/file") and not just a file (such as "file").

I do not know under which circumstances the dataDir would be missing and when it would be impossible to create it, but it doesn't sound unlikely that it for some reason can't be created in the Android 2.3.3 Emulator.



回答5:

see this complete example

URL url = new URL (strURL);
input = url.openStream();
byte[] buffer = new byte[1500];
OutputStream output = new FileOutputStream ("/sdcard/abc.png");
int bytesRead = 0;
while ((bytesRead = input.read(buffer, 0, buffer.length)) >= 0) .
{
 output.write(buffer, 0, bytesRead);
 }