Sibainu Relax Room

愛犬の柴犬とともに過ごす部屋

Android Asynctask から@UiThread @WorkerThreadにしてみる

柴犬は神社の狛犬が大好きです。今日の朝の散歩も狛犬の前で一緒になって寛いだ顔しています。

キッチンカーで食事を購入して食べるところとなるテントもあります。

今日は、植木まつりの最終日です。賑わうんだろーなと思って眺めている柴犬です。

概要

PDF などのイメージを印刷するプリンター(ブラーザーのみ)の検索をする機能も付けてみます。

また、操作・処理の過程を記録してサーバーに送信するクラスの作成も合わせてしてみました。

2023年11月30日投稿「Android データのアップロード」で非同期で使用していたAsynctask が非推奨ですので、Asynctask を使っていたところを @UiThread @WorkerThread を使ってみました。

WEBのみでは断片的で覚えにくいので最初に購入した Kotlin の本です。

MainActivity-1

copy

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

copy

    @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

copy

    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

copy

    @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

copy

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

copy

<?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 エディター

こんな感じのデザインです。

ここまでとします。