Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

java.io.FileNotFoundException #80

Open
Piasy opened this issue Dec 11, 2016 · 6 comments
Open

java.io.FileNotFoundException #80

Piasy opened this issue Dec 11, 2016 · 6 comments

Comments

@Piasy
Copy link
Contributor

Piasy commented Dec 11, 2016

E/LogWriter: save: 
 java.io.FileNotFoundException: /data/com.github.piasy.bootstrap/performance/looper-2016-12-11_23-48-25.844.log (No such file or directory)
     at java.io.FileOutputStream.open(Native Method)
     at java.io.FileOutputStream.<init>(FileOutputStream.java:221)
     at java.io.FileOutputStream.<init>(FileOutputStream.java:140)
     at com.github.moduth.blockcanary.LogWriter.save(LogWriter.java:108)
     at com.github.moduth.blockcanary.LogWriter.save(LogWriter.java:56)
     at com.github.moduth.blockcanary.BlockCanaryInternals$1.onBlockEvent(BlockCanaryInternals.java:63)
     at com.github.moduth.blockcanary.LooperMonitor$1.run(LooperMonitor.java:74)
     at android.os.Handler.handleCallback(Handler.java:751)
     at android.os.Handler.dispatchMessage(Handler.java:95)
     at android.os.Looper.loop(Looper.java:154)
     at android.os.HandlerThread.run(HandlerThread.java:61)
@markzhai
Copy link
Owner

markzhai commented Dec 13, 2016

Does the file really exist?And do you really have permission to write file in that directory?

Normally the log file should be in sdcard if sdcard exists

@Piasy
Copy link
Contributor Author

Piasy commented Dec 13, 2016

It seems /data/com.github.piasy.bootstrap/performance/ does not locate in external storage, so I can't verify whether this file exist, but as this exception is thrown, it obviously doesn't exist.

This exception doesn't cause crash, it's just printed in logcat, but when I run my app next time, I can't see it again. I just want to verify that does the log writing logic is correct :)

And I haven't request for permission manually, so do I need request it by myself?

@zjupure
Copy link

zjupure commented Dec 20, 2016

@Piasy this problem is produced when the external storage is not available or not writable. For example, the external storage is unmounted and it is really exist in some devices.
When external storage is not exist, the library with choose the /data/{your_provided_dir}, but this directory is not accessible by normal app due to permission limit.
the code in BlockCanaryInternals.getPath() have some drawback in design.

static String getPath() {
        String state = Environment.getExternalStorageState();
        String logPath = BlockCanaryInternals.getContext()
                == null ? "" : BlockCanaryInternals.getContext().providePath();

        if (Environment.MEDIA_MOUNTED.equals(state)
                && Environment.getExternalStorageDirectory().canWrite()) {
            return Environment.getExternalStorageDirectory().getPath() + logPath;
        }
        return Environment.getDataDirectory().getAbsolutePath() + BlockCanaryInternals.getContext().providePath();
    }

There is another permission problem, the external storage is not writeable default except the app-specifical directory due to the dynamic permission mechanism since Android 6.0.
So i suggest BlockCanary library can modify the implementation of BlockCanaryInternals.getPath(), there are two choice:

  1. the path is provided entirely by user through BlockCanaryContext.providePath(), the path validation is decided by user.
  2. change the external storage directory to app-specifical directory, when this path is not avaiable, use the internal storage directory instead.

App-specifical directory usually locates in external storage /Android/data/{pacakage_name}, you can use Context.getExternalCacheDir() or Context.getExternalFilesDir() to obtain a cache directory(/cache) or a files directory(/files). Those directory do not need to declare permission in Android Mainifest since Kitkat.
If abvoe path is not avaiable (external storage is unmount), use Context.getCacheDir() or Context.getFilesDir() instead. The result are usually located in /data/data/{pacakage_name} in internal storage and always available to normal app.

A reference implementation for choice 2

static String getPath() {
        String state = Environment.getExternalStorageState();
        String logPath = BlockCanaryInternals.getContext()
                == null ? "" : BlockCanaryInternals.getContext().providePath();

        Context context = BlockCanaryInternals.getContext().provideContext();
        File appCache = context.getExternalCacheDir();
        if (appCache != null && Environment.MEDIA_MOUNTED.equals(state)) {
            return  appCache.getAbsolutePath() + logPath;
        }
        return context.getCacheDir() + logPath;
    }

@markzhai
Copy link
Owner

Wow, cool, thanks @zjupure

@initialjie
Copy link

@markzhai @zjupure
Yes, Android's external storage is really a complicated thing to deal with, like permission or path etc,.
I totally agree that the path should be provided entirely by user.

@markzhai
Copy link
Owner

markzhai commented Jan 4, 2017

@initialjie @zjupure thanks, I adopt that the path should be provided entirely by user, the library itself should not do any magic to it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants