Sunday, 3 June 2012

Introduction to Intents & Intent Filters

Category: Android Development

Android Application ประกอบไปด้วย Components หลักๆ 3 ตัว ได้แก่ Activities, Services และ Broadcast Receivers ซึ่งเราสามารถสั่ง Activate Components เหล่านี้ ผ่าน Messages หรือตามศัพท์ในการพัฒนา Android เรียกว่า Intents  เราสามารถใช้ Intents ในการสั่ง Activate Components ดังกล่าวได้ ไม่ว่า Components ดังกล่าวจะอยู่ใน Application เดียวกัน หรือแม้กระทั่งต่าง Application กัน ก็สามารถทำได้

ภายใน Intent Object จะระบุถึง Operation ที่ต้องการจะทำ  ส่วนถ้าเป็นกรณีของ Broadcast Intent Object จะระบุถึงเหตุการณ์ที่เกิดขึ้น สำหรับวิธีการส่ง Intent ไปยัง Components ต่างๆเหล่านี้ จะมีวิธีการแตกต่างกันออกไป ดังนี้

  • สำหรับการสั่ง Launch Activity นั้น เราจะส่ง Intent Object ผ่าน Context.startActivity() หรือ Activity.startActivityForResult()
  • สำหรับการสั่ง Initiate Service นั้น เราจะส่ง Intent Object ผ่าน Context.startService()
  • สำหรับการส่ง Intent Object ไปยัง Broadcast Receiver นั้น เราสามารถส่งผ่าน Broadcast Methods อาทิเช่น Context.sendBroadcast(), Context.sendOrderedBroadcast() หรือ Context.sendStickyBroadcast()
สำหรับในแต่ละกรณีดังกล่าวข้างต้น Android จะพยายามหา Activity, Service หรือ Broadcast Receivers ที่เหมาะสมสำหรับ Intent นั้นๆ

Intent Objects

ภายใน Intent Object ประกอบไปด้วยข้อมูลหลัก 2 ส่วน ส่วนแรกสำหรับ Component ที่ได้รับ Intent อาทิเช่น Action และ Data ส่วนที่สองเป็นข้อมูลสำหรับ Android System อาทิเช่น Category ของ Component ที่ควรจะเป็นผู้ได้รับสิทธิ์ในการดำเนินการกับ Intent นั้นๆ รวมไปถึงข้อมูลวิธีการ Launch Activity

Intent Resolution

เราสามารถแบ่ง Intent ออกได้เป็น 2 กลุ่ม ดังนี้

Explicit Intents

ใช้วิธีการระบุชื่อของ Component เป้าหมาย (ระบุผ่าน field Component Name) มักจะใช้สำหรับส่ง Message ภายใน Application เดียวกันเท่านั้น เพราะโดยทั่วไปเราคงไม่รู้ว่า Application อื่นๆ ตั้ง Component Name ไว้อย่างไร แต่ถ้าเรารู้ เราก็สามารถเรียกได้เช่นเดียวกัน

Implicit Intents

วิธีนี้จะไม่ระบุชื่อของ Component เป้าหมาย (ปล่อยให้ file Component Name เป็นค่าว่าง) เรามักจะใช้วิธีการนี้ในการสั่ง Activate Components ที่อยู่ใน Application อื่น

วิธีการตีความสำหรับ Explicit Intents  นัั้น Android จะดูจาก Instance ของ Class ที่ระบุใน field Component Name เป็นหลัก สำหรับ field อื่นๆที่ส่งมาใน Intent จะไม่มีผลใดๆ

วิธีการตีความสำหรับ Implicit Intents จะค่อนข้างหลาย ในกรณีที่ไม่ได้ระบุ Component เป้าหมายชัดเจน Android จะหา Component ที่เหมาะสมที่สุดที่จะมารับหน้าที่ในการ Handle Intent นั้นๆ ซึ่ง Android จะใช้วิธีเปรียบเทียบข้อมูลใน Intent Object กับ Intent Filters (เดี๋ยวจะพูดถึงต่อไป) ของ Component ที่ Android เลือกมา (Component ที่มีแนวโน้มที่จะสามารถรับ Intent ดังกล่าวได้)

Intent Filters เป็นการประกาศความสามารถของ Component ซึ่งก็หมายถึง Intent ที่ Component นั้นจะรองรับได้ อีกที้งการประกาศ Intent Filters ยังเป็นการทำให้ Component นั้น มีคุณสมบัติที่จะรับ Implicit Intent ได้ ถ้าหาก Component ไหนไม่ได้ระบุ Intent Filters ก็จะสามารถรับได้เฉพาะ Explicit Intent เท่านั้น

