อยู่ในระหว่างการปรับปรุงเนื้อหา

ผู้ที่หลงเข้ามาอ่านเคยเห็น URL แบบนี้กันมั้ย?

จะเห็นว่า URL เหล่านี้ไม่ได้ขึ้นต้นด้วย http หรือ https และก็ไม่ได้มีไว้สำหรับเปิดหน้าเว็ปด้วย โดย URL เหล่านี้จะเรียกกันว่า Deep Link (อิงตาม Google) หรือ URL Scheme (อิงตามที่เจ้าของบล็อกเรียกกัน)

แล้วมันมีไว้ทำอะไรกันล่ะ?

ยกตัวอย่างเช่น

market://details?id=com.google.android.youtube

ให้ลองกดเปิด URL นี้บนอุปกรณ์แอนดรอยด์ดู (บนคอมไม่ได้รองรับนะจ๊ะ)

หมายเหตุ — ให้คลิกจากลิ้งโดยตรง อย่าพิมพ์หรือแปะลงใน Address Bar แล้วค่อยกดเปิด เพราะว่าแอพ Web Browser อย่างเช่น Chrome มันจะดักการทำงานในส่วนนี้ไว้

จะเห็นว่า URL ดังกล่าวเมื่อกดแล้วจะเปิดหน้าดาวน์โหลดของ YouTube บน Google Play Store ทันที โดยที่ com.google.android.youtube คือชื่อ Package ของ YouTube นั่นเอง

โดย URL แบบนี้จะนิยมนำมาใช้งานกันอุปกรณ์พกพาเหล่านี้เพิ้อเป็นอีกวิธีหนึ่งที่ทำให้สามารถสั่งเปิดแอพนั้นๆผ่าน URL ได้ในทันที (เช่น หน้าเว็ปมีปุ่มให้กดเพื่อเปิดแอพบนมือถือ เป็นต้น) และสามารถส่งค่า Parameter แบบง่ายๆได้อีกด้วย

แล้วจะทำให้ให้แอพรองรับได้ยังไงล่ะ?


การทำให้แอพรองรับ Deep Link หรือ URL Scheme บนแอนดรอยด์นั้นทำได้ไม่ยาก เพราะสามารถกำหนดใน Activity นั้นๆได้เลย เมื่อ URL ตรงกับที่กำหนดไว้ก็จะให้แอพนั้นๆเปิด Activity ตามที่กำหนดไว้ขึ้นมาทันที

จากตัวอย่างในตอนแรก Google Play Store ก็จะดักเอา Parameter จาก URL มาดูว่าเป็นชื่อ Package ของแอพตัวไหนแล้วเปิดหน้าดาวน์โหลดแอพตัวนั้นๆ

โดย Activity นั้นๆสามารถรับข้อมูล URL มาใช้งานได้ด้วยคำสั่ง

Uri uri = getIntent().getData();


จะเห็นว่าเป็นคำสั่งจาก Activity โดยตรงเลย ดังนั้นผู้ที่หลงเข้ามาอ่านก็สามารถกำหนด Activity ใดๆก็ได้ให้รองรับ

แต่เดี๋ยวก่อนนนน เจ้าของบล็อกไม่แนะนำให้กำหนด Activity หลักของแอพให้รองรับ URL โดยตรงนะ (เช่น MainActivity เป็นต้น)

ทั้งนี้ก็เพราะว่า Activity เหล่านั้น อาจจะไม่ได้ถูกเปิดจาก Deep Link หรือ URL Scheme เสมอไป อาจจะทำงานจากการเปิดใช้งานแอพตามปกติก็ได้ ดังนั้นก็จะทำให้ Uri ที่ได้มีค่าเป็น Null ได้ ผู้ที่หลงเข้ามาอ่านจะ Handle เรื่อง Null เองเลยก็ได้

แต่ถ้าอยากจะลดความวุ่นวายของโค๊ดใน Activity หลัก ก็ให้สร้าง Activity สำหรับ URL นั้นๆขึ้นมาโดยเฉพาะ แล้วจัดการกับ URL ให้เรียบร้อย แล้วค่อย Intent ไปยัง Activity หลักๆอีกที

