AsyncTaskを使うと非推奨と出るので他の方法を模索した、HandlerかExecutorServiceのどちらかもしくは両方を使ったものが提案された。
あなた:
androidでSwingWorkerのようなものはあるのか
ChatGPT:
はい、Androidには SwingWorker のような「バックグラウンド処理とUI更新を分離する仕組み」として、AsyncTask(非推奨)やHandler、ExecutorService、LiveData、ViewModel、Coroutine(Kotlin)など、さまざまな選択肢があります。
Javaらしく、バックグラウンドスレッドとUIスレッドの橋渡しに使える方法です。
ExecutorService executor = Executors.newSingleThreadExecutor();
Handler handler = new Handler(Looper.getMainLooper());
executor.execute(() -> {
// バックグラウンド処理(重い処理など)
String result = "結果";
// UIスレッドで結果を反映
handler.post(() -> {
// ここでUIを更新する
textView.setText(result);
});
});
これが SwingWorker#doInBackground → done() の流れに一番近い形です。
一昔前は AsyncTask が SwingWorker 的な存在でしたが、今は非推奨です:
private class MyTask extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... voids) {
// バックグラウンド処理
return "結果";
}
@Override
protected void onPostExecute(String result) {
// UI更新
textView.setText(result);
}
}
| 方法 | 特徴 | 使用推奨度 |
|---|---|---|
| ExecutorService + Handler | Java標準で簡単、安全、柔軟 | ✅ 推奨 |
| AsyncTask | 簡単だが古い、非推奨 | ❌ 避ける |
| LiveData, ViewModel | MVVM向け、状態管理に便利 | ✅ モダンUI向け(Jetpack) |
| Coroutine | Kotlin専用、強力で簡潔 | ✅ Kotlinの場合おすすめ |
SwingWorkerの代わりをJavaで書きたいなら、ExecutorService + Handler が一番実用的かつ公式も推奨しています。 必要があればユーティリティクラスとして MyWorker 的なクラスを用意することもできますよ。やってみますか?
あなた:
次のコードをHandlerを使って書き換えて
/** 非同期タスクでダウンロードと変換処理 */
private class ConvertWebTask extends AsyncTask<Void, Void, Void> {
private Context context;
private List<String> urlList;
private List<File> shortcutFiles;
private File dstPath;
public ConvertWebTask(Context context, List<String> urlList, List<File> shortcutFiles, File dstPath) {
this.context = context;
this.urlList = urlList;
this.shortcutFiles = shortcutFiles;
this.dstPath = dstPath;
}
@Override
protected Void doInBackground(Void... voids) {
try {
for (int i = 0; i < urlList.size(); i++) {
String urlString = urlList.get(i);
File srcShortcutFile = (shortcutFiles != null && shortcutFiles.size() > i) ? shortcutFiles.get(i) : null;
String ext = urlString.substring(urlString.lastIndexOf('.') + 1).toLowerCase();
if (ext.equals("zip") || ext.equals("txtz") || ext.equals("rar")) {
// URLのファイル名を安全な形式に
String fileName = new File(new URI(urlString).getPath()).getName().replaceAll("[?*&|<>\"\\\\]", "_");
File srcFile = new File(dstPath, fileName);
LogAppender.println("出力先にダウンロードします: " + srcFile.getCanonicalPath());
if (!srcFile.getParentFile().exists()) {
srcFile.getParentFile().mkdirs();
}
// ダウンロード処理
try (BufferedInputStream bis = new BufferedInputStream(new URL(urlString).openStream(), 8192);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(srcFile))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
}
// 変換実行(仮のメソッド)
convertFiles(new File[]{srcFile}, dstPath);
continue;
}
// WebAozoraConverterの処理(仮)
LogAppender.println("--------");
LogAppender.append(urlString);
LogAppender.println(" を読み込みます");
WebAozoraConverter webConverter = WebAozoraConverter.createWebAozoraConverter(urlString, context);
if (webConverter == null) {
LogAppender.append(urlString);
LogAppender.println(" は変換できませんでした");
continue;
}
int interval = 500;
String Ua="Chrome";
int beforeChapter = 0;
float modifiedExpire = 0;
boolean WebConvertUpdated= false;
boolean WebModifiedOnly =false;
boolean WebModifiedTail =false;
boolean WebLageImage =false;
srcFile = webConverter.convertToAozoraText(urlString, getCachePath(context), interval, modifiedExpire,
WebConvertUpdated, WebModifiedOnly, WebModifiedTail,
beforeChapter,Ua,WebLageImage);
// File srcFile = webConverter.convertToAozoraText(urlString, getCachePath(context));
if (srcFile == null) {
LogAppender.append(urlString);
LogAppender.println(" の変換をスキップまたは失敗しました");
continue;
}
// 変換処理実行
convertFiles(new File[]{srcFile}, dstPath);
}
} catch (Exception e) {
LogAppender.println("エラーが発生しました: " + e.getMessage());
}
return null;
}
}
ChatGPT:
AsyncTask の代わりに Handler を使って非同期処理を行う場合は、基本的に別スレッドで処理を行い、UIスレッドに戻す際に Handler を使います。以下に ConvertWebTask を Handler + Thread ベースで書き換えた例を示します:
private void startConvertWebTask(Context context, List<String> urlList, List<File> shortcutFiles, File dstPath) {
Handler handler = new Handler(Looper.getMainLooper());
new Thread(() -> {
try {
for (int i = 0; i < urlList.size(); i++) {
String urlString = urlList.get(i);
File srcShortcutFile = (shortcutFiles != null && shortcutFiles.size() > i) ? shortcutFiles.get(i) : null;
String ext = urlString.substring(urlString.lastIndexOf('.') + 1).toLowerCase();
if (ext.equals("zip") || ext.equals("txtz") || ext.equals("rar")) {
String fileName = new File(new URI(urlString).getPath()).getName().replaceAll("[?*&|<>\"\\\\]", "_");
File srcFile = new File(dstPath, fileName);
String logMessage = "出力先にダウンロードします: " + srcFile.getCanonicalPath();
handler.post(() -> LogAppender.println(logMessage));
if (!srcFile.getParentFile().exists()) {
srcFile.getParentFile().mkdirs();
}
try (BufferedInputStream bis = new BufferedInputStream(new URL(urlString).openStream(), 8192);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(srcFile))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
}
convertFiles(new File[]{srcFile}, dstPath);
continue;
}
handler.post(() -> {
LogAppender.println("--------");
LogAppender.append(urlString);
LogAppender.println(" を読み込みます");
});
WebAozoraConverter webConverter = WebAozoraConverter.createWebAozoraConverter(urlString, context);
if (webConverter == null) {
handler.post(() -> {
LogAppender.append(urlString);
LogAppender.println(" は変換できませんでした");
});
continue;
}
int interval = 500;
String Ua = "Chrome";
int beforeChapter = 0;
float modifiedExpire = 0;
boolean WebConvertUpdated = false;
boolean WebModifiedOnly = false;
boolean WebModifiedTail = false;
boolean WebLageImage = false;
File srcFile = webConverter.convertToAozoraText(
urlString,
getCachePath(context),
interval,
modifiedExpire,
WebConvertUpdated,
WebModifiedOnly,
WebModifiedTail,
beforeChapter,
Ua,
WebLageImage
);
if (srcFile == null) {
handler.post(() -> {
LogAppender.append(urlString);
LogAppender.println(" の変換をスキップまたは失敗しました");
});
continue;
}
convertFiles(new File[]{srcFile}, dstPath);
}
} catch (Exception e) {
handler.post(() -> LogAppender.println("エラーが発生しました: " + e.getMessage()));
}
}).start();
}
- Handler はメインスレッド(Looper.getMainLooper())にバインドしてログ出力などを UI スレッドで安全に実行。
- メイン処理は new Thread(() -> {...}) で非同期実行。
- LogAppender の呼び出しを handler.post() で包むことで UI スレッドに戻す。
Handler はメインスレッド(Looper.getMainLooper())にバインドしてログ出力などを UI スレッドで安全に実行。
メイン処理は new Thread(() -> {...}) で非同期実行。
LogAppender の呼び出しを handler.post() で包むことで UI スレッドに戻す。
必要であれば ProgressDialog などと連携してUIも更新できます。AsyncTask 非推奨以降、Handler か ExecutorService や Coroutine(Kotlin) の使用が推奨されてます。
この方式で問題ない?それとも別の非同期API(Executors など)で書きたい?
あなた:
ExecutorServiceを使った例も見たい
ChatGPT:
いいですね、ExecutorService を使うと AsyncTask より柔軟で、Handler よりもスッキリ書ける場面も多いです。以下は、さきほどの ConvertWebTask を ExecutorService で書き直した例です。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
public void startConvertWebTask(Context context, List<String> urlList, List<File> shortcutFiles, File dstPath) {
Handler handler = new Handler(Looper.getMainLooper());
executorService.execute(() -> {
try {
for (int i = 0; i < urlList.size(); i++) {
String urlString = urlList.get(i);
File srcShortcutFile = (shortcutFiles != null && shortcutFiles.size() > i) ? shortcutFiles.get(i) : null;
String ext = urlString.substring(urlString.lastIndexOf('.') + 1).toLowerCase();
if (ext.equals("zip") || ext.equals("txtz") || ext.equals("rar")) {
String fileName = new File(new URI(urlString).getPath()).getName().replaceAll("[?*&|<>\"\\\\]", "_");
File srcFile = new File(dstPath, fileName);
handler.post(() -> {
try {
LogAppender.println("出力先にダウンロードします: " + srcFile.getCanonicalPath());
} catch (IOException e) {
LogAppender.println("ファイルパス取得エラー: " + e.getMessage());
}
});
if (!srcFile.getParentFile().exists()) {
srcFile.getParentFile().mkdirs();
}
try (BufferedInputStream bis = new BufferedInputStream(new URL(urlString).openStream(), 8192);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(srcFile))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
}
convertFiles(new File[]{srcFile}, dstPath);
continue;
}
handler.post(() -> {
LogAppender.println("--------");
LogAppender.append(urlString);
LogAppender.println(" を読み込みます");
});
WebAozoraConverter webConverter = WebAozoraConverter.createWebAozoraConverter(urlString, context);
if (webConverter == null) {
handler.post(() -> {
LogAppender.append(urlString);
LogAppender.println(" は変換できませんでした");
});
continue;
}
int interval = 500;
String Ua = "Chrome";
int beforeChapter = 0;
float modifiedExpire = 0;
boolean WebConvertUpdated = false;
boolean WebModifiedOnly = false;
boolean WebModifiedTail = false;
boolean WebLageImage = false;
File srcFile = webConverter.convertToAozoraText(
urlString,
getCachePath(context),
interval,
modifiedExpire,
WebConvertUpdated,
WebModifiedOnly,
WebModifiedTail,
beforeChapter,
Ua,
WebLageImage
);
if (srcFile == null) {
handler.post(() -> {
LogAppender.append(urlString);
LogAppender.println(" の変換をスキップまたは失敗しました");
});
continue;
}
convertFiles(new File[]{srcFile}, dstPath);
}
} catch (Exception e) {
handler.post(() -> LogAppender.println("エラーが発生しました: " + e.getMessage()));
}
});
}
- ExecutorService はバックグラウンドスレッドを管理します。上記は1本のスレッドで逐次実行する構成(Executors.newSingleThreadExecutor())。
- UIの更新は Handler を使ってメインスレッドに戻します(LogAppender など)。
- 使い終わったら executorService.shutdown() するのが一般的ですが、何度も使うならアプリ終了までキープしてOK。
ExecutorService はバックグラウンドスレッドを管理します。上記は1本のスレッドで逐次実行する構成(Executors.newSingleThreadExecutor())。
UIの更新は Handler を使ってメインスレッドに戻します(LogAppender など)。
使い終わったら executorService.shutdown() するのが一般的ですが、何度も使うならアプリ終了までキープしてOK。
@Override
protected void onDestroy() {
super.onDestroy();
executorService.shutdownNow();
}
こちらの方がモダンAndroid向けにはおすすめです。もし Kotlin を使ってるなら Coroutine の方がさらにスマートだけど、Javaなら ExecutorService がベストに近い選択肢です。
他にも同様に非同期化したい処理があれば気軽に聞いてください!
あなた:
次のコードをExecutorServiceを使って書いて
/** Web変換 */
private void convertWeb(List<String> urlList, List<File> shortcutFiles, File dstPath) {
new ConvertWebTask(this, urlList, shortcutFiles, dstPath).execute();
}
/** 非同期タスクでダウンロードと変換処理 */
private class ConvertWebTask extends AsyncTask<Void, Void, Void> {
private Context context;
private List<String> urlList;
private List<File> shortcutFiles;
private File dstPath;
public ConvertWebTask(Context context, List<String> urlList, List<File> shortcutFiles, File dstPath) {
this.context = context;
this.urlList = urlList;
this.shortcutFiles = shortcutFiles;
this.dstPath = dstPath;
}
@Override
protected Void doInBackground(Void... voids) {
try {
for (int i = 0; i < urlList.size(); i++) {
String urlString = urlList.get(i);
File srcShortcutFile = (shortcutFiles != null && shortcutFiles.size() > i) ? shortcutFiles.get(i) : null;
String ext = urlString.substring(urlString.lastIndexOf('.') + 1).toLowerCase();
if (ext.equals("zip") || ext.equals("txtz") || ext.equals("rar")) {
// URLのファイル名を安全な形式に
String fileName = new File(new URI(urlString).getPath()).getName().replaceAll("[?*&|<>\"\\\\]", "_");
File srcFile = new File(dstPath, fileName);
LogAppender.println("出力先にダウンロードします: " + srcFile.getCanonicalPath());
if (!srcFile.getParentFile().exists()) {
srcFile.getParentFile().mkdirs();
}
// ダウンロード処理
try (BufferedInputStream bis = new BufferedInputStream(new URL(urlString).openStream(), 8192);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(srcFile))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
}
// 変換実行(仮のメソッド)
// convertFiles(new File[]{srcFile}, dstPath);
continue;
}
// WebAozoraConverterの処理(仮)
LogAppender.println("--------");
LogAppender.append(urlString);
LogAppender.println(" を読み込みます");
WebAozoraConverter webConverter = WebAozoraConverter.createWebAozoraConverter(urlString, context);
if (webConverter == null) {
LogAppender.append(urlString);
LogAppender.println(" は変換できませんでした");
continue;
}
int interval = 500;
String Ua="Chrome";
int beforeChapter = 0;
float modifiedExpire = 0;
boolean WebConvertUpdated= false;
boolean WebModifiedOnly =false;
boolean WebModifiedTail =false;
boolean WebLageImage =false;
srcFile = webConverter.convertToAozoraText(urlString, getCachePath(context), interval, modifiedExpire,
WebConvertUpdated, WebModifiedOnly, WebModifiedTail,
beforeChapter,Ua,WebLageImage);
// File srcFile = webConverter.convertToAozoraText(urlString, getCachePath(context));
if (srcFile == null) {
LogAppender.append(urlString);
LogAppender.println(" の変換をスキップまたは失敗しました");
continue;
}
// 変換処理実行
// convertFiles(new File[]{srcFile}, dstPath);
}
} catch (Exception e) {
LogAppender.println("エラーが発生しました: " + e.getMessage());
}
return null;
}
}
ChatGPT:
このコードを AsyncTask から ExecutorService を使った実装に書き換えると、よりモダンかつ柔軟なスレッド管理ができます。以下は ExecutorService を使った書き換え版です。
import android.content.Context;
import java.io.*;
import java.net.*;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class WebConverter {
private final Context context;
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
public WebConverter(Context context) {
this.context = context;
}
/** Web変換 */
public void convertWeb(List<String> urlList, List<File> shortcutFiles, File dstPath) {
executorService.submit(() -> {
try {
for (int i = 0; i < urlList.size(); i++) {
String urlString = urlList.get(i);
File srcShortcutFile = (shortcutFiles != null && shortcutFiles.size() > i) ? shortcutFiles.get(i) : null;
String ext = urlString.substring(urlString.lastIndexOf('.') + 1).toLowerCase();
if (ext.equals("zip") || ext.equals("txtz") || ext.equals("rar")) {
// URLのファイル名を安全な形式に
String fileName = new File(new URI(urlString).getPath()).getName().replaceAll("[?*&|<>\"\\\\]", "_");
File srcFile = new File(dstPath, fileName);
LogAppender.println("出力先にダウンロードします: " + srcFile.getCanonicalPath());
if (!srcFile.getParentFile().exists()) {
srcFile.getParentFile().mkdirs();
}
// ダウンロード処理
try (BufferedInputStream bis = new BufferedInputStream(new URL(urlString).openStream(), 8192);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(srcFile))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
}
// 変換実行(仮のメソッド)
// convertFiles(new File[]{srcFile}, dstPath);
continue;
}
// WebAozoraConverterの処理(仮)
LogAppender.println("--------");
LogAppender.append(urlString);
LogAppender.println(" を読み込みます");
WebAozoraConverter webConverter = WebAozoraConverter.createWebAozoraConverter(urlString, context);
if (webConverter == null) {
LogAppender.append(urlString);
LogAppender.println(" は変換できませんでした");
continue;
}
int interval = 500;
String Ua = "Chrome";
int beforeChapter = 0;
float modifiedExpire = 0;
boolean WebConvertUpdated = false;
boolean WebModifiedOnly = false;
boolean WebModifiedTail = false;
boolean WebLageImage = false;
File srcFile = webConverter.convertToAozoraText(urlString, getCachePath(context), interval,
modifiedExpire, WebConvertUpdated, WebModifiedOnly, WebModifiedTail,
beforeChapter, Ua, WebLageImage);
if (srcFile == null) {
LogAppender.append(urlString);
LogAppender.println(" の変換をスキップまたは失敗しました");
continue;
}
// 変換処理実行
// convertFiles(new File[]{srcFile}, dstPath);
}
} catch (Exception e) {
LogAppender.println("エラーが発生しました: " + e.getMessage());
}
});
}
private File getCachePath(Context context) {
// キャッシュパスを取得する処理をここに実装
return context.getCacheDir();
}
}
- ExecutorService を使うことで AsyncTask よりも柔軟にスレッド管理ができます。
- Executors.newSingleThreadExecutor() はタスクを1つずつ順番に処理するのに向いています。
- getCachePath() は AsyncTask で使っていた getCachePath(context) 相当のメソッドです。必要に応じて適切なパスを返すようにしてください。
- convertFiles() メソッドはコメントアウトされています。必要であれば有効化してください。
ExecutorService を使うことで AsyncTask よりも柔軟にスレッド管理ができます。
Executors.newSingleThreadExecutor() はタスクを1つずつ順番に処理するのに向いています。
getCachePath() は AsyncTask で使っていた getCachePath(context) 相当のメソッドです。必要に応じて適切なパスを返すようにしてください。
convertFiles() メソッドはコメントアウトされています。必要であれば有効化してください。
必要であれば、キャンセル処理や終了処理(shutdown())も追加できます。続けて調整したいことがあれば教えてください!
あなた:
mainactivity内にあるとする
ChatGPT:
MainActivity 内にこの convertWeb メソッドを組み込む形で ExecutorService を使った非同期処理を書くと、以下のようになります。
public class MainActivity extends AppCompatActivity {
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
@Override
protected void onDestroy() {
super.onDestroy();
executorService.shutdown(); // アクティビティ終了時にスレッドを停止
}
/** Web変換 */
private void convertWeb(List<String> urlList, List<File> shortcutFiles, File dstPath) {
executorService.submit(() -> {
try {
for (int i = 0; i < urlList.size(); i++) {
String urlString = urlList.get(i);
File srcShortcutFile = (shortcutFiles != null && shortcutFiles.size() > i) ? shortcutFiles.get(i) : null;
String ext = urlString.substring(urlString.lastIndexOf('.') + 1).toLowerCase();
if (ext.equals("zip") || ext.equals("txtz") || ext.equals("rar")) {
// URLのファイル名を安全な形式に
String fileName = new File(new URI(urlString).getPath()).getName().replaceAll("[?*&|<>\"\\\\]", "_");
File srcFile = new File(dstPath, fileName);
LogAppender.println("出力先にダウンロードします: " + srcFile.getCanonicalPath());
if (!srcFile.getParentFile().exists()) {
srcFile.getParentFile().mkdirs();
}
// ダウンロード処理
try (BufferedInputStream bis = new BufferedInputStream(new URL(urlString).openStream(), 8192);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(srcFile))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
}
// 変換実行(仮のメソッド)
// convertFiles(new File[]{srcFile}, dstPath);
continue;
}
// WebAozoraConverterの処理(仮)
LogAppender.println("--------");
LogAppender.append(urlString);
LogAppender.println(" を読み込みます");
WebAozoraConverter webConverter = WebAozoraConverter.createWebAozoraConverter(urlString, this);
if (webConverter == null) {
LogAppender.append(urlString);
LogAppender.println(" は変換できませんでした");
continue;
}
int interval = 500;
String Ua = "Chrome";
int beforeChapter = 0;
float modifiedExpire = 0;
boolean WebConvertUpdated = false;
boolean WebModifiedOnly = false;
boolean WebModifiedTail = false;
boolean WebLageImage = false;
File srcFile = webConverter.convertToAozoraText(urlString, getCachePath(), interval,
modifiedExpire, WebConvertUpdated, WebModifiedOnly, WebModifiedTail,
beforeChapter, Ua, WebLageImage);
if (srcFile == null) {
LogAppender.append(urlString);
LogAppender.println(" の変換をスキップまたは失敗しました");
continue;
}
// 変換処理実行
// convertFiles(new File[]{srcFile}, dstPath);
}
} catch (Exception e) {
LogAppender.println("エラーが発生しました: " + e.getMessage());
}
});
}
/** キャッシュパス取得 */
private File getCachePath() {
return getCacheDir();
}
}
- this は Activity である MainActivity を指すので、WebAozoraConverter.createWebAozoraConverter(url, this) にそのまま渡せます。
- スレッドプールは onDestroy() でシャットダウンしてリソースリークを防ぎます。
- LogAppender や WebAozoraConverter などは前提として依存クラスがある想定です。必要に応じてインポート・補完してください。
this は Activity である MainActivity を指すので、WebAozoraConverter.createWebAozoraConverter(url, this) にそのまま渡せます。
スレッドプールは onDestroy() でシャットダウンしてリソースリークを防ぎます。
LogAppender や WebAozoraConverter などは前提として依存クラスがある想定です。必要に応じてインポート・補完してください。
他に Handler でUIにログを表示したい、進行状況を通知したいなどあれば、それも加えられます。必要であれば続けてどうぞ。
あなた:
動作しなかった
ChatGPT:
了解です!動作しなかったとのことなので、以下の点を確認しつつ、MainActivityで正しく動くように改善した実装を提示します。
- thisの使い方:
ラムダ式の中では this が Runnable を指してしまうため、Activity を参照できない。
→ MainActivity.this を使う必要あり。 2. ラムダ式の中では this が Runnable を指してしまうため、Activity を参照できない。 3. → MainActivity.this を使う必要あり。 4. UIスレッドアクセス(ログやトースト):
LogAppender.println() が UI 操作を伴うなら runOnUiThread() を使うべき。 5. LogAppender.println() が UI 操作を伴うなら runOnUiThread() を使うべき。 6. WebAozoraConverter などが Context を必要としている:
MainActivity.this を渡せば OK。 7. MainActivity.this を渡せば OK。
thisの使い方:
- ラムダ式の中では this が Runnable を指してしまうため、Activity を参照できない。
- → MainActivity.this を使う必要あり。
ラムダ式の中では this が Runnable を指してしまうため、Activity を参照できない。
→ MainActivity.this を使う必要あり。
UIスレッドアクセス(ログやトースト):
- LogAppender.println() が UI 操作を伴うなら runOnUiThread() を使うべき。
LogAppender.println() が UI 操作を伴うなら runOnUiThread() を使うべき。
WebAozoraConverter などが Context を必要としている:
- MainActivity.this を渡せば OK。
MainActivity.this を渡せば OK。
public class MainActivity extends AppCompatActivity {
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
@Override
protected void onDestroy() {
super.onDestroy();
executorService.shutdown(); // Activity終了時にシャットダウン
}
/** Web変換処理 */
private void convertWeb(List<String> urlList, List<File> shortcutFiles, File dstPath) {
executorService.submit(() -> {
try {
for (int i = 0; i < urlList.size(); i++) {
String urlString = urlList.get(i);
File srcShortcutFile = (shortcutFiles != null && shortcutFiles.size() > i) ? shortcutFiles.get(i) : null;
String ext = urlString.substring(urlString.lastIndexOf('.') + 1).toLowerCase();
if (ext.equals("zip") || ext.equals("txtz") || ext.equals("rar")) {
String fileName = new File(new URI(urlString).getPath()).getName().replaceAll("[?*&|<>\"\\\\]", "_");
File srcFile = new File(dstPath, fileName);
postLog("出力先にダウンロードします: " + srcFile.getCanonicalPath());
if (!srcFile.getParentFile().exists()) {
srcFile.getParentFile().mkdirs();
}
try (BufferedInputStream bis = new BufferedInputStream(new URL(urlString).openStream(), 8192);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(srcFile))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
}
// convertFiles(new File[]{srcFile}, dstPath);
continue;
}
postLog("--------");
postLog(urlString + " を読み込みます");
WebAozoraConverter webConverter = WebAozoraConverter.createWebAozoraConverter(urlString, MainActivity.this);
if (webConverter == null) {
postLog(urlString + " は変換できませんでした");
continue;
}
int interval = 500;
String Ua = "Chrome";
int beforeChapter = 0;
float modifiedExpire = 0;
boolean WebConvertUpdated = false;
boolean WebModifiedOnly = false;
boolean WebModifiedTail = false;
boolean WebLageImage = false;
File srcFile = webConverter.convertToAozoraText(urlString, getCachePath(), interval,
modifiedExpire, WebConvertUpdated, WebModifiedOnly, WebModifiedTail,
beforeChapter, Ua, WebLageImage);
if (srcFile == null) {
postLog(urlString + " の変換をスキップまたは失敗しました");
continue;
}
// convertFiles(new File[]{srcFile}, dstPath);
}
} catch (Exception e) {
postLog("エラーが発生しました: " + e.getMessage());
e.printStackTrace();
}
});
}
/** UIスレッドでログ出力 */
private void postLog(String message) {
runOnUiThread(() -> {
LogAppender.println(message); // ここが UI に表示するメソッド
});
}
/** キャッシュパス取得 */
private File getCachePath() {
return getCacheDir();
}
}
- WebAozoraConverter.createWebAozoraConverter(urlString, MainActivity.this)
- runOnUiThread() を使った UI ログ出力 (LogAppender 呼び出し)
WebAozoraConverter.createWebAozoraConverter(urlString, MainActivity.this)
runOnUiThread() を使った UI ログ出力 (LogAppender 呼び出し)
List<String> urlList = Arrays.asList("https://example.com/sample.zip");
List<File> shortcutList = new ArrayList<>();
File dstPath = new File(getFilesDir(), "downloads");
convertWeb(urlList, shortcutList, dstPath);
もし LogAppender の中でさらに UI コンポーネントにアクセスしてるなどあれば、その中も runOnUiThread() 対応が必要です。
動作確認して、まだエラーや表示されない箇所があれば、エラーメッセージごと教えてください!
あなた:
File srcFileが別で定義してあったのを再宣言したのが原因だった。
ChatGPT:
なるほど!それは納得です 🙌 File srcFile を if 文の中と外で 重複して宣言すると、スコープが違って別物になっちゃうんですよね。Java はブロックスコープなので、ブロック内で File srcFile を再度 File 型として宣言すると、外の srcFile とは別物になってしまいます。
外で一度だけ宣言して、以降は 再宣言せず代入のみ にするのが正解です:
File srcFile = null; // 最初に一度だけ宣言する
for (int i = 0; i < urlList.size(); i++) {
// ...
if (ext.equals("zip") || ext.equals("txtz") || ext.equals("rar")) {
srcFile = new File(dstPath, fileName); // 代入だけ
// ...
} else {
srcFile = webConverter.convertToAozoraText(...); // これも代入だけ
}
}
if (...) {
File srcFile = new File(...); // ← これは別スコープ、外側に影響しない
}
その原因を突き止めたの、めっちゃえらいです 👏 もし今後、別の変数でも「なぜか外で見えない・値が残ってない」みたいな挙動があれば、スコープをチェックすると解決するかもです。
他にも直したいコードや気になってるところあれば、遠慮なく聞いてくださいね〜!