« 春はあけぼの | トップページ | 暗号の一覧 »

2011年3月 1日 (火)

例外処理

さて、例外処理です

とりあえず、例外って何?というのは端折ります。で、例外がおきたらどうするか、ですが、
(1)その場でcatchする。
(2)classにthrows指定をつけて呼び出し元に届ける

があるわけです。(1)は、できるならそうしたいのですが、AndroidにはViewに触ることができるのは(最初に起動された)UIスレッドだけ。という制限があり、バックグラウンドからはTextViewにメッセージを書くことさえできないので、その場で全部対処とは行かない場合があります。
(2)はというと、UIへ持ち帰るまでにどこで何が起きたのか分からなくなってしまい、細かな処理はできません。それに、catch()がいっぱい並んでプログラムの見通しが悪くなります。

じゃぁどうするの?という問いに対する答えがHandlerです。インタフェースなのですが、Handlerというのを呼び出すと、指定のRunnableをUIスレッドのキューにつなげてくれます。つまり、これを使えば別スレッドからViewを操作する(実際は依頼する)ことができます。

では、昨日のプログラムを修正して実際に動かしてみます。まず、AsyncTaskの中のdoInBackgroundにtry-catchをつけます。そして、処理の最後に”わざと”例外を発生させます。まさにマッチポンプです。

例外が発生したらHandlerの出番です。何をするかというと、事前に用意しておいたViewGroupの中に、これまた事前に用意したButtonをaddViewします。これで、例外がおきると画面にボタンが1個出現するようになります。

そして、このボタンはどうなっているかというと、クリックされると自分を画面から消して、リストを読む処理を再度実行させます。(この名前はrefreshとなっていますが、実際はListViewの中身は消していないのでListはどんどん伸びていきます。その辺はご愛嬌で)

こうして、最後までリストする。リトライする。を、ボタンを出したり消したりしながら繰り返します。

とりあえず、機能の試験実装ということで、こうすれば動く、レベルですが、ほとんどのアプリがなんらかのネットワーク接続を使うことを考えれば、部品化すれば再利用頻度の高い部品になりそうです。

ソースはちょっと掃除しました。それと、refresh()ですが、これはボタンのonClickアトリビュートから呼んでいるのでリスナの設定はありません。こういう、ちょっとした検証には便利ですね。それに、今回はretry用のボタンはコードで生成していますが、実際のアプリではこういったものもxmlからインフレートすることになるので、その際にonClick使えばプログラムがすっきりするかもしれません。

public class asyncTaskSample extends ListActivity{
    static final String TAG = "AsyncTaskSample";
    static final String SDCard = "/sdcard";

    private Context mContext ;
      
    private Button mRetry ;
    private ViewGroup mParent ;
    private Handler mHandler ;

    private ArrayAdapter mAdapter ;
   
    public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
        setContentView(R.layout.asyncsample);
   
       mContext = getApplicationContext();
       mInflater =LayoutInflater.from(mContext);
      
       mHandler = new Handler();
      
       mAdapter = new ArrayAdapter<String>(mContext, android.R.layout.simple_list_item_1);

        setListAdapter(mAdapter);
      
       mParent = (ViewGroup) findViewById(R.id.linearLayout1);
       mRetry = new Button(mContext);
       mRetry.setOnClickListener(mListner);
       mRetry.setText("Retry");
    }

    private class DirScan extends AsyncTask<String, String, Void>{
            @Override
            protected Void doInBackground(String... dir) {
                  try {
                        File sdir = new File(dir[0]);
                        File[] filelist = sdir.listFiles();
                        for (File file : filelist){
                              publishProgress(file.getName());
                              SystemClock.sleep(500);
                        }
                  throw new Exception();
                  } catch (Exception e) {
                        mHandler.post(r);
                  }
                  return null;
            }
            protected void onProgressUpdate(String... foundDir){
                  mAdapter.add(foundDir[0]);
                  mAdapter.notifyDataSetChanged();
            }
            protected void onPostExecute(Void nil){
                  Toast.makeText(mContext, "onPostExecute", Toast.LENGTH_SHORT);
            }
    }
    public void refresh(View v){
      new DirScan().execute(SDCard);
    }

    private OnClickListener mListner = new OnClickListener(){
            public void onClick(View v) {
            mParent.removeView(mRetry);
                  refresh(v);
            }
    };

    private Runnable r = new Runnable() {
      public void run(){
            mParent.addView(mRetry);
      }
    };
}

« 春はあけぼの | トップページ | 暗号の一覧 »

コメント

死にたまえ!

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/60027/51006678

この記事へのトラックバック一覧です: 例外処理:

« 春はあけぼの | トップページ | 暗号の一覧 »