In most of the applications there is a need to include files like database, files, audio, images in apk. This can be achieved by saving files either inside assets
or raw
folder. Both folders can be used to store files and there is a very slight difference between the two.
raw
is a subfolder of resources and Android source compilation automatically generates resource ID for any file present in raw folder. So the files can easily be referenced by their generated IDs. While, files stored in asset folder are accessed by the filenames and there is no reference created for these files. Other difference is that files can be grouped in sub folders inside assets while raw folder follows a flat hierarchy.
In this post we will see how to read and share files from assets
as well as raw
folder.
How to read file from Assets folder
Let’s check first where to keep assets folder. In order to save files in your application first create assets folder inside your apps main directory. Create assets
folder inside app/src/main/
folder. You can create subfolders and save files.
Using Apache IOUtils
Add Apache Common IOUtils
dependency in app’s gradle file.
implementation 'commons-io:commons-io:2.5'
Call IOUtils toString
function to read from InputStream
.
public String loadJSONFromAssetSubfolder(Context ctx) { String json = null; try { InputStream inputStream = ctx.getAssets().open("subfolder/example_2.json"); json = IOUtils.toString(inputStream, StandardCharsets.UTF_8); Log.i("tag", json); } catch (IOException ex) { ex.printStackTrace(); } return json; }
Using BufferedReader
public String readFileFromAssets(Context ctx) { StringBuilder strFile = new StringBuilder(); try { BufferedReader reader = null; InputStream inputStream = ctx.getAssets().open("sampletextfile.txt"); reader = new BufferedReader(new InputStreamReader(inputStream)); String mLine; while ((mLine = reader.readLine()) != null) { strFile.append(mLine); } } catch (IOException ex) { ex.printStackTrace(); } Log.i("tag", strFile.toString()); return strFile.toString(); }
Read file from Raw folder
Similar to assets folder inputstream can be opened for raw folder. Use openRawResource
to create inputstream.
public String readFileFromRaw(Context ctx) { String json = null; try { InputStream inputStream = ctx.getResources().openRawResource(R.raw.example_2); json = IOUtils.toString(inputStream, StandardCharsets.UTF_8); Log.i("tag", json); } catch (IOException ex) { ex.printStackTrace(); } return json; }
Share files saved in Assets folder
Android do not allow and expose files stored in assets. To allow sharing of files included in assets, app has to expose these files by creating a ContentProvider
. Content Provider is used to share data between applications.
Add no compress options in the gradle file inside android block to disable compression of text, json files.
aaptOptions { noCompress '.json' , '.txt' }
This will make sure that files inside asset folder are not compressed.
Full sample of app.gradle
file
apply plugin: 'com.android.application' android { compileSdkVersion 28 defaultConfig { applicationId "com.codeexa.com.readfiles" minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } aaptOptions { noCompress '.json' , '.txt' } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'commons-io:commons-io:2.5' }
Now create our ContentProvider class that will handle the file sharing. In the ContentProvider we will override openAssetFile
to create FileDescriptor for assets files.
Create MyContentProvider.java and paste the below content.
package com.codeexa.com.readfiles; import android.content.ContentProvider; import android.content.ContentValues; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.database.Cursor; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; public class MyContentProvider extends ContentProvider { @Override public boolean onCreate() { return true; } @Nullable @Override public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; } @Nullable @Override public String getType(@NonNull Uri uri) { return null; } @Nullable @Override public Uri insert(@NonNull Uri uri, ContentValues values) { return null; } @Override public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) { return 0; } @Override public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } @Override public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { AssetManager am = getContext().getAssets(); String fileName = uri.getLastPathSegment(); if(fileName == null) throw new FileNotFoundException(); AssetFileDescriptor fileDescriptor = null; try { fileDescriptor = am.openFd(fileName); } catch (IOException e) { e.printStackTrace(); } return fileDescriptor; } }
openAssetFile
is implemented by providers that need to be able to return sub-sections of files, often assets inside of their .apk.
Add ContentProvider tag in AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.codeexa.com.readfiles"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" tools:ignore="GoogleAppIndexingWarning"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <provider android:name=".MyContentProvider" android:authorities="com.codeexa.com.readfiles" android:grantUriPermissions="true" android:exported="true" /> </application> </manifest>
Replace android:authorities
value with your package id.
Now we are ready to share files from assets directory. In your activity class you can share file as shown in the code below.
private Context mContext; private static final String AUTHORITY= "com.codeexa.com.readfiles"; private static final Uri PROVIDER= Uri.parse("content://"+AUTHORITY); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mContext = this; //shareAssetFile("sampletextfile.txt"); shareAssetFile("subfolder/example_2.json"); } public void shareAssetFile(String fileName){ Uri uriFile = PROVIDER .buildUpon() .appendPath(fileName) .build(); Intent fileShareIntent = new Intent(Intent.ACTION_SEND); fileShareIntent.setType("*/*"); fileShareIntent.putExtra(Intent.EXTRA_STREAM,uriFile); fileShareIntent.putExtra(Intent.EXTRA_TEXT, "#Message"); fileShareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(Intent.createChooser(fileShareIntent, "Share Asset")); }
If you have a file example_2.json
inside assets/subfolder
then pass the file name like the snipped shared below.
shareAssetFile("subfolder/example_2.json");
in the method: “shareAssetFile”: PROVIDER is undefined
Hey Gil,
I have updated the code. You will have to create uri object PROVIDER and define it like this.
private static final String AUTHORITY= "com.codeexa.com.readfiles";
private static final Uri PROVIDER= Uri.parse("content://"+AUTHORITY);
Good Luck
I have read your tutorial and try to share my .json file but it work only for google drive. For exapmle when i try the whatsapp it doesn’t work. How can we fix it?
Uri uriFile = PROVIDER.buildUpon().appendPath(view).build(); I am getting error over .buildOpen
You have to add package of WhatsApp .setPackage(com.whatsapp
)
I have read your tutorial and try to share my .json file but it work only for google drive. For exapmle when i try the whatsapp it doesn’t work. How can we fix it?