とりあえず移転してみました

ニュースねたや、IT系の記事を書いていくつもり・・・ですが、どうなるかわかりません。まあ、とりあえず やってみます。

android 非同期処理まとめ

非同期処理は必要か?

androidで時間のかかる処理を行う場合には、マルチスレッドを利用してバックグランドで処理をする必要があります。新しいAPIでは、メインのスレッドでURLアクセスやDBアクセスを記述するだけでエラーになります。厳しい!!!

なので、必ず非同期処理が必要になってきます。
まずは、URLアクセス

okhttp3

便利なライブラリがあるので使います。okhttp3です。
build.gradle(app)にライブラリを使う宣言をします。

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.squareup.okhttp3:okhttp:3.6.0' //<==追加

使用するclassでは、いつものようにimport文が必要。

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

これからが、本番です。

protected void urlAccess(String str){
        OkHttpClient client = new OkHttpClient();
        //RequestBody requestbodey = new FormBody.Builder()
        //        .build();

        Request request = new Request.Builder()
                .url(str)
                //.post(requestbodey)
                .get()
                .build();

        client.newCall(request).enqueue(new Callback() {
            final Handler mainHandler = new Handler(Looper.getMainLooper());
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String result = response.body().string();
                String callString = call.toString();
		//ここにViewの操作を記述する
                }
	}
}

メソッドを作成して、URLアクセスをする場合は、メソッドを呼び出すようにしています。URLは、引数として渡します。
POSTの場合の処理は、変わるのでコメントアウトしている部分がPOSTの場合の処理です。
サーバーからレスポンスがあった場合には、onResponseメソッドが実行されます。しかし、Handlerに登録するだけで実行されるのはHandlerの処理が空いた時になります。

この記述だけで非同期でのURLアクセスが可能になります。
ただ、このままでは足りません。非同期処理をメインスレッドで行わないのは、時間のかかる処理を別で行う間に画面上では別の処理を行いプログラムが止まったのか?重い処理をしているのか?を知らせる必要があります。

そのための非同期処理なので、追加でプログレスダイヤログを表示させます。

//フィールドに追加
private ProgressDialog progressDialog;

//メソッドとして追加
private void showProgressDialog(){
        progressDialog = new ProgressDialog(this);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        progressDialog.setMessage("WEBアクセス中です");
        progressDialog.setCancelable(true);
        progressDialog.show();
    }

showProgressDialog()を呼び出せば「WEBアクセス中です」と表示されたプログレスダイヤログが表示されます。
これで、プログラムがなにをやっているかわかります。

URLアクセスがあったら、次のコードで表示を消します。

progressDialog.dismiss();

Viewの操作になりますので、onResponseメソッド内に追加します。

@Override
            public void onResponse(Call call, Response response) throws IOException {
                String result = response.body().string();
                String callString = call.toString();
		//ここにViewの操作を記述する
        progressDialog.dismiss();
                }

okhttp3ライブラリのおかげで、非同期処理も比較的楽に実装できます。

次に、AsyncTaskLoaderを使う方法です。ネット上でも色々とコードが出ていますので、参考に。

AsyncTaskLoader

まずは、AsyncTaskLoaderを継承した独自クラスを作成します。だいたい、こんなパターンで始まります。面倒なのですが。

public class AsyncDbLoader extends AsyncTaskLoader<List> implements LoaderManager.LoaderCallbacks{
    Bundle bundle;

    public AsyncDbLoader(Context context,Bundle args) {
        super(context);
        bundle = args;
    }

    @Override
    public List loadInBackground() {
        List listData;
        SQLiteDatabase myDatabase;
        Sql_db helper = new Sql_db(getContext());
        myDatabase = helper.getReadableDatabase();

        Cursor c = null;
        try {
            c = myDatabase.query("mainTable", null, null, null, null, null, null);

            listData = new ArrayList();

            //adapterにDBから文字列を追加
            boolean isEof = c.moveToFirst();
            while (isEof) {
                ArrayList<String> rowstring = new ArrayList<String>();
                rowstring.add(c.getString(1));
                rowstring.add(c.getString(2));

                listData.add(rowstring);
                isEof = c.moveToNext();
            }
        }
            finally {
            if (c != null) {
                c.close();
            }
            myDatabase.close();
            helper.close();
        }
        return listData;
    }

    @Override
    public void onStartLoading(){
        forceLoad();
    }

    @Override
    public Loader onCreateLoader(int id, Bundle args) {
        return null;
    }

    @Override
    public void onLoadFinished(Loader loader, Object data) {

    }

    @Override
    public void onLoaderReset(Loader loader) {

    }
}

実際にSQLiteからデータを取得しているコードですが、ここで重要なのはloadInBackgroundメソッドで結局、ここに実行する中身を記述するだけです。実行した結果を戻り値として返しています。

非同期でなければ、DBから取得した値をTextViewなどに書き込みわけですが、一旦List形式などにして返す必要があります。それを、メインスレッド側が受け取って、Viewに反映するという事になります。

呼び出す側のコードは、こんな感じで使います。showProgressDialogメソッドも入っていますので、プログレスダイヤログも表示されます。

getLoaderManager().initLoader(0,args, new AsyncDbLoader(this,args) {
            @Override
            public Loader<List> onCreateLoader(int id, Bundle args) {
                showProgressDialog();
                return new AsyncDbLoader(getContext(),args);
            }

            @Override
            public void onLoadFinished(Loader loader, Object data) {
                progressDialog.dismiss();
                List listData = (List)data;
                CustomAdapter adapter = new CustomAdapter(getContext(),2,listData,"list");
                ListView listView = (ListView)findViewById(R.id.listView);
                listView.setAdapter(adapter);
            }
        });

onCreateLoaderメソッドで、AsyncDbLoaderクラスのloadInBackgroundが実行されます。処理が終わるとonLoadFinishedが呼び出されます。ここでは、引数として渡されたdataをListとして格納して、ListViewに表示するまでの処理を行っています。
Viewへの変更ができるのはメイン側なので、ここでしか変更できません。

これで、非同期の処理としては2つの方法が可能で、URLアクセスの場合はokhttp3で簡単に記述でき、別の処理の場合は独自クラスを作成して、そのクラス内で処理を記述。
呼び出す側は、結果を受け取ってViewに反映という処理が必要です。

まあ、面倒といえば面倒なのですが、一度中身を理解してしまえば難しく感じません。