Skip to content

Instantly share code, notes, and snippets.

@MohammadSamandari
Last active April 5, 2020 21:20
Show Gist options
  • Save MohammadSamandari/12b104f534fa606818cc58bfee821440 to your computer and use it in GitHub Desktop.
Save MohammadSamandari/12b104f534fa606818cc58bfee821440 to your computer and use it in GitHub Desktop.
Data Storage - Shared Preferences
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<TextView
android:id="@+id/count_textview"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/default_background"
android:gravity="center"
android:text="@string/default_count"
android:textColor="@android:color/white"
android:textSize="112sp"
app:layout_constraintBottom_toTopOf="@+id/guideline_upper"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<android.support.constraint.Guideline
android:id="@+id/guideline_upper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_end="120dp"/>
<Button
android:id="@+id/black_background_button"
style="@style/AppTheme.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/black"
android:onClick="changeBackground"
android:text="@string/black_button"
app:layout_constraintBottom_toTopOf="@+id/guideline_lower"
app:layout_constraintEnd_toStartOf="@+id/red_background_button"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline_upper"/>
<Button
android:id="@+id/red_background_button"
style="@style/AppTheme.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/red_background"
android:onClick="changeBackground"
android:text="@string/red_button"
app:layout_constraintBottom_toTopOf="@+id/guideline_lower"
app:layout_constraintEnd_toStartOf="@+id/blue_background_button"
app:layout_constraintStart_toEndOf="@+id/black_background_button"
app:layout_constraintTop_toTopOf="@+id/guideline_upper"/>
<Button
android:id="@+id/blue_background_button"
style="@style/AppTheme.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/blue_background"
android:onClick="changeBackground"
android:text="@string/blue_button"
app:layout_constraintBottom_toTopOf="@+id/guideline_lower"
app:layout_constraintEnd_toStartOf="@+id/green_background_button"
app:layout_constraintStart_toEndOf="@+id/red_background_button"
app:layout_constraintTop_toTopOf="@+id/guideline_upper"/>
<Button
android:id="@+id/green_background_button"
style="@style/AppTheme.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/green_background"
android:onClick="changeBackground"
android:text="@string/green_button"
app:layout_constraintBottom_toTopOf="@+id/guideline_lower"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/blue_background_button"
app:layout_constraintTop_toTopOf="@+id/guideline_upper"/>
<android.support.constraint.Guideline
android:id="@+id/guideline_lower"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_end="56dp"/>
<Button
android:id="@+id/count_button"
style="@style/AppTheme.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:onClick="countUp"
android:text="@string/count_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/reset_button"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/guideline_lower"/>
<Button
android:id="@+id/reset_button"
style="@style/AppTheme.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="reset"
android:text="@string/reset_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/count_button"
app:layout_constraintTop_toBottomOf="@+id/guideline_lower"/>
</android.support.constraint.ConstraintLayout>

Data storage

Files

  1. Internal Storage
  2. External Storage

Internal Storage

  • You don't need any permissions to save files on the internal storage. Your app always has permission to read and write files in its internal storage directory.

To create a new file in one of these directories, you can use the File() constructor, passing the File provided by one of the above methods that specifies your internal storage directory. For example:

File file = new File(context.getFilesDir(), filename);

Alternatively, you can call openFileOutput() to get a FileOutputStream that writes to a file in your internal directory. For example, here's how to write some text to a file:

String filename = "myfile";
String string = "Hello world!";
FileOutputStream outputStream;

try {
  outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
  outputStream.write(string.getBytes());
  outputStream.close();
} catch (Exception e) {
  e.printStackTrace();
}

Or, if you need to cache some files, instead use createTempFile(). For example, the following method extracts the filename from a URL and creates a file with that name in your app's internal cache directory:

public File getTempFile(Context context, String url) {
    File file;
    try {
        String fileName = Uri.parse(url).getLastPathSegment();
        file = File.createTempFile(fileName, null, context.getCacheDir());
    } catch (IOException e) {
        // Error while creating file
    }
    return file;
}

