アプリからLINEにメッセージを送る手段として、LINE Notifyを使用する方法がありますが、ネット検索してもWEBアプリからの実装コード例は多く見つかりますが、androidアプリからの実装コード例が、ほぼほぼ見つかりませんでした。
この記事では、わずかながらの参考記事を元に試行錯誤を繰り返し、androidアプリとLINEとの連携を実現した内容について解説します。
この記事を読むと以下のことができるようになります。
・androidアプリから画像付きのメッセージがLINEに届く。以下はそのイメージ図。
アプリとLINEの連携を行うにはコード実装と人手設定を行う必要があります。設定と実装に分けて解説します。
設定
- ステップ1LINE Notify公式ページにログイン
- ステップ2アクセストークンの発行
- ステップ3LINE Notifyを友だち追加
手順に関しては、以下の記事が参考になります。
実装
実装にあたり、参考にしたのがLINEの公式ドキュメント「LINE Notify API Document」です。一読すると記事の理解が深まります。以下の表はドキュメントの抜粋になりますが、気になった点をコメントします。
リクエストメソッド/ヘッダ | 値 |
---|---|
Method | POST |
Content-Type | application/x-www-form-urlencoded OR multipart/form-data |
Authorization | Bearer <access_token> |
Content-Typeは「multipart/form-data」も使用可能とのことですが、ファイル送信する想定が無かったので試していません。
パラメータ名 | 必須 | 型 | 説明 |
---|---|---|---|
message | 必須 | String | 最大 1000文字 |
imageThumbnail | 省略可能 | HTTP/HTTPS URL | 最大 240×240px / JPEG のみ許可されます |
imageFullsize | 省略可能 | HTTP/HTTPS URL | 最大 2048×2048px / JPEG のみ許可されます |
imageFile | 省略可能 | File | LINE上の画像サーバーにアップロードします。 対応している画像形式は、png, jpegです。 imageThumbnail/imageFullsizeと同時に指定された場合は、imageFileが優先されます。 1時間にuploadできる量に制限があります。 詳しくは、API Rate Limitの項を見てください。 |
stickerPackageId | 省略可能 | Number | パッケージ識別子。 Stickerの識別子は以下を参照ください。 Sticker一覧 |
stickerId | 省略可能 | Number | Sticker識別子 |
notificationDisabled | 省略可能 | Boolean | true: メッセージ送信時に、ユーザに通知されない。 false: メッセージ送信時に、ユーザに通知される。ただし、LINEで通知をオフにしている場合は通知されません。 デフォルト値は false です。 |
パラメータ名は「imageFile」「notificationDisabled」も指定可能とのことですが、想定に無かったので試していません。「stickerPackageId」「stickerId」は動作確認できましたが、実装においてLINEスタンプ表示は見送りました。
実装中に悩まされたのが「imageThumbnail」「imageFullsize」です。共に省略可能とあるので「message + imageThumbnail」指定、「message + imageFullsize」指定を試しましたが正常に動作せず、結局「message + imageThumbnail + imageFullsize」指定でうまく行きました。片方だけの省略はダメなようです。
又、公式では「imageThumbnail」「imageFullsize」で指定可能なファイルがJPEGのみとなっていますが、動作確認ではPNGでも問題ありませんでした。
リクエスト周りの実装コードはこんな感じです。
try {
URL url = new URL("https://notify-api.line.me/api/notify");
connection = (HttpURLConnection) url.openConnection();
// リクエストのボディ送信を許可
connection.setDoOutput(true);
// HTTPリクエストメソッド(POST)
connection.setRequestMethod("POST");
// リソースのメディア種別
connection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");
// HTTP認証ヘッダー
connection.addRequestProperty("Authorization", "Bearer " + token);
try (OutputStream os = connection.getOutputStream();
OutputStreamWriter out = new OutputStreamWriter(os)) {
out.append("message=").append(URLEncoder.encode(message, "UTF-8"));
// ラインスタンプ付与時 out.append("&stickerPackageId=1&stickerId=403");
out.append("&");
out.append("imageThumbnail=").append(URLEncoder.encode(image, "UTF-8"));
out.append("&");
out.append("imageFullsize=").append(URLEncoder.encode(image, "UTF-8"));
out.flush();
name | type | value description |
---|---|---|
status | number | HTTP ステータスコードに準拠した値 200: 成功時 400: リクエストが不正 401: アクセストークンが無効 |
message | string | 人間可読なメッセージです |
レスポンスは正常時、以下の内容で取得できます。
{“status”:200,”message”:”ok”}
レスポンス周りの実装コードはこんな感じです。
// レスポンスの受信
try (InputStream is = connection.getInputStream();
BufferedReader r = new BufferedReader(new InputStreamReader(is))) {
String res = r.lines().collect(Collectors.joining());
if (!res.contains("\"message\":\"ok\"")) {
Log.e("ERROR", "Response NG");
return res;
}
}
その他、異常系の処理の実装ですが、異常を想定した確認結果がこちらになります。
1.LINEアプリから「LINE Notify」を友だち登録削除した場合。
⇒ 処理が正常終了。LINEに通知無し。
2.LINE Notifyページからサービス連携解除した場合。
⇒ 処理が異常終了。例外検出(java.io.FileNotFoundException)
レスポンス異常となる場合が確認できませんでしたが、レスポンス異常、例外検出の両方に対応した処理(結果を画面出力)としました。
異常系周りの実装コードはこんな感じです。
ExecutorService ex = Executors.newSingleThreadExecutor();
Future<String> futureResult = ex.submit(new Comm(token, message, image));
try {
return futureResult.get(); // 非同期処理結果を返却。
} catch (Exception e) {
return e.getMessage(); // 例外検出内容を返却。
}
以上を踏まえて、動作確認できた実装コードを丸っと記載しておきます。実装の参考になれば幸いです。
処理側(LineNotify.java)
package <your package name>;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
class LineNotify {
public String notify (String token, String message, String image) {
ExecutorService ex = Executors.newSingleThreadExecutor();
Future<String> futureResult = ex.submit(new Comm(token, message, image));
try {
return futureResult.get(); // 非同期処理結果を返却。
} catch (Exception e) {
return e.getMessage(); // 例外検出内容を返却。
}
}
class Comm implements Callable<String> {
private final String token;
private final String message;
private final String image;
Comm(String token, String message, String image) {
this.token = token;
this.message = message;
this.image = image;
}
@Override
public String call() throws IOException {
HttpURLConnection connection = null;
try {
URL url = new URL("https://notify-api.line.me/api/notify");
connection = (HttpURLConnection) url.openConnection();
// リクエストのボディ送信を許可
connection.setDoOutput(true);
// HTTPリクエストメソッド(POST)
connection.setRequestMethod("POST");
// リソースのメディア種別
connection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");
// HTTP認証ヘッダー
connection.addRequestProperty("Authorization", "Bearer " + token);
try (OutputStream os = connection.getOutputStream();
OutputStreamWriter out = new OutputStreamWriter(os)) {
out.append("message=").append(URLEncoder.encode(message, "UTF-8"));
// ラインスタンプ付与時 out.append("&stickerPackageId=1&stickerId=403");
out.append("&imageThumbnail=").append(URLEncoder.encode(image, "UTF-8"));
out.append("&imageFullsize=").append(URLEncoder.encode(image, "UTF-8"));
out.flush();
// レスポンスの受信
try (InputStream is = connection.getInputStream();
BufferedReader r = new BufferedReader(new InputStreamReader(is))) {
String res = r.lines().collect(Collectors.joining());
if (!res.contains("\"message\":\"ok\"")) {
Log.e("ERROR", "Response NG");
return res;
}
}
}
} catch (Exception e) {
Log.e("ERROR", "Exception detected");
throw e;
} finally {
if (connection != null) {
connection.disconnect();
}
}
return "";
}
}
}
呼出側(抜粋)
String contents = <your message>;
String image = <your site> (例)"https://<your domain>/sample.png";
String token = <your access token>;
LineNotify classln = new LineNotify();
String ret = classln.notify(token, contents, image);
if (ret.equals("")) {
text.setText("正常終了しました。");
} else {
text.setText("異常終了しました。\n" + ret);
}
以上です。