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 エディター
こんな感じのデザインです。

ここまでとします。