Lompat ke konten
dokumentasi.id
Commit 98ceeb4
v2
Star3,6 rb

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

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-30

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-56

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-72

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.
Fig. 1: Perjalanan nilai dari put sampai Parcel dan getterNilai 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-80 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:383-391

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-125

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-158

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-174 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:176-202

GetterTipe targetDefault saat cast gagal
getAsShort, getAsByteShort, Byte0
getAsCharCharacter'\u0000'
getAsBooleanBooleanfalse
getAsInteger, getAsLong, getAsFloat, getAsDoubleInteger, Long, Float, Double0
getAsStringStringnull
getAsShortArray sampai getAsStringArrayarray primitif dan String[]null
getAsObjectapa puntidak 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-246

⚠️ Nilai default di tabel hanya berlaku bila ClassCastException benar-benar dilempar. Untuk key yang tidak ada, Class.cast(null) mengembalikan null tanpa exception, sehingga isCompatibleCast mengembalikan true dan getter primitif melanjutkan ke unboxing terhadap null. Pemeriksaan mValues.get(key) instanceof Number && obj == null juga tidak pernah benar karena sisi kiri sudah memastikan obj bukan null. resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:367-381

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-288 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:290-295 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:311-327

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-309

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-360 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:71

⚠️ Parameter descending tidak mengubah hasil. Nilai balik treeMap.descendingMap() tidak dipakai, dan putAll(treeMap) mengembalikan isi ke mValues yang bertipe HashMap, sehingga urutan iterasi printAll() dan keySet() tetap ditentukan oleh hashing HashMap. Sample memanggil holder.sort(true), padahal efeknya sama dengan sort(false). resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:297-309 sample/src/main/java/com/anggrayudi/hiddenapi/sample/Receiver.kt:18

⚠️ printAll() memanggil value.toString() sebelum memeriksa apakah nilai berupa array, tanpa penjagaan null. put(String, Object) sendiri tidak menolak null, jadi satu nilai null menggagalkan printAll() sebelum entri lain tercetak. resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:329-337 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:155-158

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.
Fig. 2: Alur pengiriman ResourcesHolder lewat broadcastPemanggil 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-260 resources-helper/src/main/java/com/anggrayudi/hiddenapi/ResourcesHolder.java:262-273

MethodExtraJalur 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-35

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-19

⚠️ Receiver di-declare di manifest, jadi ia dilayani jalur Context.sendBroadcast. LocalBroadcastManager hanya menjangkau receiver yang didaftarkan secara dinamis di proses yang sama, sehingga sendViaLocalBroadcastManager tidak akan sampai ke Receiver tersebut. 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-164 Pembacaan resource internal itu sendiri dibahas di halaman InternalAccessor: Membaca Resource com.android.internal.