首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android AsyncTask 圖片載入空指標:ImageView 宣告時機錯誤

     編輯:程式設計綜合問答

問題現象

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 內部類別...
}

核心原則

  • findViewById 必須在 setContentView 之後:佈局膨脹完成,View 才存在於視圖樹中。
  • 類別層級欄位初始化順序早於 onCreate:宣告時賦值會在 super.onCreate() 前執行。
  • AsyncTask 內部類別持有 View 參考:傳入 thumbnail 參考而非在欄位初始化時查找。

現代替代方案:Glide/Coil + 協程

// 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、程式碼極簡。

AsyncTask 改良版(若必須維護舊碼)

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 的基本功。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved