6.2 Data Storage and Access - SharedPreferences for Saving User Preferences
Category Android Basic Tutorial
Introduction:
>
This section introduces the second method of storing user data, using SharedPreferences (for saving user preferences) to store data. When our application needs to save some user preference parameters, such as whether to auto-login, remember account and password, or connect only under Wifi, using a database would be overkill. We refer to these configuration settings as user preferences, which are typically stored in specific files. For example, Windows uses ini files, J2SE uses properties files and XML files for software configuration, and in Android, we usually use a lightweight storage class - SharedPreferences to save user preference parameters. SharedPreferences also uses XML files and stores data in a key-value format, similar to a Map collection. We can retrieve values using SharedPreferences' getXxx(name) method, which is very convenient.
1. SharedPreferences Usage Example:
Flowchart:
Code Example:
Runtime Effect Diagram:
The process involves entering the account and password, clicking login, saving the information to the SharedPreference file, and then restarting the app to see that the data is displayed in the text boxes.
After saving, we can open data/data/<package name> in File Explorer and see an XML file generated in the shared_prefs directory (due to the N5 not being rooted, an older effect diagram is used here):
Clicking export to the desktop shows the contents:
Code Implementation:
Layout file activity_main.xml writing:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MyActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="User Login" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="Please enter username" />
<EditText
android:id="@+id/editname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Username" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Please enter password" />
<EditText
android:id="@+id/editpasswd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password"
android:inputType="textPassword" />
<Button
android:id="@+id/btnlogin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Login" />
</LinearLayout>
Writing a simple SP utility class: SharedHelper.java:
/**
* Created by Jay on 2015/9/2 0002.
*/
public class SharedHelper {
private Context mContext;
public SharedHelper() {
}
public SharedHelper(Context mContext) {
this.mContext = mContext;
}
// Method to save data
public void save(String username, String passwd) {
SharedPreferences sp = mContext.getSharedPreferences("mysp", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("username", username);
editor.putString("passwd", passwd);
editor.commit();
Toast.makeText(mContext, "Information has been written to SharedPreference", Toast.LENGTH_SHORT).show();
}
// Method to read from SP file
public Map<String, String> read() {
Map<String, String> data = new HashMap<String, String>();
SharedPreferences sp = mContext.getSharedPreferences("mysp", Context.MODE_PRIVATE);
data.put("username", sp.getString("username", ""));
data.put("passwd", sp.getString("passwd", ""));
return data;
}
}
Finally, MainActivity.java implements the relevant logic:
public class MainActivity extends AppCompatActivity {
private EditText editname;
private EditText editpasswd;
private Button btnlogin;
private String strname;
private String strpasswd;
private SharedHelper sh;
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = getApplicationContext();
sh = new SharedHelper(mContext);
bindViews();
}
private void bindViews() {
editname = (EditText)findViewById(R.id.editname);
editpasswd = (EditText)findViewById(R.id.editpasswd);
btnlogin = (Button)findViewById(R.id.btnlogin);
btnlogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
strname = editname.getText().toString();
strpasswd = editpasswd.getText().toString();
sh.save(strname,strpasswd);
}
});
}
@Override
protected void onStart() {
super.onStart();
Map<String,String> data = sh.read();
editname.setText(data.get("username"));
editpasswd.setText(data.get("passwd"));
}
}
2. Reading Another Application's SharedPreferences
>
Core: To obtain another app's Context, which represents an interface to global information about an application environment, and the unique identifier of an application is its package name, so we can obtain the corresponding app's Context by its package name. Another point to note is that whether another application's SP file can be read or written depends on whether the SP file has specified readable or writable permissions. The MODE_PRIVATE we created earlier does not allow this. Therefore, it is difficult to read data from another's SP, and critical information like passwords stored in SP is usually encrypted, so it's mainly for self-use. We will discuss the commonly used MD5 encryption method later.
Implementation Flowchart:
Code Example:
Runtime Effect Diagram:
Code Implementation:
We perform the SP reading operation in MainActivity.java, and display it with a Toast after clicking the button:
public class MainActivity extends AppCompatActivity {
private Context othercontext;
private SharedPreferences sp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnshow = (Button) findViewById(R.id.btnshow);
btnshow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Obtain the package name of the first application to get the corresponding Context, exception handling is required
try {
othercontext = createPackageContext("com.jay.sharedpreferencedemo", Context.CONTEXT_IGNORE_SECURITY);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
// Retrieve the SharedPreferences based on the Context
sp = othercontext.getSharedPreferences("mysp", Context.MODE_WORLD_READABLE);
String name = sp.getString("username", "");
String passwd = sp.getString("passwd", "");
Toast.makeText(getApplicationContext(), "SharedPreference from Demo1\nUsername: " + name + "\nPassword: " + passwd, Toast.LENGTH_SHORT).show();
}
});
3. Encrypting Important Data in SharedPreference with MD5
Hey, above we stored the account and password directly in SharedPreferences. If the phone isn't rooted, other apps can't access the data. But if it's rooted and data is accessed by other apps, leading to consequences, well... that's not our fault, haha. You rooted it yourself! We're not taking that blame. True, but as responsible app developers, we shouldn't do that, right? We can use encryption algorithms to encrypt user passwords. Typically, the passwords are what we encrypt. Below is a simple diagram to help understand the encryption process:
1. Simple Encryption Process
Flowchart:
Flowchart Explanation:
Step 1. Users register their account and password. After validating the account and password (e.g., checking for duplicates, password length > 6 characters), if valid and registration is successful, we submit the account and locally encrypted password to the server.
Step 2. The server saves the user's submitted account and encrypted password in the server database. The server does not store the original plaintext passwords.
Step 3. Back to the client side, if registration or login is successful and you want to save the account and password in SharedPreferences, the password should be encrypted before saving. If not saved, the plaintext password must be encrypted each time before sending it to the server.
Step 4. The server verifies the account and encrypted password. If successful, the server assigns a session token to the client, which the client can use to access services provided by the server.
Got it? There are many encryption methods. I'm not an expert in this area, but I've used MD5 encryption before. This section briefly introduces MD5 encryption and demonstrates its usage.
2. Brief Introduction to MD5:
1) What is MD5?
Answer: Message Digest Algorithm MD5 (MD5) is a widely used cryptographic hash function that produces a 128-bit (16-byte) hash value. It's used to ensure data integrity. In simpler terms, it's an encryption algorithm that converts a string or file into a fixed 128-bit hash, which is almost unique. Since one hexadecimal digit requires 4 bits, the MD5 string length is 128 / 4 = 32 characters. Sometimes you might see a 16-character MD5 hash, which is the 32-character hash with the first and last 8 characters removed. You can test this on an MD5 decryption site like http://www.cmd5.com/.
2) Can MD5 be cracked?
Answer: MD5 is not reversible, meaning there's no algorithm to reverse an MD5 hash back to the original data. However, brute force attacks can crack simple MD5 hashes by comparing them to a database of known hashes.
3) Is the MD5 hash unique?
Answer: No, a single piece of original data corresponds to one MD5 hash, but one MD5 hash might correspond to multiple pieces of original data.
3. MD5 Encryption Example:
There are many MD5 examples online. Here's a tool class for MD5 encryption that I use:
Md5Util.java:
public class MD5 {
public static String getMD5(String content) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(content.getBytes());
return getHashString(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
private static String getHashString(MessageDigest digest) {
StringBuilder builder = new StringBuilder();
for (byte b : digest.digest()) {
builder.append(Integer.toHexString((b >> 4) & 0xf));
builder.append(Integer.toHexString(b & 0xf));
}
return builder.toString();
}
}
MainActivity.java calls the static method getMD5:
Log.e("HeHe", MD5.getMD5("呵呵"));
This is the encrypted "呵呵". You can copy this hash to an MD5 decryption site:
If you want more security, you can encrypt the hash 100 times. Here's a method to do that:
public static String getMD5x100(String content){
String s1 = content;
for(int i = 0;i < 100;i++){
s1 = getMD5(s1);
}
return s1;
}
Calling this method results in a very long hash that's difficult to crack:
4. SharedPreference Utility Class:
It's cumbersome to manually instantiate SharedPreferences classes every time. Here's a utility class for SharedPreferences from a blog by a respected developer:
SPUtils.java:
public class SPUtils {
public static final String FILE_NAME = "my_sp";
public static void put(Context context, String key, Object obj) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
if (obj instanceof Boolean) {
editor.putBoolean(key, (Boolean) obj);
} else if (obj instanceof Float) {
editor.putFloat(key, (Float) obj);
} else if (obj instanceof Integer) {
editor.putInt(key, (Integer) obj);
} else if (obj instanceof Long) {
editor.putLong(key, (Long) obj);
} else {
editor.putString(key, (String) obj);
}
editor.commit();
}
public static Object get(Context context, String key, Object defaultObj) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
if (defaultObj instanceof Boolean) {
return sp.getBoolean(key, (Boolean) defaultObj);
} else if (defaultObj instanceof Float) {
return sp.getFloat(key, (Float) defaultObj);
} else if (defaultObj instanceof Integer) {
return sp.getInt(key, (Integer) defaultObj);
} else if (defaultObj instanceof Long) {
return sp.getLong(key, (Long) defaultObj);
} else {
return sp.getString(key, (String) defaultObj);
}
}
}
} else if (defaultObj instanceof Long) {
return sp.getLong(key, (Long) defaultObj);
} else if (defaultObj instanceof String) {
return sp.getString(key, (String) defaultObj);
}
return null;
}
/**
* Delete specified data
*/
public static void remove(Context context, String key) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.remove(key);
editor.commit();
}
/**
* Return all key-value pairs
*/
public static Map<String, ?> getAll(Context context) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
Map<String, ?> map = sp.getAll();
return map;
}
/**
* Delete all data
*/
public static void clear(Context context) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
editor.commit();
}
/**
* Check if data for the given key exists
*/
public static boolean contains(Context context, String key) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
return sp.contains(key);
}
}
5. Code Download:
SharedPreferenceDemo.zip: Download SharedPreferenceDemo.zip
SharedPreferenceDemo2.zip: Download SharedPreferenceDemo2.zip
SharedPreferenceDemo3.zip: Download SharedPreferenceDemo3.zip
Summary:
>
That's all about the second method of storing data in Android: SharedPreference for saving user preferences. This should cover your needs for using SharedPreferences in your daily development. If anything is missing, please feel free to suggest. Thank you!
-1.0 Android Basic Beginner Tutorial
-1.0.1 2015 Latest Android Basic Beginner Tutorial Table of Contents
-1.1 Background and System Architecture Analysis
-1.2 Development Environment Setup
-1.2.1 Using Eclipse + ADT + SDK to Develop Android APP
-1.2.2 Using Android Studio to Develop Android APP
-1.3 Solving SDK Update Issues
-1.4 Genymotion Emulator Installation
-1.5.1 Git Tutorial for Basic Local Repository Operations
-1.5.2 Git: Setting Up a Remote Repository on GitHub
-1.6 How to Use 9-Patch Images
-1.7 Interface Prototype Design
-1.8 Project Source Analysis (Various Files, Resource Access)
-1.9 Android Application Signing and Packaging
-1.11 Decompiling APK to Retrieve Code & Resources
-2.1 Concepts of View and ViewGroup
-2.2.1 LinearLayout (Linear Layout)
-2.2.2 RelativeLayout (Relative Layout)
-2.2.3 TableLayout (Table Layout)
-2.2.4 FrameLayout (Frame Layout)
-2.2.5 GridLayout (Grid Layout)
-2.2.6 AbsoluteLayout (Absolute Layout)
-2.3.1 TextView (Text View) Detailed Explanation
-2.3.2 EditText (Input Box) Detailed Explanation
-2.3.5.RadioButton (Radio Button) & Checkbox (Checkbox)
-2.3.6 ToggleButton and Switch
-2.3.7 ProgressBar (Progress Bar)
-2.3.9 RatingBar (Star Rating Bar)
-2.4.1 ScrollView (Scroll Bar)
-2.4.2 Date & Time Components (Part 1)
-2.4.3 Date & Time Components (Part 2)
-2.4.6 BaseAdapter Optimization
-2.4.8 Solving ListView Checkbox Misalignment Issues
-2.4.9 ListView Data Update Issues
-2.5.0 Building a Reusable Custom BaseAdapter
-2.5.1 Implementing Multi-Layout ListView Items
-2.5.2 GridView (Grid View) Basic Usage
-2.5.3 Spinner (List Option Box) Basic Usage
-2.5.4 AutoCompleteTextView (Auto-Complete Text Box) Basic Usage
-2.5.5 ExpandableListView (Collapsible List) Basic Usage
-2.5.6 ViewFlipper (Flip View) Basic Usage
-2.5.8 Notification (Status Bar Notification) Detailed Explanation
-2.5.9 AlertDialog (Dialog Box) Detailed Explanation ``` -2.6.0 Basic Usage of Several Common Dialogs
-2.6.1 Basic Usage of PopupWindow (Floating Box)
-2.6.3 Simple Usage of ViewPager
-2.6.4 Simple Usage of DrawerLayout (Official Side Sliding Menu)
-3.1.1 Event Handling Mechanism Based on Listeners
-3.2 Event Handling Mechanism Based on Callbacks
-3.3 Analysis of Handler Message Passing Mechanism
-3.4 TouchListener vs OnTouchEvent + Multi-touch
-3.5 Listening for Content Changes in EditText
-3.6 Responding to System Setting Events (Configuration Class)
-3.7 AsyncTask Asynchronous Task
-4.1.1 Introduction to Activity
-4.1.2 Getting Started with Activity
-4.2.1 Introduction to Service
-4.3.1 Introduction to BroadcastReceiver
-4.3.2 In-depth BroadcastReceiver
-4.4.1 Introduction to ContentProvider
-4.4.2 Further Exploration of ContentProvider - Document Provider
-4.5.2 Passing Complex Data with Intent
-5.1 Basic Overview of Fragment
-5.2.1 Fragment Example Analysis - Bottom Navigation Bar Implementation (Method 1)
-5.2.2 Fragment Example Analysis - Bottom Navigation Bar Implementation (Method 2)
-5.2.3 Fragment Example Analysis - Bottom Navigation Bar Implementation (Method 3)
-5.2.4 Fragment Example Analysis - Bottom Navigation Bar + ViewPager Swipe to Switch Pages
-5.2.5 Fragment Example Analysis - Simple Implementation of News (Shopping) App List Fragment
-6.1 Data Storage and Access - File Storage and Reading
- 6.2 Data Storage and Access - Saving User Preferences with SharedPreferences
-6.3.1 Data Storage and Access - Introduction to SQLite Database
-6.3.2 Data Storage and Access - Further Exploration of SQLite Database
-7.1.1 Android Network Programming Essentials and Learning HTTP Protocol
-7.1.2 Learning Android HTTP Request Headers and Response Headers
-7.1.3 Android HTTP Request Method: HttpURLConnection
-7.1.4 Android HTTP Request Method: HttpClient
-7.2.1 Android XML Data Parsing
-7.2.2 Android JSON Data Parsing
-7.3.2 Android File Download (1)
-7.3.3 Android File Download (2)
-7.4 Android Calling WebService
-7.5.1 Basic Usage of WebView (Web View)
-7.5.2 Basic Interaction Between WebView and JavaScript
-7.5.3 Important Considerations for WebView in Android 4.4 and Later
-7.5.6 WebView Handling Error Code Information from Web Pages
-7.6.1 Network Basics Preparation for Socket Learning
-7.6.2 Socket Communication Based on TCP Protocol (1)
-7.6.3 Socket Communication Based on TCP Protocol (2)
-7.6.4 Socket Communication Based on UDP Protocol
-8.1.1 Summary of 13 Drawable Types in Android Part 1
-8.1.2 Summary of 13 Drawable Types in Android Part 2
-8.1.3 Summary of 13 Drawable Types in Android Part 3
-8.2.1 Comprehensive Analysis of Bitmap (Bitmap) Part 1
-8.2.2 OOM Issues Caused by Bitmap
-8.3.1 Detailed Explanation of Three Drawing Tool Classes
-8.3.2 Practical Examples of Drawing Classes
-8.3.3 Paint API - MaskFilter (Mask)
-8.3.4 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 1)
-8.3.5 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 2)
-8.3.6 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 3)
-8.3.7 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 4)
-8.3.8 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 5)
-8.3.9 Paint API - ColorFilter (Color Filter) (1/3)
-8.3.10 Paint API - ColorFilter (Color Filter) (2/3)
-8.3.11 Paint API - ColorFilter (Color Filter) (3/3)
-8.3.12 Paint API - PathEffect (Path Effect)
-8.3.13 Paint API - Shader (Image Rendering)
-8.3.14 Paint Enum/Constant Values and ShadowLayer Shadow Effect
-8.3.15 Paint API - Typeface (Font Style)
- 8.3.16 Detailed Explanation of Canvas API (Part 1)
- 8.3.17 Detailed Explanation of Canvas API (Part 2) Clipping Methods Collection
- 8.3.18 Detailed Explanation of Canvas API (Part 3) Matrix and drawBitmapMesh
- 8.4.1 Frame Animation in Android Animation Collection
- 8.4.2 Tween Animation in Android Animation Collection
- 8.4.3 Property Animation in Android Animation Collection - First Encounter
- 8.4.4 Property Animation in Android Animation Collection - Second Encounter
- 9.1 Playing Sound Effects with SoundPool (Duang~)
- 9.2 Playing Audio and Video with MediaPlayer
- 9.3 Using Camera to Take Photos
- 9.4 Using MediaRecord to Record Audio
- 10.1 TelephonyManager (Phone Manager)
- 10.2 SmsManager (SMS Manager)
- 10.3 AudioManager (Audio Manager)
- 10.4 Vibrator (Vibrator)
- 10.5 AlarmManager (Alarm Service)
- 10.6 PowerManager (Power Service)
- 10.7 WindowManager (Window Management Service)
- 10.8 LayoutInflater (Layout Service)
- 10.9 WallpaperManager (Wallpaper Manager)
- 10.10 Sensor Topic (1) - Introduction
- 10.11 Sensor Topic (2) - Orientation Sensor
- 10.12 Sensor Topic (3) - Accelerometer/Gyroscope Sensor
- 10.12 Sensor Topic (4) - Understanding Other Sensors
- 10.14 Android GPS Introduction
- 11.0《2015 Latest Android Basic Beginner's Tutorial》Completion Celebration~
- 12.1 Android Practice: DrySister Look at Girls App (First Edition) - Project Setup and Simple Implementation
- 12.2 DrySister Look at Girls App (First Edition) - 2. Parsing Backend Data
- 12.3 DrySister Look at Girls App (First Edition) - 3. Image Loading Optimization (Writing an Image Cache Small Framework)
- 12.4 DrySister Look at Girls App (First Edition) - 4. Adding Data Caching (Introducing SQLite)
- 12.5 DrySister Look at Girls App (First Edition) - 5. Code Review, Adjustment, and Logging Class Writing
- 12.6 DrySister Look at Girls App (First Edition) - 6. Icon Creation, Obfuscation, Signing, Packaging, APK Slimming, App Release