Triển khai Parcelable cho Custom Object
Parcelable là interface Android (android.os.Parcelable) để serialize object hiệu năng cao. Không dùng reflection, yêu cầu viết code đọc/ghi thủ công nhưng nhanh hơn Serializable 10-20 lần.
Bước 1: Tạo dự án mới
Khởi tạo project Android Studio.
Bước 2: Model Class Parcelable (src/ParcelableObject.java)
package com.example.myapplication;
import android.os.Parcel;
import android.os.Parcelable;
public class ParcelableObject implements Parcelable {
private String name;
private int age;
private String email;
private boolean isActive;
// Constructor thông thường
public ParcelableObject(String name, int age, String email, boolean isActive) {
this.name = name;
this.age = age;
this.email = email;
this.isActive = isActive;
}
// ========== PARCELABLE IMPLEMENTATION ==========
// 1. Constructor đọc từ Parcel - THỨ TỰ PHẢI KHỚP writeToParcel
protected ParcelableObject(Parcel in) {
name = in.readString();
age = in.readInt();
email = in.readString();
isActive = in.readByte() != 0; // Boolean đọc qua byte
}
// 2. CREATOR - Bắt buộc, public static final
public static final Creator<ParcelableObject> CREATOR = new Creator<ParcelableObject>() {
@Override
public ParcelableObject createFromParcel(Parcel in) {
return new ParcelableObject(in);
}
@Override
public ParcelableObject[] newArray(int size) {
return new ParcelableObject[size];
}
};
@Override
public int describeContents() {
return 0; // Thường trả về 0, trừ khi có FileDescriptor
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
dest.writeString(email);
dest.writeByte((byte) (isActive ? 1 : 0)); // Boolean ghi qua byte
}
// ========== GETTERS ==========
public String getName() { return name; }
public int getAge() { return age; }
public String getEmail() { return email; }
public boolean isActive() { return isActive; }
@Override
public String toString() {
return "ParcelableObject{name='" + name + "', age=" + age + ", email='" + email + "', active=" + isActive + "}";
}
}
Bước 3: Layout (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<Button
android:id="@+id/btnParcelable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tạo & Truyền Parcelable Object"
android:padding="16dp" />
</LinearLayout>
Bước 4: MainActivity.java - Truyền qua Intent
package com.example.myapplication;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btnParcelable).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ParcelableObject user = new ParcelableObject(
"Sairam Krishna", 28, "sairam@example.com", true
);
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
intent.putExtra("user_parcelable", user); // Parcelable object
startActivity(intent);
Toast.makeText(MainActivity.this, "Đã gửi Parcelable", Toast.LENGTH_SHORT).show();
}
});
}
}
Bước 5: DetailActivity.java - Nhận object
package com.example.myapplication;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
public class DetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
// Nhận Parcelable - KHÔNG cần cast explicit như Serializable
ParcelableObject user = getIntent().getParcelableExtra("user_parcelable");
TextView tvInfo = findViewById(R.id.tvInfo);
if (user != null) {
tvInfo.setText("Nhận Parcelable:\n" + user.toString());
} else {
tvInfo.setText("Không nhận được object");
}
}
}
Android Studio Auto-generate Parcelable (Rất quan trọng!)
- Viết class với các field
- Alt+Insert (Windows/Linux) / Cmd+N (Mac)
- Chọn Parcelable
- Chọn các field cần parcel
- OK → IDE sinh toàn bộ code boilerplate
Parcelable với các kiểu dữ liệu phức tạp
public class ComplexParcelable implements Parcelable {
private String name;
private List<String> tags; // List
private List<ParcelableObject> items; // List
private ParcelableObject detail; // Nested Parcelable
private int[] scores; // Primitive array
private Bundle extras; // Bundle
private SparseArray<String> map; // SparseArray
protected ComplexParcelable(Parcel in) {
name = in.readString();
// List
tags = new ArrayList<>();
in.readStringList(tags);
// List
items = new ArrayList<>();
in.readTypedList(items, ParcelableObject.CREATOR);
// Nested Parcelable
detail = in.readParcelable(ParcelableObject.class.getClassLoader());
// Primitive array
scores = in.createIntArray();
// Bundle
extras = in.readBundle(getClass().getClassLoader());
// SparseArray
map = new SparseArray<>();
int size = in.readInt();
for (int i = 0; i < size; i++) {
int key = in.readInt();
String value = in.readString();
map.put(key, value);
}
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeStringList(tags);
dest.writeTypedList(items);
dest.writeParcelable(detail, flags);
dest.writeIntArray(scores);
dest.writeBundle(extras);
// SparseArray custom write
dest.writeInt(map.size());
for (int i = 0; i < map.size(); i++) {
dest.writeInt(map.keyAt(i));
dest.writeString(map.valueAt(i));
}
}
// CREATOR... (tương tự)
}
Parcelable vs Serializable - Tóm tắt
| Đặc điểm | Serializable | Parcelable |
|---|---|---|
| Interface | java.io.Serializable | android.os.Parcelable |
| Cơ chế | Reflection | Manual read/write |
| Tốc độ | Chậm | Nhanh (10-20x) |
| Garbage objects | Nhiều | Ít |
| Code boilerplate | Không (chỉ implements) | Nhiều (nhưng IDE auto-gen) |
| Intent.putExtra | putExtra(key, Serializable) | putExtra(key, Parcelable) |
| Intent.getExtra | getSerializableExtra(key) + cast | getParcelableExtra(key) - no cast |
Best Practices
- Luôn dùng IDE auto-generate - đừng viết tay
- Giữ thứ tự read/write y hệt nhau
- Dùng
writeTypedList()/readTypedList()cho List<Parcelable> - Dùng
writeParcelable()/readParcelable()cho nested object - Primitive arrays:
writeIntArray(),createIntArray(), etc. - Bundle:
writeBundle()/readBundle() - Enum:
writeInt(ordinal)/values()[readInt()]
@Parcelize (Kotlin only)
// Kotlin - chỉ cần annotation
@Parcelize
data class User(
val name: String,
val age: Int,
val email: String
) : Parcelable