ดังนั้นเจ้าของบล็อกจึงสร้าง Activity ที่ชื่อว่า SchemeActivity ขึ้นมา โดยไฟล์นี้จะไม่มี Layout เพราะจะ Intent ทันทีที่ทำงาน

package com.akexorcist.urlscheme; import android.app.Activity; import android.content.Intent; import android.os.Bundle; public class SchemeActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = new Intent(this, MainActivity.class); startActivity(intent); finish(); } }


และหัวใจสำคัญจะอยู่ที่ตอนประกาศ Activity ใน Android Manifest

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.akexorcist.urlscheme" > <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".SchemeActivity" > <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="yay" android:host="call" /> </intent-filter> </activity> </application> </manifest>


ต้องกำหนดค่าใน <intent-filter> แบบนี้นะ ถึงจะใช้งานกับ Deep Link หรือ URL Scheme ได้ และสามารถกำหนดค่าต่างๆนอกเหนือจากนี้เพิ่มเติมได้ตามใจชอบ

มันดูยังไงล่ะเนี่ย?


ให้สังเกตที่ android:scheme ที่เจ้าของบล็อกกำหนดไว้ว่า yay และ android:host ที่กำหนดไว้ว่า call

จากตัวอย่างนี้เจ้าของบล็อกกำหนดให้ SchemeActivity ดัก URL ว่า yay://call นั่นเอง เมื่อเข้า URL ตัวนี้บนมือถือดู

มันก็จะเด้งเข้า SchemeActivity ทันที ซึ่งในนั้นก็จะสั่งให้เปิดหน้า MainActivity อีกที จึงดูเสมือนว่าเปิด URL แล้วเปิดหน้า MainActivity ขึ้นมาทันที

ถ้าไม่เชื่อว่ามันเข้า SchemeActivity ก่อน ก็ลองแสดง Log หรือ Toast ใน SchemeActivity ดูก็ได้

และสำหรับ <data> ที่อยู่ใน Android Manifest จะกำหนดแค่ android:scheme อย่างเดียวก็ได้

เวลาที่เปิด URL อย่างเช่น yay://hello, yay://public หรือ yay://home ก็ตาม มันก็จะทำงานเสมอ เพราะมันอิงที่ android:scheme เพียงอย่างเดียว

แต่เจ้าของบล็อกก็ไม่ค่อยแนะนำเท่าไร เพราะว่ามันมีโอกาสเกิดการซ้ำซ้อนกันได้ค่อนข้างสูง ดังนั้นการใช้ android:host ด้วยจะช่วยให้แยก URL แต่ละชุดออกจากกันได้ เช่น

จากตัวอย่างข้างบนนี้ก็จะเห็นได้ว่า URL สามารถใช้ได้มากกว่า 1 Activity ได้ ดังนั้นจึงประยุกต์ใช้ URL ที่แตกต่างกันเพื่อเปิด Activity คนละตัวได้ โดยใช้ android:host เป็นหัวใจสำคัญหลักในการแยกการทำงาน

รับค่าจาก Parameter ใน URL ยังไง


จากตัวอย่าง Google Play Store ที่อธิบายในตอนแรกสุด จะเห็นว่า market คือ Scheme ส่วน detail คือ Host และก็จะเห็นว่ามี Parameter ต่อท้ายด้วยอยู่ด้วย เป็น id=com.google.android.youtube โดยที่ id คือ Key ส่วน com.google.android.youtube คือ Value

ทีนี้ดูที่ Parameter ต่อ จะเห็นว่ามีการส่งค่ามา 3 ตัว คือ

Value : android

เวลาจะดึงข้อมูลเหล่านี้ไปใช้งาน ก็จะต้องดึงจากใน Activity ที่ถูกเรียกด้วย URL จากที่ยกตัวอย่าง

ดังนั้นเจ้าของบล็อกก็จะต้องเพิ่มคำสั่งใน SchemeActivity ดังนี้

Uri uri = getIntent().getData();


เมื่อลองแสดง uri ออกทาง Log ก็จะได้ข้อมูลดังนี้


