Android 통신 - Sizuha/devdog GitHub Wiki

네트워크가 사용 가능한지 확인

    // ネットワークの接続を確認
    public static boolean networkCheck(Context context){
        final ConnectivityManager cm =  (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (cm == null) return false;

        final NetworkInfo info = cm.getActiveNetworkInfo();
        if (info != null ) {
            return info.isConnected();
        } 
        else {
            return false;
        }
    }

전화번호 가져오기

// <uses-permission android:name="android.permission.READ_PHONE_STATE" />
TelephonyManager tMgr = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
String mPhoneNumber = tMgr.getLine1Number();

HTTP

네트워크 통신을 메인 스레드에서 처리하면 런타임에 android.os.NetworkOnMainThreadException 예외가 발생한다. 네트워크 통신은 반드시 AsyncTask 등을 이용해서 비동기로 처리해야 한다.

Apache HTTP Client

Android 6에서 Apache HTTP Client 라이브러리가 제거되었다. 대신 [https://developer.android.com/reference/java/net/HttpURLConnection.html| HttpURLConnection] 을 권장하고 있다.

그래도 계속 사용하고 싶다면, App/build.gradle 파일의 첫 부분을 다음과 같이 수정한다.

apply plugin: 'com.android.application'

android {
    useLibrary 'org.apache.http.legacy'

...

HttpURLConnection

public class NetUtil {

    public interface IOnHttpTaskEvent {
        void onResponse(Response response);
    }

    public static class Response {
        public boolean succeed;
        public String error;
        public String content;
        private JSONObject json = null;

        public JSONObject getJSONObject() {
            if (json != null) return json;

            if (content != null) {
                try {
                    json = new JSONObject(content);
                    return json;
                }
                catch (JSONException e) {
                    error = e.toString();
                    succeed = false;
                }
            }

            return json = new JSONObject();
        }
    }

    public static Response http_get(String url, Map<String,String> params) {
        return http_request(url, params, RequestMethod.GET);
    }

    public static Response http_post(String url, Map<String,String> params) {
        return http_request(url, params, RequestMethod.POST);
    }

    public static Response http_post(String url, String post_params) {
        return http_request_raw(url, post_params, RequestMethod.POST);
    }

    public static Response http_post(String url, JSONObject json) {
        return http_request_raw(url, json.toString(), RequestMethod.POST);
    }

    public enum RequestMethod {
        GET(0),
        POST(1),
        DELETE(2);

        int method;

        RequestMethod(int method_code) {
            method = method_code;
        }

        public String toString() {
            switch (method) {
                case 1: return "POST";
                case 2: return "DELETE";
                default: return "GET";
            }
        }

        public boolean is_post() {
            return method == 1;
        }

    }

    public static String mapToString(Map<String,String> params) {
        if (params == null) return null;

        String param_data = "";
        boolean first = true;

        for (Map.Entry<String,String> kv: params.entrySet()) {
            try {
                param_data += (first ? "" : "&") +
                        URLEncoder.encode(kv.getKey(), "UTF-8") + "=" + URLEncoder.encode(kv.getValue(), "UTF-8");

                first = false;
            }
            catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }

        return param_data;
    }


    public static Response http_request(String url, Map<String,String> params, RequestMethod request_method) {
        return http_request_raw(url, mapToString(params), request_method);
    }

    protected static Response http_request_raw(String url, String param_str, RequestMethod request_method) {
        final Response response = new Response();
        HttpURLConnection urlConn = null;

        try {
            URL _url;

            if (request_method.is_post()) {
                _url = new URL(url);
            }
            else if (param_str != null && param_str.length() > 0) {
                _url = new URL(url + "?" + param_str);
            }
            else {
                _url = new URL(url);
            }

            // for DEBUG
            Logger.d("http_request_raw[URL]: " + _url.toString());

            urlConn = (HttpURLConnection) _url.openConnection();
            urlConn.setReadTimeout(60000);
            urlConn.setConnectTimeout(60000);
            urlConn.setDoInput(true);
            urlConn.setDoOutput(request_method.is_post());
            urlConn.setRequestMethod(request_method.toString());

            if (param_str != null && param_str.length() > 0 && request_method.is_post()) {
                final OutputStream os = urlConn.getOutputStream();
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
                writer.write(param_str);
                writer.flush();
                writer.close();
                os.close();
            }

            urlConn.connect();

            // HTTPレスポンスコード
            final int status = urlConn.getResponseCode();
            response.succeed = status == HttpURLConnection.HTTP_OK;

            if (response.succeed) {
                // 取得したテキストを格納する変数
                final StringBuilder result = new StringBuilder();

                // 通信に成功した
                // テキストを取得する
                final InputStream in = urlConn.getInputStream();

                String encoding = urlConn.getContentEncoding();
                Logger.d("encoding: " + encoding);
                if (encoding == null || encoding.length() < 1) {
                    encoding = "UTF-8";
                }

                final InputStreamReader inReader = new InputStreamReader(in, encoding);
                final BufferedReader bufReader = new BufferedReader(inReader);

                String line;
                // 1行ずつテキストを読み込む
                while ((line = bufReader.readLine()) != null) {
                    result.append(line);
                }

                bufReader.close();
                inReader.close();
                in.close();

                response.content = result.toString();

                // for DEBUG
                Logger.d("http_request_raw[response.content]: " + response.content);
            }
            else {
                response.error = String.valueOf(status);

                // for DEBUG
                Logger.d("http_request_raw[response.error]: " + response.error);
            }
        }
        catch (MalformedURLException e) {
            e.printStackTrace();
            response.succeed = false;
            response.error = e.toString();
        }
        catch (IOException e) {
            e.printStackTrace();
            response.succeed = false;
            response.error = e.toString();
        }
        finally {
            if (urlConn != null) urlConn.disconnect();
        }

        return response;
    }


    public static class HttpAsyncTask extends AsyncTask<String, Void, Response> {
        private String request_params;
        private IOnHttpTaskEvent eventRecv;
        private RequestMethod method = RequestMethod.GET;

        public HttpAsyncTask setMethod(RequestMethod request_method) {
            method = request_method;
            return this;
        }

        public HttpAsyncTask setParams(Map<String,String> params) {
            request_params = mapToString(params);
            return this;
        }

        public HttpAsyncTask setParams(JSONObject params) {
            request_params = params.toString();
            method = RequestMethod.POST;
            return this;
        }

        public HttpAsyncTask setListener(IOnHttpTaskEvent listener) {
            eventRecv = listener;
            return this;
        }


        @Override
        protected Response doInBackground(String... params) {
            String url = null;
            for (String p: params) {
                url = p;
                break;
            }

            Logger.d("request params: " + request_params);
            return http_request_raw(url, request_params, method);
        }

        @Override
        protected void onPostExecute(Response response) {
            if (eventRecv != null)
                eventRecv.onResponse(response);
        }
    }

}

파일 내려받기

public class FileDownloader extends AsyncTask<String, String, Integer> {

    final static int BUFFER_SIZE = 4096;

    private String storagePath = "";
    public void setStagePath(String path) {
        storagePath = path;
    }

    public static interface IOnDownloadEvent {
        void onOneFileDownloadEnd(String filenameWithPath);
        void onDownloadTotalEnd(int download_count);
    }

    public IOnDownloadEvent downloadEventRecv;

    public boolean overwrite = false;


    @Override
    protected Integer doInBackground(String... urls) {
        int dn_cnt = 0;

        for (String url_str : urls) {
            // Escape early if cancel() is called
            if (isCancelled()) break;

            File outFile = null;

            try {
                URL url = new URL(url_str);

                final String out_filename = URLUtil.guessFileName(url_str, null, null);
                final String out_filenameWithPath = Environment.getExternalStorageDirectory().toString() +
                        "/" + storagePath + "/" + out_filename;

                outFile = new File(out_filenameWithPath);
                if (!overwrite &&outFile.exists()) continue;

                URLConnection connection = url.openConnection();
                connection.connect();

                outFile.mkdirs();

                InputStream input = new BufferedInputStream(url.openStream(), BUFFER_SIZE);
                OutputStream output = new FileOutputStream(outFile);

                byte data[] = new byte[BUFFER_SIZE];

                int read_count;
                while ((read_count = input.read(data)) != -1) {
                    output.write(data, 0, read_count);
                    dn_cnt++;
                }

                output.flush();
                output.close();
                input.close();

                if (downloadEventRecv != null) downloadEventRecv.onOneFileDownloadEnd(out_filenameWithPath);
            }
            catch (MalformedURLException e) {
                e.printStackTrace();
                continue;
            }
            catch (IOException e) {
                e.printStackTrace();
                if (outFile != null) outFile.delete();

                continue;
            }
        }

        return dn_cnt;
    }

    @Override
    protected void onPostExecute(Integer dnCnt) {
        if (downloadEventRecv != null) downloadEventRecv.onDownloadTotalEnd(dnCnt);

        super.onPostExecute(dnCnt);
    }
}

다운 받기 전에 파일 크기 알아내기

URL url = new URL("http://server.com/file.mp3");
URLConnection urlConnection = url.openConnection();
urlConnection.connect();
int file_size = urlConnection.getContentLength();

multipart/form-data

http://qiita.com/informationsea/items/778d9525c3aaded73577

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.util.Map;
import java.util.UUID;

/**
 * Copyright (C) 2014 Yasunobu OKAMURA
 * MIT License
 */
public class HttpMultipartSender {
    private HttpMultipartSender(){} // cannot make instance

    // this function is implemented based on http://www.androidsnippets.com/multipart-http-requests
    public static void sendMultipart(HttpURLConnection connection, String filefield, File filepath, Map<String, String> textdata) throws IOException {

        final String twoHyphens = "--";
        final String boundary =  "*****"+ UUID.randomUUID().toString()+"*****";
        final String lineEnd = "\r\n";
        final int maxBufferSize = 1024*1024*3;

        DataOutputStream outputStream;

        connection.setDoInput(true);
        connection.setDoOutput(true);
        connection.setUseCaches(false);

        connection.setRequestMethod("POST");
        connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setRequestProperty("Content-Type", "multipart/form-data; boundary="+boundary);

        outputStream = new DataOutputStream(connection.getOutputStream());
        outputStream.writeBytes(twoHyphens + boundary + lineEnd);
        outputStream.writeBytes("Content-Disposition: form-data; name=\"" + filefield + "\"; filename=\"" + filepath.getName() +"\"" + lineEnd);
        outputStream.writeBytes("Content-Type: application/octet-stream" + lineEnd);
        outputStream.writeBytes("Content-Transfer-Encoding: binary" + lineEnd);
        outputStream.writeBytes(lineEnd);

        FileInputStream fileInputStream = new FileInputStream(filepath);
        int bytesAvailable = fileInputStream.available();
        int bufferSize = Math.min(bytesAvailable, maxBufferSize);
        byte[] buffer = new byte[bufferSize];

        int bytesRead = fileInputStream.read(buffer, 0, bufferSize);
        while(bytesRead > 0) {
            outputStream.write(buffer, 0, bufferSize);
            bytesAvailable = fileInputStream.available();
            bufferSize = Math.min(bytesAvailable, maxBufferSize);
            bytesRead = fileInputStream.read(buffer, 0, bufferSize);
        }

        outputStream.writeBytes(lineEnd);

        for (Map.Entry<String, String> entry : textdata.entrySet()) {
            outputStream.writeBytes(twoHyphens + boundary + lineEnd);
            outputStream.writeBytes("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + lineEnd);
            outputStream.writeBytes("Content-Type: text/plain"+lineEnd);
            outputStream.writeBytes(lineEnd);
            outputStream.writeBytes(entry.getValue());
            outputStream.writeBytes(lineEnd);
        }

        outputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

        outputStream.close();
    }
}

SMS

필요한 퍼미션

<uses-permission android:name="android.permission.SEND_SMS" />

SmsManager API

SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage("phoneNo", null, "sms message", null, null);

Built-in SMS application

Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.putExtra("sms_body", "--- SMS 내용 ---"); 
sendIntent.setType("vnd.android-dir/mms-sms");
startActivity(sendIntent);

Email

protected void sendEmail() {
      Log.i("Send email", "");
      String[] TO = {""};
      String[] CC = {""};
      Intent emailIntent = new Intent(Intent.ACTION_SEND);
      
      emailIntent.setData(Uri.parse("mailto:"));
      emailIntent.setType("text/plain");
      emailIntent.putExtra(Intent.EXTRA_EMAIL, TO);
      emailIntent.putExtra(Intent.EXTRA_CC, CC);
      emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Your subject");
      emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message goes here");
      
      try {
         startActivity(Intent.createChooser(emailIntent, "Send mail..."));
         finish();
         Log.i("Finished sending email...", "");
      }
      catch (android.content.ActivityNotFoundException ex) {
         Toast.makeText(MainActivity.this, "There is no email client installed.", Toast.LENGTH_SHORT).show();
      }
}

URL Scheme

일단은 다음과 같은 intent 스킴을 이용하는 방법을 권장하고 있다.

   intent:
   HOST/URI-path // Optional host 
   #Intent; 
      package=[string]; 
      action=[string]; 
      category=[string]; 
      component=[string]; 
      scheme=[string]; 
   end; 

Example:

   <a href="intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;S.browser_fallback_url=http%3A%2F%2Fzxing.org;end"> Take a QR code </a>

간단히 iOS처럼 App Scheme 이름을 그대로 활용할 수도 있다.

    public static void openIntentByUrl(Context c, String url) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        intent.addCategory(Intent.CATEGORY_BROWSABLE);
        intent.setData(Uri.parse(url)); // ex) "bill2://test2?r=received"
        c.startActivity(intent);
    }