Android จะใช้ข้อมูลใน Intent Object แค่ 3 ส่วน ได้แก่ action, data (ทั้ง URI และ data type), category ในการเปรียบเทียบกับ Intent Filter

Intent Filters

เราระบุ Intent Filters ใน Component เพื่อเป็นการบอกว่า Component นั้น สามารถรองรับ Implicit Intent อะไรได้บ้าง ในแต่ละ Component สามารถมีได้หลาย Intent Filters

แต่ละ Intent Filter จะเป็นการบอกถึงความสามารถของ Component รวมทั้งกลุ่มของ Intents ที่ Component รองรับ Intent Filter จะทำหน้าที่คัดเลือก (filter) เฉพาะ Intent  ที่ต้องการเท่านั้น ซึ่งกระบวนการ filter นี้ จะมีผลกับ Implicit Intent เท่านั้น แต่ไม่มีผลกับ Explicit Content

ในแต่ละ Component มักจะมีการแบ่ง Filter ตามหน้าที่การทำงาน ยกตัวอย่าง Note Pad Application มี Activity ที่ชื่อว่า NoteEditor  Note Pad Application ประกาศ Filters ไว้ 2 ตัว ตัวแรกไว้สำหรับสั่งเปิด Note ที่สร้างไว้แล้ว ซึ่ง User สามารถที่จะ View หรือ Edit ในขณะที่ Filter อีกตัวนึง ทำหน้าที่สร้าง Note ใหม่ ซึ่ง User สามารถพิมพ์ข้อความและสั่ง Save

Intent Filter เป็น instance ของ IntentFilter Class แต่โดยทั่วไปเราจะไม่ได้สร้าง Intent Filters โดยใช้ Java Code เพราะ Android จะต้องรู้ความสามารถของแต่ละ Component ก่อนที่จะ Launch ส่วนใหญ่แล้วเราจึงประกาศ Intent Filter ไว้ใน Application Manifest File (AndroidManifest.xml) โดยประกาศไว้ภายใน element ที่มีชื่อว่า <intent-filter>

Filter แต่ละตัว จะมี fields ที่สอดคล้องกับ fields ใน Intent Object ซึ่งได้แก่ action, data และ category ซึ่งจะใช้ในการเปรียบเทียบกับ Implicit Intent การที่ Intent จะส่งถึง Component ได้นั้น การเปรียบเทียบทั้ง 3 fields ดังกล่าวจะต้องผ่านทั้งหมด ถ้าไม่ผ่านแม้แต่ field เดียว Intent ก็จะไม่สามารถส่งถึง Component ได้ สำหรับ Component ที่มีหลาย Filter ถ้าหาก Filter แรกไม่ผ่าน Android ก็จะข้ามไปเปรียบเทียบตัวถัดมา (ถ้ายังมี)

การเปรียบเทียบทั้ง 3 fields สามารถอธิบายเป็นตัวอย่างได้ดังนี้

Action Test

สมมติว่า <intent-filter> element ใน Manifest File มีการประกาศ  <action> elements ไว้ดังนี้

<intent-filter . . . >
    <action android:name="com.example.project.SHOW_CURRENT" />
    <action android:name="com.example.project.SHOW_RECENT" />
    <action android:name="com.example.project.SHOW_PENDING" />
    . . . 
</intent-filter>

ในขณะที่ Intent Object ระบุเพียงแค่ Action เดียว แต่ในหนึ่ง Filter เราสามารถประกาศได้หลาย action และต้องมีอย่างน้อย 1 Action ไม่ประกาศเลยไม่ได้ ไม่เช่นนั้นแล้ว Intent จะถูก Block ทั้งหมด
การที่จะผ่านการเปรียบเทียบนั้น Action ที่ระบุไว้ใน Intent Object จะต้องตรงกับ Action อันใดอันหนึ่งที่เราประกาศไว้ ไม่เช่นนั้นแล้ว Intent ก็จะถูก Block ถ้าหาก Intent Object หรือ Filter ไม่ได้ระบุ Action ผลที่ได้จะเป็นดังต่อไปนี้

  • ถ้าหาก Filter ไม่ได้ระบุ Action ใดๆ ก็จะไม่มีอะไรให้ Intent เปรียบเทียบ ซึ่งหมายความว่า Intent ทั้งหมดจะไม่ผ่านการเปรียบเทียบ และ Intent จะถูก Block ทั้งหมด
  • และในทางกลับกัน ถ้าหาก Intent Object ไม่ได้ระบุ Action ก็จะถือว่าผ่านการเปรียบเทียบโดยอัตโนมัติ (ทั้งนี้ filter จะต้องมีอย่างหน้อย 1 Action)
Category Test

สมมติว่า <intent-filter> elements ระบุ categories ไว้ดังนี้

