Android Asynctask から@UiThread @WorkerThreadにしてみる
柴犬は神社の狛犬が大好きです。今日の朝の散歩も狛犬の前で一緒になって寛いだ顔しています。
キッチンカーで食事を購入して食べるところとなるテントもあります。
今日は、植木まつりの最終日です。賑わうんだろーなと思って眺めている柴犬です。
概要
PDF などのイメージを印刷するプリンター(ブラーザーのみ)の検索をする機能も付けてみます。
また、操作・処理の過程を記録してサーバーに送信するクラスの作成も合わせてしてみました。
2023年11月30日投稿「Android データのアップロード」で非同期で使用していたAsynctask が非推奨ですので、Asynctask を使っていたところを @UiThread @WorkerThread を使ってみました。
翔泳社 齊藤 新三 著
2024年2月26日現在
WEBのみでは断片的で覚えにくいので最初に購入した Kotlin の本です。
MainActivity-1
public class MainActivity extends AppCompatActivity { infoPost infopost; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); printerSearch(); infopost = new infoPost("https://www.sibainu.org/", "uderstr", "idstr", "accessstr", "keystr"); infopost.add("onCreate"); ClickListener cl = new ClickListener(); TextView tv; tv = findViewById(R.id.textView01); tv.setOnClickListener(cl); tv = findViewById(R.id.textView02); tv.setOnClickListener(cl); tv = findViewById(R.id.textView03); tv.setOnClickListener(cl); } @Override protected void onDestroy(){ super.onDestroy(); infopost.add("onDestroy").postinfo(); } private class ClickListener implements View.OnClickListener { @Override public void onClick(View view) { TextView tv; // クリックした TextView の値を取得 int objid = view.getId(); tv = findViewById(objid); String str = tv.getText().toString(); //表示先の初期化 tv = findViewById(R.id.tvText1); tv.setText(""); //表示先の初期化 tv = findViewById(R.id.textView04); tv.setText(""); if (objid == R.id.textView01 || objid == R.id.textView02 || objid == R.id.textView03) { // 正規表現でマッチを確認 Pattern p = Pattern.compile("[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}"); Matcher m = p.matcher(str); if (m.find()) { // IPアドレスの表示 tv.setText(str); // プリントの実行 imagePrint(str, "mypdffile.pdf"); } } } }
MainActivity-2
@UiThread private void printerSearch() { //クラス printerSearchTask の実体を作成 printerSearchTask backgroundReceiver = new printerSearchTask(this); //executorService を作成 ExecutorService executorService = Executors.newSingleThreadExecutor(); //executorService.submit の引数に実体を渡します //バックグラウンドで非同期処理が行われ戻り値が future に格納されます Future<PrinterSearchResult> future; future = executorService.submit(backgroundReceiver); PrinterSearchResult channels; try { //future から channels の配列を取り出します channels = future.get(); if (channels != null) { TextView[] tvlist = {findViewById(R.id.textView01), findViewById(R.id.textView02), findViewById(R.id.textView03)}; String modelname = ""; String ipaddress = ""; int i = 0; String[] printerList = new String[channels.getChannels().size()]; for (Channel channel : channels.getChannels()) { modelname = channel.getExtraInfo().get(Channel.ExtraInfoKey.ModelName); ipaddress = channel.getChannelInfo(); printerList[i] = "プリンタ名: " + modelname + "\n" + "IPアドレス: " + ipaddress; if (i < tvlist.length ) { tvlist[i].setText(printerList[i]); } i += 1; } TextView tv; tv = findViewById(R.id.tvText1); if (printerList.length > 0) { tv.setText(String.join("\n", printerList)); } else { tv.setText("使用できるプリンタはありません"); } } } catch(ExecutionException | InterruptedException ex) { Log.w("printerSearch catch", "非同期処理結果の取得で例外発生: ", ex); } finally { Log.d("printerSearch finally", "非同期処理完了"); executorService.shutdown(); } } public class printerSearchTask implements Callable<PrinterSearchResult> { PrinterSearchResult _channels; Context _context; //constructor です public printerSearchTask(Context context) { _context = context; Log.d("constructor", "printerSearchTask constructor"); } // 非同期 @WorkerThread @Override public PrinterSearchResult call() { try { NetworkSearchOption option = new NetworkSearchOption(5, false); this._channels = PrinterSearcher.startNetworkSearch(_context, option, new Consumer<Channel>() { @Override public void accept(Channel channel) { } }); } catch (Exception e) { Log.d("Error call catch", "PrinterSearchResult"); e.printStackTrace(); } return this._channels; } }
MainActivity-3
private class infoPost { List<String> _info; String _urlstr; Map<String, String> postbody; public infoPost(String urlstr, String userstr, String idstr, String accessstr, String keystr){ postbody =new HashMap<>(); postbody.put("userstr",userstr); postbody.put("idstr",idstr); postbody.put("accessstr",accessstr); postbody.put("keystr",keystr); this._urlstr = urlstr; this._info = new ArrayList<>(); } public infoPost add(String addstr) { this._info.add(addstr); return this; } @UiThread public void postinfo() { // List を配列に変換 String[] ss = new String[_info.size()]; _info.toArray(ss); // postbody に info データを追加 String data; data = String.join("\n", ss); postbody.put("data",data); // 送信データを作成します。 StringBuilder postData = new StringBuilder(); // 連想配列をペアで処理 try { for (Map.Entry<String, String> s : postbody.entrySet()) { // 先頭が & にならないようにします。 if (postData.length() != 0) postData.append('&'); // エンコードして結合します。 postData.append(URLEncoder.encode(s.getKey(), "UTF-8")) .append('=') .append(URLEncoder.encode(s.getValue(), "UTF-8")); } requestPost(_urlstr, postData.toString()); } catch(Exception e) { e.printStackTrace(); } } @UiThread private void requestPost(String urlstr, String postbody) { //クラス requestPostTask の実体を作成 requestPostTask backgroundReceiver = new requestPostTask(urlstr, postbody); //executorService を作成 ExecutorService executorService = Executors.newSingleThreadExecutor(); //executorService.submit の引数に JsonPostHttp の実体を渡します //バックグラウンドで非同期処理が行われ戻り値が future に格納されます Future<String> future; future = executorService.submit(backgroundReceiver); String result = ""; try { //future から戻り値を取り出します result = future.get(); // テキストビューに表示 TextView tv; tv = findViewById(R.id.tvText1); tv.setText(result); } catch(ExecutionException | InterruptedException ex) { Log.w("printerSearch catch", "非同期処理結果の取得で例外発生: ", ex); } finally { Log.d("printerSearch finally", "非同期処理完了"); executorService.shutdown(); } } public class requestPostTask implements Callable<String> { String _urlstr; String _postbody; //constructor です public requestPostTask(String urlstr, String postbody) { _urlstr = urlstr; _postbody = postbody; Log.d("constructor", "requestPostTask constructor"); } // 非同期 @WorkerThread @Override public String call() { String response = ""; try { // URLを設定します。 // 送信するデータをバイト配列に変換 byte[] postDataBytes = postbody.toString().getBytes("UTF-8"); // URLを設定します。 URL url = new URL(_urlstr); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setDoOutput(true); conn.setConnectTimeout(30000); // 送信 conn.getOutputStream().write(postDataBytes); // レスポンスを受け取る int responseCode=conn.getResponseCode(); // 送信の結果 if (responseCode == HttpsURLConnection.HTTP_OK) { String line; BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); while ((line=br.readLine()) != null) { response += line; } } else { response = "送信に不具合がありました"; } } catch (Exception e) { response = "送信エラー: " + e.getMessage(); } // future に渡す値 return response; } } }
MainActivity-4
@UiThread private void imagePrint(String ipaddress, String filename) { //クラス requestPostTask の実体を作成 imagePrintTask backgroundReceiver = new imagePrintTask(ipaddress, filename); //executorService を作成 ExecutorService executorService = Executors.newSingleThreadExecutor(); //executorService.submit の引数に JsonPostHttp の実体を渡します //バックグラウンドで非同期処理が行われ戻り値が future に格納されます Future<String> future; future = executorService.submit(backgroundReceiver); String result = ""; try { //future から戻り値を取り出します result = future.get(); // テキストビューに表示 TextView tv; tv = findViewById(R.id.tvText1); tv.setText(result); Log.d("imagePrint result", result); } catch(ExecutionException | InterruptedException ex) { Log.w("Error imagePrint catch", "非同期処理結果の取得で例外発生: ", ex); } finally { executorService.shutdown(); } } private class imagePrintTask implements Callable<String> { String _ipaddress; String _filename; public imagePrintTask(String ipaddress, String filename) { _ipaddress = ipaddress; _filename = filename; Log.d("constructor", "imagePrintTask constructor"); } // 非同期 @WorkerThread @Override public String call() { // https://support.brother.com/g/s/es/htmldoc/mobilesdk/guide/getting-started/getting-started-android.html // https://support.brother.com/g/s/es/htmldoc/mobilesdk/guide/print-image.html // https://support.brother.com/g/s/es/htmldoc/mobilesdk/guide/discover-printer.html Channel channel = Channel.newWifiChannel(_ipaddress); PrinterDriverGenerateResult result = PrinterDriverGenerator.openChannel(channel); if (result.getError().getCode() != OpenChannelError.ErrorCode.NoError) { Log.e("Error OpenChannel", "Error - Open Channel: " + result.getError().getCode()); // future に渡す値 return "Error OpenChannel - IPアドレスのエラー"; } File dir = getExternalFilesDir(null); File file = new File(dir, _filename); if (file == null){ // future に渡す値 return "Error File - イメージが取得できません"; } PrinterDriver printerDriver = result.getDriver(); String response = ""; try { QLPrintSettings printSettings = new QLPrintSettings(PrinterModel.QL_820NWB); printSettings.setLabelSize(QLPrintSettings.LabelSize.RollW103); printSettings.setAutoCut(true); printSettings.setWorkPath(dir.toString()); PrintError printError = printerDriver.printImage(file.toString(), printSettings); if (printError.getCode() != PrintError.ErrorCode.NoError) { Log.d("Error Print", "Error - Print Image: " + printError.getCode()); response = "Error - Print Image"; } else { Log.d("Success Print", "Success - Print Image"); response = "Success - Print Image"; } } catch(Exception e) { response = "Error Another catch - " + e.toString(); } finally { printerDriver.closeChannel(); } // future に渡す値 return response; } } }
MainActivity-5
package org.sibainu.relax.room.last1; import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; import androidx.appcompat.app.AppCompatActivity; import android.content.Context; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.TextView; import com.brother.sdk.lmprinter.Channel; import com.brother.sdk.lmprinter.NetworkSearchOption; import com.brother.sdk.lmprinter.OpenChannelError; import com.brother.sdk.lmprinter.PrintError; import com.brother.sdk.lmprinter.PrinterDriver; import com.brother.sdk.lmprinter.PrinterDriverGenerateResult; import com.brother.sdk.lmprinter.PrinterDriverGenerator; import com.brother.sdk.lmprinter.PrinterModel; import com.brother.sdk.lmprinter.PrinterSearchResult; import com.brother.sdk.lmprinter.PrinterSearcher; import com.brother.sdk.lmprinter.setting.QLPrintSettings; import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.HttpsURLConnection;
layout.activity_main
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/tvText1" android:layout_width="0dp" android:layout_height="241dp" android:layout_marginTop="76dp" android:text="" app:layout_constraintEnd_toStartOf="@+id/guideline3" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <WebView android:id="@+id/webView1" android:layout_width="0dp" android:layout_height="40dp" android:layout_marginTop="40dp" app:layout_constraintEnd_toStartOf="@+id/guideline3" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tvText1" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_begin="321dp" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_begin="48dp" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.83" /> <TextView android:id="@+id/textView01" android:layout_width="0dp" android:layout_height="60dp" android:layout_marginTop="8dp" android:text="01" android:clickable="true" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@+id/guideline3" app:layout_constraintTop_toTopOf="@+id/guideline2" /> <TextView android:id="@+id/textView02" android:layout_width="0dp" android:layout_height="60dp" android:layout_marginTop="8dp" android:text="02" android:clickable="true" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintStart_toStartOf="@+id/guideline3" app:layout_constraintTop_toBottomOf="@+id/textView01" /> <TextView android:id="@+id/textView03" android:layout_width="0dp" android:layout_height="60dp" android:layout_marginTop="10dp" android:text="03" android:clickable="true" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@+id/guideline3" app:layout_constraintTop_toBottomOf="@+id/textView02" /> <TextView android:id="@+id/textView04" android:layout_width="0dp" android:layout_height="60dp" android:text="TextView" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@+id/guideline3" app:layout_constraintTop_toTopOf="@+id/guideline" /> /> </androidx.constraintlayout.widget.ConstraintLayout>
Design エディター
こんな感じのデザインです。
ここまでとします。