DetailsActivity 中宣告 ImageView thumbnail = findViewById(...) 在類別層級,導致 setContentView 前呼叫 findViewById 回傳 null,後續 onPostExecute 設定圖片拋出 NPE。
public class DetailsActivity extends Activity {
ImageView thumbnail = (ImageView) findViewById(R.id.btnThumbnail); // 錯誤:onCreate 前執行,返回 null
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.detail); // 此時才膨脹佈局
// ...
}
}
public class DetailsActivity extends Activity {
private ImageView thumbnail; // 僅宣告
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.detail); // 先膨脹佈局
thumbnail = findViewById(R.id.btnThumbnail); // 再 findViewById
String img = getIntent().getStringExtra("Thumbnail");
new DownloadImageTask(thumbnail).execute("https://mysite.com/images/" + img);
}
// AsyncTask 內部類別...
}
super.onCreate() 前執行。thumbnail 參考而非在欄位初始化時查找。// build.gradle
implementation "com.github.bumptech.glide:glide:4.16.0"
annotationProcessor "com.github.bumptech.glide:compiler:4.16.0"
// 使用
Glide.with(this)
.load("https://mysite.com/images/" + img)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.into(thumbnail);
優勢:自動生命週期管理、記憶體快取、GIF 支援、避免 OOM、程式碼極簡。
private static class DownloadImageTask extends AsyncTask {
private final WeakReference viewRef; // 弱參考防洩漏
DownloadImageTask(ImageView view) { this.viewRef = new WeakReference<>(view); }
@Override
protected Bitmap doInBackground(String... urls) {
try (InputStream in = new URL(urls[0]).openStream()) {
return BitmapFactory.decodeStream(in);
} catch (IOException e) { return null; }
}
@Override
protected void onPostExecute(Bitmap bitmap) {
ImageView view = viewRef.get();
if (view != null && bitmap != null) view.setImageBitmap(bitmap);
}
}
理解 Android View 生命週期與欄位初始化順序,是避免 NPE 的基本功。