Mengobati Aplikasi
Yang
Amnesia! 🧠
Pernah main game Android, pas ditutup dan dibuka lagi skornya balik ke 0? Ngajak berantem kan? Yuk belajar cara menyimpan data secara permanen di HP pakai SharedPreferences!
1. RAM vs Storage (Hierarki Memori)
Semua variabel yang kamu buat di Java (misal int skor = 10;) itu disimpannya di
Memori RAM. Sifat RAM itu sementara (volatile). Saat aplikasi ditutup paksa,
Android membuang isi RAM tersebut sehingga aplikasimu jadi "amnesia".
Kapan Pakai Penyimpanan Apa?
1. Bundle (State)
Gunakan
onSaveInstanceState(). Hanya untuk menyelamatkan data UI saat layar HP
dirotasi. Hilang kalau aplikasi di-kill.
2. SharedPreferences
Menyimpan data kecil (Teks, Angka, True/False) secara permanen di File Storage HP. Cocok untuk Setting & Auto-Login.
3. Room / SQLite Database
Gunakan ini jika datamu berupa tabel ribuan baris yang saling berelasi (seperti history transaksi toko).
Kaitan Erat dengan Activity Lifecycle
SharedPreferences (baik getSharedPreferences maupun
getPreferences) adalah "Penyelamat" dari Lifecycle yang rawan terbunuh (App
Process Killed). Pola kerjanya sangat erat dengan status Activity:
-
Fase onCreate() ➔ Waktunya BACA (Load Data)
Saat layar baru dibentuk, kita langsung membuka loker untuk memuat status terakhir (misal: "Apakah user sudah login?", "Berapa High Score-nya?"). -
Fase onPause() / onStop() ➔ Waktunya TULIS (Auto-Save)
Sesaat sebelum aplikasi masuk ke background (kehilangan fokus), ini adalah momen kritis untuk menyimpan draf ketikan atau progres game secara otomatis menggunakaneditor.apply()agar tidak hilang jika tiba-tiba sistem kehabisan RAM.
Konsep Loker Penitipan (Key-Value)
Sistem SharedPreferences bekerja persis seperti loker penitipan barang. Kamu menitipkan barang (Value) ke dalam loker, lalu diberi nomor kunci (Key). Besok-besok saat mengambil, kamu wajib menyerahkan Kunci yang sama persis!
Mengintip Wujud Asli "DataApp.xml"
Di kode Java, kamu hanya mengetik
getSharedPreferences("DataApp", MODE_PRIVATE). Kamu tidak pernah menulis
kode untuk "membuat file baru", kan? Lalu file itu datang dari mana?
Kehebatan Android adalah ia secara diam-diam (otomatis) menciptakan file DataApp.xml tersebut saat kamu pertama kali menyimpan data! File XML dipilih karena sangat ringan dan secepat kilat untuk dibaca.
- Di Simulator Web Ini: Coba lihat
panel sebelah kanan (Isi Folder Android). Kotak hitam yang berisi kode
<map>...</map>itulah wujud asli dari `DataApp.xml`. Coba ketik namamu dan tekan Masuk, perhatikan bagaimana isinya langsung tertulis! - Di HP Android Asli: File ini
benar-benar ada secara fisik! Ia disimpan di rute rahasia:
/data/data/com.aplikasi.kamu/shared_prefs/DataApp.xml. User biasa tidak akan bisa melihat folder ini lewat Galeri/File Manager, karena sistem Android menguncinya ketat khusus hanya untuk aplikasimu saja!
Pilih Loker Berdasarkan Skalanya:
-
1. Global (getSharedPreferences)
Ibarat "Brankas Utama". Data bisa dibaca dari layar mana saja. Digunakan untuk data inti aplikasi.
Contoh Asli: Token Login/Session, Pengaturan Bahasa Aplikasi, Tema Global. -
2. Privat (getPreferences)
Ibarat "Laci Meja Pribadi". Hanya khusus untuk 1 layar itu saja agar tidak mengotori loker utama.
Contoh Asli: Pilihan Sortir (Termurah/Terlaris) di halaman Katalog, Status pop-up "Jangan tampilkan tutorial ini lagi" di layar Kamera.
Simulasi Skala Loker
/shared_prefs/DataApp.xml
1 File Dipakai Bersama
getSharedPreferences("DataApp", 0)
Standar Industri (Google Rules)
1. Hanya 5 Tipe Data
Hanya muat untuk
tipe primitif: String, int, float,
long, dan boolean.
2. Penamaan File
Gunakan format
Package Name aplikasimu agar datanya unik. Contoh: com.kampus.app.DATA.
3. Hapus Data
Gunakan
editor.remove("KUNCI") menghapus 1 data spesifik, atau
editor.clear() membersihkan seluruh isi loker.
2. Simulator Auto-Login
Coba isi nama, centang "Ingat Saya", lalu tekan Masuk. Setelah tiba di Dashboard, coba klik Restart App. Perhatikan bagaimana aplikasi otomatis melompati layar Login karena datamu sudah terselamatkan di SharedPreferences!
KafeApp
Dashboard
Halo,
User!
Selamat datang di Halaman Utama.
Pengaturan Layar
State "Mode Gelap"
disimpan ke Loker Privat layarnya sendiri menggunakan
getPreferences().
Simulasi Auto-Login
Tutup paksa & buka lagi aplikasinya.
<map>
SharedPreferences secara diam-diam membuat file berekstensi .xml tersembunyi di memori internal HP. Kamu bisa melihat simulasi isi file tersebut berubah di kotak atas saat kamu berinteraksi!
3. Bedah Kode: Membangun Auto-Login
Inilah rahasia di balik sistem Auto-Login layaknya aplikasi profesional! Kita akan menggunakan 3 pasang file: Login (MainActivity), Dashboard (HomeActivity), dan Settings (ArtikelActivity).
getString(Kunci, Nilai_Bawaan)), kamu
WAJIB menyertakan parameter kedua yaitu Default Value. Nilai ini akan
dikembalikan oleh sistem jika Kunci tersebut tidak ditemukan di dalam loker (karena user
belum pernah login).
| Menyimpan (Editor) | Membaca (Load) |
|---|---|
| putInt(key, val) | getInt(key, default_val) |
| putString(key, val) | getString(key, default_val) |
| putBoolean(key, val) | getBoolean(key, default_val) |
Layar Login (UI)
Membuat form simpel berisi inputan nama, checkbox (Ingat Saya), dan tombol Login.
<!-- activity_main.xml -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="32dp"
android:gravity="center">
<EditText
android:id="@+id/etUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Masukkan Username"
android:layout_marginBottom="16dp" />
<CheckBox
android:id="@+id/cbRemember"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Ingat Saya (Auto-Login)"
android:layout_marginBottom="24dp" />
<Button
android:id="@+id/btnLogin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="MASUK" />
</LinearLayout>
Cek Auto-Login (Bypass)
Diletakkan di dalam
onCreate() awal. Jika loker sudah berisi nama user, aplikasi akan
langsung menembakkan Intent ke HomeActivity dan menutup layar Login!
Nulis Data (Save)
Diletakkan di dalam klik tombol Login. Memanggil Editor, menyimpan data jika dicentang, lalu pindah ke HomeActivity.
package com.example.sharedpref;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
// 1. Deklarasi Nama File (Global) & Kunci Loker
private static final String PREF_NAME = "DataApp";
private static final String KEY_NAMA = "kunci_username";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ==========================================
// PROSES CEK AUTO-LOGIN SAAT SPLASH SCREEN
// ==========================================
SharedPreferences pref = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
// Fungsi contains mengecek apakah kunci "kunci_username" sudah pernah dibuat sebelumnya?
if (pref.contains(KEY_NAMA)) {
// Jika iya, user pernah mencentang "Ingat Saya". Langsung lompat ke Home!
startActivity(new Intent(MainActivity.this, HomeActivity.class));
finish(); // WAJIB! Tutup layar login ini biar ngga bisa di-Back
return; // Stop eksekusi kode ke bawah
}
// Jika belum login, barulah kita tampilkan form XML-nya
setContentView(R.layout.activity_main);
EditText etUsername = findViewById(R.id.etUsername);
CheckBox cbRemember = findViewById(R.id.cbRemember);
Button btnLogin = findViewById(R.id.btnLogin);
// ==========================================
// PROSES TULIS DATA SAAT TOMBOL KLIK
// ==========================================
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String namaKetikan = etUsername.getText().toString();
if(namaKetikan.isEmpty()) {
Toast.makeText(MainActivity.this, "Isi nama dulu!", Toast.LENGTH_SHORT).show();
return;
}
// Panggil agen "Editor" khusus untuk nulis/ngedit isi brankas
SharedPreferences.Editor editor = pref.edit();
if (cbRemember.isChecked()) {
// Jika dicentang, simpan ketikan secara permanen ke HP
editor.putString(KEY_NAMA, namaKetikan);
editor.apply();
Toast.makeText(MainActivity.this, "Auto-Login Aktif!", Toast.LENGTH_SHORT).show();
} else {
// Jika tidak dicentang, pastikan brankas bersih (Login Sesi Sementara)
editor.remove(KEY_NAMA);
editor.apply();
}
// Pindah ke layar Dashboard / Home
startActivity(new Intent(MainActivity.this, HomeActivity.class));
finish(); // Tutup layar login
}
});
}
}
Layar Dashboard (UI)
Menampilkan teks ucapan selamat datang, tombol Pengaturan Tema, dan tombol Logout berwarna merah.
<!-- activity_home.xml -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="32dp"
android:gravity="center">
<TextView
android:id="@+id/tvWelcome"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Halo, User!"
android:textSize="28sp"
android:textStyle="bold"
android:layout_marginBottom="48dp" />
<Button
android:id="@+id/btnSettings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="PENGATURAN TEMA"
android:layout_marginBottom="16dp" />
<Button
android:id="@+id/btnLogout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="#ef4444" <!-- Warna Merah -->
android:text="LOGOUT (HAPUS SESI)" />
</LinearLayout>
Fitur Logout (Hapus Data)
Sangat penting! Saat user Logout, kita
panggil editor.clear() untuk mengosongkan brankas, lalu menembakkan Intent
kembali ke layar Login.
package com.example.sharedpref;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class HomeActivity extends AppCompatActivity {
private static final String PREF_NAME = "DataApp";
private static final String KEY_NAMA = "kunci_username";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
TextView tvWelcome = findViewById(R.id.tvWelcome);
Button btnSettings = findViewById(R.id.btnSettings);
Button btnLogout = findViewById(R.id.btnLogout);
// 1. BACA DATA LOKER GLOBAL
SharedPreferences pref = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
// Ambil namanya. Jika kosong (Login sementara), ganti dengan "Guest"
String namaTersimpan = pref.getString(KEY_NAMA, "Guest");
tvWelcome.setText("Halo, " + namaTersimpan + "!");
// 2. PINDAH KE LAYAR PENGATURAN (INTENT)
btnSettings.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(HomeActivity.this, ArtikelActivity.class));
}
});
// 3. AKSI LOGOUT (HAPUS SESI)
btnLogout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Bersihkan seluruh isi brankas loker secara permanen
SharedPreferences.Editor editor = pref.edit();
editor.clear();
editor.apply();
Toast.makeText(HomeActivity.this, "Logout Berhasil", Toast.LENGTH_SHORT).show();
// Kembali ke Layar Login
startActivity(new Intent(HomeActivity.this, MainActivity.class));
finish(); // Tutup layar Home ini
}
});
}
}
Tampilan Layar Pengaturan (Privat)
Pastikan kamu menambahkan atribut
android:id="@+id/layoutRoot" pada LinearLayout utamanya agar kita bisa
mengubah warna background layar dari Java!
<!-- activity_artikel.xml -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layoutRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="32dp"
android:gravity="center">
<TextView
android:id="@+id/tvJudul"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Layar Pengaturan Tema"
android:textSize="20sp"
android:textStyle="bold"
android:layout_marginBottom="24dp" />
<Switch
android:id="@+id/switchTheme"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Aktifkan Mode Gelap" />
</LinearLayout>
Implementasi Loker Privat
Menggunakan
getPreferences(MODE_PRIVATE). Kita tidak perlu menyuplai nama file, karena
file otomatis dinamai sesuai nama class (contoh: ArtikelActivity.xml).
package com.example.sharedpref;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
public class ArtikelActivity extends AppCompatActivity {
private static final String KEY_DARK_MODE = "is_dark_mode";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_artikel);
Switch switchTheme = findViewById(R.id.switchTheme);
// 1. PROSES BACA DATA DARI LOKER PRIVAT
// Ingat, panggil getPreferences(), BUKAN getSharedPreferences()
SharedPreferences prefLokal = getPreferences(MODE_PRIVATE);
// Baca data boolean. Default-nya false (Mode Terang)
boolean isDark = prefLokal.getBoolean(KEY_DARK_MODE, false);
// Terapkan ke UI
switchTheme.setChecked(isDark);
terapkanTemaKeLayar(isDark);
// 2. PROSES TULIS DATA SAAT SWITCH DIGESER
switchTheme.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// Terapkan fungsi ganti warna background
terapkanTemaKeLayar(isChecked);
// Simpan ke Loker Privat secara permanen
SharedPreferences.Editor editor = prefLokal.edit();
editor.putBoolean(KEY_DARK_MODE, isChecked);
editor.apply();
}
});
}
// Fungsi bantuan untuk merubah warna background
private void terapkanTemaKeLayar(boolean isDark) {
View root = findViewById(R.id.layoutRoot);
TextView tvJudul = findViewById(R.id.tvJudul);
Switch swTheme = findViewById(R.id.switchTheme);
if (isDark) {
root.setBackgroundColor(Color.parseColor("#121212")); // Gelap
tvJudul.setTextColor(Color.WHITE);
swTheme.setTextColor(Color.WHITE);
} else {
root.setBackgroundColor(Color.parseColor("#FFFFFF")); // Terang
tvJudul.setTextColor(Color.BLACK);
swTheme.setTextColor(Color.BLACK);
}
}
}
4. Ilmu Tingkat Lanjut (Advanced)
Di industri nyata (aplikasi modern 2024+), menggunakan `SharedPreferences` biasa ternyata punya celah keamanan dan keterbatasan. Inilah trik pro untuk mengatasinya:
Kamus Mini: Apa arti MODE_PRIVATE?
Pernah bingung kenapa kita harus selalu mengetik
getSharedPreferences("Nama", MODE_PRIVATE)?
MODE_PRIVATE (atau angka 0) adalah parameter Hak Akses (Izin Keamanan). Ini
berarti, file XML loker yang kamu buat di HP user HANYA BISA dibaca dan diedit
oleh aplikasimu sendiri. Aplikasi lain tidak akan bisa membobol isinya!
Security Alert!
Di tutorial Android jadul, kamu mungkin melihat opsi
MODE_WORLD_READABLE. JANGAN PERNAH DIPAKAI! Mode itu membuka
gembok file-mu untuk semua aplikasi, bikin sangat gampang diretas hacker! Mode usang
tersebut kini sudah dilarang keras oleh sistem Android modern.
A. Mengapa MODE_WORLD_READABLE Diharamkan?
Dulu, developer bisa memaksa loker XML agar bisa dibaca oleh aplikasi lain dengan
mengganti parameter MODE_PRIVATE menjadi
MODE_WORLD_READABLE.
Ini adalah malapetaka keamanan!
Coba pilih mode hak akses di bawah, lalu lihat apa yang terjadi ketika aplikasi
Hacker mencoba membaca lokermu!
B. Keamanan Ekstra: EncryptedSharedPreferences
Bahkan jika kamu sudah memakai MODE_PRIVATE, jika HP user sudah di-root,
sang hacker masih bisa membuka file XML lokermu dengan akses super-admin dan membaca
isinya secara gamblang (plain text)!
Solusinya dari Google: Gunakan library AndroidX Security. Ia akan mengenkripsi nama
kunci dan isinya menjadi teks acak (gibberish) yang tidak bisa dibaca manusia.
// 1. Tambahkan di build.gradle: implementation "androidx.security:security-crypto:1.1.0-alpha06"
// 2. Cara pakainya (sedikit lebih panjang dari biasa):
MasterKey masterKey = new MasterKey.Builder(this)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
this,
"rahasia_app_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
// 3. Simpan data seperti biasa! Hasil di XML-nya akan berbentuk acak spt: "XyZ1@#kK="
sharedPreferences.edit().putString("TOKEN", "Rahasia123").apply();
C. Reactive: Memantau Perubahan Data
Bagaimana jika kamu punya Layar A (Home) dan Layar B (Setting)? Jika user mengubah Mode
Gelap di Layar B, bagaimana Layar A bisa tahu dan ikut berubah secara otomatis
(real-time)?
Jawabannya: Pasang "telinga pendengar" bernama
OnSharedPreferenceChangeListener.
// Di dalam MainActivity (Layar A), pasang pendengar:
SharedPreferences.OnSharedPreferenceChangeListener listener =
new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if (key.equals("is_dark_mode")) {
// Data berubah! Segera update UI layar ini secara otomatis!
boolean isDark = prefs.getBoolean(key, false);
// ... panggil fungsi ganti warna ...
}
}
};
// Daftarkan telinga tersebut ke lokernya
pref.registerOnSharedPreferenceChangeListener(listener);
D. Trik Pro GSON: Gimana Kalau Mau Nyimpen Object?
Aturan
resminya kan nggak boleh nyimpen Object Java utuh (misal class User yang
berisi Nim, Nama, Umur). Tapi di dunia kerja, developer mengakalinya menggunakan
Library GSON buatan Google! Coba simulator di bawah ini:
1. Object Java (User.java)
2. Hasil di SharedPreferences XML
<!-- Disimpan sebagai String panjang! -->
<string name="DATA_USER_LENGKAP">
{"nama":"Budi Santoso","umur":20}
</string>
</map>
Implementasi Kodenya:
// 1. Tambahkan di build.gradle: implementation 'com.google.code.gson:gson:2.10.1'
// 2. Menulis Data (Save)
User userBaru = new User("Budi Santoso", 20);
// Blender object jadi string format JSON
Gson gson = new Gson();
String jsonTeks = gson.toJson(userBaru);
editor.putString("DATA_USER_LENGKAP", jsonTeks);
editor.apply();
// 3. Membaca Data (Load) & Mengembalikannya jadi Object
String jsonMasuk = pref.getString("DATA_USER_LENGKAP", "");
if (!jsonMasuk.isEmpty()) {
// Sulap string JSON kembali menjadi Object User.java
User userTersimpan = gson.fromJson(jsonMasuk, User.class);
// Sekarang bisa pakai method Getter-nya lagi!
String namanya = userTersimpan.getNama();
}
5. Tugas Implementasi (Challenge)
Udah paham konsep penitipan tasnya? Coba aplikasikan ilmu ini ke kasus lain yang sangat populer: Sistem High Score Game!
Misi Aplikasi "Game Clicker":
- Buat project baru. Tampilan cukup berisi 2 teks (Teks "Skor Sekarang" & Teks "High Score") dan 1 Tombol "KLIK SAYA!".
- Tiap kali tombol diklik, variabel skor (integer) bertambah 1 dan mengubah tulisan "Skor Sekarang".
- Syarat SharedPreferences: Setiap kali tombol diklik, cek apakah "Skor Sekarang"
lebih besar dari "High Score" yang tersimpan? Jika iya, selamatkan skor baru
tersebut ke SharedPreferences pakai
editor.putInt(...)! - Pastikan di dalam `onCreate()`, kamu menarik data High Score dari SharedPreferences untuk langsung ditampilkan ke layar saat aplikasi dibuka pertama kali. Gunakan angka 0 sebagai Nilai Default.
- Tantangan Ekstra: Tambahkan satu Tombol "RESET REKOR". Jika ditekan, hapus
data High Score di SharedPreferences menggunakan
editor.remove(), lalu kembalikan angka High Score di layar menjadi 0. - Tutup paksa aplikasi (swipe up dari Recent Apps), buka lagi. Jika High Score tidak balik ke 0, KAMU BERHASIL! 🏆
Kunci Jawaban Lengkap (XML & Java):
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="32dp"
android:gravity="center">
<TextView
android:id="@+id/tvHighScore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="High Score: 0"
android:textSize="24sp"
android:textStyle="bold"
android:layout_marginBottom="48dp" />
<TextView
android:id="@+id/tvSkor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Skor: 0"
android:textSize="20sp"
android:layout_marginBottom="16dp" />
<Button
android:id="@+id/btnKlik"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="KLIK SAYA!" />
<Button
android:id="@+id/btnReset"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="RESET REKOR"
android:layout_marginTop="16dp" />
</LinearLayout>