<intent-filter . . . >
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    . . . 
</intent-filter>

  
สำหรับการเปรียบเทียบ Category การที่ Intent จะถือว่าผ่านการเปรียบเทียบได้นั้น ทุก Category ใน Intent Object จะต้องตรงกับ Category อันใดอันหนึ่งใน Filter

ตามหลักแล้ว Intent Object ที่ไม่ได้ระบุ Category ควรจะผ่านการเปรียบเทียบเสมอ ไม่ว่าใน Filter จะประกาศไว้อย่างไร แต่อย่างไรก็ตาม ยังมีข้อยกเว้นบางอย่างที่เราควรจะรู้ นั่นก็คือ Android จะถือว่า Implicit Intents ที่ส่งผ่าน startActivity() จะมี default category เสมอ นั่นก็คือ "android.intent.category.DEFAULT" ดังนั้น Activity ใดก็ตามที่ต้องการรับ Implicit Intents จะต้องประกาศ "android.intent.category.DEFAULT" ไว้ใน Intent Filters

Data Test

Filter สามารถมีได้หลาย data element หรือ ไม่มีเลยก็ได้

<intent-filter . . . >
    <data android:mimeType="video/mpeg" android:scheme="http" . . . />
    <data android:mimeType="audio/mpeg" android:scheme="http" . . . />
    . . .</intent-filter>

ในแต่ละ data element นั้น เราสามารถระบุ URI และ data type (MIME media type) ซึ่ง Android ได้เตรียม attribute สำหรับแต่ละ part ของ URI ไว้ให้ดังนี้ scheme, host, port และ path

scheme://host:port/path

ยกตัวอย่างเช่น URL ดังต่อไปนี้

content://com.example.project:200/folder/subfolder/etc

scheme="content" , host="com.example.project" port="200" และ path="floder/subfolder/etc"  Host และ Port รวมกันเรียกว่า URI Authority ซึ่ง Attribute ที่กล่าวมาทั้งหมดนี้ optional

การเปรียบเทียบ URI ใน Intent Object กับ URI ใน Intent Filters นั้นจะเปรียบเทียบเฉพาะส่วนของ URI ที่ระบุใน filter เท่านั้น ยกตัวอย่างเช่น

ถ้าหากว่าใน Filter ระบุแค่เพียง scheme จะหมายความว่า URIs ใดที่มี scheme ตรงกับที่ระบุไว้ใน Filter จะถือว่าผ่านการเปรียบเทียบ

ถ้าหากใน Filter ระบุ Scheme และ Authority แต่ไม่ได้ระบุ Path หมายความว่า URLs ใดที่มี Scheme และ Authority ตรงกับที่ระบุ จะถือว่าผ่านการเปรียบเทียบ ไม่ว่า Path จะเป็นอย่างไร

ถ้าหากใน Filter ระบุ Scheme, Authority และ Path หมายความว่า เฉพาะ URIs ที่มี Scheme, Authority และ Path ตรงกับที่ระบุไว้ใน Filter ถึงจะถือว่าผ่านการเปรียบเทียบ

Type Attribute ใน Data Element ระบุถึง MIME type ของ Data เราสามารถใช้ * (wildcard) ใน Subtype field สำหรับ Intent Object และ Filter ได้ ยกตัวอย่างเช่น "text/*" หรือ "audio/*" หมายความว่า Subtype จะเป็นอะไรก็ได้

วิธีการเปรียบเทียบ Data จะเปรียบเทียบทั้ง URI และ Data Type ใน Intent Object  กับ URI และ Data Type ที่ระบุไว้ใน Filter สำหรับกฎในการเปรียบเทียบจะเป็นดังต่อไปนี้

  1. Intent Object ที่ไม่ได้ระบุ URI หรือ Data Type ถือว่าผ่านการเปรียบเทียบเฉพาะในกรณีที่ Filter ไม่ได้ระบุ URL หรือ Data Type เหมือนกัน
  2. Intent Object ที่ระบุ URI แต่ไม่ระบุ Data Type ถือว่าผ่านการเปรียบเทียบเฉพาะในกรณีที่ URI ตรงกับ URI ที่ระบุไว้ใน Filter และ Filter นั้นไม่ได้ระบุ Type ไว้เช่นเดียวกัน
  3. Intent Object ที่ระบุ Data Type แต่ไม่ได้ระบุ URI ถือว่าผ่านการเปรียบเทียบเฉพาะในกรณีที่ Filter ระบุ Data Type ไว้ตรงกัน และไม่ได้ระบุ URI ไว้เช่นเดียวกัน

แหล่งที่มา: http://developer.android.com/guide/topics/intents/intents-filters.html

No comments:

Post a Comment