Android AsyncTask#doInBackground内での例外をUIスレッドに通知する

Android AsyncTaskでProgressDialogを表示 その1
Android AsyncTaskでProgressDialogを表示 その2

前回までに作ったProgressBarを表示するAsyncTaskですが
doInBackgroundメソッド内で発生した例外をUIスレッドに通知したいと思います。

バックグラウンドで発生した例外をUIスレッドに通知するにはHandlerクラスを使用します。
詳しい説明や使い方はコチラなどが参考になりました。
愚鈍人 HandlerとMessage - 別スレッドでのGUI操作

ようはUIスレッドでHandlerクラスのインスタンスを生成し、
このインスタンスのsendMessageメソッドでバックグラウンドからUIスレッドに通知できます。
またHandlerクラスのhandleMessageメソッドをオーバライドして、バックグラウンドから通知を受けたときの処理を書きます。

MainActivity.java
ボタンをクリックするとバックグラウンドで処理を実行します。
処理を行うクラス(ReceiveDataクラス)のコンストラクタの引数にHandlerオブジェクトを指定しています。
またHandlerクラスのhandleMessageメソッドをオーバライドして、発生した例外のメッセージを表示します。
※「This Handler class should be static or leaks might occur」と警告が出ますが、今はとりあえず無視します(^^;
package com.example.helloandroid;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {
    //Activityが破棄されても、オブジェクトを保持できるようにstaticで宣言する。      
    private static ProgressAsyncTask<Void,Boolean> mTask;  

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //プログレスダイアログを再表示する。      
        if (mTask != null && mTask.getIsShowProgress()){      
            mTask.showDialog();      
        }    

    }

    @Override     
    protected void onPause() {      
        //プログレスダイアログを閉じる。      
        if (mTask != null && mTask.getIsShowProgress()) {      
            mTask.dismissDialog();       
        }      
        super.onPause();      
    }      


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    /**
     * button1クリック処理
     * @param view
     * バックグラウンドで処理を行う
     */
    public void button1Click(View view){
        final Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                Throwable e = (Throwable)msg.obj;
                Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
            }
        };
        //final MyHandler handler = new MyHandler(this);
        mTask = new ProgressAsyncTask<Void,Boolean>(this){   
            @Override  
            protected Boolean doInBackground(Void... params) {   
                ReceiveData receive = new ReceiveData(mTask,handler);   
                Boolean ret = receive.getMethod1();
                return ret;
            }};   
        mTask.execute();  
    }
}

ProgressAsyncTask.java
プログレスバーを表示するAsyncTaskクラスです。
package com.example.helloandroid;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;

/**
 * ProgressAsyncTask
 * プログレスダイアログを表示し、バックグラウンドで処理を行う抽象クラスです。
 * @param <Param>
 * @param <Result>
 *
 */
public abstract class ProgressAsyncTask<Param,Result> extends AsyncTask<Param,Bundle,Result>{
    private Context mContext;
    private ProgressDialog mProgressDialog;
    private Boolean isShowProgress;

    /**
     * コンストラクタ
     * @param context
     * @param procName
     */
    public ProgressAsyncTask(Context context){
        mContext = context;
    }

    /**
     * getIsShowProgress
     * プログレスダイアログが表示中かどうかを返します。
     * @return
     */
    public Boolean getIsShowProgress() {
        return isShowProgress;
    }

    /**
     * onPreExecute
     * 最初にUIスレッドで呼び出されます。
     * UIに関わる処理をします。
     */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        showDialog();
        isShowProgress = true;
    }

    /**
     * onProgressUpdate
     * doInBackground内でpublishProgressメソッドが呼ばれると、 UIスレッド上でこのメソッドが呼ばれます。 
     * このメソッドの引数の型はAsyncTaskの二つ目のパラメータです。 
     * @param values
     */
    @Override
    protected void onProgressUpdate(Bundle... values) {
        super.onProgressUpdate(values);
        Bundle bundle = values[0];  
        if (bundle.containsKey("Message")){
            mProgressDialog.setMessage(bundle.getString("Message"));
        }
        if (bundle.containsKey("Max")){
            mProgressDialog.setMax(bundle.getInt("Max"));
        }
        if (bundle.containsKey("Progress")){
            mProgressDialog.setProgress(bundle.getInt("Progress"));
        }
    }

    /**
     * onPostExecute
     * doInBackground が終わるとそのメソッドの戻り値をパラメータとして渡して onPostExecute が呼ばれます。
     * このパラメータの型は AsyncTask を extends するときの三つめのパラメータです。
     * バックグラウンド処理が終了し、メインスレッドに反映させる処理をここに書きます。
     * @param result
     */
    @Override
    protected void onPostExecute(Result result) {
        super.onPostExecute(result);
        dismissDialog();
        isShowProgress = false;
    }

    /**
     * onCancelled
     * cancelメソッドが呼ばれるとonCancelledメソッドが呼ばれます。
     */
    @Override
    protected void onCancelled() {
        super.onCancelled();
        dismissDialog();
        isShowProgress = false;
    }

    /**
     * showDialog
     * プログレスダイアログを表示します。
     */
    public void showDialog() { 
        mProgressDialog = new ProgressDialog(mContext);
        mProgressDialog.setMessage(""); //途中でメッセージを変更するために必要
        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.setIndeterminate(false);
        mProgressDialog.setCancelable(true);
        mProgressDialog.setButton("キャンセル", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                cancel(true); // 非同期処理をキャンセルする
            } 
        });
        mProgressDialog.show();
    }

    /**
     * dismissDialog
     * ダイアログを終了します。
     */
    public void dismissDialog() {
        mProgressDialog.dismiss();
        mProgressDialog = null;
    }

    /**
     * setProgressMessage
     * プログレスダイアログのメッセージを設定します。
     * @param message
     */
    public void setProgressMessage(String message){
        Bundle data = new Bundle();
        data.putString("Message", message);
        publishProgress(data);
    }
    /**
     * setMax
     * プログレスダイアログの最大値を設定します。
     * @param max
     */
    public void setProgressMax(int max){
        Bundle data = new Bundle();
        data.putInt("Max", max);
        publishProgress(data);
    }
    /**
     * setProgress
     * プログレスダイアログの進捗値を設定します。
     * @param max
     */
    public void setProgress(int progress){
        Bundle data = new Bundle();
        data.putInt("Progress", progress);
        publishProgress(data);
    }
}

ReceiveData.java
実際にバックグラウンドで行う処理です。
ループ変数 i が 7 になったときRuntimeExceptionをスローし、 catch句でHandlerオブジェクトのsendMessageメソッドを実行しています。
package com.example.helloandroid;

import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;

public class ReceiveData {
    
    private ProgressAsyncTask<?,?> mTask;
    private Handler mHandler;
    
    /**
     * コンストラクタ
     * @param task
     * @param handler
     */
    public ReceiveData(ProgressAsyncTask<?,?> task, Handler handler){
        mTask = task;
        mHandler = handler;
    }
    
    /**
     * getMethod1
     
     */
    public Boolean getMethod1(){
        try {
            mTask.setProgressMessage("Method1_Start");
            mTask.setProgressMax(10);
            for(int i=0; i<10; i++){
                mTask.setProgress(i+1);
                SystemClock.sleep(1000);
                if (i == 7) {
                    throw new RuntimeException("エラー! i==7です。");
                }
                if (mTask.isCancelled()) {      
                    return false;                   
                } 
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            final Message msg = new Message();
            msg.obj = e;
            new Thread(new Runnable(){
                public void run() {
                    mHandler.sendMessage(msg);
                }}).start();
            mTask.cancel(true);    
            return false;
        }
    }
}

実行するとUIスレッドでメッセージが表示されます。


さて次は「This Handler class should be static or leaks might occur」と警告が出ないようMainActivityを修正します。
修正方法はコチラを参考に
http://stackoverflow.com/questions/11407943/this-handler-class-should-be-static-or-leaks-might-occur-incominghandler
HandlerクラスをStaticクラスにします。

MainActivity
package com.example.helloandroid;

import java.lang.ref.WeakReference;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {
    //Activityが破棄されても、オブジェクトを保持できるようにstaticで宣言する。      
    private static ProgressAsyncTask<Void,Boolean> mTask;  

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //プログレスダイアログを再表示する。      
        if (mTask != null && mTask.getIsShowProgress()){      
            mTask.showDialog();      
        }    

    }

