トップ 一覧 置換 検索 ヘルプ RSS ログイン

Android 非同期処理を行うの変更点

  • 追加された行はこのように表示されます。
  • 削除された行はこのように表示されます。
http://li4shi2.wordpress.com/2011/08/21/picasa%E3%81%AB%E7%94%BB%E5%83%8F%E3%82%92%E3%82%A2%E3%83%83%E3%83%97%E3%81%99%E3%82%8B%EF%BC%88android%EF%BC%89/
http://tomokey.blogspot.jp/2011/06/javaoauth.html
http://android.keicode.com/basics/async-asynctask.php

!!!ネット上のサンプルをいじったバージョン
Android で重い処理を行う場合は、AsyncTask を使うと良い。

!必要なパーミッション
  <uses-permission android:name="android.permission.INTERNET" />  
  <uses-permission android:name="android.permission.GET_ACCOUNTS" />  
  <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />  
  <uses-permission android:name="android.permission.USE_CREDENTIALS" /> 
非同期処理を開始
{{code Java,
package com.keicode.android.test;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

!アカウント選択
{{code Java,
public void selectAccount(final OnSelectAccountListener listener){
    AlertDialog.Builder builder = new AlertDialog.Builder(activity);
    builder.setTitle("Select a Google account");
    final AccountManager manager = AccountManager.get(activity);
    final Account[] accounts = manager.getAccountsByType("com.google");
    final int size = accounts.length;
    String[] names = new String[size];
    for (int i = 0; i < size; i++) {
      names[i] = accounts[i].name;
public class AsyncTest2 extends Activity 
  implements OnClickListener {
  
  final String TAG = "AsyncTest2";
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    ((Button)findViewById(R.id.button1))
      .setOnClickListener(this);
  }
  
  @Override
  public void onClick(View v) {
    if(v.getId() == R.id.button1){
      new MyAsyncTask(this).execute("Param1");
    }
    // names[size] = "New Account";
    builder.setItems(names, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
          if (which == size) {
            // addAccount(manager);
            // listener.onSelect();
          } else {
            listener.onSelect(accounts[which]);
            // gotAccount(manager, accounts[which]);
          }
        }
    });
    builder.create().show();
  }
  
}
}}

!取得したアカウントで Authトークン取得
非同期処理のメイン
 extends AsyncTask<String, Integer, Long> 
の後ろ(ダイアモンド演算子)はそれぞれ
 doInBackground
 onProgressUpdate
 onPostExecute
に渡す引数の型を指定している。
処理のキャンセルは、
 isCanceled
をループ内で呼び出して、キャンセルされていたらループを抜けるようにするとよい。

{{code Java,
public boolean authenticate(Account account, boolean clearCache, boolean retry ){
package com.keicode.android.test;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.os.AsyncTask;
import android.util.Log;

public class MyAsyncTask 
  extends AsyncTask<String, Integer, Long> 

  implements OnCancelListener{

  final String TAG = "MyAsyncTask";
  ProgressDialog dialog;
  Context context;
  
  public MyAsyncTask(Context context){
    this.context = context;
  }
  
  @Override
  protected void onPreExecute() {
    Log.d(TAG, "onPreExecute");
    dialog = new ProgressDialog(context);
    dialog.setTitle("Please wait");
    dialog.setMessage("Loading data...");
    dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    dialog.setCancelable(true);
    dialog.setOnCancelListener(this);
    dialog.setMax(100);
    dialog.setProgress(0);
    dialog.show();
  }

  @Override
  protected Long doInBackground(String... params) {
    Log.d(TAG, "doInBackground - " + params[0]);
    
    AccountManager manager = AccountManager.get(activity);
    if (clearCache){
      manager.invalidateAuthToken("com.google", this.authToken);
    }
    
    try {
      Bundle bundle =
      manager.getAuthToken(account, PicasaWebAlbums.AUTH_TOKEN_TYPE, true,
        null, null).getResult();
        if (bundle.containsKey(AccountManager.KEY_INTENT)) {
          Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
          int flags = intent.getFlags();
          flags &= ~Intent.FLAG_ACTIVITY_NEW_TASK;
          intent.setFlags(flags);
          activity.startActivityForResult(intent, REQUEST_AUTHENTICATE);
          return false;
        } else 
        if (bundle.containsKey(AccountManager.KEY_AUTHTOKEN)) {
          this.authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
          this.account = account;
          transport.setClientLoginToken(this.authToken);
          return true;
      for(int i=0; i<10; i++){
        if(isCancelled()){
          Log.d(TAG, "Cancelled!");
          break;
        }
        return false;
    } catch (Exception e){
      if (retry){
        return reauthenticate(e, account);
      } else {
        return false;
        Thread.sleep(1000);
        publishProgress((i+1) * 10);
      }
    }
    } catch (InterruptedException e) {
      Log.d(TAG, "InterruptedException in doInBackground");
    }  
    return 123L;
  }
  
  @Override
  protected void onProgressUpdate(Integer... values) {
    Log.d(TAG, "onProgressUpdate - " + values[0]);
    dialog.setProgress(values[0]);
  }
  
  @Override
  protected void onCancelled() {
    Log.d(TAG, "onCancelled");
    dialog.dismiss();
  }

  private boolean reauthenticate(Exception e, Account account) {
    // e.printStackTrace();
    if (e instanceof HttpResponseException) {
      int statusCode = ((HttpResponseException) e).response.statusCode;
      if (statusCode == 401 || statusCode == 403) {
        return authenticate(account, true, false);
      }
    }
    return false;
  @Override
  protected void onPostExecute(Long result) {
    Log.d(TAG, "onPostExecute - " + result);
    dialog.dismiss();
  }

  @Override
  public void onCancel(DialogInterface dialog) {
    Log.d(TAG, "Dialog onCancell... calling cancel(true)");
    //this.cancel(true);
    this.cancel(false);
  }
}
}}
Authトークンは、一定時間で無効になる。その場合は
 AccountManager#invalidateAuthToken()
してから、
 AccountManager#getAuthToken()
する。また、Authトークンが無効な状態でアクセスすると
 HttpResponseException
が発生し、
 response.statusCode が 401 か 403 
になる・・・はず。

!!!AndroidのAccountManager経由でGoogleのOAuth2認証を行う
http://kinsentansa.blogspot.jp/2012/08/androidaccountmanagergoogleoauth2.html
http://kinsentansa.blogspot.jp/2012/04/androidgoogleoauth2.html

!必要なパーミッション
  <uses-permission android:name="android.permission.INTERNET" />  
  <uses-permission android:name="android.permission.GET_ACCOUNTS" />  
  <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />  
  <uses-permission android:name="android.permission.USE_CREDENTIALS" /> 

!アカウント選択
{{code Java,
  protected static final String ACCOUNT_TYPE = "com.google";  
  protected void chooseAccount() {  
    Log.v("chooseAccount", "AuthToken取得開始(アカウント選択)");  
    accountManager.getAuthTokenByFeatures(ACCOUNT_TYPE, authTokenType, null, AccountManagerOAuth2Activity.this, null, null,  
      new AccountManagerCallback<bundle>() {  
        public void run(AccountManagerFuture<bundle> future) {  
          onGetAuthToken(future);  
        }  
      },  
      null);  
  }  
}}
!アカウント名取得
{{code Java,
 Bundle bundle = future.getResult();  
 accountName = bundle.getString(AccountManager.KEY_ACCOUNT_NAME);  
}}

!トークン取得
{{code Java,
protected static final int REQUEST_CODE_AUTH = 0;  
protected void getAuthToken() {  
  Account account = null;  
  Account[] accounts = accountManager.getAccounts();  
  for (int i = 0; i < accounts.length; i++) {  
    account = accounts[i];  
    if (account.name.equals(accountName)) {  
      break;  
    }  
  }  
  Log.v("getAuthToken", "AuthToken取得開始");  
  accountManager.getAuthToken(account, authTokenType, true,  
    new AccountManagerCallback<Bundle>() {  
      public void run(AccountManagerFuture<Bundle> future) {  
        try {  
          Bundle bundle = future.getResult();  
          if (bundle.containsKey(AccountManager.KEY_INTENT)) {  
            //まだAPIアクセス許可が出ていない場合にgetAuthToken()すると  
            //BundleにKEY_INTENTが含まれる。この場合AuthTokenはNULLとなる。  
            Log.v("getAuthToken", "アクセス許可画面へ");  
            Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);  
            //「FLAG_ACTIVITY_NEW_TASK」の前の「~」はビット反転演算子  
            //これをしないとアクセス許可画面でのボタンクリックを待たずにonActivityResult()が呼ばれてしまう  
            intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);  
            startActivityForResult(intent, REQUEST_CODE_AUTH);  
          } else if (bundle.containsKey(AccountManager.KEY_AUTHTOKEN)) {  
            onGetAuthToken(future);  
          }  
        } catch (Exception e) {  
          Log.v("getAuthToken", "AuthToken取得失敗", e);  
        }  
      }  
    },  
    null);  
}  
}}

!Authトークン取得時に Activity に処理が流れた場合
{{code Java,
protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  super.onActivityResult(requestCode, resultCode, data);  
  Log.v("onActivityResult", "requestCode=" + requestCode + " resultCode=" + resultCode);  
  switch (requestCode) {  
  case REQUEST_CODE_AUTH:  
    if (resultCode == RESULT_OK) {  
      getAuthToken();  
    } else {  
      Log.v("onActivityResult", "アクセス許可画面で拒否された");  
    }  
    break;  
  }  
}  
}}
 resultCode == RESULT_OK
になれば Authトークン取得の準備が完了する。
再度、getAuthTokenを呼び
 accountManager.getAuthToken
で Authトークンを取得する。

!Authトークンについて
authTokenは一定期間過ぎると無効になるので再取得する必要がある。
一定時間が過ぎたauthTokenを使ってAPIにリクエストすると「"code":401」を含むjsonが返される。



{{category2 プログラミング言語,Java,Android}}