読者です 読者をやめる 読者になる 読者になる

debugする時に追加している記述の備忘録(2)

android gradle MockWebServer okhttp

はじめに

前回までの話

テスト用に通信鯖をandroid内部に立てたい

キッカケ的な話

の話と絡むのですが、

  • AS 2.3-Beta3 InstantRun
    • OFFれば確かに問題ないが、使わない前提だとAS Beta2 以降激遅
  • Charles Proxyのライセンス絡みのスクリーンロック*1

と重なると開発端末が固まるのですorz *2

なにげに結構ストレスが溜まるのでで、なんとかならないかと思って調べた

ほしいもの

  • テスト用のAndroid組み込みサーバーがほしい
  • assetとかに置いたjsonファイルでレスポンス返したい
    • Charles Proxy の Map Local ライク!
  • できれば、httpsのテストがしたい
    • MockWebServer::useHttps で可能?

MockWebServerを使うのが現実でないかと

できなかったこと/まだ調べきれてないこと

  • MockWebServer::useHttps で証明書をセット
    • どうしても証明書関係のエラーが出て、通信ができない*3
    • 現状は諦めて httpに変更して使う
  • 画像等のバイナリを返すレスポンス

実装的な話

  • app/build.gradle
dependencies {
    compile 'com.squareup.okhttp3:okhttp:3.5.0'
    debugCompile 'com.squareup.okhttp3:mockwebserver:3.5.0'
}

okhttp と mockwebserver のバージョンは揃えること!!

  • app/debug/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="hoge.fuga.myapp">

    <application
        android:name=".application.DebugHogeApplication"
        tools:replace="android:name" />
</manifest>
public class RestUtil{

    //URLのSCHEMEの定数
    public static String SCHEME_HTTPS = "https";
    public static String SCHEME_HTTP  = "http";
    

    public static String BASE_SCHEME = "https";
    public static String BASE_HOST = "www.hoge.com";
    public static int BASE_PORT = 443;

    //カスタムURL用
     public static HttpUrl.Builder createBaseUrl(){

        HttpUrl.Builder builder = new HttpUrl.Builder()
            .scheme(BASE_SCHEME)
            .host(BASE_HOST)
            .port(BASE_PORT)   
            .addPathSegment("api")
            .addPathSegment("v1")
            .addQueryParameter("sensor","false");

        return builder;
    }
    
    public static void getApiA(String address, AsyncOkHttpClient.Callback callback){
        HttpUrl.Builder builder = createBaseUrl();
        builder.addQueryParameter("address","address");

        RestService(context).get(builder.build(), null, callback);
    }
  • app/debug/java/DebugHogeApplication.java
public class DebugHogeApplication extends HogeApplication {
    private MockWebServer server;

    @Override
    public void onCreate() {
        super.onCreate();
        
        //StricModeでNW通信を許可する◎
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectNetwork()
                .build());

        initServer();
        
        //StricModeを元に戻す◎
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectAll()
                .build());
    }

     @Override
     public void finalize(){
        super.onCreate();
        shutdown();
     }

     @Override
     public void  onTerminate(){
        super.onTerminate();
        shutdown();
     }
     
    private void shutdown(){
          if(server != null){
               try{
                    server.shutdown();
               }catch (Exception e){
               }
          }
     }
    
    private void initServer(){
        server = new MockWebServer();

        Dispatcher dispatcher = new Dispatcher() {

            @Override
            public MockResponse dispatch(final RecordedRequest request) throws InterruptedException {
                if (request == null || request.getPath() == null) {
                    return new MockResponse().setResponseCode(400);
                }
                
                MockResponse res = new MockResponse()
                                        .addHeader("Content-Type", "application/json") 
                                        .addHeader("charset", "utf-8") 
                                        .setResponseCode(200);
                String path = request.getPath();

                switch (path) {
                    case RestUtil.BASE_MAIN_URL + "/hoge":
                        return res.setBody(readJson("hoge.json"));
                    case RestUtil.BASE_MAIN_URL + "/fuga":
                        return res.setBody(readJson("fuga.json"));
                    default:
                        return new MockResponse().setResponseCode(404);
                }
            }
        };

        server.setDispatcher(dispatcher);

        try{
            server.start();
            RestUtil.BASE_SCHEME = "http";
            RestUtil.BASE_HOST = server.getHost();
            RestUtil.BASE_PORT = server.getPort();
        }catch(Exception ex){
                        shutdown();
            server = null;     
        }
    }
    
    private String readJson(String filenme){
        InputStream is = null;
        BufferedReader br = null;
        String json = "";    
    
        try {
            AssetManager as = getApplicationContext().getResources().getAssets();   
            is = as.open(filename);  
            br = new BufferedReader(new InputStreamReader(is));

            // 1行ずつ読み込み、改行を付加する
            String str;
            while ((str = br.readLine()) != null) {
                json += str + "\n";
            }
        }catch(Exception e){
            return "";
        } finally {
            if (is != null) is.close();
            if (br != null) br.close();
        }   
        return json;
    }
}   
  • ◎に関して
    • 4系の初期頃は、Applicationクラスでの StrictModeの変更が正常に動かない事があったらしい。*4
    • 其の場合は、下記の関数で囲むと良いとのこと
 //Message Queueの先頭にRunnableを渡す
 new Handler().postAtFrontOfQueue(
                new Runnable() {
                    @Override
                    public void run() {
                        //StrickModeの記述
                    }
                }
        );

参考:StrickMode

*1:まあフリーで使うほうが悪いのですが・・

*2:ここらへんTw等で言及すると、開発PCとして良いMacPro買えとかいう話になりますが、Appleさんつい最近不具合出したばかりですしね<汗

*3:java鯖で最適な証明書セットの知識が必要。でもその手の情報あまり出てこないんですよね><

*4:5系以降では問題ないよう = InstantRun最低環境だから普通は記述無くても気づかない?