    @Override     
    protected void onPause() {      
        //プログレスダイアログを閉じる。      
        if (mTask != null && mTask.getIsShowProgress()) {      
            mTask.dismissDialog();       
        }      
        super.onPause();      
    }      


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    /**
     * button1クリック処理
     * @param view
     * バックグラウンドで処理を行う
     */
    public void button1Click(View view){
        final MyHandler handler = new MyHandler(this);
        mTask = new ProgressAsyncTask<Void,Boolean>(this){   
            @Override  
            protected Boolean doInBackground(Void... params) {   
                ReceiveData receive = new ReceiveData(mTask,handler);   
                Boolean ret = receive.getMethod1();
                return ret;
            }};   
        mTask.execute();  
    }
    
    /**
     * MyHandlerクラス
     * バックグラウンドで例外発生時の処理
     */
    private static class MyHandler extends Handler {    
        private final WeakReference<MainActivity> mActivity;    
        public MyHandler(MainActivity activity) {        
            mActivity = new WeakReference<MainActivity>(activity);    
        }    
        @Override    
        public void handleMessage(Message msg) {        
            MainActivity activity = mActivity.get();        
            if (activity == null){
                return;
            }
            Throwable e = (Throwable)msg.obj;
            Toast.makeText(mActivity.get(), e.getMessage(), Toast.LENGTH_LONG).show();
        }
    } 
}

Android JSONObjectのNull判定

ちょっぴりハマったのでメモです。

JSONObjectのNull判定は以下のようにします。
if (jsonObject.get(name) == JSONObject.NULL){
    //JsonObjectはnull
}

しらずに「jsonObject.get(name) == null」って書いて小一時間ムダにした(;´Д`A

Android AsyncTaskでProgressDialogを表示 その2

Android AsyncTaskでProgressDialogを表示 その1
進捗を表示しながらバックグラウンドで処理を行うことってよくあるのですが、 まいどまいどAsyncTaskを作っていられないので自分なりの進捗を表示するAsyncTaskを作ってみました。
自分用のメモです。

ProgressAsyncTask
package com.example.helloandroid.tool;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;


/**
 * ProgressAsyncTask
 * プログレスダイアログを表示し、バックグラウンドで処理を行う抽象クラスです。
 * @param <Param>
 * @param <Result>
 *
 */
public abstract class ProgressAsyncTask<Param,Result> extends AsyncTask<Param,Bundle,Result>{
    private Context mContext;
    private ProgressDialog mProgressDialog;
    private Boolean isShowProgress;

    /**
     * コンストラクタ
     * @param context
     * @param procName
     */
    public ProgressAsyncTask(Context context){
        mContext = context;
    }

    /**
     * getIsShowProgress
     * プログレスダイアログが表示中かどうかを返します。
     * @return
     */
    public Boolean getIsShowProgress() {
        return isShowProgress;
    }

    /**
     * onPreExecute
     * 最初にUIスレッドで呼び出されます。
     * UIに関わる処理をします。
     */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        showDialog();
        isShowProgress = true;
    }

    /**
     * onProgressUpdate
     * doInBackground内でpublishProgressメソッドが呼ばれると、 UIスレッド上でこのメソッドが呼ばれます。 
     * このメソッドの引数の型はAsyncTaskの二つ目のパラメータです。 
     * @param values
     */
    @Override
    protected void onProgressUpdate(Bundle... values) {
        super.onProgressUpdate(values);
        Bundle bundle = values[0];  
        if (bundle.containsKey("Message")){
            mProgressDialog.setMessage(bundle.getString("Message"));
        }
        if (bundle.containsKey("Max")){
            mProgressDialog.setMax(bundle.getInt("Max"));
        }
        if (bundle.containsKey("Progress")){
            mProgressDialog.setProgress(bundle.getInt("Progress"));
        }
    }

    /**
     * onPostExecute
     * doInBackground が終わるとそのメソッドの戻り値をパラメータとして渡して onPostExecute が呼ばれます。
     * このパラメータの型は AsyncTask を extends するときの三つめのパラメータです。
     * バックグラウンド処理が終了し、メインスレッドに反映させる処理をここに書きます。
     * @param result
     */
    @Override
    protected void onPostExecute(Result result) {
        super.onPostExecute(result);
        dismissDialog();
        isShowProgress = false;
    }

    /**
     * onCancelled
     * cancelメソッドが呼ばれるとonCancelledメソッドが呼ばれます。
     */
    @Override
    protected void onCancelled() {
        super.onCancelled();
        dismissDialog();
        isShowProgress = false;
    }

    /**
     * showDialog
     * プログレスダイアログを表示します。
     */
    public void showDialog() { 
        mProgressDialog = new ProgressDialog(mContext);
        mProgressDialog.setMessage(""); //途中でメッセージを変更するために必要
        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.setIndeterminate(false);
        mProgressDialog.setCancelable(true);
        mProgressDialog.setButton("キャンセル", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                cancel(true); // 非同期処理をキャンセルする
            } 
        });
        mProgressDialog.show();
    }

    /**
     * dismissDialog
     * ダイアログを終了します。
     */
    public void dismissDialog() {
        mProgressDialog.dismiss();
        mProgressDialog = null;
    }

    /**
     * setProgressMessage
     * プログレスダイアログのメッセージを設定します。
     * @param message
     */
    public void setProgressMessage(String message){
        Bundle data = new Bundle();
        data.putString("Message", message);
        publishProgress(data);
    }
    /**
     * setMax
     * プログレスダイアログの最大値を設定します。
     * @param max
     */
    public void setProgressMax(int max){
        Bundle data = new Bundle();
        data.putInt("Max", max);
        publishProgress(data);
    }
    /**
     * setProgress
     * プログレスダイアログの進捗値を設定します。
     * @param max
     */
    public void setProgress(int progress){
        Bundle data = new Bundle();
        data.putInt("Progress", progress);
        publishProgress(data);
    }


}


Activity
ボタンをクリックするとバックグラウンドでgetMethod1()とgetMethod2()を実行します。
import android.app.Activity;

import android.view.Menu;
import android.view.View;

import android.widget.Toast;

import com.example.helloandroid.R;
import com.example.helloandroid.service.ReceiveData;
import com.example.helloandroid.tool.DatabaseHelper;
import com.example.helloandroid.tool.ProgressAsyncTask;



public class MainActivity extends Activity {
    //Activityが破棄されても、オブジェクトを保持できるようにstaticで宣言する。   
    private static ProgressAsyncTask<?,?> mTask;  

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //プログレスダイアログを再表示する。   
        if (mTask != null && mTask.getIsShowProgress()){   
            mTask.showDialog();   
        } 
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    } 

    @Override  
    protected void onPause() {   
        //プログレスダイアログを閉じる。   
        if (mTask != null && mTask.getIsShowProgress()) {   
            mTask.dismissDialog();    
        }   
        super.onPause();   
    }   


    /**
     * button1Click
     * @param view
     * button1クリック時の処理
     * 
     */
    public void button2Click(View view) {
        ProgressAsyncTask<Void,Boolean> task = new ProgressAsyncTask<Void,Boolean>(this){
            @Override
            protected Boolean doInBackground(Void... params) {
                ReceiveData receive = new ReceiveData(MainActivity.this);
                if (receive.getMethod1(this) == false ){
                    return false;
                }
                if (receive.getMethod2(this) == false) {
                    return false;
                }
                return true;
            }};
            mTask = task;
            task.execute();
    }

}

getMethod1()とgetMethod2()メソッドを定義しているクラスです。
package com.example.helloandroid.service;

import com.example.helloandroid.tool.ProgressAsyncTask;

import android.os.SystemClock;


public class ReceiveData {

    /**
     * getMethod1
     * @param task
     */
    public Boolean getMethod1(ProgressAsyncTask<?,?> task){
        task.setProgressMessage("Method1_Start");
        task.setProgressMax(10);
        for(int i=0; i<10; i++){
            task.setProgress(i+1);
            SystemClock.sleep(1000);
            if (task.isCancelled()) {      
                return false;                   
            } 
        }
        return true;
    }

    /**
     * getMethod2
     * @param task
     */
    public Boolean getMethod2(ProgressAsyncTask<?,?> task){
        task.setProgressMessage("Method2_Start");
        task.setProgressMax(20);
        for(int i=0; i<20; i++){  
            task.setProgress(i+1);
            SystemClock.sleep(1000);  
            if (task.isCancelled()) {      
                return false;                   
            } 

        }
        return true;
    }

}

Android AsyncTaskでProgressDialogを表示 その1

AsyncTaskは別スレッドでのバックグラウンド処理とバックグラウンド前後にUIスレッドの操作を行う抽象クラスです。
AsyncTaskを継承するとき実行時引数(Param)、進捗単位(Progress)、処理結果(Result)で扱う[型]を指定します。
実行時引数(Param)はdoInBackgroundメソッドの引数の型になり、
処理結果(Result)はdoInBackgroundメソッドの戻り値の型になります。
進捗単位(Progress)はonProgressUpdateメソッドの引数の型になります。
また、doInBackgroundメソッドの戻り値を引数にonPostExecuteメソッドが呼ばれるので、このメソッドの引数の型は処理結果(Result)の型と同じになります。
public class ProgressTask extends AsyncTask<String, Integer, Boolean>{
    protected Boolean doInBackground(String... params) {}
    protected void onPostExecute(Boolean result) {
    protected void onProgressUpdate(Integer... values) {}
}
型を何も指定しない場合は「Void」を指定します。
public class ProgressTask extends AsyncTask<Void, Void, Void>{
    protected Void doInBackground(Void... params) {}
    protected void onProgressUpdate(Void... values) {}
    protected void onPostExecute(Void result){}
}

AsyncTaskには以下のメソッドがあります。
  • onPreExecute()・・・最初にUIスレッドで呼び出されます。UIに関わる処理をします。
  • doInBackground()・・・ワーカースレッド上で実行されます。ここでバックグラウンドで行う時間のかかる処理をします。
  • onProgressUpdate()・・・doInBackground内でpublishProgressメソッドが呼ばれると、 UIスレッドでこのメソッドが呼び出されます。
  • onPostExecute()・・・doInBackgroundメソッドの処理が終了すると、 UIスレッドで呼び出されます。
  • onCancelled()・・・タスクがキャンセルされると、 UIスレッドで呼び出されます。

AsyncTaskでProgressDialogを表示する際のキモは、以下の2点です。
  1. プログレスダイアログを表示している時にディスプレイの縦横を変更した時の処理
  2. プログレスダイアログをキャンセルした時の処理
とりあえずコードです。
AsyncTaskを継承したProgressTaskクラス
package com.example.helloandroid.service;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.SystemClock;


public class ProgressTask extends AsyncTask<:Void,Integer,Boolean>{
    private Context mContext;
    private ProgressDialog mProgressDialog;
    private Boolean isShowProgress;

    /**
     * コンストラクタ
     * @param context
     */
    public ProgressTask(Context context){
        mContext = context;
    }

    /**
     * getIsShowProgress
     * プログレスダイアログが表示中かどうかを返します。
     * @return
     */
    public Boolean getIsShowProgress() {
        return isShowProgress;
    }

    /**
     * onPreExecute
     * 最初にUIスレッドで呼び出されます。
     * UIに関わる処理をします。
     */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        isShowProgress = true;
        showDialog();
    }

    /**
     * doInBackground
     * ワーカースレッド上で実行されます。
     * このメソッドに渡されるパラメータの型はAsyncTaskの一つ目のパラメータです。
     * このメソッドの戻り値は AsyncTaskの三つ目のパラメータです。
     * @return 
     */
    @Override
    protected Boolean doInBackground(Void... params) {
        for(int i=0; i<:10; i++){
            SystemClock.sleep(1000);
            // キャンセルが押された場合                
            if (isCancelled()) {   
                return false;                
            }
            publishProgress((i+1) * 10);
        }
        return true;
    }

    /**
     * onProgressUpdate
     * doInBackground内でpublishProgressメソッドが呼ばれると、 UIスレッド上でこのメソッドが呼ばれます。 
     * このメソッドの引数の型はAsyncTaskの二つ目のパラメータです。 
     * @param values
     */
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        mProgressDialog.setProgress(values[0]);  
    }

    /**
     * onPostExecute
     * doInBackground が終わるとそのメソッドの戻り値をパラメータとして渡して onPostExecute が呼ばれます。
     * このパラメータの型は AsyncTask を extends するときの三つめのパラメータです。
     * バックグラウンド処理が終了し、メインスレッドに反映させる処理をここに書きます。
     * @param result
     */
    @Override
    protected void onPostExecute(Boolean result) {
        super.onPostExecute(result);
        dismissDialog();
        isShowProgress = false;
    }

    /**
     * onCancelled
     * cancelメソッドが呼ばれるとonCancelledメソッドが呼ばれます。
     */
    @Override
    protected void onCancelled() {
        super.onCancelled();
        dismissDialog();
        isShowProgress = false;
    }

    /**
     * showDialog
     * プログレスダイアログを表示します
     */
    public void showDialog() { 
        mProgressDialog = new ProgressDialog(mContext);
        mProgressDialog.setMessage("データ読み込み中...");
        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.setIndeterminate(false);
        mProgressDialog.setMax(100);
        mProgressDialog.incrementProgressBy(0);
        mProgressDialog.setCancelable(true);
        mProgressDialog.setButton("キャンセル", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                cancel(true); // 非同期処理をキャンセルする
            } 
        });
        mProgressDialog.show();
    }

    /**
     * dismissDialog
     * ダイアログを終了する。
     */
    public void dismissDialog() {
        mProgressDialog.dismiss();
        mProgressDialog = null;
    }

}
呼び出しもとのMainActivityクラス
package com.example.helloandroid.activity;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;

import com.example.helloandroid.R;
import com.example.helloandroid.service.ProgressTask;

public class MainActivity extends Activity {
    //Activityが破棄されても、オブジェクトを保持できるようにstaticで宣言する。
    private static ProgressTask mTask; 

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);        
        //プログレスダイアログを再表示する。
        if (mTask != null && mTask.getIsShowProgress()){
            mTask.showDialog();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    } 

    @Override
    protected void onPause() {
        //プログレスダイアログを閉じる。
        if (mTask != null && mTask.getIsShowProgress()) {
            mTask.dismissDialog(); 
        }
        super.onPause();
    }

    public void button1Click(View view) {
        //時間のかかる処理を別スレッドで実行
        mTask = new ProgressTask(this);
        mTask.execute();
    }  
}
ダイアログ表示中にディスプレイの縦横を切り替えるとダイアログが消えてしまいます。
これはディスプレイの縦横を変更すると呼び出し元のアクティビティが破棄され、新しい向きのActivityが生成されます。 そのためダイアログも消えてしまうようです。
またAsyncTaskの処理が終了した時点でIllegalArgumentExceptionが発生してしまいます。
これはダイアログのdismissメソッドを呼び出す時に、ダイアログを表示したアクティビティが破棄されているのが原因のようです。
詳しくはこちらに書かれています。
Tech Racho「Android: ダイアログを表示して縦横が変わるとdismissでエラー」
kasa0の部屋「ProgressDialogとAsyncTaskの甘い罠」
対策は
ProgressTaskクラスでダイアログ表示中かどうかの状態を管理します。
MainActivityクラスでProgressTaskオブジェクトをstatic変数で保持します。
MainActivityクラスのonPauseメソッドでプログレスダイアログを閉じます。
MainActivityクラスのonCreateメソッドでプログレスダイアログを表示します。

キャンセル時の処理について
キャンセルボタンクリック時にProgressTask#cancel(true)を呼びます。
doInBackgroundメソッドではキャンセルされたかどうかを見て、処理を行います。
キャンセルされるとonPostExecute()メソッドは呼ばれない為、onCancelledメソッドで終了処理を行います。

Android プログレスダイアログの表示

進捗状況を表示する水平スタイルのプログレスダイアログを表示します。
public void button1Click(View view) {
    ProgressDialog dialog = new ProgressDialog(this);
    //タイトルを設定します。
    dialog.setTitle("Title");
    //メッセージを設定します。
    dialog.setMessage("Message");
    //水平バースタイルに設定します。
    dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    //falseの場合、プログレスバーの進捗状況を表示します。
    dialog.setIndeterminate(false);
    //最大値を設定します。
    dialog.setMax(100);
    //進捗の値を設定します
    dialog.incrementProgressBy(30);
    //セカンダリ値を設定します
    dialog.incrementSecondaryProgressBy(70);
    //キャンセルできるかどうかです。
    dialog.setCancelable(true);
    //ダイアログにキャンセルボタンを追加します。
    dialog.setButton("キャンセル", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            dialog.cancel();
        } 
    });
    //ダイアログを表示
    dialog.show();
}   


処理中かどうかを表示する水平スタイルのプログレスダイアログを表示します。
setIndeterminateメソッドにtrueを設定すると、処理中かどうかを表すプログレスバーになります。
setMax()メソッドやincrementProgressBy()メソッド、ementSecondaryProgressBy()メソッドの設定は無視されるようです。
public void button1Click(View view) {
    ProgressDialog dialog = new ProgressDialog(this);
    //タイトルを設定します。
    dialog.setTitle("Title");
    //メッセージを設定します。
    dialog.setMessage("Message");
    //水平バースタイルに設定します。
    dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    //falseの場合、プログレスバーの進捗状況を表示します。
    dialog.setIndeterminate(true);
    //最大値を設定します。
    dialog.setMax(100);
    //進捗の値を設定します
    dialog.incrementProgressBy(30);
    //セカンダリ値を設定します
    dialog.incrementSecondaryProgressBy(70);
    //キャンセルできるかどうかです。
    dialog.setCancelable(true);
    //ダイアログにキャンセルボタンを追加します。
    dialog.setButton("キャンセル", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            dialog.cancel();
        } 
    });
    //ダイアログを表示
        dialog.show();
}   


円スタイルのプログレスダイアログを表示します。
setProgressStyle()メソッドにProgressDialog.STYLE_SPINNERを設定すると円スタイルになります。
setIndeterminate()メソッドやsetMax()メソッド、incrementProgressBy()メソッド、ementSecondaryProgressBy()メソッドの設定は無視されるようです。
public void button1Click(View view) {
    ProgressDialog dialog = new ProgressDialog(this);
    //タイトルを設定します。
    dialog.setTitle("Title");
    //メッセージを設定します。
    dialog.setMessage("Message");
    //水平バースタイルに設定します。
    dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
    //falseの場合、プログレスバーの進捗状況を表示します。
    dialog.setIndeterminate(true);
    //最大値を設定します。
    dialog.setMax(100);
    //進捗の値を設定します
    dialog.incrementProgressBy(30);
    //セカンダリ値を設定します
    dialog.incrementSecondaryProgressBy(70);
    //キャンセルできるかどうかです。
    dialog.setCancelable(true);
    //ダイアログにキャンセルボタンを追加します。
    dialog.setButton("キャンセル", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            dialog.cancel();
        } 
    });
    //ダイアログを表示
    dialog.show();
}   

Android ASP.NET(MVC2)で作成したWebAPIに接続する その4(JSON)

以前まで作成したWebAPIはXML文字列を返すものでした。
Android ASP.NET(MVC2)で作成したWebAPIに接続する その1
Android ASP.NET(MVC2)で作成したWebAPIに接続する その2
Android ASP.NET(MVC2)で作成したWebAPIに接続する その3

Android側ではXML文字列を解析するのですが、スマートフォンでXMLの解析は負荷が大きいので
WebAPI側でJSONを返すようにしたいと思います。

ASP.NET(MVC2)で作成したWebAPIでJSONを返す


以前作成したWebAPIのTestControler.vbのIndexメソッドを変更します。

TestControler.vb
Namespace MvcApplication1
    Public Class TestController
        Inherits System.Web.Mvc.Controller

        <HttpPost()>
        <ValidateInput(False)> _
        Public Function Index(ByVal collection As FormCollection) As JsonResult
             'パラメータとして渡されたID,NAMEを取得
            Dim id As Integer = CInt(collection.Item("id"))
            Dim name As String = collection.Item("name")

            'JSON形式で返す。返せるものはシリアライズ可能なオブジェクト
            Dim lst As New List(Of KeyValuePair(Of Integer, String))
            lst.Add(New KeyValuePair(Of Integer, String)(id, name))

            Return Json(lst)
        End Function

    End Class
End Namespace

WebアプリケーションにデータをPostするhtmlも変更します。
TestMvcApplication1.html
<html>
    <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 </head>

 <body>
  <h2>MvcApplication1テスト</h2>
     <form action="http://localhost:3766/Test" method="post">
      <input type="text" name = "id" value="1" />
      <input type="text" name = "name" value="田中" />
         <input type=submit />
     </form>
 </body>
</html>

実行結果
[{"ID":1,"NAME":"田中"}]

ASP.NET(MVC2)で作成したWebAPIでDataTableのJSONを返す


.NETだったら、DataTableオブジェクトを返したいですよね~
そこでTestControler.vbのIndexメソッドを変更してみました。
    Dim tbl As New DataTable() 
    tbl.Columns.Add("ID", GetType(Integer)) 
    tbl.Columns.Add("NAME", GetType(String)) 
    tbl.Rows.Add(New Object() {id, name}) 
    Return Json(tbl)
TestMvcApplication1.htmlを実行すると、「型 'System.Reflection.Module' のオブジェクトのシリアル化を実行中に循環参照が見つかりました。 」とエラーになりました。
DataTableはJsonにシリアライズできないようです。

DataTableオブジェクトをJSONで返す方法は、きっと他にもあると思いますが今回は「JSON.NET」を使ってみたいと思います。
JSON.NETは.NETのインスタンスをJSONに変換してくれるライブラリです。
以下よりダウンロードします。
http://json.codeplex.com/
ダウンロードした「Json45r8.zip」を解凍します。
解凍してできたフォルダの「Bin」配下より、使用するFrameworkのバージョンフォルダからdllを、ソースコードの「bin」フォルダにコピーします。
※今回は「Json45r8\Bin\Net35\Newtonsoft.Json.dll」を使用します。

それではTestControler.vbのIndexメソッドをJSON.NETを使用した方法に変更してみます。

TestControler.vb
参照設定で先ほどコピーしたNewtonsoft.Json.dllを追加します。
Namespace MvcApplication1
    Public Class TestController
        Inherits System.Web.Mvc.Controller

        <HttpPost()> _
        <ValidateInput(False)> _
        Public Function Index(ByVal collection As FormCollection) As ActionResult
            'パラメータとして渡されたID,NAMEを取得
            Dim id As Integer = CInt(collection.Item("id"))
            Dim name As String = collection.Item("name")
            'DataTableを作成
            Dim tbl As New DataTable
            tbl.Columns.Add("ID", GetType(Integer))
            tbl.Columns.Add("NAME", GetType(String))
            tbl.Rows.Add(New Object() {id, name})
            'Json.NETを使用して返す。
            Dim jsonstr As String = Newtonsoft.Json.JsonConvert.SerializeObject(tbl, New Newtonsoft.Json.Converters.DataTableConverter())
            Return Content(jsonstr, "application/json")

        End Function

    End Class
End Namespace
TestMvcApplication1.htmlを実行した結果
[{"ID":1,"NAME":"田中"}]

AndroidでJSONを解析する。


Android側のコードです。
JSONを解析するにはJSONArrayやJSONObjectを使用します。
package com.example.helloandroid.activity;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;

import com.example.helloandroid.R;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    } 

    public void button1Click(View view) {
        //DefaultHttpClientを生成   
        HttpClient httpClient = new DefaultHttpClient();   
        //HttpPostを生成   
        HttpPost req = new HttpPost("http://IPアドレス:ポート番号/AndroidTest/Test");   
        //パラメータ作成   
        List<NameValuePair> params = new ArrayList<NameValuePair>(1);   
        params.add(new BasicNameValuePair("ID", "1"));   
        params.add(new BasicNameValuePair("NAME", "田中"));   
        //リクエスト   
        String strJson = "";   
        try {    
         req.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));   
         HttpResponse res = httpClient.execute(req);   
         int status = res.getStatusLine().getStatusCode();   
         if (status == HttpStatus.SC_OK) {   
          strJson = EntityUtils.toString(res.getEntity());   
         } else {   
           Toast.makeText(this, "接続失敗" + String.valueOf(status), Toast.LENGTH_LONG).show();
           return;
         }   
        } catch (Exception e) {   
           e.printStackTrace();   
        }   
        //Jsonを解析
        try {
            JSONArray element = new JSONArray(strJson);
            for (int i = 0; i < element.length(); i++) {
                JSONObject jsonObject = element.getJSONObject(i); 
                Toast.makeText(this, jsonObject.getString("NAME"), Toast.LENGTH_LONG).show();
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

    }
    

}

Android stopped unexpectedly (予期せず停止)

アプリを実行して「stopped unexpectedly (予期せず停止)」が表示され起動できない時は マニフェストファイルのパッケージ名やアクティビティ名が間違っている可能性が高いです。