External Storage

Obtain Permission to access the external storage

To write to the external storage, you must request the WRITE_EXTERNAL_STORAGE permission in your Android manifest. This implicitly includes permission to read.

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

If your app needs to read the external storage (but not write to it), then you will need to declare the READ_EXTERNAL_STORAGE permission.

<manifest ...>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    ...
</manifest>

Always check whether external storage is mounted

/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state) ||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return true;
    }
    return false;
}

Getting file descriptors

To access a public external storage directory, get a path and create a file calling getExternalStoragePublicDirectory().

File path = Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES);
File file = new File(path, "DemoPicture.jpg");

To access a private external storage directory, get a path and create a file calling getExternalFilesDir().

File file = new File(getExternalFilesDir(null), "DemoFile.jpg");

Querying storage space

If you know ahead of time how much data you're saving, you can find out whether sufficient space is available without causing an IOException by calling getFreeSpace() or getTotalSpace(). These methods provide the current available space and the total space in the storage volume, respectively.

Deleting files

You should always delete files that you no longer need. The most straightforward way to delete a file is to have the opened file reference call delete() on itself.

myFile.delete();

If the file is saved on internal storage, you can also ask the Context to locate and delete a file by calling deleteFile():

myContext.deleteFile(fileName);

Shared preferences

Shared preferences allow you to store small amounts of primitive data as key/value pairs in a file on the device.

Creating a shared preferences file

private String sharedPrefFile = 
   "com.example.android.hellosharedprefs";
mPreferences = getSharedPreferences(sharedPrefFile, MODE_PRIVATE);

Saving shared preferences

You save preferences in the onPause() state of the activity lifecycle using the SharedPreferences.Editor interface.

@Override
protected void onPause() {
super.onPause();
SharedPreferences.Editor preferencesEditor = mPreferences.edit();
preferencesEditor.putInt("count", mCount);
preferencesEditor.putInt("color", mCurrentColor);
preferencesEditor.apply();
}

Restoring shared preferences

mPreferences = getSharedPreferences(sharedPrefFile, MODE_PRIVATE);
if (savedInstanceState != null) {
    mCount = mPreferences.getInt("count", 1);
    mShowCount.setText(String.format("%s", mCount));

    mCurrentColor = mPreferences.getInt("color", mCurrentColor);
    mShowCount.setBackgroundColor(mCurrentColor);
} else { ... }

Clearing shared preferences

To clear all the values in the shared preferences file, call the clear() method on the shared preferences editor and apply the changes.

SharedPreferences.Editor preferencesEditor = mPreferences.edit();
preferencesEditor.putInt("number", 42);
preferencesEditor.clear();
preferencesEditor.apply();

Listening for preference changes

public class SettingsActivity extends PreferenceActivity
                              implements OnSharedPreferenceChangeListener {
    public static final String KEY_PREF_SYNC_CONN = 
       "pref_syncConnectionType";

    // ...

    public void onSharedPreferenceChanged(
                              SharedPreferences sharedPreferences,
                              String key) {
        if (key.equals(KEY_PREF_SYNC_CONN)) {
            Preference connectionPref = findPreference(key);
            // Set summary to be the user-description for 
            // the selected value
            connectionPref.setSummary(
               sharedPreferences.getString(key, ""));
        }
    }
}

For proper lifecycle management in the activity, register and unregister your SharedPreferences.OnSharedPreferenceChangeListener during the onResume() and onPause() callbacks, respectively:

@Override
protected void onResume() {
    super.onResume();
    getPreferenceScreen().getSharedPreferences()
            .registerOnSharedPreferenceChangeListener(this);
}

@Override
protected void onPause() {
    super.onPause();
    getPreferenceScreen().getSharedPreferences()
            .unregisterOnSharedPreferenceChangeListener(this);
}

