11
ResourcesHolder dan Pengiriman via Broadcast
ResourcesHolder adalah kelas Parcelable di modul resources-helper yang menyimpan objek berkunci pada satu HashMap<String, Object>. Sejak versi 0.0.5 perannya dialihkan: kelas ini tidak lagi dipakai untuk menghindari Java reflection dengan menampung resource internal hasil InternalAccessor, melainkan wadah umum untuk objek apa pun yang perlu dipindahkan antar kelas, misalnya dari activity ke service lewat broadcast. resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:18-301
Bagian KDoc yang bertanda "Obsolete documentation (v0.0.4 and lower)" masih memuat contoh lama seperti putString dan putResourceId; nama method itu sudah tidak ada di kelas ini, sedangkan contoh pengiriman broadcast di bagian bawahnya tetap sesuai dengan API sekarang. resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:25-562
Konstanta ACTION_SEND_RESOURCES_HOLDER bernilai com.anggrayudi.hiddenapi.ACTION_SEND_RESOURCES_HOLDER, dan seluruh isi disimpan pada field privat mValues. resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:58-723
Bentuk penyimpanan dan serialisasi Parcel
Diagram berikut menelusuri satu nilai dari put sampai diambil kembali oleh getter setelah melewati Parcel.
graph LR
put["put(String, T)"] --> mValues["mValues: HashMap"]
mValues --> writeToParcel["writeToParcel(dest, flags)"]
writeToParcel --> parcel["Parcel"]
parcel --> creator["CREATOR.createFromParcel"]
creator --> restored["mValues hasil readValue"]
restored --> getters["getAsShort ... getAsObject"]
getters --> cast{"isCompatibleCast"}
cast -->|true| value["nilai asli"]
cast -->|false| fallback["0 / null"]
mValues --> printAll["printAll()"]
printAll --> log["Log.d pada TAG ResourcesHolder"]
style mValues fill:#336791,color:#fff
style writeToParcel fill:#f59e0b,color:#000
style creator fill:#8b5cf6,color:#fff
style restored fill:#8b5cf6,color:#fff
style value fill:#2d7d46,color:#fff
style fallback fill:#dc2626,color:#fff
style log fill:#2d7d46,color:#fff
%% fig: Perjalanan nilai dari put sampai Parcel dan getter
%% desc: Nilai masuk ke mValues, ditulis oleh writeToParcel ke Parcel, dibaca balik oleh CREATOR, lalu diambil getter bertipe dengan pemeriksaan isCompatibleCast.Serialisasi hanya menyentuh satu field: konstruktor privat membaca in.readValue(HashMap.class.getClassLoader()), sedangkan writeToParcel memanggil dest.writeValue(mValues) dan describeContents mengembalikan 0. resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:78-804 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:383-3915
put dan get bertipe
Sembilan overload put(String, ...) menerima short, byte, char, boolean, int, long, float, double, dan String; semuanya mengembalikan this sehingga pemanggilan bisa dirantai. resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:82-1256
Untuk tipe non-primitif ada put(String, Object). KDoc-nya menyebut Drawable, XmlResourceParser, List, dan array, dengan pengambilan kembali lewat cast ke kelas asal. resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:127-1587
Getter memeriksa tipe lebih dulu lewat isCompatibleCast, yang memanggil classToCast.cast(obj) dan menangkap ClassCastException. Bila cast gagal, getter mengembalikan nilai default. resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:160-1748 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:176-2029
| Getter | Tipe target | Default saat cast gagal |
|---|---|---|
getAsShort, getAsByte | Short, Byte | 0 |
getAsChar | Character | '\u0000' |
getAsBoolean | Boolean | false |
getAsInteger, getAsLong, getAsFloat, getAsDouble | Integer, Long, Float, Double | 0 |
getAsString | String | null |
getAsShortArray sampai getAsStringArray | array primitif dan String[] | null |
getAsObject | apa pun | tidak ada pemeriksaan, isi mValues dikembalikan apa adanya |
Getter array mencakup getAsShortArray, getAsByteArray, getAsCharArray, getAsBooleanArray, getAsIntArray, getAsLongArray, getAsFloatArray, getAsDoubleArray, dan getAsStringArray; getAsObject melewati isCompatibleCast sama sekali. resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:204-24610
⚠️ Nilai default di tabel hanya berlaku bila
ClassCastExceptionbenar-benar dilempar. Untuk key yang tidak ada,Class.cast(null)mengembalikannulltanpa exception, sehinggaisCompatibleCastmengembalikantruedan getter primitif melanjutkan ke unboxing terhadapnull. PemeriksaanmValues.get(key) instanceof Number && obj == nulljuga tidak pernah benar karena sisi kiri sudah memastikanobjbukannull. resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:367-38111
Method pengelolaan isi yang tersedia: clear(), size(), containsKey(String), remove(String), keySet(), dan valueSet(). resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:275-28812 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:290-29513 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:311-32714
sort dan printAll
sort(boolean descending) menyalin mValues ke TreeMap, memanggil treeMap.descendingMap() bila descending bernilai true, lalu mengisi mValues kembali dengan putAll(treeMap). resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:297-30915
printAll() mengiterasi mValues.entrySet(), memformat nilai array dengan Arrays.toString untuk sembilan tipe array, lalu menulis Log.d dengan TAG ResourcesHolder. resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:329-36016 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:7117
⚠️ Parameter
descendingtidak mengubah hasil. Nilai baliktreeMap.descendingMap()tidak dipakai, danputAll(treeMap)mengembalikan isi kemValuesyang bertipeHashMap, sehingga urutan iterasiprintAll()dankeySet()tetap ditentukan oleh hashingHashMap. Sample memanggilholder.sort(true), padahal efeknya sama dengansort(false). resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:297-30915 sample/src/main/java/com/anggrayudi/hiddenapi/sample/Receiver.kt:1818
⚠️
printAll()memanggilvalue.toString()sebelum memeriksa apakah nilai berupa array, tanpa penjagaannull.put(String, Object)sendiri tidak menolaknull, jadi satu nilainullmenggagalkanprintAll()sebelum entri lain tercetak. resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:329-33719 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:155-15820
Mengirim lewat ACTION_SEND_RESOURCES_HOLDER
sequenceDiagram
participant Caller as Pemanggil
participant Holder as ResourcesHolder
participant Intent as Intent
participant Ctx as Context
participant Receiver as Receiver
Caller->>Holder: "put(key, value)"
Caller->>Holder: "sendBroadcast(context, key)"
Holder->>Intent: "new Intent(ACTION_SEND_RESOURCES_HOLDER)"
Holder->>Intent: "putExtra(key, this)"
Holder->>Ctx: "context.sendBroadcast(intent)"
Ctx->>Receiver: "onReceive(context, intent)"
Receiver->>Intent: "IntentCompat.getParcelableExtra(intent, key)"
alt extra tersedia
Intent-->>Receiver: ResourcesHolder
Receiver->>Holder: "sort(true)"
Receiver->>Holder: "printAll()"
Holder->>Holder: "Log.d pada TAG ResourcesHolder"
else extra null
Note over Receiver: onReceive berhenti lewat ?: return
end
style Holder fill:#336791,color:#fff
style Intent fill:#f59e0b,color:#000
style Receiver fill:#2d7d46,color:#fff
%% fig: Alur pengiriman ResourcesHolder lewat broadcast
%% desc: Pemanggil mengisi holder, mengirimnya sebagai extra Intent dengan action ACTION_SEND_RESOURCES_HOLDER, lalu Receiver membacanya dan mencetak seluruh isi lewat printAll.Dua method pembungkus menyiapkan Intent dengan action ACTION_SEND_RESOURCES_HOLDER dan meletakkan holder pada extra dengan key yang diberikan pemanggil. sendBroadcast(Context, String) meneruskannya ke context.sendBroadcast(intent), sedangkan sendViaLocalBroadcastManager(Context, String) mengirimnya lewat LocalBroadcastManager.getInstance(context).sendBroadcast(intent). resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:248-26021 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:262-27322
| Method | Extra | Jalur pengiriman |
|---|---|---|
sendBroadcast(Context, String) | intent.putExtra(key, this) | context.sendBroadcast(intent) |
sendViaLocalBroadcastManager(Context, String) | intent.putExtra(key, this) | LocalBroadcastManager.getInstance(context).sendBroadcast(intent) |
Sisi penerima ada di sample. Receiver dideklarasikan pada manifest dengan android:exported="false" dan intent-filter yang memakai action sama persis dengan konstanta tersebut. sample/src/main/AndroidManifest.xml:29-3523
Receiver.onReceive menampilkan Toast ResourceHolder is received. See detail on LogCat., lalu mengambil extra dengan IntentCompat.getParcelableExtra(intent, "holder", ResourcesHolder::class.java). Bila hasilnya null, method berhenti lewat ?: return; bila tidak, holder di-sort(true) dan di-printAll(). sample/src/main/java/com/anggrayudi/hiddenapi/sample/Receiver.kt:13-1924
⚠️
Receiverdi-declare di manifest, jadi ia dilayani jalurContext.sendBroadcast.LocalBroadcastManagerhanya menjangkau receiver yang didaftarkan secara dinamis di proses yang sama, sehinggasendViaLocalBroadcastManagertidak akan sampai keReceivertersebut. Nama action di manifest dan nama extra"holder"juga ditulis manual, jadi keduanya harus disamakan dengan pemanggil.
Posisi di sample
Sample tidak memuat pemanggil sendBroadcast untuk kelas ini. MainActivity hanya menyentuh ResourcesHolder lewat contoh InternalAccessor.Builder yang sudah ditandai DEPRECATED EXAMPLE dan memanggil builder.getResourcesHolder(). sample/src/main/java/com/anggrayudi/hiddenapi/sample/MainActivity.kt:151-16425 Pembacaan resource internal itu sendiri dibahas di halaman InternalAccessor: Membaca Resource com.android.internal.