ผู้ใช้จำนวนมากยังคงจัดการข้อมูลเข้าสู่ระบบของตนเองเมื่อตั้งค่า Android เครื่องใหม่ อุปกรณ์ กระบวนการที่ต้องดำเนินการด้วยตนเองนี้อาจมีความท้าทาย และมักจะส่งผลในทางลบ ประสบการณ์ของผู้ใช้ Block Store API ไลบรารีที่ขับเคลื่อนโดย Google Play บริการจะแก้ไขปัญหานี้โดยมอบวิธีให้แอป ข้อมูลเข้าสู่ระบบของผู้ใช้ที่ไม่มีความซับซ้อนหรือความเสี่ยงด้านความปลอดภัยที่เกี่ยวข้องกับการบันทึก รหัสผ่านผู้ใช้
Block Store API ทำให้แอปของคุณจัดเก็บข้อมูลในภายหลังได้ เรียกข้อมูลเพื่อตรวจสอบสิทธิ์ผู้ใช้ในอุปกรณ์เครื่องใหม่ วิธีนี้ช่วยเพิ่ม ที่ราบรื่นสำหรับผู้ใช้ เนื่องจากไม่จำเป็นต้องเห็นหน้าจอลงชื่อเข้าใช้ เมื่อเปิดแอปเป็นครั้งแรกในอุปกรณ์เครื่องใหม่
ประโยชน์ของการใช้ Block Store มีดังนี้
- โซลูชันที่จัดเก็บข้อมูลรับรองที่เข้ารหัสสำหรับนักพัฒนาซอฟต์แวร์ ข้อมูลเข้าสู่ระบบคือ จากต้นทางถึงปลายทาง หากเป็นไปได้
- บันทึกโทเค็นแทนชื่อผู้ใช้และรหัสผ่าน
- ขจัดความยุ่งยากในขั้นตอนการลงชื่อเข้าใช้
- ช่วยผู้ใช้จากความยุ่งยากในการจัดการรหัสผ่านที่ซับซ้อน
- Google จะยืนยันตัวตนของผู้ใช้
ก่อนเริ่มต้น
ทําตามขั้นตอนในส่วนต่อไปนี้เพื่อเตรียมแอป
กำหนดค่าแอป
ในไฟล์ build.gradle
ระดับโปรเจ็กต์ ให้ใส่ Maven ของ Google
ที่เก็บ ทั้งใน buildscript
ของคุณ
และ allprojects
ส่วน:
buildscript {
repositories {
google()
mavenCentral()
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
เพิ่มบริการ Google Play
Dependency สำหรับ Block Store API ไปยัง
ไฟล์บิลด์ Gradle ของโมดูล
ซึ่งโดยทั่วไปคือ app/build.gradle
dependencies {
implementation 'com.google.android.gms:play-services-auth-blockstore:16.4.0'
}
วิธีการทำงาน
Block Store ช่วยให้นักพัฒนาซอฟต์แวร์บันทึกและกู้คืนอาร์เรย์สูงสุด 16 ไบต์ได้ วิธีนี้ช่วยให้คุณสามารถบันทึกข้อมูลสำคัญเกี่ยวกับเซสชันของผู้ใช้ปัจจุบัน และสามารถบันทึกข้อมูลนี้ได้ตามต้องการ ข้อมูลนี้สามารถเข้ารหัสจากต้นทางถึงปลายทางได้ และโครงสร้างพื้นฐานที่รองรับ Block Store นั้นสร้างขึ้นจากโครงสร้างพื้นฐานของการสำรองข้อมูลและคืนค่า
คู่มือนี้จะกล่าวถึงกรณีการใช้งานโทเค็นของผู้ใช้ไปยัง Block Store ขั้นตอนต่อไปนี้จะสรุปวิธีการทำงานของแอปที่ใช้ Block Store
- ในระหว่างขั้นตอนการตรวจสอบสิทธิ์ของแอป หรือเมื่อใดก็ตามหลังจากนั้น คุณสามารถจัดเก็บ โทเค็นการตรวจสอบสิทธิ์ของผู้ใช้ไปยัง "บล็อก Store" เพื่อดึงข้อมูลในภายหลัง
- ระบบจะจัดเก็บโทเค็นไว้ในเครื่องและ สามารถสำรองข้อมูลไปยังระบบคลาวด์ได้ จากต้นทางถึงปลายทาง หากเป็นไปได้
- ระบบจะโอนข้อมูลเมื่อผู้ใช้เริ่มขั้นตอนการคืนค่าในอุปกรณ์เครื่องใหม่
- หากผู้ใช้กู้คืนแอปของคุณระหว่างขั้นตอนการกู้คืน แอปของคุณจะทำสิ่งต่อไปนี้ได้ เรียกโทเค็นที่บันทึกไว้จาก Block Store ในอุปกรณ์ใหม่
กำลังบันทึกโทเค็น
เมื่อผู้ใช้ลงชื่อเข้าใช้แอป คุณจะบันทึกโทเค็นการตรวจสอบสิทธิ์ที่สร้างให้ผู้ใช้รายดังกล่าวเพื่อบล็อก Store ได้ คุณสามารถจัดเก็บโทเค็นนี้โดยใช้ค่าคู่คีย์ที่ไม่ซ้ำกันซึ่งมีขนาดไม่เกิน 4 KB ต่อรายการ
หากต้องการจัดเก็บโทเค็น โปรดโทรติดต่อ setBytes()
และ setKey()
ในอินสแตนซ์
StoreBytesData.Builder
เพื่อจัดเก็บข้อมูลเข้าสู่ระบบของผู้ใช้ในอุปกรณ์ต้นทาง หลังจากบันทึกโทเค็น
ใน Block Store โทเค็นจะได้รับการเข้ารหัสและจัดเก็บไว้ในอุปกรณ์
ตัวอย่างต่อไปนี้แสดงวิธีบันทึกโทเค็นการตรวจสอบสิทธิ์ไปยัง อุปกรณ์ในเครือข่ายเดียวกัน:
Java
BlockstoreClient client = Blockstore.getClient(this); byte[] bytes1 = new byte[] { 1, 2, 3, 4 }; // Store one data block. String key1 = "com.example.app.key1"; StoreBytesData storeRequest1 = StoreBytesData.Builder() .setBytes(bytes1) // Call this method to set the key value pair the data should be associated with. .setKeys(Arrays.asList(key1)) .build(); client.storeBytes(storeRequest1) .addOnSuccessListener(result -> Log.d(TAG, "stored " + result + " bytes")) .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));
Kotlin
val client = Blockstore.getClient(this) val bytes1 = byteArrayOf(1, 2, 3, 4) // Store one data block. val key1 = "com.example.app.key1" val storeRequest1 = StoreBytesData.Builder() .setBytes(bytes1) // Call this method to set the key value with which the data should be associated with. .setKeys(Arrays.asList(key1)) .build() client.storeBytes(storeRequest1) .addOnSuccessListener { result: Int -> Log.d(TAG, "Stored $result bytes") } .addOnFailureListener { e -> Log.e(TAG, "Failed to store bytes", e) }
ใช้โทเค็นเริ่มต้น
ข้อมูลที่บันทึกโดยใช้ StoreBytes โดยไม่มีคีย์จะใช้คีย์เริ่มต้น BlockstoreClient.DEFAULT_BYTES_DATA_KEY
Java
BlockstoreClient client = Blockstore.getClient(this); // The default key BlockstoreClient.DEFAULT_BYTES_DATA_KEY. byte[] bytes = new byte[] { 9, 10 }; StoreBytesData storeRequest = StoreBytesData.Builder() .setBytes(bytes) .build(); client.storeBytes(storeRequest) .addOnSuccessListener(result -> Log.d(TAG, "stored " + result + " bytes")) .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));
Kotlin
val client = Blockstore.getClient(this); // the default key BlockstoreClient.DEFAULT_BYTES_DATA_KEY. val bytes = byteArrayOf(1, 2, 3, 4) val storeRequest = StoreBytesData.Builder() .setBytes(bytes) .build(); client.storeBytes(storeRequest) .addOnSuccessListener { result: Int -> Log.d(TAG, "stored $result bytes") } .addOnFailureListener { e -> Log.e(TAG, "Failed to store bytes", e) }
กำลังเรียกโทเค็น
หลังจากนั้น เมื่อผู้ใช้ทำตามขั้นตอนการกู้คืนใน
อุปกรณ์นั้น บริการ Google Play จะยืนยันผู้ใช้ก่อน จากนั้นจึงดึงข้อมูล "บล็อก"
จัดเก็บข้อมูล ผู้ใช้ตกลงที่จะกู้คืนข้อมูลแอปแล้ว
ขั้นตอนการคืนค่า จึงไม่จำเป็นต้องได้รับความยินยอมเพิ่มเติม เมื่อผู้ใช้เปิด
แอปของคุณ คุณสามารถขอโทเค็นจาก Block Store ได้โดยโทรไปที่
retrieveBytes()
จากนั้นจะใช้โทเค็นที่ดึงมาเพื่อให้ผู้ใช้ยังคงลงชื่อเข้าใช้บัญชี
อุปกรณ์
ตัวอย่างต่อไปนี้แสดงวิธีเรียกข้อมูลโทเค็นหลายรายการตามคีย์ที่ระบุ
Java
BlockstoreClient client = Blockstore.getClient(this); // Retrieve data associated with certain keys. String key1 = "com.example.app.key1"; String key2 = "com.example.app.key2"; String key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY; // Used to retrieve data stored without a key ListrequestedKeys = Arrays.asList(key1, key2, key3); // Add keys to array RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder() .setKeys(requestedKeys) .build(); client.retrieveBytes(retrieveRequest) .addOnSuccessListener( result -> { Map<String, BlockstoreData> blockstoreDataMap = result.getBlockstoreDataMap(); for (Map.Entry<String, BlockstoreData> entry : blockstoreDataMap.entrySet()) { Log.d(TAG, String.format( "Retrieved bytes %s associated with key %s.", new String(entry.getValue().getBytes()), entry.getKey())); } }) .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));
Kotlin
val client = Blockstore.getClient(this) // Retrieve data associated with certain keys. val key1 = "com.example.app.key1" val key2 = "com.example.app.key2" val key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY // Used to retrieve data stored without a key val requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array val retrieveRequest = RetrieveBytesRequest.Builder() .setKeys(requestedKeys) .build() client.retrieveBytes(retrieveRequest) .addOnSuccessListener { result: RetrieveBytesResponse -> val blockstoreDataMap = result.blockstoreDataMap for ((key, value) in blockstoreDataMap) { Log.d(ContentValues.TAG, String.format( "Retrieved bytes %s associated with key %s.", String(value.bytes), key)) } } .addOnFailureListener { e: Exception? -> Log.e(ContentValues.TAG, "Failed to store bytes", e) }
กำลังเรียกโทเค็นทั้งหมด
ด้านล่างเป็นตัวอย่างวิธีเรียกโทเค็นทั้งหมดที่บันทึกลงใน BlockStore
Java
BlockstoreClient client = Blockstore.getClient(this) // Retrieve all data. RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder() .setRetrieveAll(true) .build(); client.retrieveBytes(retrieveRequest) .addOnSuccessListener( result -> { Map<String, BlockstoreData> blockstoreDataMap = result.getBlockstoreDataMap(); for (Map.Entry<String, BlockstoreData> entry : blockstoreDataMap.entrySet()) { Log.d(TAG, String.format( "Retrieved bytes %s associated with key %s.", new String(entry.getValue().getBytes()), entry.getKey())); } }) .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));
Kotlin
val client = Blockstore.getClient(this) val retrieveRequest = RetrieveBytesRequest.Builder() .setRetrieveAll(true) .build() client.retrieveBytes(retrieveRequest) .addOnSuccessListener { result: RetrieveBytesResponse -> val blockstoreDataMap = result.blockstoreDataMap for ((key, value) in blockstoreDataMap) { Log.d(ContentValues.TAG, String.format( "Retrieved bytes %s associated with key %s.", String(value.bytes), key)) } } .addOnFailureListener { e: Exception? -> Log.e(ContentValues.TAG, "Failed to store bytes", e) }
ด้านล่างนี้เป็นตัวอย่างวิธีเรียกข้อมูลคีย์เริ่มต้น
Java
BlockStoreClient client = Blockstore.getClient(this); RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder() .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY)) .build(); client.retrieveBytes(retrieveRequest);
Kotlin
val client = Blockstore.getClient(this) val retrieveRequest = RetrieveBytesRequest.Builder() .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY)) .build() client.retrieveBytes(retrieveRequest)
กำลังลบโทเค็น
อาจต้องลบโทเค็นจาก BlockStore ด้วยเหตุผลต่อไปนี้
- ผู้ใช้ทำตามขั้นตอนการออกจากระบบของผู้ใช้
- โทเค็นถูกเพิกถอนหรือไม่ถูกต้อง
คุณสามารถระบุโทเค็นที่ต้องลบได้โดยการตั้งค่าอาร์เรย์ของคีย์ที่ต้องลบ เช่นเดียวกับการดึงข้อมูลโทเค็น
ด้านล่างเป็นตัวอย่างสำหรับการลบคีย์บางรายการ
Java
BlockstoreClient client = Blockstore.getClient(this); // Delete data associated with certain keys. String key1 = "com.example.app.key1"; String key2 = "com.example.app.key2"; String key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY; // Used to delete data stored without key ListrequestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array DeleteBytesRequest deleteRequest = new DeleteBytesRequest.Builder() .setKeys(requestedKeys) .build(); client.deleteBytes(deleteRequest)
Kotlin
val client = Blockstore.getClient(this) // Retrieve data associated with certain keys. val key1 = "com.example.app.key1" val key2 = "com.example.app.key2" val key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY // Used to retrieve data stored without a key val requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array val retrieveRequest = DeleteBytesRequest.Builder() .setKeys(requestedKeys) .build() client.deleteBytes(retrieveRequest)
ลบโทเค็นทั้งหมด
ตัวอย่างด้านล่างจะลบโทเค็นทั้งหมดที่บันทึกไว้ใน BlockStore ในปัจจุบัน
Java
// Delete all data. DeleteBytesRequest deleteAllRequest = new DeleteBytesRequest.Builder() .setDeleteAll(true) .build(); client.deleteBytes(deleteAllRequest) .addOnSuccessListener(result -> Log.d(TAG, "Any data found and deleted? " + result));
Kotlin
val deleteAllRequest = DeleteBytesRequest.Builder() .setDeleteAll(true) .build() client.deleteBytes(deleteAllRequest) .addOnSuccessListener { result: Boolean -> Log.d(TAG, "Any data found and deleted? $result") }
การเข้ารหัสจากต้นทางถึงปลายทาง
หากต้องการใช้การเข้ารหัสจากต้นทางถึงปลายทาง อุปกรณ์จะต้อง
ใช้ Android 9 ขึ้นไป และผู้ใช้ต้องตั้งค่าการล็อกหน้าจอ
(PIN, รูปแบบ หรือรหัสผ่าน) ของอุปกรณ์ คุณสามารถยืนยันได้ว่าการเข้ารหัสจะ
บนอุปกรณ์ที่จะมีให้บริการโดยโทรไปที่ isEndToEndEncryptionAvailable()
ตัวอย่างต่อไปนี้แสดงวิธีตรวจสอบว่าการเข้ารหัสพร้อมใช้งานหรือไม่ การสำรองข้อมูลในระบบคลาวด์:
client.isEndToEndEncryptionAvailable()
.addOnSuccessListener { result ->
Log.d(TAG, "Will Block Store cloud backup be end-to-end encrypted? $result")
}
เปิดใช้การสำรองข้อมูลในระบบคลาวด์
หากต้องการเปิดใช้การสำรองข้อมูลในระบบคลาวด์ ให้เพิ่ม
setShouldBackupToCloud()
ลงใน
StoreBytesData
ออบเจ็กต์ Block Store จะสำรองข้อมูลไบต์ที่เก็บไว้ไปยังระบบคลาวด์เป็นระยะเมื่อ
ตั้งค่า setShouldBackupToCloud()
เป็น "จริง" แล้ว
ตัวอย่างต่อไปนี้แสดงวิธีเปิดใช้การสำรองข้อมูลในระบบคลาวด์เฉพาะเมื่อสำรองข้อมูลในระบบคลาวด์เท่านั้น มีการเข้ารหัสจากต้นทางถึงปลายทาง ดังนี้
val client = Blockstore.getClient(this)
val storeBytesDataBuilder = StoreBytesData.Builder()
.setBytes(/* BYTE_ARRAY */)
client.isEndToEndEncryptionAvailable()
.addOnSuccessListener { isE2EEAvailable ->
if (isE2EEAvailable) {
storeBytesDataBuilder.setShouldBackupToCloud(true)
Log.d(TAG, "E2EE is available, enable backing up bytes to the cloud.")
client.storeBytes(storeBytesDataBuilder.build())
.addOnSuccessListener { result ->
Log.d(TAG, "stored: ${result.getBytesStored()}")
}.addOnFailureListener { e ->
Log.e(TAG, “Failed to store bytes”, e)
}
} else {
Log.d(TAG, "E2EE is not available, only store bytes for D2D restore.")
}
}
วิธีทดสอบ
ใช้วิธีการต่อไปนี้ในระหว่างการพัฒนาเพื่อทดสอบการคืนค่า ขั้นตอน
ถอนการติดตั้ง/ติดตั้งอุปกรณ์เดียวกันอีกครั้ง
หากผู้ใช้เปิดใช้บริการสำรองข้อมูล (สามารถตรวจสอบได้ที่การตั้งค่า > Google > การสำรองข้อมูล) "บล็อกข้อมูลร้านค้า" คือ คงอยู่ตลอดขั้นตอนการถอนการติดตั้ง/ติดตั้งแอปอีกครั้ง
คุณสามารถทำตามขั้นตอนต่อไปนี้เพื่อทดสอบ
- ผสานรวม BlockStore API กับแอปทดสอบ
- ใช้แอปทดสอบเพื่อเรียกใช้ BlockStore API เพื่อจัดเก็บข้อมูล
- ถอนการติดตั้งแอปทดสอบแล้วติดตั้งแอปในอุปกรณ์เดียวกันอีกครั้ง
- ใช้แอปทดสอบเพื่อเรียกใช้ BlockStore API เพื่อเรียกข้อมูล
- ยืนยันว่าไบต์ที่ดึงมาตรงกับข้อมูลที่เก็บไว้ก่อนหน้านี้ และการถอนการติดตั้ง
ระหว่างอุปกรณ์
ในกรณีส่วนใหญ่ การดำเนินการนี้ต้องมีการรีเซ็ตอุปกรณ์เป้าหมายเป็นค่าเริ่มต้น คุณสามารถ แล้วป้อนขั้นตอนการคืนค่า Android แบบไร้สาย หรือการคืนค่าเคเบิลของ Google (สำหรับอุปกรณ์ที่รองรับ)
การคืนค่าระบบคลาวด์
- ผสานรวม Blockstore API กับแอปทดสอบ แอปทดสอบต้องมีลักษณะดังนี้ ที่ส่งไปยัง Play Store
- ในอุปกรณ์ต้นทาง ให้ใช้แอปทดสอบเพื่อเรียกใช้ Blockstore API เพื่อจัดเก็บ ข้อมูลของคุณได้โดยตั้งค่า shouldBackUpToCloud เป็น "จริง"
- สำหรับอุปกรณ์ O ขึ้นไป คุณจะเรียกใช้การสำรองข้อมูลในระบบคลาวด์ของ Block Store ได้ด้วยตนเองดังนี้
ไปที่การตั้งค่า > Google "สำรองข้อมูล" คลิกปุ่ม "สำรองข้อมูลทันที"
- หากต้องการยืนยันว่าการสำรองข้อมูลในระบบคลาวด์ของ Block Store สำเร็จแล้ว ให้ทำดังนี้
- หลังจากสำรองข้อมูลเสร็จแล้ว ให้ค้นหาบรรทัดในบันทึกที่มีแท็ก "CloudSyncBpTkSvc"
- คุณควรเห็นบรรทัดต่างๆ ในลักษณะนี้: "......, CloudSyncBpTkSvc: ให้เลือก" ผลลัพธ์: สำเร็จ, ..., ขนาดที่อัปโหลด: XXX ไบต์ ..."
- หลังจากสำรองข้อมูลในระบบคลาวด์ของ Block Store แล้ว จะมีระยะเวลา "พัก" 5 นาที ภายใน 5 นาทีนั้น การคลิกปุ่ม "สำรองข้อมูลทันที" จะไม่ทริกเกอร์ ข้อมูลสำรองใน Block Store อีกรายการหนึ่ง
- หากต้องการยืนยันว่าการสำรองข้อมูลในระบบคลาวด์ของ Block Store สำเร็จแล้ว ให้ทำดังนี้
- รีเซ็ตอุปกรณ์เป้าหมายเป็นค่าเริ่มต้นและดำเนินการตามขั้นตอนการคืนค่าในระบบคลาวด์ เลือก เพื่อคืนค่าแอปทดสอบในระหว่างขั้นตอนการคืนค่า สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ ขั้นตอนการคืนค่าระบบคลาวด์ โปรดดูขั้นตอนการคืนค่าระบบคลาวด์ที่รองรับ
- ในอุปกรณ์เป้าหมาย ให้ใช้แอปทดสอบเพื่อเรียกใช้ Blockstore API เพื่อ เรียกข้อมูลของคุณ
- ตรวจสอบว่าไบต์ที่ดึงมาตรงกับข้อมูลที่เก็บไว้ใน อุปกรณ์ต้นทาง
ข้อกำหนดของอุปกรณ์
การเข้ารหัสจากต้นทางถึงปลายทาง
- การเข้ารหัสจากต้นทางถึงปลายทางใช้ได้ในอุปกรณ์ที่ใช้ Android 9 (API 29) ขึ้นไป
- อุปกรณ์จะต้องตั้งค่าการล็อกหน้าจอด้วย PIN, รูปแบบ หรือรหัสผ่านเพื่อเปิดใช้การเข้ารหัสจากต้นทางถึงปลายทางและเข้ารหัสข้อมูลของผู้ใช้อย่างถูกต้อง
ขั้นตอนการคืนค่าระหว่างอุปกรณ์
ระหว่างการกู้คืนอุปกรณ์ คุณจะต้องมีอุปกรณ์ต้นทางและอุปกรณ์เป้าหมาย อุปกรณ์ 2 เครื่องที่กำลังโอนข้อมูล
อุปกรณ์ต้นทางต้องใช้ Android 6 (API 23) ขึ้นไปเพื่อสำรองข้อมูล
กำหนดเป้าหมายอุปกรณ์ที่ใช้ Android 9 (API 29) ขึ้นไปเพื่อให้สามารถคืนค่าได้
ดูข้อมูลเพิ่มเติมเกี่ยวกับขั้นตอนการคืนค่าอุปกรณ์ไปยังอุปกรณ์ได้ที่นี่
ขั้นตอนการกู้คืนและสำรองข้อมูลในระบบคลาวด์
การสำรองและกู้คืนข้อมูลในระบบคลาวด์จะต้องมีอุปกรณ์ต้นทางและอุปกรณ์เป้าหมาย
อุปกรณ์ต้นทางต้องใช้ Android 6 (API 23) ขึ้นไปเพื่อสำรองข้อมูล
ระบบรองรับอุปกรณ์ที่กำหนดเป้าหมายโดยอิงตามผู้ให้บริการ อุปกรณ์ Pixel ใช้ฟีเจอร์นี้ได้จาก Android 9 (API 29) และอุปกรณ์อื่นๆ ทั้งหมดต้องใช้ Android 12 (API 31) ขึ้นไป