Hold a reference to the listener

When you call registerOnSharedPreferenceChangeListener(), the preference manager does not currently store a reference to the listener. You must hold onto a reference to the listener, or it will be susceptible to garbage collection. Keep a reference to the listener as a class member variable in an object such as an activity that will exist as long as you need the listener.

SharedPreferences.OnSharedPreferenceChangeListener listener =
    new SharedPreferences.OnSharedPreferenceChangeListener() {
       public void onSharedPreferenceChanged(
                            SharedPreferences prefs, String key) {
          // listener implementation
       }
};

prefs.registerOnSharedPreferenceChangeListener(listener);
/*
* Copyright (C) 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.hellosharedprefs;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
/**
* HelloSharedPrefs is an adaptation of the HelloToast app from chapter 1.
* It includes:
* - Buttons for changing the background color.
* - Maintenance of instance state.
* - Themes and styles.
* - Read and write shared preferences for the current count and the color.
* <p>
* This is the starter code for HelloSharedPrefs.
*/
public class MainActivity extends AppCompatActivity {
// Current count
private int mCount = 0;
// Current background color
private int mColor;
// Text view to display both count and color
private TextView mShowCountTextView;
// Key for current count
private final String COUNT_KEY = "count";
// Key for current color
private final String COLOR_KEY = "color";
// Name for my shared preference
private final String SHARED_NAME = "com.example.android.hellosharedprefs";
SharedPreferences mSharedPreferences;
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Initiating the shared preferences
mSharedPreferences = getSharedPreferences(SHARED_NAME, MODE_PRIVATE);
// Initialize views, color
mShowCountTextView = findViewById(R.id.count_textview);
mColor = ContextCompat.getColor(this,
R.color.default_background);
// Restore the saved instance state.
if (savedInstanceState != null) {
mCount = savedInstanceState.getInt(COUNT_KEY);
mColor = savedInstanceState.getInt(COLOR_KEY);
} else {
//Restoring data from shared preferences to the variables
mCount = mSharedPreferences.getInt(COUNT_KEY, 0);
mColor = mSharedPreferences.getInt(COLOR_KEY, ContextCompat.getColor(this,
R.color.default_background));
}
if (mCount != 0) {
mShowCountTextView.setText(String.format("%s", mCount));
}
mShowCountTextView.setBackgroundColor(mColor);
}
/**
* Handles the onClick for the background color buttons. Gets background
* color of the button that was clicked, and sets the TextView background
* to that color.
*
* @param view The view (Button) that was clicked.
*/
public void changeBackground (View view) {
int color = ((ColorDrawable) view.getBackground()).getColor();
mShowCountTextView.setBackgroundColor(color);
mColor = color;
}
/**
* Handles the onClick for the Count button. Increments the value of the
* mCount global and updates the TextView.
*
* @param view The view (Button) that was clicked.
*/
public void countUp (View view) {
mCount++;
mShowCountTextView.setText(String.format("%s", mCount));
}
/**
* Saves the instance state if the activity is restarted (for example,
* on device rotation.) Here you save the values for the count and the
* background color.
*
* @param outState The state data.
*/
@Override
protected void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(COUNT_KEY, mCount);
outState.putInt(COLOR_KEY, mColor);
}
/**
* Handles the onClick for the Reset button. Resets the global count and
* background variables to the defaults and resets the views to those
* default values.
*
* @param view The view (Button) that was clicked.
*/
public void reset (View view) {
// Reset count
mCount = 0;
mShowCountTextView.setText(String.format("%s", mCount));
// Reset color
mColor = ContextCompat.getColor(this,
R.color.default_background);
mShowCountTextView.setBackgroundColor(mColor);
}
@Override
protected void onPause () {
super.onPause();
//putting data into shared preferences
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putInt(COUNT_KEY, mCount);
editor.putInt(COLOR_KEY, mColor);
editor.apply();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment