Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีใช้ Android Loader (LoaderManager)

ภาพรวม

ตัวอย่างนี้แสดงวิธีใช้ LoaderManager และ CursorLoader เพื่อโหลดข้อมูลจาก Content Provider (Contacts) แบบ Asynchronous ซึ่งช่วยป้องกัน ANR และจัดการ Lifecycle อัตโนมัติ

หมายเหตุสำคัญ

Loader API ถูก Deprecated ตั้งแต่ API 28 (Android 9.0) แนะนำให้ใช้ ViewModel + LiveData + Room หรือ Coroutines + Flow แทน แต่ตัวอย่างนี้ยังคงมีประโยชน์สำหรับการบำรุงรักษาโค้ดเก่า (Legacy Code)

ขั้นตอนการสร้าง

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

เปิด Android Studio ไปที่ File → New Project และกรอกข้อมูลที่จำเป็น

ขั้นตอนที่ 2: ออกแบบ Layout (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true" />
</LinearLayout>

ขั้นตอนที่ 3: เขียนโค้ดหลัก (src/MainActivity.java)

package com.example.myapplication;

import android.Manifest;
import android.content.CursorLoader;
import android.content.Loader;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.SimpleCursorAdapter;
import android.widget.ListView;

public class MainActivity extends FragmentActivity 
        implements android.app.LoaderManager.LoaderCallbacks<Cursor> {

    private static final int LOADER_CONTACTS = 100;
    private static final int PERMISSION_CONTACTS = 101;
    private static final String[] PROJECTION = {
        ContactsContract.Contacts._ID,
        ContactsContract.Contacts.DISPLAY_NAME
    };
    
    ListView lstContact;
    SimpleCursorAdapter adapter;
    String[] from = {ContactsContract.Contacts.DISPLAY_NAME};
    int[] to = {android.R.id.text1};

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        adapter = new SimpleCursorAdapter(this, 
                android.R.layout.simple_list_item_1, null, from, to, 0);
        
        ListView listView = findViewById(R.id.list);
        listView.setAdapter(adapter);
        
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) 
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, 
                    new String[]{Manifest.permission.READ_CONTACTS}, PERMISSION_CONTACTS);
        } else {
            getLoaderManager().initLoader(LOADER_CONTACTS, null, this);
        }
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
        if (id == LOADER_CONTACTS) {
            return new CursorLoader(this, 
                    ContactsContract.Contacts.CONTENT_URI, 
                    PROJECTION, null, null, null);
        }
        return null;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        adapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        adapter.swapCursor(null);
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == PERMISSION_CONTACTS && grantResults.length > 0 
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            getLoaderManager().initLoader(LOADER_CONTACTS, null, this);
        }
    }
}

หลักการทำงานของ Loader

  1. onCreateLoader() - สร้าง Loader ใหม่ (รันบน Background Thread)
  2. onLoadFinished() - เรียกเมื่อโหลดเสร็จ (รันบน UI Thread)
  3. onLoaderReset() - เรียกเมื่อ Loader ถูก Reset

ข้อดีของ Loader

  • โหลดข้อมูลแบบ Asynchronous ป้องกัน ANR
  • จัดการ Lifecycle อัตโนมัติ (หยุดโหลดเมื่อ Activity หยุด ทำต่อเมื่อ Resume)
  • Cache ผลลัพธ์ ไม่ต้องโหลดซ้ำ عند Configuration Change
  • Monitor การเปลี่ยนแปลงข้อมูล Content Provider อัตโนมัติ

ทางเลือกสมัยใหม่ (แนะนำ)

// ViewModel + LiveData + Room
@Dao
interface ContactDao {
    @Query("SELECT * FROM contacts")
    LiveData<List<Contact>> getAllContacts();
}

class ContactViewModel extends AndroidViewModel {
    LiveData<List<Contact>> contacts = contactDao.getAllContacts();
}

// ใน Activity/Fragment
viewModel.contacts.observe(this, contacts -> adapter.submitList(contacts));