Log.i("Check", "Data : " + uri);


ผลลัพธ์ที่ได้

Data : app://play?id=10&name=SleepingForLess&os=android


ถ้าอยากรู้ว่า uri มี Key อะไรอยู่ข้างในบ้าง


for(String key : uri.getQueryParameterNames()) { Log.i("Check", "Key : " + key); }


ผลลัพธ์ที่ได้

Key : id Key : name Key : os


ถ้าอยากรู้ว่า Key ตัวนั้นๆมีข้อมูลอะไรอยู่บ้าง


String id = uri.getQueryParameter("id"); String name = uri.getQueryParameter("name"); String os = uri.getQueryParameter("os"); Log.i("Check", "ID : " + id); Log.i("Check", "Name : " + name); Log.i("Check", "OS : " + os);


ผลลัพธ์ที่ได้

ID : 10 Name : SleepingForLess OS : android


ถ้าอยากรู้ว่า Scheme, Host (Authority) และ Parameter มีค่าเป็นอะไร


Log.i("Check", "Parameter : " + uri.getQuery() ); Log.i("Check", "Authority : " + uri.getAuthority()); Log.i("Check", "Scheme : " + uri.getScheme());


ผลลัพธ์ที่ได้

Scheme : app Authority : play Parameter : id=10&name=SleepingForLess&os=android


กรณีที่ Parameter เป็น String Array ล่ะ?


ยกตัวอย่างเช่น

app://play?ver=JellyBean&ver=KitKat&ver=Lollipop

จะเห็นว่ามี Key ที่ชื่อว่า version ซ้ำกันอยู่ 3 ตัวด้วยกัน ซึ่ง Key ตัวนี้จะถูกมองเป็น Array ดังนั้นเวลาเอาค่ามาใช้ก็จะต้องเรียกออกมาในรูปของ Array เช่นกัน

List<String> listVersion = uri.getQueryParameters("ver"); for(String version : listVersion) { Log.i("Check", "Version : " + version); }


ผลลัพธ์ที่ได้

Version : JellyBean Version : KitKat Version : Lollipop


ถ้าอยากใช้ใน Activity หลักโดยตรง


<activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="app" android:host="play" /> </intent-filter> </activity>


จากเดิม MainActivity จะมีแค่ Action Main กับ Category Launcher เจ้าของบล็อกก็ได้เพิ่มตัวอื่นๆที่ต้องใช้สำหรับ URL นั้นๆเข้าไปจนครบ

แต่อย่างที่บอกในตอนแรกว่า ถ้าจะทำใน Activity หลักโดยตรงแบบนี้ ให้จัดการกับ Uri ให้ดีๆ เพราะมันจะมีโอกาสที่มีค่าเป็น Null ได้

public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Uri uri = getIntent().getData(); if(uri != null) { // Do something here when this activity was called by url scheme } } }


ประยุกต์ไปใช้เป็น Secret Code เวลากดเบอร์โทรออกได้ด้วยนะ


เรื่องนี้เจ้าของบล็อกได้เขียนบทความไปพักใหญ่ๆแล้ว เป็นการใช้ Deep Link หรือ URL Scheme เพื่อดักการกดรหัสโทรออกพิเศษๆ อย่าง *#*#1234#*#* เป็นต้น โดยจะดัก Event ที่เกิดขึ้นบน Broadcast Receiver แทน

ใส่รหัสลับสำหรับเปิดแอปพลิเคชันด้วย Secret Code

สรุป


Deep Link หรือ URL Scheme เป็นอีกวิธีหนึ่งที่จะช่วยให้สามารถสั่งเปิดแอพภายในเครื่องได้โดยผ่าน URL บนหน้าเว็ป อีกทั้งยังสามารถส่งข้อมูลเบื้องต้นได้ด้วย จึงทำให้สามารถนำไปประยุกต์ใช้งานได้ แต่ทว่าเมื่อใดที่แอพนั้นๆไม่ได้ติดตั้ง ก็จะไม่มีผลใดๆกับเครื่องนั้นๆเลย