คำแนะนำนี้อธิบายวิธีตั้งค่าแอปพลิเคชัน Android ง่ายๆ ที่ส่งคำขอไปยัง YouTube Data API
ข้อกำหนดเบื้องต้น
หากต้องการเรียกใช้การเริ่มต้นอย่างรวดเร็วนี้ คุณจะต้องมีสิ่งต่อไปนี้
- Android Studio SDK 1.2 ขึ้นไป
- แพ็กเกจ Android SDK สำหรับ API 23 ขึ้นไป รวมถึง Google Repository, ไลบรารีการสนับสนุนของ Android และบริการ Google Play เวอร์ชันล่าสุด
- เข้าถึงอินเทอร์เน็ตในอุปกรณ์ทดสอบ
- บัญชี Google
การเริ่มต้นอย่างรวดเร็วนี้จะถือว่าคุณใช้ Android Studio IDE (ตรงข้ามกับเครื่องมือ SDK แบบสแตนด์อโลน) และสะดวกในการค้นหา สร้างและแก้ไขไฟล์ภายในโปรเจ็กต์ Studio
ขั้นตอนที่ 1: รับลายนิ้วมือ SHA1
จากนั้นเรียกใช้คำสั่ง Keytool utility ต่อไปนี้เพื่อรับลายนิ้วมือ SHA1 ที่คุณจะใช้เปิดใช้ API
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v
เมื่อระบบขอรหัสผ่านคีย์สโตร์ ให้ป้อน "android"
เครื่องมือกุญแจจะพิมพ์ลายนิ้วมือบนเคส เช่น
$ keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v Enter keystore password: Type "android" if using debug.keystore Alias name: androiddebugkey Creation date: Dec 4, 2014 Entry type: PrivateKeyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=Android Debug, O=Android, C=US Issuer: CN=Android Debug, O=Android, C=US Serial number: 503bd581 Valid from: Mon Aug 27 13:16:01 PDT 2012 until: Wed Aug 20 13:16:01 PDT 2042 Certificate fingerprints: MD5: 1B:2B:2D:37:E1:CE:06:8B:A0:F0:73:05:3C:A3:63:DD SHA1: D8:AA:43:97:59:EE:C5:95:26:6A:07:EE:1C:37:8E:F4:F0:C8:05:C8 SHA256: F3:6F:98:51:9A:DF:C3:15:4E:48:4B:0F:91:E3:3C:6A:A0:97:DC:0A:3F:B2:D2:E1:FE:23:57:F5:EB:AC:13:30 Signature algorithm name: SHA1withRSA Version: 3
คัดลอกลายนิ้วมือ SHA1 ซึ่ง ไฮไลต์ในตัวอย่างด้านบน
ขั้นตอนที่ 2: เปิด YouTube Data API
-
ใช้วิซาร์ดนี้เพื่อสร้างหรือเลือกโปรเจ็กต์ใน Google Developers Console แล้วเปิดใช้ API โดยอัตโนมัติ คลิกต่อไป แล้วไปที่ข้อมูลเข้าสู่ระบบ
-
ในหน้าสร้างข้อมูลเข้าสู่ระบบ ให้คลิกปุ่มยกเลิก
-
เลือกแท็บหน้าจอขอความยินยอม OAuth ที่ด้านบนของหน้า เลือกอีเมล ป้อนชื่อผลิตภัณฑ์หากยังไม่ได้ตั้งค่า แล้วคลิกปุ่มบันทึก
-
เลือกแท็บข้อมูลเข้าสู่ระบบ คลิกปุ่มสร้างข้อมูลเข้าสู่ระบบ แล้วเลือกรหัสไคลเอ็นต์ OAuth
- เลือกประเภทแอปพลิเคชัน Android
- คัดลอกลายนิ้วมือ SHA1 จากขั้นตอนที่ 1 ลงในช่องลายนิ้วมือของใบรับรองในการลงนาม
- ในช่องชื่อแพ็กเกจ ให้ป้อน
com.example.quickstart
- คลิกที่ปุ่มสร้าง
ขั้นตอนที่ 3: สร้างโปรเจ็กต์ Android ใหม่
- เปิด Android Studio และเริ่มโปรเจ็กต์ Android Studio ใหม่
- ในหน้าจอโครงการใหม่ ให้ตั้งชื่อแอปพลิเคชันว่า "การเริ่มต้นอย่างรวดเร็ว"
- ตั้งค่าโดเมนของบริษัทเป็น "example.com" และยืนยันว่าชื่อแพ็กเกจที่สร้างขึ้นโดยอัตโนมัติตรงกับชื่อที่คุณป้อนในแผงควบคุมสำหรับนักพัฒนาซอฟต์แวร์ในขั้นตอนที่ 2 คลิกถัดไป
- ในหน้าจออุปกรณ์ Android เป้าหมาย ให้เลือกช่องทำเครื่องหมายโทรศัพท์และแท็บเล็ต แล้วเลือก SDK ขั้นต่ำเป็น "API 14: Android 4.0 (IceCreamS วิธี)" ไม่ต้องเลือกช่องทำเครื่องหมายอื่น คลิกถัดไป
- คลิกเพิ่มไม่มีกิจกรรมในหน้าจอเพิ่มกิจกรรมลงในอุปกรณ์เคลื่อนที่
- คลิกเสร็จ
ในขั้นตอนนี้ Android Studio จะสร้างและเปิดโปรเจ็กต์
ขั้นตอนที่ 4: เตรียมโครงการ
แถบด้านข้างของโปรเจ็กต์คือรายการแบบขยายได้ของไฟล์โปรเจ็กต์เริ่มต้นที่ Android Studio สร้างขึ้น ในรายการดังกล่าว ให้ขยายรายการสคริปต์ Gradle แล้วเปิดไฟล์ build.gradle
ที่เชื่อมโยงกับโมดูล "แอป" (ไม่ใช่โปรเจ็กต์)
- เปิดไฟล์
build.gradle
ของแอปและแทนที่เนื้อหาด้วยข้อมูลต่อไปนี้ - ในแถบเครื่องมือ ให้เลือกเครื่องมือ > Android > ซิงค์โปรเจ็กต์ด้วยไฟล์ Gradle การดำเนินการนี้จะจัดหาไลบรารีและให้บริการไลบรารีที่โปรเจ็กต์ของคุณต้องการ
- ค้นหาและเปิดไฟล์
src/main/AndroidManifest.xml
เริ่มต้น ในแถบด้านข้างของโปรเจ็กต์ ไฟล์นี้ซ้อนอยู่ใต้app
และอยู่ใต้manifests
แทนที่เนื้อหาของไฟล์ด้วยโค้ดต่อไปนี้<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.quickstart"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="YouTube Data API Android Quickstart" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="YouTube Data API Android Quickstart" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
ขั้นตอนที่ 5: ตั้งค่าตัวอย่าง
สร้างคลาส Java ใหม่ ขั้นแรกให้เลือกโฟลเดอร์ java
ในแถบด้านข้างของโปรเจ็กต์ โฟลเดอร์นี้จะปรากฏในกลุ่มไฟล์ app
ไฟล์ หลังจากคลิกโฟลเดอร์ คุณสามารถเลือก.../app/src/main/java
ตั้งชื่อชั้นเรียนว่า "MainActivity" และคลิกตกลง แทนที่เนื้อหาของไฟล์ใหม่ด้วยโค้ดต่อไปนี้
package com.example.quickstart; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; import com.google.api.client.extensions.android.http.AndroidHttp; import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential; import com.google.api.client.googleapis.extensions.android.gms.auth.GooglePlayServicesAvailabilityIOException; import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException; import com.google.api.client.http.HttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.util.ExponentialBackOff; import com.google.api.services.youtube.YouTubeScopes; import com.google.api.services.youtube.model.*; import android.Manifest; import android.accounts.AccountManager; import android.app.Activity; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; import android.text.TextUtils; import android.text.method.ScrollingMovementMethod; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import pub.devrel.easypermissions.AfterPermissionGranted; import pub.devrel.easypermissions.EasyPermissions; public class MainActivity extends Activity implements EasyPermissions.PermissionCallbacks { GoogleAccountCredential mCredential; private TextView mOutputText; private Button mCallApiButton; ProgressDialog mProgress; static final int REQUEST_ACCOUNT_PICKER = 1000; static final int REQUEST_AUTHORIZATION = 1001; static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002; static final int REQUEST_PERMISSION_GET_ACCOUNTS = 1003; private static final String BUTTON_TEXT = "Call YouTube Data API"; private static final String PREF_ACCOUNT_NAME = "accountName"; private static final String[] SCOPES = { YouTubeScopes.YOUTUBE_READONLY }; /** * Create the main activity. * @param savedInstanceState previously saved instance data. */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout activityLayout = new LinearLayout(this); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); activityLayout.setLayoutParams(lp); activityLayout.setOrientation(LinearLayout.VERTICAL); activityLayout.setPadding(16, 16, 16, 16); ViewGroup.LayoutParams tlp = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); mCallApiButton = new Button(this); mCallApiButton.setText(BUTTON_TEXT); mCallApiButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mCallApiButton.setEnabled(false); mOutputText.setText(""); getResultsFromApi(); mCallApiButton.setEnabled(true); } }); activityLayout.addView(mCallApiButton); mOutputText = new TextView(this); mOutputText.setLayoutParams(tlp); mOutputText.setPadding(16, 16, 16, 16); mOutputText.setVerticalScrollBarEnabled(true); mOutputText.setMovementMethod(new ScrollingMovementMethod()); mOutputText.setText( "Click the \'" + BUTTON_TEXT +"\' button to test the API."); activityLayout.addView(mOutputText); mProgress = new ProgressDialog(this); mProgress.setMessage("Calling YouTube Data API ..."); setContentView(activityLayout); // Initialize credentials and service object. mCredential = GoogleAccountCredential.usingOAuth2( getApplicationContext(), Arrays.asList(SCOPES)) .setBackOff(new ExponentialBackOff()); } /** * Attempt to call the API, after verifying that all the preconditions are * satisfied. The preconditions are: Google Play Services installed, an * account was selected and the device currently has online access. If any * of the preconditions are not satisfied, the app will prompt the user as * appropriate. */ private void getResultsFromApi() { if (! isGooglePlayServicesAvailable()) { acquireGooglePlayServices(); } else if (mCredential.getSelectedAccountName() == null) { chooseAccount(); } else if (! isDeviceOnline()) { mOutputText.setText("No network connection available."); } else { new MakeRequestTask(mCredential).execute(); } } /** * Attempts to set the account used with the API credentials. If an account * name was previously saved it will use that one; otherwise an account * picker dialog will be shown to the user. Note that the setting the * account to use with the credentials object requires the app to have the * GET_ACCOUNTS permission, which is requested here if it is not already * present. The AfterPermissionGranted annotation indicates that this * function will be rerun automatically whenever the GET_ACCOUNTS permission * is granted. */ @AfterPermissionGranted(REQUEST_PERMISSION_GET_ACCOUNTS) private void chooseAccount() { if (EasyPermissions.hasPermissions( this, Manifest.permission.GET_ACCOUNTS)) { String accountName = getPreferences(Context.MODE_PRIVATE) .getString(PREF_ACCOUNT_NAME, null); if (accountName != null) { mCredential.setSelectedAccountName(accountName); getResultsFromApi(); } else { // Start a dialog from which the user can choose an account startActivityForResult( mCredential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER); } } else { // Request the GET_ACCOUNTS permission via a user dialog EasyPermissions.requestPermissions( this, "This app needs to access your Google account (via Contacts).", REQUEST_PERMISSION_GET_ACCOUNTS, Manifest.permission.GET_ACCOUNTS); } } /** * Called when an activity launched here (specifically, AccountPicker * and authorization) exits, giving you the requestCode you started it with, * the resultCode it returned, and any additional data from it. * @param requestCode code indicating which activity result is incoming. * @param resultCode code indicating the result of the incoming * activity result. * @param data Intent (containing result data) returned by incoming * activity result. */ @Override protected void onActivityResult( int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch(requestCode) { case REQUEST_GOOGLE_PLAY_SERVICES: if (resultCode != RESULT_OK) { mOutputText.setText( "This app requires Google Play Services. Please install " + "Google Play Services on your device and relaunch this app."); } else { getResultsFromApi(); } break; case REQUEST_ACCOUNT_PICKER: if (resultCode == RESULT_OK && data != null && data.getExtras() != null) { String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME); if (accountName != null) { SharedPreferences settings = getPreferences(Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.putString(PREF_ACCOUNT_NAME, accountName); editor.apply(); mCredential.setSelectedAccountName(accountName); getResultsFromApi(); } } break; case REQUEST_AUTHORIZATION: if (resultCode == RESULT_OK) { getResultsFromApi(); } break; } } /** * Respond to requests for permissions at runtime for API 23 and above. * @param requestCode The request code passed in * requestPermissions(android.app.Activity, String, int, String[]) * @param permissions The requested permissions. Never null. * @param grantResults The grant results for the corresponding permissions * which is either PERMISSION_GRANTED or PERMISSION_DENIED. Never null. */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); EasyPermissions.onRequestPermissionsResult( requestCode, permissions, grantResults, this); } /** * Callback for when a permission is granted using the EasyPermissions * library. * @param requestCode The request code associated with the requested * permission * @param list The requested permission list. Never null. */ @Override public void onPermissionsGranted(int requestCode, List<String> list) { // Do nothing. } /** * Callback for when a permission is denied using the EasyPermissions * library. * @param requestCode The request code associated with the requested * permission * @param list The requested permission list. Never null. */ @Override public void onPermissionsDenied(int requestCode, List<String> list) { // Do nothing. } /** * Checks whether the device currently has a network connection. * @return true if the device has a network connection, false otherwise. */ private boolean isDeviceOnline() { ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); return (networkInfo != null && networkInfo.isConnected()); } /** * Check that Google Play services APK is installed and up to date. * @return true if Google Play Services is available and up to * date on this device; false otherwise. */ private boolean isGooglePlayServicesAvailable() { GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance(); final int connectionStatusCode = apiAvailability.isGooglePlayServicesAvailable(this); return connectionStatusCode == ConnectionResult.SUCCESS; } /** * Attempt to resolve a missing, out-of-date, invalid or disabled Google * Play Services installation via a user dialog, if possible. */ private void acquireGooglePlayServices() { GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance(); final int connectionStatusCode = apiAvailability.isGooglePlayServicesAvailable(this); if (apiAvailability.isUserResolvableError(connectionStatusCode)) { showGooglePlayServicesAvailabilityErrorDialog(connectionStatusCode); } } /** * Display an error dialog showing that Google Play Services is missing * or out of date. * @param connectionStatusCode code describing the presence (or lack of) * Google Play Services on this device. */ void showGooglePlayServicesAvailabilityErrorDialog( final int connectionStatusCode) { GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance(); Dialog dialog = apiAvailability.getErrorDialog( MainActivity.this, connectionStatusCode, REQUEST_GOOGLE_PLAY_SERVICES); dialog.show(); } /** * An asynchronous task that handles the YouTube Data API call. * Placing the API calls in their own task ensures the UI stays responsive. */ private class MakeRequestTask extends AsyncTask<Void, Void, List<String>> { private com.google.api.services.youtube.YouTube mService = null; private Exception mLastError = null; MakeRequestTask(GoogleAccountCredential credential) { HttpTransport transport = AndroidHttp.newCompatibleTransport(); JsonFactory jsonFactory = JacksonFactory.getDefaultInstance(); mService = new com.google.api.services.youtube.YouTube.Builder( transport, jsonFactory, credential) .setApplicationName("YouTube Data API Android Quickstart") .build(); } /** * Background task to call YouTube Data API. * @param params no parameters needed for this task. */ @Override protected List<String> doInBackground(Void... params) { try { return getDataFromApi(); } catch (Exception e) { mLastError = e; cancel(true); return null; } } /** * Fetch information about the "GoogleDevelopers" YouTube channel. * @return List of Strings containing information about the channel. * @throws IOException */ private List<String> getDataFromApi() throws IOException { // Get a list of up to 10 files. List<String> channelInfo = new ArrayList<String>(); ChannelListResponse result = mService.channels().list("snippet,contentDetails,statistics") .setForUsername("GoogleDevelopers") .execute(); List<Channel> channels = result.getItems(); if (channels != null) { Channel channel = channels.get(0); channelInfo.add("This channel's ID is " + channel.getId() + ". " + "Its title is '" + channel.getSnippet().getTitle() + ", " + "and it has " + channel.getStatistics().getViewCount() + " views."); } return channelInfo; } @Override protected void onPreExecute() { mOutputText.setText(""); mProgress.show(); } @Override protected void onPostExecute(List<String> output) { mProgress.hide(); if (output == null || output.size() == 0) { mOutputText.setText("No results returned."); } else { output.add(0, "Data retrieved using the YouTube Data API:"); mOutputText.setText(TextUtils.join("\n", output)); } } @Override protected void onCancelled() { mProgress.hide(); if (mLastError != null) { if (mLastError instanceof GooglePlayServicesAvailabilityIOException) { showGooglePlayServicesAvailabilityErrorDialog( ((GooglePlayServicesAvailabilityIOException) mLastError) .getConnectionStatusCode()); } else if (mLastError instanceof UserRecoverableAuthIOException) { startActivityForResult( ((UserRecoverableAuthIOException) mLastError).getIntent(), MainActivity.REQUEST_AUTHORIZATION); } else { mOutputText.setText("The following error occurred:\n" + mLastError.getMessage()); } } else { mOutputText.setText("Request cancelled."); } } } }
ขั้นตอนที่ 6: เรียกใช้แอป
- หากต้องการทดสอบแอป ให้คลิกรายการในเมนูเรียกใช้ > เรียกใช้แอป
- ระบบจะแจ้งให้คุณเลือกอุปกรณ์ที่เชื่อมต่อ (แนะนำ) หรือโปรแกรมจำลองที่จะเรียกใช้แอป หากคุณเรียกใช้การจำลอง ให้ตรวจสอบว่ามีการกำหนดค่าให้ใช้อิมเมจระบบที่มี Google APIs แบบใดแบบหนึ่ง หากคุณพยายามเรียกใช้การเริ่มต้นอย่างรวดเร็วในอุปกรณ์ที่ไม่ได้ติดตั้งบริการ Google Play ไว้ในขณะนั้น การเริ่มต้นอย่างรวดเร็วจะแสดงกล่องโต้ตอบที่คุณสามารถติดตั้งได้
- หากทำงานอยู่ในโปรแกรมจำลอง ให้ปล่อยให้โปรแกรมเริ่มต้นและสร้างการเชื่อมต่อเครือข่ายอย่างสมบูรณ์
- หากเริ่มโปรแกรมจำลองเป็นครั้งแรก คุณอาจต้องปลดล็อกหน้าจอของโปรแกรมจำลอง อย่างไรก็ตาม แอปเริ่มต้นอย่างรวดเร็วควรเริ่มทำงานโดยอัตโนมัติ
- ครั้งแรกที่คุณเรียกใช้แอป ระบบจะแจ้งให้คุณระบุบัญชี ทำตามขั้นตอนการลงชื่อเข้าใช้ให้เสร็จเพื่อเลือกบัญชีที่จะเชื่อมต่อ
- หลังจากเลือกบัญชีแล้ว แอปจะแจ้งให้คุณให้สิทธิ์เข้าถึง คลิกตกลงเพื่อให้สิทธิ์
Notes
- ระบบจัดเก็บข้อมูลการให้สิทธิ์ไว้กับแอป ดังนั้นการดำเนินการในครั้งต่อๆ ไปจะไม่แสดงข้อความแจ้งให้ให้สิทธิ์
อ่านเพิ่มเติม
- เอกสารความช่วยเหลือเกี่ยวกับ Google Developers Console
- เอกสารประกอบเกี่ยวกับไคลเอ็นต์ Google APIs สำหรับ Java
- คู่มือเกี่ยวกับ Android API
- บริการ Google Play
- เอกสารอ้างอิงสำหรับ YouTube Data API
การแก้ปัญหา
แอปพลิเคชัน Android ที่ไม่ได้ลงทะเบียน
เมื่อกล่องโต้ตอบ OAuth มีรายการที่เขียนว่า "แอปพลิเคชัน Android ที่ไม่ได้ลงทะเบียน" แสดงว่าไม่พบรหัสไคลเอ็นต์ OAuth2 ที่คุณสร้างในขั้นตอนที่ 2 และ Android จะกลับไปใช้ไคลเอ็นต์เริ่มต้น ระบบจะไม่กำหนดค่าไคลเอ็นต์เริ่มต้นให้ใช้ API นี้ ดังนั้นคำขอจะล้มเหลวโดยมีข้อผิดพลาด เช่น accessNotConfigured.
ข้อผิดพลาดเหล่านี้อาจมีการระบุโปรเจ็กต์เริ่มต้นหมายเลข 608941808256
ด้วย
หากต้องการแก้ไขปัญหา ให้ตรวจสอบว่าลายนิ้วมือ SHA1 ที่ดึงมาในขั้นตอนที่ 1 และ applicationId
ที่ระบุไว้ในไฟล์ build.gradle
ตรงกับค่าที่กำหนดไว้ใน Google Developers Console ทุกประการ