2017-06-02 20 views
2

Googleスタジオでアプリを作成して、Googleスプレッドシートからデータを取得して使用するプロジェクトです。Android用のGoogleスプレッドシートクイックスタートの例(APIv4)が動作しません

Googleのドキュメントで、Android用のスプレッドシートクイックスタート(Apiv4)を試してみました。

私はデバイス上でクイックスタートアプリケーションを起動します。

しかし、私はすべての時間が、私はプログラムは、Googleのシートを呼び出す場合は特に、私のデバイス上で私のアプリを起動し、私はシートに接続できないエラーが表示されました:

The following error occurred: null 

私はプログラムを見て、デバッグするエラーがどこにあるのかを取得しました。方法はonCancelled()です。作業、

protected List<String> doInBackground(Void... params) 

は、私は完全にチュートリアルに従っ:しかし、私は本当に私のエラーをデバッグするために、たとえば、この方法「...」重要な役割を持っているようだ使用されているプログラムのいくつかの部分を理解していませんAndroid Studioでは、3回以上開始されましたが、常に同じ問題です。私はJavaとAndroid(最初の実際のアプリ)には自信がありません。だから、私が間違っていることを学ぶために、私はあなたの助けが必要です。

私は続くステップの下に再開し、私がやったプログラム

  • 私のコンピュータ上で私のSHA1フィンガープリント(CMD.EXEコマンド)
  • は私のGoogleスプレッドシート
  • アクティベーションを作成、取得私のアプリケーションのために使用するパッケージ
  • チュートリアルとして私のアプリケーションを作成すると言った
  • build.gradle(良いもの)を変更し、それを同期
  • AndroidManifest.xmlを変更してください:
  • 私のMainActivity.javaを作成し、このコードに置き換えてください。
  • apkをビルドし、USB経由でOnePlus3(私のAndroid搭載端末)に転送します。
  • このapkをインストールして起動します。
  • このボタンをクリックし、Googleアカウントにログインし、表示Calling Google Sheet Api、それを停止し、このエラーを表示します。 は、次のエラーが発生しました:ヌル

screenshot_error_1

screenshot_error_2

ビルドを。階調:

apply plugin: 'com.android.application' 
android { 
    compileSdkVersion 25 
    buildToolsVersion "25.0.3" 
    defaultConfig { 
     applicationId "com.example.quickstart" 
     minSdkVersion 15 
     targetSdkVersion 25 
     versionCode 1 
     versionName "1.0" 
     testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 
     multiDexEnabled true 
    } 
    buildTypes { 
     release { 
      minifyEnabled false 
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 
     } 
    } 
    configurations.all { 
     resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9' 
    } } 

dependencies { 
    compile fileTree(dir: 'libs', include: ['*.jar']) 
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 
     exclude group: 'com.android.support', module: 'support-annotations' 
    }) 
    compile 'com.google.android.gms:play-services:8.1.0' 
    testCompile 'junit:junit:4.12' 
    compile 'pub.devrel:easypermissions:0.2.1' 
    compile('com.google.api-client:google-api-client-android:1.22.0') { 
     exclude group: 'org.apache.httpcomponents' 
    } 
    compile('com.google.apis:google-api-services-sheets:v4-rev476-1.22.0') { 
     exclude group: 'org.apache.httpcomponents' 
    } 

} 

AndroidManifest XML:

<?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="Google Sheets API Android Quickstart" 
     android:theme="@style/AppTheme" > 
     <activity 
      android:name=".MainActivity" 
      android:label="Google Sheets API Android Quickstart" > 
      <intent-filter> 
       <action android:name="android.intent.action.MAIN" /> 
       <category android:name="android.intent.category.LAUNCHER" /> 
      </intent-filter> 
     </activity> 

    </application> 
</manifest> 

MainActivity.java:

package com.example.quickstart; 

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 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.sheets.v4.SheetsScopes; 
import com.google.api.services.sheets.v4.model.ValueRange; 

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 Google Sheets API"; 
    private static final String PREF_ACCOUNT_NAME = "accountName"; 
    private static final String[] SCOPES = { SheetsScopes.SPREADSHEETS_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 Google Sheets 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 Google Sheets 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.sheets.v4.Sheets mService = null; 
     private Exception mLastError = null; 

     MakeRequestTask(GoogleAccountCredential credential) { 
      HttpTransport transport = AndroidHttp.newCompatibleTransport(); 
      JsonFactory jsonFactory = JacksonFactory.getDefaultInstance(); 
      mService = new com.google.api.services.sheets.v4.Sheets.Builder(
        transport, jsonFactory, credential) 
        .setApplicationName("Google Sheets API Android Quickstart") 
        .build(); 
     } 

     /** 
     * Background task to call Google Sheets 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 a list of names and majors of students in a sample spreadsheet: 
     * https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit 
     * @return List of names and majors 
     * @throws IOException 
     */ 
     private List<String> getDataFromApi() throws IOException { 
      String spreadsheetId = "629235369054-3cc387mt7mlnacv1kiq8d30auhqrh278.apps.googleusercontent.com"; 
      String range = "GroupMar!A2:C"; 
      List<String> results = new ArrayList<String>(); 
      ValueRange response = this.mService.spreadsheets().values() 
        .get(spreadsheetId, range) 
        .execute(); 
      List<List<Object>> values = response.getValues(); 
      if (values != null) { 
       results.add("Name, Major"); 
       for (List row : values) { 
        results.add(row.get(0) + ", " + row.get(1) + ", " + row.get(2)); 
       } 
      } 
      return results; 
     } 



     @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 Google Sheets 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."); 
      } 
     } 
    } 
} 

誰も私を理解するのに役立つことはできますか?

+0

投稿のヒント:自分のイメージホストではなくimgur.comを使用することが理想的です。アップロードボタンを使用するのが理想的です。そのため、正しいドメインが使用されます。これは画像を将来に保存するのに役立ちます。次に、あなたのペーストビンをコメントから削除します。もしそれらが必須であれば、それらを質問に入れます。最後に、ペーストビンをコードブロックに変換します。必要に応じてリンクを維持しますが、質問自体にその素材が表示されるようにします。 – halfer

+0

これらのガイドラインは、リモートイメージ/コードサイトでの404および削除による将来の管理の量を削減するのに役立ちます。 – halfer

+0

@halfer。私はあなたが言ったことに注意します。私はimgurと置き換えました。コードの場合、私は私のコードブロックに私のペーストビンを変換することはできません、私は以前試みたが、それは動作していないようです。私はもう一度お試しになります。 –

答えて

2

私は同じ問題を抱えていました。このエラーは、アプリがログインできない、つまり無効な資格情報である場合に発生します。だから、問題は

  • 資格情報が作成されていないことができた(しかし、あなたは、あなたがやった言及した)..だからアプリと開発者の間で
  • 資格情報の不一致は、例えばコンソール間違ったSHA1キー

私が直面した問題は、keytoolで取得したSHA1キーが、アプリケーションで使用されている実際のキーと一致しなかったことです。最初に実際にアプリケーションで使用されているSHA1キーを取得します。アプリで使用されているSHA1キーを取得する方法については、this投稿を確認してください。 keytoolキーと異なる場合は、開発者コンソールで更新して問題を解決します。

関連する問題