- 投稿日:2020-06-27T21:25:55+09:00
Javaの繰り返し処理
はじめに
正直クラスの書き方全然理解できない(本内容とは無関係)
繰り返し処理
繰り返し処理とは、一定の処理を自動で繰り返し行う処理のことwhile文とfor文
kane.javawhile(条件){ 繰り返す処理; } for(条件){ 繰り返す処理; }kane.javaint = 1; //初期化 while(i <= 5){ //条件 System.out.println(i); //繰り返す処理 i++; //1を追加していく 変数の更新 } for(int i = 1; i <= 5; i++){ //初期化、条件、更新をまとめて書ける System.out.println(i); //繰り返す処理 }変数に1を足し忘れると変数は1のまま変わらず、条件が永遠にtrueになるので、繰り返し処理が無限に行われる(無限ループ)
break
繰り返しを終わらせるためには、条件をfalseにする以外に、breakを使って強制的に終了させる方法がある。if文などの条件分岐と組み合わせることで、任意の箇所で繰り返し処理を終わらせることができる。
kane.javafor(int i = 1; i <= 10; i++){ if (i > 5){ //6になると強制終了 break; } System.out.println(i); }continue
continueはその周の処理だけをスキップして、次の周を実行できる。continueもif文などと組み合わせて利用するのが一般的。
kane.javafor(int i = 1; i <= 10; i++){ if (i % 3 == 0){ //3の倍数の時飛ばして次の処理を行う continue; } System.out.println(i); }コンソール1 2 4 5 7 8 10
3の倍数は飛ばされてる
終わりに
コーヒーの効果に今頃気づく28才。
- 投稿日:2020-06-27T20:42:53+09:00
Java 11 から正式導入された HTTP Client API で Yahoo!ショッピング商品検索(v3) の API をコールするサンプルコード
概要
- Java 11 から正式導入された HTTP Client API (java.net.http パッケージの HttpClient, HttpRequest, HttpResponse クラスなど) を使って、Yahoo!ショッピング商品検索(v3) の API をコールするサンプルプログラムを作る
今回の環境
- Java 11 (AdoptOpenJDK 11.0.7+10)
- Jackson Databind 2.11.0
- Gradle 6.5
- macOS Catalina
ソースコード
ソースコード一覧
├── build.gradle └── src └── main └── java └── com └── example ├── ErrorResponse.java ├── ItemSearch.java └── ResultSet.javabuild.gradle
plugins { id 'application' id 'java' } group 'org.example' version '0.0.1' mainClassName = "com.example.ItemSearch" sourceCompatibility = '11' repositories { mavenCentral() } dependencies { // JSON とクラスのマッピングに必要 // https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.0' }src/main/java/com/example/ItemSearch.java
package com.example; import com.fasterxml.jackson.databind.ObjectMapper; import java.net.URI; import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.time.Duration; /** * ショッピング:商品検索(v3) - Yahoo!デベロッパーネットワーク * https://developer.yahoo.co.jp/webapi/shopping/shopping/v3/itemsearch.html */ public class ItemSearch { public static void main(String[] args) { try { String appid = args[0]; String query = args[1]; System.out.println("appid: " + appid); // アプリケーションID System.out.println("query: " + query); // 検索キーワード // 商品を検索 ResultSet rs = new ItemSearch().search(appid, query); if (rs != null) { for (ResultSet.Hit hit : rs.hits) { System.out.println("**************************************************"); System.out.println("商品名: " + hit.name); System.out.println("商品説明: " + hit.description); System.out.println("キャッチコピー: " + hit.headLine); System.out.println("76×76サイズの画像URL: " + hit.image.small); System.out.println("146×146サイズの画像URL: " + hit.image.medium); System.out.println("商品URL: " + hit.url); System.out.println("価格: " + hit.price); } } } catch (Exception e) { System.out.println("エラー発生: " + e); e.printStackTrace(); } } /** * 商品を検索する。 * @param appid アプリケーションID * @param query 検索キーワード * @return 検索結果 * @throws Exception エラー発生時 */ public ResultSet search(String appid, String query) throws Exception { try { // API コール用の URL を組み立てる String baseurl = "https://shopping.yahooapis.jp/ShoppingWebService/V3/itemSearch"; String url = baseurl + "?query=" + URLEncoder.encode(query, StandardCharsets.UTF_8) + // 検索キーワード "&results=2"; // 検索結果2件まで System.out.println("URL: " + url); // HTTP リクエスト情報を構築 HttpRequest req = HttpRequest.newBuilder(new URI(url)) .GET() .setHeader("User-Agent", "Yahoo AppID: " + appid) // アプリケーションID .timeout(Duration.ofSeconds(10)) .build(); // API をコールして結果を取得 HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_1_1) .followRedirects(HttpClient.Redirect.NORMAL) .connectTimeout(Duration.ofSeconds(10)) .build(); HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString()); String body = res.body(); // レスポンスのステータスコードを出力 int statusCode = res.statusCode(); System.out.println("statusCode: " + statusCode); // ステータスコードで成功・失敗を判断 switch (res.statusCode()) { case 200: // HTTP レスポンスの JSON を ResultSet クラスにマッピング return new ObjectMapper().readValue(body, ResultSet.class); case 403: // エラー情報を出力 ErrorResponse errorResponse = new ObjectMapper().readValue(body, ErrorResponse.class); System.out.println("エラーメッセージ: " + errorResponse.error.message); return null; default: return null; } } catch (Exception e) { throw e; } } }src/main/java/com/example/ResultSet.java
検索結果のレスポンスを表すクラス。
package com.example; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import java.util.List; /** * 検索結果。 */ @JsonIgnoreProperties(ignoreUnknown = true) // 不明な JSON プロパティを無視する public class ResultSet { public List<Hit> hits; @JsonIgnoreProperties(ignoreUnknown = true) // 不明な JSON プロパティを無視する public static class Hit { // hits/name string 商品名 public String name; // hits/description string 商品説明 public String description; // hits/headLine string キャッチコピー public String headLine; // hits/image public Image image; // hits/url string 商品URL public String url; // hits/price integer 価格 public int price; } public static class Image { // hits/image/small string 76×76サイズの画像URL public String small; // hits/image/medium string 146×146サイズの画像URL public String medium; } }src/main/java/com/example/ErrorResponse.java
エラーレスポンス情報を表すクラス。
import com.fasterxml.jackson.annotation.JsonProperty; /** * エラー情報。 */ public class ErrorResponse { @JsonProperty("Error") public Error error; public static class Error { @JsonProperty("Message") public String message; } }実行例
使用可能なアプリケーションIDと検索キーワード「猫」を指定する
検索結果のレスポンスが ResultSet クラスにマッピングされて情報が出力されている。
$ gradle run -q --args="your_application_id 猫" appid: your_application_id query: 猫 URL: https://shopping.yahooapis.jp/ShoppingWebService/V3/itemSearch?query=%E7%8C%AB&results=2 statusCode: 200 ************************************************** 商品名: 猫 ハーネス 猫用リード 猫用品 ペット用品 商品説明: 脱走対策として<br><br>猫は通院やお引越しなどで慣れない移動をさせられると、パニックを起こして逃げ出そうとしたり、暴れたりすることが多いものです。そんな時、ペットケージに入れておくだけでなく、ハーネスを装着した上でペットケージに入れるという二重の備えをしておくと、とても安心です。また万が一、大災害の被害を受けて避難しなければならなくなった時にも脱走防止に役立つので、防災グッズの中に常備しておく飼い主も増えています。<br><br>お散歩用として<br><br>脱走防止目的だけでなく、猫のお散歩用として使用するのもおすすめです。ハーネスは猫の身体の動きを邪魔しにくいので、のびのびと運動できて良いストレス発散になることでしょう。ただし、猫の性格によっては外に連れ出すことがかえってストレスとなる場合もありますので、お散歩はくれぐれも猫の様子をみつつ、交通状況などにも気を配りながら行ってください。<br><br>カラーバリエーションは、赤・青・黒・ピンクの4色です。<br>ナイロン製<br><br>首回り16〜26センチ<br>胴回り26〜36センチ<br>リードの長さ110cm<br><br>検索キーワード:猫リード・猫ハーネス・猫用リード・ポイント消化・送料無料 キャッチコピー: 猫ハーネス猫リード猫用リード猫首輪 76×76サイズの画像URL: https://item-shopping.c.yimg.jp/i/c/sam-store_0030 146×146サイズの画像URL: https://item-shopping.c.yimg.jp/i/g/sam-store_0030 商品URL: https://store.shopping.yahoo.co.jp/sam-store/0030.html 価格: 599 ************************************************** 商品名: 猫おもちゃ 魚ロボット 猫電動おもちゃ 猫自動おもちゃ 猫おもちゃ自動 猫おもちゃ電動 猫おもちゃ魚 商品説明: 【ペット用おもちゃ】 電動魚ロボット 魚ロボット おもちゃ猫 電動おもちゃ猫 <br>【自動水泳機能】 水センサーで自動的にオン/オフします。自動ロボットフィンを使用して設計されています <br> 水中に置かれるとすぐに動き始める、取り出すと自動的にオフになり、電力を節約します。 <br>【LEDライト付】LEDライトが内蔵されているので暗闇で光ります。光って動く魚に動物の狩猟本能がくすぐられます <br>【商品使用シーン】 自動猫おもちゃ <br>【適用ペット】 スコティッシュフォールド メインクーン ラグドール ロシアンブルー ブリティッシュ ショー ヘア <br> サイベリアン キジトラ 野良猫 アメリカンカール スコティッシュ サバトラ ハチワレ 猫ちゃん<br> キャッチコピー: ネコちゃんの狩猟本能を刺激する電動魚ロボット 76×76サイズの画像URL: https://item-shopping.c.yimg.jp/i/c/himawaridifang-store_robo-fish1 146×146サイズの画像URL: https://item-shopping.c.yimg.jp/i/g/himawaridifang-store_robo-fish1 商品URL: https://store.shopping.yahoo.co.jp/himawaridifang-store/robo-fish1.html 価格: 880使用不能なアプリケーションIDと検索キーワード「猫」を指定する
エラーレスポンスが ErrorResponse クラスにマッピングされて情報が出力されている。
$ gradle run -q --args="invalid_application_id 猫" appid: invalid_application_id query: 猫 URL: https://shopping.yahooapis.jp/ShoppingWebService/V3/itemSearch?query=%E7%8C%AB&results=2 statusCode: 403 エラーメッセージ: Your Request was Forbidden参考資料
- 投稿日:2020-06-27T19:37:19+09:00
CameraXとOpenCVを使った画像処理Androidアプリのひな形
はじめに
Androidでカメラ画像を入力して、何らかの画像処理を行うためのサンプルプロジェクトです。
以前別の記事( Androidで OpenCV 4を使う方法とカメラライブビューの表示 )で、カメラ読み込みもOpenCVを使った方法を記載しましたが、最新のAndroid SDKだと使えないようでした。
Camera APIが廃止され、Camera2 APIを使うことが推奨されていましたが非常に使いづらいです。より簡単にAndroid上でカメラを触れるCameraXというものが存在しました。今回はこれを使ってみます。
CameraXは使い方が簡単で、チュートリアルも充実しています (https://developer.android.com/training/camerax ) 。ネット上のほとんどのサンプルがKotolin向けだったので、ここではJavaで書いてみようと思います。今回は、入力画像と前フレームの画像との差分を計算して、変化したところが分かるような簡単な画像処理をサンプルにしてみます。
↓の動画は、上がオリジナルのカメラ画像(CameraXのプレビュー画像)、下が画像処理をした結果になります。qiita用 pic.twitter.com/HXYE9fq2Ej
— iwatake (@iwatake2222) June 27, 2020環境
- Host
- Windows 10 64-bit
- Android Studio 4.0
- Android SDK API Level 30
- 多少低くても大丈夫なはず
- Android NDK Version 21.3.6528147
- NDKは不要かも
- opencv-4.3.0-android-sdk.zip
- Target
- Galaxy S7 (Android 8.0.0)
プロジェクトの用意
プロジェクトを作る
Android StudioのCreate New Projectから、Empty Activityを作ります。
Minimum SDKはAPI23としておきました。CameraXがサポートするAndroid 5.0(API レベル 21)以降であれば何でも大丈夫なはずです。
Manifestの設定
カメラを使用するので、そのための設定をします。
AndroidManifest.xml
を開き、以下のように編集します。(「追加」で検索)AndroidManifest.xml<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.samplecameraxandopencv"> <!-- ↓↓↓ 追加 ↓↓↓ --> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-feature android:name="android.hardware.camera" android:required="false"/> <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/> <!-- ↑↑↑ 追加 ↑↑↑ --> <application android:allowBackup="true" 省略 </application> </manifest>OpenCVを取り込む
- https://github.com/opencv/opencv/releases からopencv-4.3.0-android-sdk.zipをダウンロードして解凍します
- バージョンは4以降なら何でも大丈夫です。3以前だと少しやり方が変わると思います
- メニューバー -> File -> New -> Import Module で、
OpenCV-android-sdk/sdk
の場所を指定します- メニューバー -> File -> Project Structure の、Dependenciesを開き、appを選択します。
Declared Dependencies
内で+
->3 Moduel Dependency
をクリックして追加したsdkを選択します。手順詳細はこちら (https://qiita.com/iwatake2222/items/2642669419fdaa20a8a6#opencvのダウンロード ) を参照。
CameraXを使えるようにする
CameraXのチュートリアルの通りです。(https://codelabs.developers.google.com/codelabs/camerax-getting-started/#1 )
build.gradle(Module: app)
を開き、android
セクションとdependencies
セクションを以下のように編集します。(「追加」で検索)build.gradleapply plugin: 'com.android.application' android { compileSdkVersion 30 buildToolsVersion "30.0.0" defaultConfig { applicationId "com.example.samplecameraxandopencv" minSdkVersion 23 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } // ↓↓↓ 追加 ↓↓↓ compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // ↑↑↑ 追加 ↑↑↑ } dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' // ↓↓↓ 追加 ↓↓↓ def camerax_version = "1.0.0-beta03" // CameraX core library using camera2 implementation implementation "androidx.camera:camera-camera2:$camerax_version" // CameraX Lifecycle Library implementation "androidx.camera:camera-lifecycle:$camerax_version" // CameraX View class implementation "androidx.camera:camera-view:1.0.0-alpha10" // ↑↑↑ 追加 ↑↑↑ }編集後、右上に
Sync Now
と表示されるのでクリックします。
ここまで完了したら、一度ビルドと実行が出来ることを確認することをお勧めします。レイアウトを作る
まず最初にレイアウトを作ります。
res->layout->activity_main.xmlを開き、コードを表示します。
デフォルトで用意されているTextView
は消して、LinearLayout
でCameraXのプレビュー用のandroidx.camera.view.PreviewView
と処理結果表示用のImageView
を上下に並べます。activity_main.xml<?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"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <androidx.camera.view.PreviewView android:id="@+id/previewView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1"/> <ImageView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1"/> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>コード説明
onCreate()
プレビュー表示と結果表示用のviewを取得します。その後、カメラ使用の権限を取得し、カメラ処理開始関数(
startCamera
)を呼びます。startCamera()
チュートリアル(https://codelabs.developers.google.com/codelabs/camerax-getting-started/#3 )に倣ってCameraXの設定をしています。
CameraXではUseCaseという概念で、カメラに関係する処理を登録するようです。チュートリアルでは以下の3つを使用していました。
- Preview
- ImageCapture
- ImageAnalysis
ImageCaptureは静止画撮影用ですが、今回は未使用です。
ImageAnalysisは各フレーム画像を解析して、何らかの処理を行うことが出来ます。チュートリアルでは平均輝度を計算するだけでしたが、今回はこの部分にOpenCVを用いた画像処理を実装します。独自のImageAnalysisを行うためには、
ImageAnalysis.Analyzer
インターフェイスを持つクラスを実体化して指定します。今回はMyImageAnalyzer
というクラスを作りました。MyImageAnalyzerクラス
public void analyze(@NonNull ImageProxy image)
という関数を持つクラスを自分で定義します。この中で処理を行います。
ImageProxy imageにYUV(NV21)形式の画像が入力され、毎フレーム呼ばれます(image.close();
するまで次の処理は呼ばれない模様)。最初に、入力されるNV21画像をOpenCVのmatに変換します。変換処理は https://stackoverflow.com/questions/30510928/convert-android-camera2-api-yuv-420-888-to-rgb を参考にしました (
getMatFromImage()
)。次に、入力画像を適切に回転します。過去の方法でAndroidカメラを使ったことがある方には常識的な処理ですが、どうもCameraXのプレビューだとCameraX側で適切に処理してくれているようです。一方、この関数に入ってくる画像に対してはケアされていないようなので、自分で回転処理をする必要があります。今回はここでもOpenCVを使って回転・反転処理を行いました (
fixMatRotation()
)。OpenCVのmat(RGB)になったら、後は好きな処理をするだけです。
今回は前回フレームとの画素値差分を計算して、変化のあったところだけ表示するような処理にしてみました。
ついでに、適当に四角形と文字を出力してみました。最後に、OpenCVのmatをBitmapに変換し、ImageViewに出力します。ImageViewへの描画はUIスレッドで行っています。
コード全文
MainActivity.javapackage com.example.samplecameraxandopencv; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.camera.core.Camera; import androidx.camera.core.CameraSelector; import androidx.camera.core.ImageAnalysis; import androidx.camera.core.ImageProxy; import androidx.camera.core.Preview; import androidx.camera.lifecycle.ProcessCameraProvider; import androidx.camera.view.PreviewView; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.lifecycle.LifecycleOwner; import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.os.Bundle; import android.util.Log; import android.view.Surface; import android.widget.ImageView; import com.google.common.util.concurrent.ListenableFuture; import org.opencv.android.Utils; import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.Point; import org.opencv.core.Rect; import org.opencv.core.Scalar; import org.opencv.imgproc.Imgproc; import java.nio.ByteBuffer; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MainActivity extends AppCompatActivity { /*** Fixed values ***/ private static final String TAG = "MyApp"; private int REQUEST_CODE_FOR_PERMISSIONS = 1234;; private final String[] REQUIRED_PERMISSIONS = new String[]{"android.permission.CAMERA", "android.permission.WRITE_EXTERNAL_STORAGE"}; /*** Views ***/ private PreviewView previewView; private ImageView imageView; /*** For CameraX ***/ private Camera camera = null; private Preview preview = null; private ImageAnalysis imageAnalysis = null; private ExecutorService cameraExecutor = Executors.newSingleThreadExecutor(); static { System.loadLibrary("opencv_java4"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); previewView = findViewById(R.id.previewView); imageView = findViewById(R.id.imageView); if (checkPermissions()) { startCamera(); } else { ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_FOR_PERMISSIONS); } } private void startCamera() { final ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); Context context = this; cameraProviderFuture.addListener(new Runnable() { @Override public void run() { try { ProcessCameraProvider cameraProvider = cameraProviderFuture.get(); preview = new Preview.Builder().build(); imageAnalysis = new ImageAnalysis.Builder().build(); imageAnalysis.setAnalyzer(cameraExecutor, new MyImageAnalyzer()); CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build(); cameraProvider.unbindAll(); camera = cameraProvider.bindToLifecycle((LifecycleOwner)context, cameraSelector, preview, imageAnalysis); preview.setSurfaceProvider(previewView.createSurfaceProvider(camera.getCameraInfo())); } catch(Exception e) { Log.e(TAG, "[startCamera] Use case binding failed", e); } } }, ContextCompat.getMainExecutor(this)); } private class MyImageAnalyzer implements ImageAnalysis.Analyzer { private Mat matPrevious = null; @Override public void analyze(@NonNull ImageProxy image) { /* Create cv::mat(RGB888) from image(NV21) */ Mat matOrg = getMatFromImage(image); /* Fix image rotation (it looks image in PreviewView is automatically fixed by CameraX???) */ Mat mat = fixMatRotation(matOrg); Log.i(TAG, "[analyze] width = " + image.getWidth() + ", height = " + image.getHeight() + "Rotation = " + previewView.getDisplay().getRotation()); Log.i(TAG, "[analyze] mat width = " + matOrg.cols() + ", mat height = " + matOrg.rows()); /* Do some image processing */ Mat matOutput = new Mat(mat.rows(), mat.cols(), mat.type()); if (matPrevious == null) matPrevious = mat; Core.absdiff(mat, matPrevious, matOutput); matPrevious = mat; /* Draw something for test */ Imgproc.rectangle(matOutput, new Rect(10, 10, 100, 100), new Scalar(255, 0, 0)); Imgproc.putText(matOutput, "leftTop", new Point(10, 10), 1, 1, new Scalar(255, 0, 0)); /* Convert cv::mat to bitmap for drawing */ Bitmap bitmap = Bitmap.createBitmap(matOutput.cols(), matOutput.rows(),Bitmap.Config.ARGB_8888); Utils.matToBitmap(matOutput, bitmap); /* Display the result onto ImageView */ runOnUiThread(new Runnable() { @Override public void run() { imageView.setImageBitmap(bitmap); } }); /* Close the image otherwise, this function is not called next time */ image.close(); } private Mat getMatFromImage(ImageProxy image) { /* https://stackoverflow.com/questions/30510928/convert-android-camera2-api-yuv-420-888-to-rgb */ ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); int ySize = yBuffer.remaining(); int uSize = uBuffer.remaining(); int vSize = vBuffer.remaining(); byte[] nv21 = new byte[ySize + uSize + vSize]; yBuffer.get(nv21, 0, ySize); vBuffer.get(nv21, ySize, vSize); uBuffer.get(nv21, ySize + vSize, uSize); Mat yuv = new Mat(image.getHeight() + image.getHeight() / 2, image.getWidth(), CvType.CV_8UC1); yuv.put(0, 0, nv21); Mat mat = new Mat(); Imgproc.cvtColor(yuv, mat, Imgproc.COLOR_YUV2RGB_NV21, 3); return mat; } private Mat fixMatRotation(Mat matOrg) { Mat mat; switch (previewView.getDisplay().getRotation()){ default: case Surface.ROTATION_0: mat = new Mat(matOrg.cols(), matOrg.rows(), matOrg.type()); Core.transpose(matOrg, mat); Core.flip(mat, mat, 1); break; case Surface.ROTATION_90: mat = matOrg; break; case Surface.ROTATION_270: mat = matOrg; Core.flip(mat, mat, -1); break; } return mat; } } private boolean checkPermissions(){ for(String permission : REQUIRED_PERMISSIONS){ if(ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){ return false; } } return true; } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { // super.onRequestPermissionsResult(requestCode, permissions, grantResults); if(requestCode == REQUEST_CODE_FOR_PERMISSIONS){ if(checkPermissions()){ startCamera(); } else{ Log.i(TAG, "[onRequestPermissionsResult] Failed to get permissions"); this.finish(); } } } }
- 投稿日:2020-06-27T16:47:51+09:00
Vue.jsで糖質制限の情報をまとめたサイトを作った
はじめに
こんなサイトを作りました。
https://lowcarb-recommend.com/
3月中旬くらいにそろそろ手を出すか~とVue.jsのお勉強を始め、ゴールデンウィーク終わりごろにそろそろ何か作りたいなーと思っていた時、ちょうど自粛生活で太り気味で糖質制限ダイエットを始めていたので、それに関する情報をまとめたサイトを作ろうと思い作成を始めました。
どんな機能があるの?
機能としては下記3点です。
- 糖質制限食をランキングで閲覧
- 糖質制限レシピの閲覧
- 糖質制限に関するニュースの閲覧
1.の糖質制限食に関しては楽天市場系APIより情報を取得し、ショップごと、もしくはジャンルごと(パンや麺類など)で検索をかけられるようにしました
2.の糖質制限レシピについては、これも楽天レシピ系APIより情報を取得しています。
3.のニュースに関してはGoogle NewsよりRSSを取得しています。
環境
- Java
- Spring Boot
- Vue.js(Vue Router、Vuex、Vuetify)
- Swiper.js
- Heroku
- Mongo DB
- Netlify
もともとはVue.jsを使いたかったためフロントエンドは確定として、バックエンドはどうしようかなーと思ってたのですが、全部が慣れていないものだと作るのに時間がかかる→最後まで作り切れなさそうと少しビビっていたので業務で使っているJavaを選びました。
製作期間
- 2か月弱
バックエンドは、仕事で使っているJavaということもあり、2週間足らずで終わらせることができました。
フロントエンドについては、学び始めたばかりのVue.jsということもあり、1か月半かけて調べながら完成させました。苦労したところ
下記に、苦労した点と参考にさせていただいたものをまとめます。
ニュース情報をどう集めるか
当初はニュース関連のサービスでよく使われているであろうNews APIを使おうと思ってました。
しかし、9割9分自分の使い方がまずかったと思うのですが、うまく糖質制限に関する情報を取得できず、代替案を考えていたところ、Google NewsよりRSSを取得する方法を見つけたので、それに切り替えました。Google News RSSを取得する方法は下記を参考にさせていただきました。
https://qiita.com/KMD/items/872d8f4eed5d6ebf5df1
http://mogakana.blogspot.com/2011/05/javarss.htmlSwiper.jsの使い方
「TOPページになにか動くの導入してやろ~」と思いいろいろ調べてみると、当ライブラリに出会いました。
トップページに写真の上にキャプションをうまく載せられなかったり、うまくレスポンシブにできなかったりといろいろ思考錯誤しました。Swiper.jsに関しては下記を参考にさせていただきました。
https://qiita.com/whike_chan/items/c68e094f412b04b1afc2
https://github.surmon.me/vue-awesome-swiper/
https://www.kabanoki.net/4783/PWA化
開発も終盤にかかってきたところ、せっかくだしPWA化して「ホーム画面に追加」って出るとかっこいいなと思いいろいろ試したがなかなかLighthouseでOKにならない。
コンソールを見てみると下記エラーが出てました。consoleSite cannot be installed: no matching service worker detected. You may need to reload the page, or check that the service worker for the current page also controls the start URL from the manifest
さらに「bad-precaching-response」のようなエラーも出ていました。
Netlifyのデプロイ時に置いている_redirectsファイルのキャッシュを更新しようとする→そんなファイル無いぞと怒られるのが原因みたいだったため、下記サイトを参考にキャッシュ対象から除外しました。vue.config.jsに下記を書くことで解決しました。
vue.config.jsmodule.exports = { //省略 pwa: { workboxPluginMode: 'GenerateSW', workboxOptions: { exclude: /_redirects/ } } }https://michimani.net/post/programming-build-pwa-with-vuejs/
教訓
開発中、大枠は1か月くらいでできた(つもりだった)ため、もうすぐ終わるだろうと思っていたら、結局2か月かかってしまいました。
細かいところで修正したいところがたくさん出てくるんですよね、、開発しながら下記故事成語があったな~と思いだしたので、今後開発するときはこれを頭に入れておいて、「9割終わったと思うけど多分まだ5割なんだよな~」と思っておくと気が楽になるかもしれません、、百里を行く者は九十を半ばとす
https://imidas.jp/proverb/detail/X-02-C-27-8-0010.htmlおわりに
いろいろ書きましたが、ぜひ使ってみてください!
ロカボのススメ
- 投稿日:2020-06-27T16:47:51+09:00
自粛生活で太ったので、糖質制限の情報をまとめたサイトを作った
はじめに
こんなサイトを作りました。
https://lowcarb-recommend.com/
3月中旬くらいにそろそろ手を出すか~とVue.jsのお勉強を始め、ゴールデンウィーク終わりごろにそろそろ何か作りたいなーと思っていた時、ちょうど自粛生活で太り気味で糖質制限ダイエットを始めていたので、それに関する情報をまとめたサイトを作ろうと思い作成を始めました。
どんな機能があるの?
機能としては下記3点です。
- 糖質制限食をランキングで閲覧
- 糖質制限レシピの閲覧
- 糖質制限に関するニュースの閲覧
1.の糖質制限食に関しては楽天市場系APIより情報を取得し、ショップごと、もしくはジャンルごと(パンや麺類など)で検索をかけられるようにしました
2.の糖質制限レシピについては、これも楽天レシピ系APIより情報を取得しています。
3.のニュースに関してはGoogle NewsよりRSSを取得しています。
環境
- Java
- Spring Boot
- Vue.js(Vue Router、Vuex、Vuetify)
- Swiper.js
- Heroku
- Mongo DB
- Netlify
もともとはVue.jsを使いたかったためフロントエンドは確定として、バックエンドはどうしようかなーと思ってたのですが、全部が慣れていないものだと作るのに時間がかかる→最後まで作り切れなさそうと少しビビっていたので業務で使っているJavaを選びました。
製作期間
- 2か月弱
バックエンドは、仕事で使っているJavaということもあり、2週間足らずで終わらせることができました。
フロントエンドについては、学び始めたばかりのVue.jsということもあり、1か月半かけて調べながら完成させました。苦労したところ
下記に、苦労した点と参考にさせていただいたものをまとめます。
ニュース情報をどう集めるか
当初はニュース関連のサービスでよく使われているであろうNews APIを使おうと思ってました。
しかし、9割9分自分の使い方がまずかったと思うのですが、うまく糖質制限に関する情報を取得できず、代替案を考えていたところ、Google NewsよりRSSを取得する方法を見つけたので、それに切り替えました。Google News RSSを取得する方法は下記を参考にさせていただきました。
https://qiita.com/KMD/items/872d8f4eed5d6ebf5df1
http://mogakana.blogspot.com/2011/05/javarss.htmlSwiper.jsの使い方
「TOPページになにか動くの導入してやろ~」と思いいろいろ調べてみると、当ライブラリに出会いました。
トップページに写真の上にキャプションをうまく載せられなかったり、うまくレスポンシブにできなかったりといろいろ思考錯誤しました。Swiper.jsに関しては下記を参考にさせていただきました。
https://qiita.com/whike_chan/items/c68e094f412b04b1afc2
https://github.surmon.me/vue-awesome-swiper/
https://www.kabanoki.net/4783/PWA化
開発も終盤にかかってきたところ、せっかくだしPWA化して「ホーム画面に追加」って出るとかっこいいなと思いいろいろ試したがなかなかLighthouseでOKにならない。
コンソールを見てみると下記エラーが出てました。consoleSite cannot be installed: no matching service worker detected. You may need to reload the page, or check that the service worker for the current page also controls the start URL from the manifest
さらに「bad-precaching-response」のようなエラーも出ていました。
Netlifyのデプロイ時に置いている_redirectsファイルのキャッシュを更新しようとする→そんなファイル無いぞと怒られるのが原因みたいだったため、下記サイトを参考にキャッシュ対象から除外しました。vue.config.jsに下記を書くことで解決しました。
vue.config.jsmodule.exports = { //省略 pwa: { workboxPluginMode: 'GenerateSW', workboxOptions: { exclude: /_redirects/ } } }https://michimani.net/post/programming-build-pwa-with-vuejs/
教訓
開発中、大枠は1か月くらいでできた(つもりだった)ため、もうすぐ終わるだろうと思っていたら、結局2か月かかってしまいました。
細かいところで修正したいところがたくさん出てくるんですよね、、開発しながら下記故事成語があったな~と思いだしたので、今後開発するときはこれを頭に入れておいて、「9割終わったと思うけど多分まだ5割なんだよな~」と思っておくと気が楽になるかもしれません、、百里を行く者は九十を半ばとす
https://imidas.jp/proverb/detail/X-02-C-27-8-0010.htmlおわりに
いろいろ書きましたが、ぜひ使ってみてください!
ロカボのススメ
- 投稿日:2020-06-27T16:41:51+09:00
Spring の RestTemplate で Yahoo!ショッピング商品検索(v3) の API をコールするサンプルコード
概要
- Spring が提供する HTTP クライアント RestTemplate を使って、Yahoo!ショッピング商品検索 (v3)の API をコールするサンプルプログラムを作る
今回の環境
- Java 11 (AdoptOpenJDK 11.0.7+10)
- Spring Web 5.2.7
- Jackson Databind 2.11.0
- Gradle 6.5
- macOS Catalina
ソースコード
ソースコード一覧
├── build.gradle └── src └── main └── java └── com └── example ├── ErrorResponse.java ├── ItemSearch.java └── ResultSet.javabuild.gradle
plugins { id 'application' id 'java' } group 'org.example' version '0.0.1' mainClassName = "com.example.ItemSearch" sourceCompatibility = '11' repositories { mavenCentral() } dependencies { // RestTemplate を使うために必要 // https://mvnrepository.com/artifact/org.springframework/spring-web implementation 'org.springframework:spring-web:5.2.7.RELEASE' // JSON とクラスのマッピングに必要 // https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.0' }src/main/java/com/example/ItemSearch.java
package com.example; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.http.HttpHeaders; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestClientResponseException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import java.net.URI; /** * ショッピング:商品検索(v3) - Yahoo!デベロッパーネットワーク * https://developer.yahoo.co.jp/webapi/shopping/shopping/v3/itemsearch.html */ public class ItemSearch { public static void main(String[] args) { try { String appid = args[0]; String query = args[1]; System.out.println("appid: " + appid); // アプリケーションID System.out.println("query: " + query); // 検索キーワード // 商品を検索 ResultSet rs = new ItemSearch().search(appid, query); for (ResultSet.Hit hit : rs.hits) { System.out.println("**************************************************"); System.out.println("商品名: " + hit.name); System.out.println("商品説明: " + hit.description); System.out.println("キャッチコピー: " + hit.headLine); System.out.println("76×76サイズの画像URL: " + hit.image.small); System.out.println("146×146サイズの画像URL: " + hit.image.medium); System.out.println("商品URL: " + hit.url); System.out.println("価格: " + hit.price); } } catch (Exception e) { System.out.println("エラー発生: " + e); } } /** * 商品を検索する。 * @param appid アプリケーションID * @param query 検索キーワード * @return 検索結果 * @throws Exception エラー発生時 */ public ResultSet search(String appid, String query) throws Exception { try { // API コール用の URL を組み立てる String baseurl = "https://shopping.yahooapis.jp/ShoppingWebService/V3/itemSearch"; String url = UriComponentsBuilder.fromUri(new URI(baseurl)) .queryParam("query", query) // 検索キーワード .queryParam("results", 2) // 検索結果2件まで .build(false) .encode() .toUriString(); System.out.println("URL: " + url); // HTTP リクエスト情報を構築 HttpHeaders reqHeaders = new HttpHeaders(); reqHeaders.set("User-Agent", "Yahoo AppID: " + appid); // アプリケーションID RequestEntity req = RequestEntity.get(new URI(url)) .headers(reqHeaders) .build(); // API をコールして結果を取得 RestTemplate restTemplate = new RestTemplate(); ResponseEntity<ResultSet> res = restTemplate.exchange(req, ResultSet.class); return res.getBody(); } catch (RestClientResponseException e) { // レスポンスのステータス情報を出力 int statusCode = e.getRawStatusCode(); String statusText = e.getStatusText(); System.out.println("RestClientResponseException.RawStatusCode: " + statusCode); System.out.println("RestClientResponseException.StatusText: " + statusText); // エラー情報を出力 String body = e.getResponseBodyAsString(); ObjectMapper mapper = new ObjectMapper(); ErrorResponse errorResponse = mapper.readValue(body, ErrorResponse.class); System.out.println("エラーメッセージ: " + errorResponse.error.message); throw e; } catch (Exception e) { throw e; } } }src/main/java/com/example/ResultSet.java
検索結果のレスポンスを表すクラス。
package com.example; import java.util.List; /** * 検索結果。 */ public class ResultSet { public List<Hit> hits; public static class Hit { // hits/name string 商品名 public String name; // hits/description string 商品説明 public String description; // hits/headLine string キャッチコピー public String headLine; // hits/image public Image image; // hits/url string 商品URL public String url; // hits/price integer 価格 public int price; } public static class Image { // hits/image/small string 76×76サイズの画像URL public String small; // hits/image/medium string 146×146サイズの画像URL public String medium; } }src/main/java/com/example/ErrorResponse.java
エラーレスポンス情報を表すクラス。
package com.example; import com.fasterxml.jackson.annotation.JsonProperty; /** * エラー情報。 */ public class ErrorResponse { @JsonProperty("Error") public Error error; public static class Error { @JsonProperty("Message") public String message; } }実行例
使用可能なアプリケーションIDと検索キーワード「猫」を指定する
検索結果のレスポンスが ResultSet クラスにマッピングされて情報が出力されている。
$ gradle run -q --args="your_application_id 猫" appid: your_application_id query: 猫 URL: https://shopping.yahooapis.jp/ShoppingWebService/V3/itemSearch?query=%E7%8C%AB&results=2 ************************************************** 商品名: 猫おもちゃ 魚ロボット 猫電動おもちゃ 猫自動おもちゃ 猫おもちゃ自動 猫おもちゃ電動 猫おもちゃ魚 商品説明: 【ペット用おもちゃ】 電動魚ロボット 魚ロボット おもちゃ猫 電動おもちゃ猫 <br>【自動水泳機能】 水センサーで自動的にオン/オフします。自動ロボットフィンを使用して設計されています <br> 水中に置かれるとすぐに動き始める、取り出すと自動的にオフになり、電力を節約します。 <br>【LEDライト付】LEDライトが内蔵されているので暗闇で光ります。光って動く魚に動物の狩猟本能がくすぐられます <br>【商品使用シーン】 自動猫おもちゃ <br>【適用ペット】 スコティッシュフォールド メインクーン ラグドール ロシアンブルー ブリティッシュ ショー ヘア <br> サイベリアン キジトラ 野良猫 アメリカンカール スコティッシュ サバトラ ハチワレ 猫ちゃん<br> キャッチコピー: ネコちゃんの狩猟本能を刺激する電動魚ロボット 76×76サイズの画像URL: https://item-shopping.c.yimg.jp/i/c/himawaridifang-store_robo-fish1 146×146サイズの画像URL: https://item-shopping.c.yimg.jp/i/g/himawaridifang-store_robo-fish1 商品URL: https://store.shopping.yahoo.co.jp/himawaridifang-store/robo-fish1.html 価格: 880 ************************************************** 商品名: 猫 ハーネス 猫用リード 猫用品 ペット用品 商品説明: 脱走対策として<br><br>猫は通院やお引越しなどで慣れない移動をさせられると、パニックを起こして逃げ出そうとしたり、暴れたりすることが多いものです。そんな時、ペットケージに入れておくだけでなく、ハーネスを装着した上でペットケージに入れるという二重の備えをしておくと、とても安心です。また万が一、大災害の被害を受けて避難しなければならなくなった時にも脱走防止に役立つので、防災グッズの中に常備しておく飼い主も増えています。<br><br>お散歩用として<br><br>脱走防止目的だけでなく、猫のお散歩用として使用するのもおすすめです。ハーネスは猫の身体の動きを邪魔しにくいので、のびのびと運動できて良いストレス発散になることでしょう。ただし、猫の性格によっては外に連れ出すことがかえってストレスとなる場合もありますので、お散歩はくれぐれも猫の様子をみつつ、交通状況などにも気を配りながら行ってください。<br><br>カラーバリエーションは、赤・青・黒・ピンクの4色です。<br>ナイロン製<br><br>首回り16〜26センチ<br>胴回り26〜36センチ<br>リードの長さ110cm<br><br>検索キーワード:猫リード・猫ハーネス・猫用リード・ポイント消化・送料無料 キャッチコピー: 猫ハーネス猫リード猫用リード猫首輪 76×76サイズの画像URL: https://item-shopping.c.yimg.jp/i/c/sam-store_0030 146×146サイズの画像URL: https://item-shopping.c.yimg.jp/i/g/sam-store_0030 商品URL: https://store.shopping.yahoo.co.jp/sam-store/0030.html 価格: 599使用不能なアプリケーションIDと検索キーワード「猫」を指定する
エラーレスポンスが ErrorResponse クラスにマッピングされて情報が出力されている。
$ gradle run -q --args="invalid_application_id 猫" appid: invalid_application_id query: 猫 URL: https://shopping.yahooapis.jp/ShoppingWebService/V3/itemSearch?query=%E7%8C%AB&results=2 RestClientResponseException.RawStatusCode: 403 RestClientResponseException.StatusText: Forbidden エラーメッセージ: Your Request was Forbidden エラー発生: org.springframework.web.client.HttpClientErrorException$Forbidden: 403 Forbidden: [{ "Error" : { "Message" : "Your Request was Forbidden" } } ... (512 bytes)]参考資料
- 投稿日:2020-06-27T16:20:21+09:00
SpringBoot における各コンポーネントの役割
SpringBoot における各コンポーネントの役割
コンポーネントとは
アーキテクチャが構造、構成を意味する言葉であるとしたら、コンポーネントとは何かしらの部品を現す言葉である。
車というアーキテクチャがあれば、エンジンやAT、タイヤやハンドルなどは車を構成するためのコンポーネントであるといえる。
大まかなコンポーネントの種類
SpringBootの基本構成に基づきアプリケーションのアーキテクチャスタイルをMVCモデルとして構成すると、コンポーネントは下記となる。
コンポーネント 説明 View クライアント(PCなど)に送出するレスポンスデータを構成するためのコンポーネント。JSPやThymeleafを内部的に使用する。一般的にはHTML形式のデータを構成する処理を持つ。 Model(Form) 送信されたリクエスト(http通信におけるget送信やpost送信などが代表的)と対応するFormオブジェクトのフィールドにリクエストデータをバインドする。また、リクエストデータをチェックする。(バリデーション) Controller リクエストをハンドリングするためのコンポーネント。リクエストデータを一次加工したり、下位コンポーネント(Facadeなど)に受け渡し、実行する。 Helper ViewやControllerで使用する簡易なデータの加工処理を受け持つ。 Facade Controllerに呼び出され、処理要件に対応する。ビジネスロジックの窓口となるコンポーネント。Facadeの1メソッドが1トランザクションとなり、下位コンポーネント(Service)のビジネスロジックを必要な順序で呼び出す。データの保存や参照、削除といった具体的なビジネスロジックは下位コンポーネント(Service)に任せ、内部ではそれらのビジネスロジックを協調させる処理を行う。 Service 保存や、参照、削除といった個別のビジネスロジックを各メソッドに持たせる。データアクセスは本コンポーネントで行う。 Repository 永続化されたデータへのアクセス、コントロールを実行するコンポーネント。実際にデータベースとやり取りするのは下位コンポーネント(Dao)となる。 Dao リレーショナル・データベースへのデータアクセスの実装をするコンポーネント。内部的にMyBatis等を使用する。XMLに記述したSQLを実行する。 登場用語説明
・バインドとは何かと何かを割り当てるという意味を持つ。リクエストデータをFormオブジェクトのフィールドに割り当てるなど。
・バリデーションとは入力内容や記述内容が要件を満たしているか、妥当性を確認すること。入力した電話番号が0スタートかどうかなど。
・ハンドリングとは何かを捕まえるという意味を持つ。formのリクエストデータをControllerが捕まえる。
・トランザクションとは処理単位を意味する。りんご(リクエストデータ)を受け取り、皮を剥き(ビジネスロジック)、4つに切り分け(ビジネスロジック)、食べる(ビジネスロジック)。このビジネスロジックのまとまりが一つのトランザクションであるといえる。
・データの永続化とは、アプリケーションを落としてもデータが保持され続ける。極めて簡単に言えばデータベースに保存すること。
- 投稿日:2020-06-27T15:51:08+09:00
LINEBOTを使ってオーキド博士を作った話
はじめに
LINEのMessagingAPIを使って何か作成したいなと考えていたところ、
友人がポケモンにハマっているという話を聞いて、ポケモンの検索やランダムでポケモンを取得し、種族値合計で他のユーザと対戦するようなものを作成しようと思いました。作成したBOTは↓のURLから追加できます。ぜひお試しください!
lin.ee/zmHQYl6環境
- サーバー(2台構成)
- APサーバー(Nginx)
- DBサーバー(PostgreSQL)
- 作成言語
- Java(Spring Boot)
- Messaging API(API)
できること
- ポケモン検索
- 図鑑番号検索
- ポケモン名検索(LIKE検索)
- ランダム取得
- ランダム取得(1体)
- ランダム取得(6体)
- 種族値合計ランキング
- 歴代ランキング
- 月間ランキング
- すばやさ確認
- 作成中
完成形
素晴らしい機能
FlexMessage という機能が素晴らしかったです。(今回の検索結果の表示等に使っている機能)
レイアウトについてはFLEX MESSAGE SIMULATORで簡単に作成できるので簡単にそれっぽいレイアウトが作成できます。最後に
Messaging APIをつかったLINEBOTの作成についてはLINEの公式ドキュメントが充実しているので特に詰まってしまうような箇所はありませんでした。
簡単にできるのぜひ皆さんお試しください。参考にしたサイト
- 投稿日:2020-06-27T15:27:43+09:00
【AWS SDK for Java】S3クライアントにリトライポリシーを設定する
ジャストほしい情報が日本語でなかったのでドキュメンテーションする。
AWS SDK for Javaを利用してS3にファイルをアップロードする際に、やや詳細な設定を付加する。目次
- リトライ周りのケース概略
- とりあえず実装してみる
- 関連クラスの整理
- その他
- 参考
リトライ周りのケース概略
AWS SDK for Javaを使って、ローカルにあるS3にファイルをアップロードしたい。
そして、AWS側の都合で通信断となったり諸事情により正常稼働できないケースを見越して、AWSクライアントにリトライポリシーを仕込みたい。
今回は、AWS SDK for Javaに含まれるAmazonS3
オブジェクトのシングルトンを返すユーティリティクラスを用意する。なお、本記事では自分が特に困った「クライアント生成時のオプション設定」にフォーカスを置く。そのため、ファイルアップロード前後の処理関連の説明は省略する。
とりあえず実装してみる
シンプルなクライアントオプションを詰めたクライアントを構築する。
流れとしては、リトライポリシーインスタンス、クライアント設定インスタンス、そしてクライアントの3つを準備していく。まずは、ポリシーの基本設定やAWS定義のストラテジーなどを詰めた
RetryPolicy
インスタンスを返すメソッドを用意。AmazonS3Utils.java/** * ExponentialBackoffStrategyのコンストラクタにbaseDelayとmaxBackoffTimeの適切な値を入れる。 * 注意:ここではサンプルとして適当な値を入れてます。 */ private static RetryPolicy getS3BaseRetryPolicy() { return new RetryPolicy( new PredefinedRetryPolicies.SDKDefaultRetryCondition(), new PredefinedBackoffStrategies.ExponentialBackoffStrategy(0, 0), PredefinedRetryPolicies.DEFAULT_MAX_ERROR_RETRY, true ); }次に、↑で定義した
RetryPolicy
インスタンスを返すメソッドを呼んで、クライアント設定を定義する。AmazonS3Utils.javaprivate static final ClientConfiguration clientConfiguration = new ClientConfiguration() .withRetryPolicy(getS3BaseRetryPolicy());そして、他のクラスやメソッドが呼び出すS3クライアントのエントリーポイントとなる
AmazonS3
オブジェクトを、クライアント設定を詰めて初期化。AmazonS3Utils.javaprivate static final AmazonS3 amazonS3 = AmazonS3ClientBuilder .standard() .withClientConfiguration(clientConfiguration) .withRegion(Regions.DEFAULT_REGION) .build();ここまでの設定をまとめると、
AmazonS3Utils.javapublic final class AmazonS3Utils { /** * リトライポリシーを反映した、クライアント設定を初期化。 */ private static final ClientConfiguration clientConfiguration = new ClientConfiguration() .withRetryPolicy(getS3BaseRetryPolicy()); /** * AmazonS3インスタンスを初期化。 */ private static final AmazonS3 amazonS3 = AmazonS3ClientBuilder .standard() .withClientConfiguration(clientConfiguration) .withRegion(Regions.DEFAULT_REGION).build(); /** * ファイル操作を提供するオブジェクトを返す */ public static AmazonS3 getS3() { return amazonS3; } /** * リトライポリシーをインスタンス化。 */ private static RetryPolicy getS3BaseRetryPolicy() { return new RetryPolicy( new PredefinedRetryPolicies.SDKDefaultRetryCondition(), new PredefinedBackoffStrategies.ExponentialBackoffStrategy(0, 0), PredefinedRetryPolicies.DEFAULT_MAX_ERROR_RETRY, true ); } }これで準備完了。あとは、
getS3
メソッドで設定を反映したS3クライアントを呼び出せるようになる。登場オブジェクトの整理
実装例に出てくる複数のクラスの役割を整理してみたい。
自分の調べ方が悪かったのか、やりたいことをさくっとできる感じではなかった...AmazonS3(インターフェース)
S3のオブジェクトを操作するメソッドを定義するインターフェース。
具体的な実装はクライアントビルダークラスにより提供されるようだ。
AmazonS3
このオブジェクトはインターフェースであり、具はAmazonS3ClientBuilder
などにより提供されるようだ。もともと、リトライ周りの設定を追加する前は
AmazonS3ClientBuilder.standard().build();
で済ませていた。最小限の機能で済むなら、そのようなミニマルなクライアントで十分かと思う。ClientConfiguration(クラス)
ClientConfigurationは、リトライやプロキシ、ユーザエージェント文字列などAWSクライアントに追加できるオプションの設定クラス。
今回の例では自分は多少カスタマイズを加えたRetryPolicy
を渡したが、PredefinedRetryPolicies
にあるstaticメソッドであるgetDefaultRetryPolicy()
を呼び出すことで、デフォルト設定をクライアント設定に詰められるようだ。RetryPolicy(クラス)
RetryPolicyは、
ClientConfiguration
と組み合わせて、リトライポリシーを構築するクラス。
コンストラクタに必要なオブジェクトや値を詰めて初期化する。コンストラクタには、
RetryCondition
やBackoffStrategy
などのインターフェースが引数に指定されているのだが、引数のインターフェースに合うクラスに何を指定すればよいのか、最初悩んだ。いくつかコンストラクタがあるが、そのうちの一つを挙げてみるとRetryPolicy.javapublic RetryPolicy(RetryCondition retryCondition, BackoffStrategy backoffStrategy, int maxErrorRetry, boolean honorMaxErrorRetryInClientConfig) { this(retryCondition, backoffStrategy, maxErrorRetry, honorMaxErrorRetryInClientConfig, false); }いろいろ調べていくうちに、どうやらSDKは事前にある程度定義したポリシーを詰められるクラスを用意していたとわかった。
RetryCondition
インターフェースには、今回の実装ではAWS SDKのデフォルトに従うことにした。PredefinedRetryPoliciesに、何種類か、使えそうな実装があるのでRetryPolicy
のコンストラクタに渡せばよい。
設定にこだわりがないのであれば、デフォルト設定を返してくれるstaticプロパティDEFAULT_RETRY_CONDITION
を指定すればよさそう。BackoffStrategyインターフェースを拡張した抽象クラスV2CompatibleBackoffStrategyが用意されており、この抽象クラスを拡張したアダプターおよび各種具象クラスを使えば
BackoffStrategy
インターフェース相当の引数を設定できる。詳しくいうと、PredefinedBackoffStrategiesには
V2CompatibleBackoffStrategyAdapter
を拡張した、
- FullJitterBackoffStrategy
- EqualJitterBackoffStrategy
- EqualJitterBackoffStrategy
などが用意されている。RetryPolicyを初期化する際に、コンストラクタになんとかBackoffStrategyを渡すことで細かい設定を施すことが可能である。
その他
本当は公式のドキュメントで、いい感じに説明してくれているサムシングがある気がしている...
自分は探し当てられなかったため、「そんな無理せんでもこれ読んだらええんやけど」ってのがあればご指摘いただければありがたいです。参考
- 投稿日:2020-06-27T11:16:05+09:00
【Java】気付きにくいけど恐ろしく遅くなるコード
目的
レビューをしていてIntegerだったりintだったりが混ざったコードに出会いました。
オートボクシングで変換掛かるから問題ないかなと一瞬思ったけど、性能面はどうなんだろうと気になってしまったので検証してみたいと思います。検証環境
- Eclipse Oxygen.3a Release (4.7.3a)
- Java8
検証用ソースコード
1.参照型とプリミティブ型が混ざったパターン
qiita.javalong begin = 0L; long end = 0L; begin = System.currentTimeMillis(); Integer sum = 0; for(int i = 0; i < 1000000; i++) { sum += i; } end = System.currentTimeMillis(); System.out.println(end - begin + "ミリ秒");2.プリミティブ型のみのパターン
qiita.javalong begin = 0L; long end = 0L; begin = System.currentTimeMillis(); int sum = 0; for(int i = 0; i < 1000000; i++) { sum += i; } end = System.currentTimeMillis(); System.out.println(end - begin + "ミリ秒");実施手順
- 検証用ソースコードを使用して1,000,000回加算処理を行う。それぞれ3回計測することで平均時間を算出する。小数第3位を四捨五入する。
実施結果
1.参照型とプリミティブ型が混ざったパターン
- 1回目:15ミリ秒
- 2回目:16ミリ秒
- 3回目:15ミリ秒
- 平均:15.33ミリ秒
2.プリミティブ型のみのパターン
- 1回目:5ミリ秒
- 2回目:4ミリ秒
- 3回目:4ミリ秒
- 平均:4.33ミリ秒
考察
参照型とプリミティブ型が混ざったパターンは平均15.33ミリ秒に対して、プリミティブ型のみのパターンでは平均4.33ミリ秒でした。参照型とプリミティブ型が混ざっていてオートボクシングが働いていると約4倍の時間が掛かっていることが分かる。塵も積もれば山となる、データ件数にもよりますが、無意味にオートボクシングしないようにすべきということが分かった。
ではまた(^_^)ノシ
- 投稿日:2020-06-27T09:18:04+09:00
項目57 ローカル変数のスコープを最小限にする
前提
java開発歴1ヶ月程度の新人が、Effective Javaを読んで学習した内容をアウトプットするために書いています。自分の解釈を噛み砕いて解説し、初心者が読んでもできるだけ理解しやすい記事を目指しています。
【ざっくり】
ローカル変数のスコープを最小限にすることで、コードの可読性と保守性が向上し、ミスが生まれる可能性を減らすことができる。
【悪い例1】
badPractice1String hoge = "hoge"; int fuga; double piyo = 0.1; doSomething(hoge); ... //何らかの処理 doSomething(fuga); // fugaの型なんだっけ? 初期値なんだっけ? ... //何らかの処理 { doSomething(piyo) // piyoの型なんだっけ? 初期値なんだっけ? }
- 読み手の注意を逸らしてしまう。
-> どのような処理を行っているかを理解しようとしているのに、変数の記憶に脳のリソースを使ってしまう。- 読み手は、変数が使われる時点で型や初期値を思い出しにくくなる。
- スコープが広い。
-> スコープが広ければ広いほど、意図しない場で利用され得るため、悲惨な結果を生む可能性が高くなる。【こう書きましょう】
goodPractice1String hoge = "hoge"; doSomething(hoge); ... //何らかの処理 int fuga = 10; doSomething(fuga); ... //何らかの処理 double piyo = 0.1; { doSomething(piyo) }
- ローカル変数が初めて使われたときに宣言する。
-> 変数の型、初期値の記憶の必要がなくなり、読み手は処理の理解に集中できる。- スコープを小さくする。
-> fuga, piyoは前方へのスコープが狭まっているため、その分間違って利用される可能性が減る。- ローカル変数は、基本的に初期値を含むようにする。
-> 変数を合理的に初期化するための情報が無ければ、初期化すべきタイミングではないので、情報が得られるまで先送りすべき(下記のtry-catchブロックによる例外は除く)。exceptionHoge hoge = null; try { hoge = hogeHoge(); } catch (HogeException e) { e.hoge(); } hoge.doSomething();-> このような場合には、合理的な初期化はできない。
【悪い例2】
badPractice2Iterator<Hoge> i = hoge.iterator(); while(i.hasNext()) { doSomething(i.next()); } Iterator<Hoge> i2 = hoge2.iterator(); while(i.hasNext()) { // バグがある doSomething(i2.next()); }
- ローカル変数のスコープが広いため、バグを生む原因になる。
-> コード自体に問題はないため正常にコンパイルされ、間違いに気づきにくい。【こう書きましょう】
goodPractice2for (Iteratro<Hoge> i = hoge.iterator(); i.hasNext()) { doSomething(i.next()); } // コンパイルエラー - iが見つからない for (Iteratro<Hoge> i2 = hoge2.iterator(); i.hasNext()) { doSomething(i2.next()); }
- エラーにすぐに気づくことができる。
-> i, i2のスコープはそれぞれforブロック内に収まっているため、スコープ外の変数を参照しようとすればコンパイルエラーが出ます。- 変数名の重複を避ける必要がなくなる。以下に例を示します。
goodPractice3for (Iteratro<Hoge> i = hoge.iterator(); i.hasNext()) { doSomething(i.next()); } for (Iteratro<Hoge> i = hoge2.iterator(); i.hasNext()) { doSomething(i.next()); }-> とても洗練された書き方になっていてかつ、whileと比べて読みやすい。
【悪い例3】
badPractice4public int calcTotalAmount(int price) { double taxRate = 1.1; ... // 税込み価格を計算する処理 ... // 送料を計算する処理 int totalAmount = taxIncludedPrice + shippingFee; return totalAmount; }
- その処理とは無関係なローカル変数がスコープ内に存在している。
-> taxRateというローカル変数が、送料を計算するという無関係な処理のスコープの中に存在している。【こう書きましょう】
goodPractice4public int calcTotalAmount(int price) { int taxIncludedPrice = calcTaxIncludedPrice(price); int shippingFee = calcShippingFee(price); return taxIncludedPrice + shippingFee; } public int calcTaxIncludedPrice(int price) { double taxRate = 1.1; ... // 税込み価格を計算する処理 return taxIncludedPrice ; } public int calcShippingFee(int price) { ... // 送料を計算する処理 return shippingFee; }-> スコープ内に不必要なローカル変数を含まないので、バグの生まれる可能性を減らせる。
【まとめ】
- ローカル変数が初めて使われたときに宣言する。
- ローカル変数宣言時は、基本的に初期値を含むべき。
- whileよりもforループを使う(ループ終了後にループ変数が必要ない場合に限る)。
- メソッドを小さくして焦点をはっきりさせる。
- 投稿日:2020-06-27T04:11:25+09:00
ゼロから始めるScala (環境構築 mac)
Scala をはじめよう
Scala とは?
プログラミング言語の1つ。
「オブジェクト指向」と「関数型プログラミング」の両方の性質を併せ持つ (両方できる二刀流)。
Scala の「良さ」
- オブジェクト指向と関数型プログラミングの両方を表現できる。
- オブジェクトの「不変性」が意識された設計
- 高いモジュール性
- Java と互換性がある (Java のライブラリを使うことができる)
- 非同期の計算ができる
Future
ライブラリを標準装備- 並列・分散プログラミングのためのライブラリ
Akka
があるインストール on Mac
Homebrew
を使うのが簡単だと思う。環境構築は全部こいつでなんとかなる(気がする)。
探す
# scala (本体, しかし前提として Java が動く環境が必要 - 後述) $ brew search scala ==> Formulae scala scala@2.11 scala@2.12 scalaenv scalapack scalariform scalastyle ==> Casks scala-ide # sbt (scalaのビルドツール, 色々するのに便利なやつと思えば良い) $ brew search sbt ==> Formulae sbt sbt@0.13 sbtenv(前提として) Java をインストール
やはり
Homebrew
を使う。
AdoptedOpenJDK
をインストールするのが良いっぽい。AdoptedOpenJDKはJava大好きコミュニティ()により提供されているOpenJDKのバイナリ(だと思っている)。
Oracle経由でのインストールが、アカウント登録とか、自分でフォルダ配置するとか面倒になったので、変にこだわるよりは、これでサクッと入れてしまうのがコスト低いと思う...# リポジトリの追加 $ brew tap AdoptOpenJDK/openjdk # search $ brew search openjdk # install $ brew cask install adoptopenjdk11JAVA_HOMEのPATHを設定しよう
java_home
コマンドでインストールされたディレクトリの場所を確認$ /usr/libexec/java_home -v 11 /Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home
$ なんたら/java_home -v 11
して出てきた文字列を、環境変数JAVA_HOME
としてPATH
に追加必要に応じて
.bash_profile
や.bashrc
などに記述する。# .bashrc に書いた。 export JAVA_HOME=/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home PATH=${JAVA_HOME}/bin:${PATH}設定ファイルの再読み込み
# 以下、どちらか一方でOK source ~/.bashrc exec $SHELL -l一応、チェックする。
$ java -version openjdk version "11.0.7" 2020-04-14 OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.7+10) OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.7+10, mixed mode) $ javac -version javac 11.0.7 # コンパイル例 $ javac source.java # 実行例 (.class を含めないように注意) $ java Sourcescala を入れる
(後述の)
sbt
だけでも良いが、一応書いておく。# install $ brew install scala以上。
使ってみる
# REPL起動 $ scala Welcome to Scala 2.13.3 (OpenJDK 64-Bit Server VM, Java 11.0.7). Type in expressions for evaluation. Or try :help. scala>例
// 出力 scala> println("Hello scalalala") Hello scalalala // 演算1 scala> 1 + 2 val res1: Int = 3 // 演算2 scala> 3 * 6 val res2: Int = 18 // 演算3 scala> 8.0 / 2.0 val res3: Double = 4.0 // 終了 scala> :quit // scala> :q でも OKソースファイル
拡張子は
.scala
Main.scalaobject Main { def main(args: Array[String]): Unit = { println("Hello scala program") } }
- コンパイルして実行
$ scalac Main.scala $ scala Main
- コンパイルしないで実行
$ scala Main.scala
- REPLから実行
$ scala Welcome to Scala 2.13.3 (OpenJDK 64-Bit Server VM, Java 11.0.7). Type in expressions for evaluation. Or try :help. scala> :load Main.scalasbt を入れる
# install $ brew install sbt以上。
使ってみる
# REPL起動 $ sbt console ... [info] Starting scala interpreter... Welcome to Scala 2.12.10 (OpenJDK 64-Bit Server VM, Java 11.0.7). Type in expressions for evaluation. Or try :help. scala>(あとは同じなので詳細略)
sbt でコンパイル, 実行
下準備として、何かフォルダを作っておくと良いと思います。
(名前はmyfolderとしましたが、なんでも良いです、対応する部分は読み替えてください)$ mkdir myfolder $ cd myfoldermyfolder下にソースファイルを書いて置きます。
HelloWorld.scalaobject HelloWorld { def main(args: Array[String]): Unit = { println("Hello, Scala World!") } }myfolder下に
sbt
の設定ファイルを置きますbuild.sbtscalaVersion := "2.12.10" scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked", "-Xlint")
sbt
を起動[info] ... sbt:myfolder>
run
コマンドで実行sbt:myfolder> run [info] Compiling 1 Scala source to ... [info] Running HelloWorld Hello, Scala World! [success] Total time: 1 s, completed 2015/02/09 15:44:44`
run
コマンドでは、mainメソッドを持つオブジェクトを探して実行してくれるらしい。というか、なんか日付がおかしい...??
参考
- ドワンゴ 新卒エンジニア向けの研修資料
- Scalaプログラムの基本(まとめ)
- 第一章:猿でも分かるScala!
どうして猿を引き合いに出したがるのか...
- 投稿日:2020-06-27T01:13:34+09:00
ChromeDriver 通知バー消す方法 (Java)
Selenium (ChromeDriver) での「自動テストソフトウェアによって制御されています]という通知バーを削除する方法
この表示邪魔ですよねー。
通知バーを消すと、デベロッパーモードを無効にしますか?とポップアップが出てしまい、
なかなか方法が見つかりませんでしたが、合わせ技で通知バーを消すことができたので記事に起こしておきます。import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; ChromeOptions options = new ChromeOptions(); options.setExperimentalOption("excludeSwitches", new String[] { "enable-automation" }); options.setExperimentalOption("useAutomationExtension", false); WebDriver driver = new WebDriver(options);これを丸ごとコードに記述することで通知バーが見事に消えます。
- 投稿日:2020-06-27T00:03:52+09:00