شروع سریع Android

این راهنما توضیح می‌دهد که چگونه یک برنامه ساده Android راه‌اندازی کنید که درخواست‌هایی به YouTube Data API ارسال می‌کند.

پیش‌نیازها

برای اجرای این شروع سریع، به موارد زیر نیاز دارید:

این شروع سریع فرض می‌کند که شما از Android Studio IDE (برخلاف ابزارهای کیت توسعه نرم‌افزار مستقل) استفاده می‌کنید و به‌راحتی می‌توانید فایل‌ها را در پروژه Studio پیدا، ایجاد، و ویرایش کنید.

مرحله ۱: اثر انگشت SHA1 را به‌دست آورید

در پایانه، فرمان Keytool utility زیر را اجرا کنید تا اثر انگشت SHA1 را که برای فعال کردن API استفاده خواهید کرد دریافت کنید.

keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v

وقتی گذرواژه keystore درخواست شد، «android» را وارد کنید.

‫Keytool اثر انگشت را در پوسته چاپ می‌کند. برای مثال:

$ 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 را که در مثال بالا برجسته شده است کپی کنید.

مرحله ۲: YouTube Data API را فعال کنید

  1. از این جادوگر برای ایجاد یا انتخاب پروژه در Google Developers Console و روشن کردن خودکار API استفاده کنید. روی ادامه، سپس رفتن به اطلاعات کاربری کلیک کنید.

  2. در صفحه ایجاد اطلاعات کاربری، روی دکمه لغو کلیک کنید.

  3. در بالای صفحه، برگه صفحه کسب رضایت OAuth را انتخاب کنید. نشانی ایمیل را انتخاب کنید، اگر نام محصول ازقبل تنظیم نشده است، آن را وارد کنید و روی دکمه ذخیره کلیک کنید.

  4. برگه اعتبارنامه‌ها را انتخاب کنید، روی دکمه ایجاد اعتبارنامه‌ها کلیک کنید و شناسه کارخواه OAuth را انتخاب کنید.

  5. نوع برنامه Android را انتخاب کنید.
  6. اثر انگشت SHA1 را از مرحله ۱ در فیلد اثر انگشت گواهینامه امضا کپی کنید.
  7. در فیلد نام بسته، com.example.quickstart را وارد کنید.
  8. روی دکمه ایجاد کلیک کنید.

مرحله ۳: ایجاد پروژه Android جدید

  1. استودیو Android را باز کنید، و «پروژه استودیو Android» جدیدی را شروع کنید.
  2. در صفحه پروژه جدید، نام برنامه را «شروع سریع» بگذارید.
  3. دامنه شرکت را روی «example.com» تنظیم کنید و تأیید کنید که نام بسته تولیدشده به‌طور خودکار با نام بسته‌ای که در کنسول توسعه‌دهنده در مرحله ۲ وارد کرده‌اید مطابقت داشته باشد. روی بعدی کلیک کنید.
  4. در صفحه دستگاه‌های هدف Android، چارگوش تلفن و رایانه لوحی را علامت بزنید و حداقل SDK را روی «API 14: Android 4.0 (IceCreamSandwich)» تنظیم کنید. بقیه چارگوش‌های انتخاب را لغو انتخاب کنید. روی بعدی کلیک کنید.
  5. در صفحه افزودن فعالیت به «تلفن همراه»، روی افزودن فعالیت کلیک کنید.
  6. روی تمام کلیک کنید؛

در این مرحله، Android Studio پروژه را ایجاد و باز می‌کند.

مرحله ۴: آماده کردن پروژه

نوار کناری «پروژه» فهرستی ازهم بازشدنی از فایل‌های پروژه پیش‌فرض است که استودیو Android ایجاد کرده است. در آن فهرست، فهرست دستورگان‌های Gradle را ازهم باز کنید و فایل build.gradle منسوب به واحد «برنامه» (نه پروژه) را باز کنید.

  1. فایل build.gradle برنامه را باز کنید و محتوای آن را با موارد زیر جایگزین کنید:

  2. در نوارابزار، ابزارها > Android > همگام‌سازی پروژه با فایل‌های Gradle را انتخاب کنید. این کار کتابخانه‌های موردنیاز پروژه شما را به‌دست می‌آورد و دردسترس قرار می‌دهد.

  3. فایل پیش‌فرض 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>

مرحله ۵: راه‌اندازی نمونه

کلاس 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.");
            }
        }
    }
}

مرحله ۶: اجرای برنامه

  1. برای آزمایش برنامه، روی گزینه منو اجرا > اجرای برنامه کلیک کنید.
  2. از شما خواسته می‌شود دستگاه متصل (توصیه‌شده) یا شبیه‌سازی را برای اجرای برنامه انتخاب کنید. اگر در شبیه‌سازی اجرا می‌کنید، مطمئن شوید که برای استفاده از یکی از تصاویر سیستم با Google APIs پیکربندی شده باشد. اگر بخواهید شروع سریع را در دستگاهی اجرا کنید که درحال‌حاضر خدمات Google Play در آن نصب نشده است، شروع سریع کادر گفتگویی ایجاد می‌کند که ازطریق آن می‌توانید آن را نصب کنید.
  3. اگر روی شبیه‌ساز اجرا می‌کنید، اجازه دهید به‌طور کامل شروع شود و اتصال شبکه خود را برقرار کند.
  4. اگر شبیه‌ساز را برای اولین‌بار راه‌اندازی می‌کنید، ممکن است لازم باشد قفل صفحه‌نمایش آن را باز کنید. درهرصورت، برنامه شروع سریع باید به‌طور خودکار شروع شود.
  5. اولین باری که برنامه را اجرا می‌کنید، از شما می‌خواهد حسابی را مشخص کنید. جریان ورود به سیستم را تکمیل کنید تا حسابی را برای اتصال انتخاب کنید.
  6. پس‌از انتخاب حساب، برنامه از شما می‌خواهد دسترسی را مجاز کنید. برای مجاز کردن، روی تأیید کلیک کنید.

یادداشت‌ها

  • اطلاعات صدور مجوز در برنامه ذخیره می‌شود، بنابراین اجراهای بعدی درخواست صدور مجوز نمی‌کنند.

مطالعه بیشتر

عیب‌یابی

برنامه Android ثبت نشده

وقتی کادر گفتگوی OAuth حاوی ورودی‌ای است که نوشته «برنامه Android ثبت‌نشده»، یعنی شناسه کارخواه OAuth2 که در مرحله ۲ ایجاد کردید پیدا نمی‌شود و Android به کارخواه پیش‌فرض برمی‌گردد. کارخواه پیش‌فرض برای استفاده از این «میانای برنامه‌سازی کاربردی» پیکربندی نخواهد شد، بنابراین درخواست‌ها با خطاهایی مثل accessNotConfigured. ناموفق خواهند بود. این خطاها ممکن است به شماره پروژه پیش‌فرض 608941808256 نیز اشاره کنند.

برای رفع مشکل، مطمئن شوید اثر انگشت SHA1 که در مرحله ۱ بازیابی کردید و applicationId فهرست‌شده در فایل build.gradle دقیقاً با مقدارهایی که در کنسول Google Developers تنظیم کرده‌اید مطابقت داشته باشد.