الـ Intents وتشغيل الـ Activities
نادرًا ما تتكوّن تطبيقات Android من شاشة واحدة. والآلية التي تنقل المستخدم من شاشة إلى أخرى — وتتيح لنظام Android توجيه الطلبات بين المكوّنات — هي Intent. يُعدّ فهم كيفية بناء Intent وإطلاقه من أهم مهارات التنقل الأساسية في تطوير Android.
ما هو Intent؟
Intent هو كائن رسائلي. يصف إجراءً تريد التطبيقَ تنفيذه. عندما تريد فتح Activity ثانية، تُنشئ Intent يُسمّي تلك Activity هدفًا، وتُرفق به اختياريًا البيانات التي تريد تمريرها، ثم تُسلّمه إلى نظام التشغيل. يبحث النظام عن الهدف، ويتحقق من إعلانه في الـ manifest، ويُشغّله أو يُستأنفه، ويُسلّم إليه Intent.
نوعان من Intent: يُسمّي الـ Intent الصريح (Explicit) مكوّنًا بعينه باسم الفئة — استخدم هذا دائمًا للتنقل داخل تطبيقك. أما الـ Intent الضمني (Implicit) فيُعلن عن إجراء (مثل ACTION_VIEW مع رابط URL) ويترك للنظام اكتشاف أي تطبيق قادر على التعامل معه. يُركّز هذا الدرس بالكامل على الـ Intents الصريحة.
إنشاء Intent صريح
يأخذ المُنشئ الأساسي كائن Context و.class الخاصة بـ Activity الهدف:
Intent intent = new Intent(this, DetailActivity.class);
startActivity(intent);
تُشير this إلى الـ Activity الحالية التي تنفّذ الواجهة Context. أما DetailActivity.class فهي مرجع وقت الترجمة إلى الهدف. سيُشغّل النظام DetailActivity باستدعاء onCreate() (أو onRestart() / onResume() إن كان ثمة نسخة موجودة تُعاد، بحسب وضع التشغيل launch mode).
تمرير البيانات عبر Extras
تحمل الـ Intents مخزنًا للقيم المفتاحية يُسمّى حزمة extras. استخدم putExtra(key, value) لإرفاق البيانات قبل إطلاق Intent، واستخدم الدالة المقابلة getXxxExtra(key, defaultValue) في الوجهة لاسترجاعها.
// في Activity المُطلِقة (مثلاً MainActivity.java)
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("product_id", 42);
intent.putExtra("product_name", "Wireless Keyboard");
startActivity(intent);
// في DetailActivity.java — داخل onCreate()
int productId = getIntent().getIntExtra("product_id", -1);
String name = getIntent().getStringExtra("product_name");
if (productId == -1) {
// حماية: المُستدعي نسي تمرير المعرّف
finish();
return;
}
titleTextView.setText(name);
عرّف مفاتيح extras كثوابت. أعلن عنها بوصفها حقولًا public static final String في Activity المستقبِلة. هكذا يستورد المُستدعي الثابت ولا يقع خطأ إملائي في وقت التشغيل.
// DetailActivity.java
public class DetailActivity extends AppCompatActivity {
public static final String EXTRA_PRODUCT_ID = "product_id";
public static final String EXTRA_PRODUCT_NAME = "product_name";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
int id = getIntent().getIntExtra(EXTRA_PRODUCT_ID, -1);
String n = getIntent().getStringExtra(EXTRA_PRODUCT_NAME);
// ...
}
}
// MainActivity.java — موضع الإطلاق
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra(DetailActivity.EXTRA_PRODUCT_ID, selectedItem.getId());
intent.putExtra(DetailActivity.EXTRA_PRODUCT_NAME, selectedItem.getName());
startActivity(intent);
مكدس التراجع (Back Stack)
في كل مرة تستدعي فيها startActivity()، يدفع Android الـ Activity الجديدة إلى مكدس التراجع الخاص بالمهمة الحالية. بالضغط على زر التراجع في النظام أو باستدعاء finish()، تُزال الـ Activity العلوية ويعود المستخدم إلى التي تحتها. يمنح ذلك المستخدم سجلًا متماسكًا للتنقل دون أي كود إضافي من جانبك.
// للعودة إلى الشاشة المُستدعِية برمجيًا:
finish(); // تُزيل هذه الـ Activity من المكدس
استرداد نتيجة — ActivityResultLauncher
أحيانًا توجد الشاشة الثانية لجمع معلومات تحتاجها الشاشة الأولى — مثل مربع حوار للتصفية، أو منتقي صور، أو شاشة تسجيل دخول. الواجهة البرمجية الحديثة لذلك هي ActivityResultLauncher، التي أُدخلت كبديل آمن من ناحية الأنواع للدالة المُهملة startActivityForResult().
// في Activity المُطلِقة — أعلن على مستوى الفئة
private ActivityResultLauncher<Intent> pickColorLauncher;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// سجّل قبل بدء تشغيل Activity (يجب أن يكون في onCreate)
pickColorLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
String color = result.getData().getStringExtra("chosen_color");
applyColor(color);
}
}
);
findViewById(R.id.btnPickColor).setOnClickListener(v -> {
Intent intent = new Intent(this, ColorPickerActivity.class);
pickColorLauncher.launch(intent);
});
}
// في ColorPickerActivity.java — إعادة البيانات إلى المُستدعي
Intent result = new Intent();
result.putExtra("chosen_color", "#FF6200EE");
setResult(RESULT_OK, result);
finish();
سجّل الـ launcher دائمًا في onCreate()، لا داخل مستمع النقر. يجب أن يتمّ التسجيل قبل دخول Activity في حالة STARTED. إذا استدعيت registerForActivityResult() داخل setOnClickListener()، ستحصل على IllegalStateException في وقت التشغيل عند الضغط على الزر بعد أن تكون Activity قد بدأت فعلًا.
الصورة الكاملة — تدفق شاشتين
إليك مثالًا متكاملًا مختصرًا على MainActivity التي تُطلق ProfileActivity وتُمرّر إليها سلسلة اسم المستخدم:
// MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnOpen = findViewById(R.id.btnOpenProfile);
btnOpen.setOnClickListener(v -> {
Intent intent = new Intent(MainActivity.this, ProfileActivity.class);
intent.putExtra(ProfileActivity.EXTRA_USERNAME, "alice");
startActivity(intent);
});
}
}
// ProfileActivity.java
public class ProfileActivity extends AppCompatActivity {
public static final String EXTRA_USERNAME = "username";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
String username = getIntent().getStringExtra(EXTRA_USERNAME);
TextView tvName = findViewById(R.id.tvUsername);
tvName.setText(username != null ? username : "Unknown");
}
}
يجب إعلان كلتا الـ Activities في AndroidManifest.xml (تناولنا ذلك في الدرس السادس). إن كانت ProfileActivity غائبة من الـ manifest، سيرمي النظام ActivityNotFoundException في وقت التشغيل.
الخلاصة
Intent الصريح هو الطريقة المعيارية للتنقل بين الـ Activities في Android. أنشئه بـ new Intent(context, Target.class)، أرفق أي بيانات عبر putExtra()، واستدع startActivity(). حين يحتاج الشاشة الثانية إعادة بيانات إلى الأولى، استخدم ActivityResultLauncher مُسجَّلًا في onCreate(). يتولى مكدس التراجع إدارة السجل تلقائيًا. في الدرس الأخير من هذا البرنامج التعليمي ستجمع كل هذه القطع معًا لبناء تطبيق كامل من شاشتين.