├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── org │ │ └── quanqi │ │ └── androidnetworkdemo │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── org │ │ └── quanqi │ │ └── androidnetworkdemo │ │ ├── BitmapLruCache.java │ │ ├── ImageUtils.java │ │ ├── MainActivity.java │ │ ├── OkHttpStack.java │ │ ├── RequestManager.java │ │ └── SelfSignSslOkHttpStack.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── raw │ └── kyfw.bks │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── tools └── ssl ├── bcprov-jdk16-1.46.jar └── dump.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | 4 | # generated files 5 | bin/ 6 | gen/ 7 | out/ 8 | build/ 9 | 10 | # Local configuration file (sdk path, etc) 11 | local.properties 12 | 13 | # Eclipse project files 14 | .classpath 15 | .project 16 | 17 | # Windows thumbnail db 18 | .DS_Store 19 | Thumbs.db 20 | 21 | # Proguard folder generated by Eclipse 22 | proguard/ 23 | 24 | # IDEA/Android Studio project files, because 25 | # the project can be imported from settings.gradle 26 | .idea 27 | *.iml 28 | 29 | # Old-style IDEA project files 30 | *.ipr 31 | *.iws 32 | 33 | # Gradle cache 34 | .gradle 35 | 36 | # Sandbox stuff 37 | _sandbox 38 | 39 | # Backup files 40 | *~ 41 | *.bak 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android Network Demo 2 | 3 | This demo shows how to use volley with OkHttp and security your api with https. 4 | 本文同步发于[简书](http://www.jianshu.com/p/e58161cbc3a4) 5 | 6 | ## 代码结构说明 7 | * `tools` 文件夹下是相关的工具 `dumps.sh` 是一个导出证书并转换成 BKS 存储的完整脚本. 8 | * `MainActivity` 简单的调用了了一下相关实现进行验证. 9 | * `OkHttpStack` OkHttp 实现的 HttpStack 10 | * `SelfSignSslOkHttpStack` 检验自签名证书的 HttpStack 实现. 11 | 12 | ## 使用 OkHttp 作为传输层的实现. 13 | Volley 默认根据 Android 系统版本使用不同的 Http 传输协议实现. 3.0 以上使用HttpUrlConnection, 2.3 以下使用 ApacheHttpStack, 参考[Android Http Client]. 14 | 15 | OkHttp 相较于其它的实现有以下的优点. 16 | * 支持[SPDY](http://zh.wikipedia.org/wiki/SPDY),允许连接同一主机的所有请求分享一个socket。 17 | * 如果SPDY不可用,会使用连接池减少请求延迟。 18 | * 使用GZIP压缩下载内容,且压缩操作对用户是透明的。 19 | * 利用响应缓存来避免重复的网络请求。 20 | * 当网络出现问题的时候,OKHttp会依然有效,它将从常见的连接问题当中恢复。 21 | * 如果你的服务端有多个IP地址,当第一个地址连接失败时,OKHttp会尝试连接其他的地址,这对IPV4和IPV6以及寄宿在多个数据中心的服务而言,是非常有必要的。 22 | 23 | 因此使用 OkHttp 作为替代是好的选择. 24 | 25 | 首先用 OkHttp 实现一个新的 `HurlStack` 用于构建 Volley 的 requestQueue. 26 | 27 | ``` java 28 | public class OkHttpStack extends HurlStack { 29 | 30 | private OkHttpClient okHttpClient; 31 | 32 | /** 33 | * Create a OkHttpStack with default OkHttpClient. 34 | */ 35 | public OkHttpStack() { 36 | this(new OkHttpClient()); 37 | } 38 | 39 | /** 40 | * Create a OkHttpStack with a custom OkHttpClient 41 | * @param okHttpClient Custom OkHttpClient, NonNull 42 | */ 43 | public OkHttpStack(OkHttpClient okHttpClient) { 44 | this.okHttpClient = okHttpClient; 45 | } 46 | 47 | @Override 48 | protected HttpURLConnection createConnection(URL url) throws IOException { 49 | OkUrlFactory okUrlFactory = new OkUrlFactory(okHttpClient); 50 | return okUrlFactory.open(url); 51 | } 52 | } 53 | ``` 54 | 55 | 然后使用 OkHttpStack 创建新的 Volley requestQueue. 56 | ``` java 57 | requestQueue = Volley.newRequestQueue(getContext(), new OkHttpStack()); 58 | requestQueue.start(); 59 | ``` 60 | 这样就行了. 61 | 62 | # 使用 Https 63 | 作为一个有节操的开发者应该使用 Https 来保护用户的数据, Android 开发者网站上文章[Security with HTTPS and SSL]做了详尽的阐述. 64 | 65 | OkHttp 自身是支持 Https 的. 参考文档 [OkHttp Https], 直接使用上面的 `OkHttpStack` 就可以了, 但是如果遇到服务器开发哥哥使用了自签名的证书(不要问我为什么要用自签名的), 就无法正常访问了. 66 | 67 | 网上有很多文章给出的方案是提供一个什么事情都不做的`TrustManager` 跳过 `SSL` 的验证, 这样做很容受到攻击, Https 也就形同虚设了. 68 | 69 | 我采用的方案是将自签名的证书打包入 APK 加入信任. 70 | 71 | 好处: 72 | * 应用难以逆向, 应用不再依赖系统的 trust store, 使得 Charles 抓包等工具失效. 要分析应用 API 必须反编译 APK. 73 | * 不用额外购买证书, 省钱.... 74 | 缺点: 75 | * 证书部署灵活性降低, 一旦变更证书必须升级程序. 76 | 77 | ## 实现步骤 78 | 以最著名的自签名网站12306为例说明 79 | 80 | 1. 导出证书 81 | ``` 82 | echo | openssl s_client -connect kyfw.12306.cn:443 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > kyfw.12306.cn.pem 83 | ``` 84 | 85 | 1. 将证书转为 bks 格式 86 | 下载最新的bcprov-jdk, 执行下面的命令. storepass 是导出密钥文件的密码. 87 | ``` 88 | keytool -importcert -v \ 89 | -trustcacerts \ 90 | -alias 0 \ 91 | -file <(openssl x509 -in kyfw.12306.cn.pem) \ 92 | -keystore kyfw.bks -storetype BKS \ 93 | -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \ 94 | -providerpath ./bcprov-jdk16-1.46.jar \ 95 | -storepass asdfqaz 96 | ``` 97 | 98 | 1. 将导出的 kyfw.bks 文件放入 res/raw 文件夹下. 99 | 100 | 1. 创建 `SelfSignSslOkHttpStack` 101 | ``` 102 | /** 103 | * A HttpStack implement witch can verify specified self-signed certification. 104 | */ 105 | public class SelfSignSslOkHttpStack extends HurlStack { 106 | 107 | private OkHttpClient okHttpClient; 108 | 109 | private Map socketFactoryMap; 110 | 111 | /** 112 | * Create a OkHttpStack with default OkHttpClient. 113 | */ 114 | public SelfSignSslOkHttpStack(Map factoryMap) { 115 | this(new OkHttpClient(), factoryMap); 116 | } 117 | 118 | /** 119 | * Create a OkHttpStack with a custom OkHttpClient 120 | * @param okHttpClient Custom OkHttpClient, NonNull 121 | */ 122 | public SelfSignSslOkHttpStack(OkHttpClient okHttpClient, Map factoryMap) { 123 | this.okHttpClient = okHttpClient; 124 | this.socketFactoryMap = factoryMap; 125 | } 126 | 127 | @Override 128 | protected HttpURLConnection createConnection(URL url) throws IOException { 129 | if ("https".equals(url.getProtocol()) && socketFactoryMap.containsKey(url.getHost())) { 130 | HttpsURLConnection connection = (HttpsURLConnection) new OkUrlFactory(okHttpClient).open(url); 131 | connection.setSSLSocketFactory(socketFactoryMap.get(url.getHost())); 132 | return connection; 133 | } else { 134 | return new OkUrlFactory(okHttpClient).open(url); 135 | } 136 | } 137 | } 138 | ``` 139 | 140 | 1. 然后用 `SelfSignSslOkHttpStack` 创建 Volley 的 RequestQueue. 141 | 142 | ``` 143 | String[] hosts = {"kyfw.12306.cn"}; 144 | int[] certRes = {R.raw.kyfw}; 145 | String[] certPass = {"asdfqaz"}; 146 | socketFactoryMap = new Hashtable<>(hosts.length); 147 | 148 | for (int i = 0; i < certRes.length; i++) { 149 | int res = certRes[i]; 150 | String password = certPass[i]; 151 | SSLSocketFactory sslSocketFactory = createSSLSocketFactory(context, res, password); 152 | socketFactoryMap.put(hosts[i], sslSocketFactory); 153 | } 154 | 155 | HurlStack stack = new SelfSignSslOkHttpStack(socketFactoryMap); 156 | 157 | requestQueue = Volley.newRequestQueue(context, stack); 158 | requestQueue.start(); 159 | ``` 160 | 161 | 1. done 162 | 163 | [Volley]:http://developer.android.com/training/volley/index.html 164 | [OkHttp]:http://square.github.io/okhttp/ 165 | [Gson]:https://github.com/google/gson 166 | 167 | [Security with HTTPS and SSL]:https://developer.android.com/training/articles/security-ssl.html 168 | [OkHttp Https]:https://github.com/square/okhttp/wiki/HTTPS 169 | [Github dodocat/AndroidNetworkDemo]:https://github.com/dodocat/AndroidNetworkdemo 170 | [Android Http Client]:http://android-developers.blogspot.com/2011/09/androids-http-clients.html 171 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | applicationId "org.quanqi.androidnetworkdemo" 9 | minSdkVersion 14 10 | targetSdkVersion 22 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:22.2.0' 25 | 26 | compile 'com.squareup.okio:okio:1.5.0' 27 | compile 'com.squareup.okhttp:okhttp:2.4.0' 28 | compile 'com.squareup.okhttp:okhttp-urlconnection:2.4.0' 29 | 30 | compile 'com.mcxiaoke.volley:library:1.0.16' 31 | compile 'com.google.code.gson:gson:2.3.1' 32 | 33 | } 34 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/cindy/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/org/quanqi/androidnetworkdemo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package org.quanqi.androidnetworkdemo; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/org/quanqi/androidnetworkdemo/BitmapLruCache.java: -------------------------------------------------------------------------------- 1 | package org.quanqi.androidnetworkdemo; 2 | 3 | import android.graphics.Bitmap; 4 | import android.util.LruCache; 5 | 6 | import com.android.volley.toolbox.ImageLoader; 7 | 8 | public class BitmapLruCache extends LruCache implements ImageLoader.ImageCache { 9 | 10 | public BitmapLruCache(int maxSize) { 11 | super(maxSize); 12 | } 13 | 14 | @Override 15 | protected int sizeOf(String key, Bitmap bitmap) { 16 | return ImageUtils.getBitmapSize(bitmap); 17 | } 18 | 19 | @Override 20 | public Bitmap getBitmap(String url) { 21 | return get(url); 22 | } 23 | 24 | @Override 25 | public void putBitmap(String url, Bitmap bitmap) { 26 | put(url, bitmap); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/org/quanqi/androidnetworkdemo/ImageUtils.java: -------------------------------------------------------------------------------- 1 | package org.quanqi.androidnetworkdemo; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.ContentResolver; 5 | import android.content.res.Resources; 6 | import android.graphics.Bitmap; 7 | import android.graphics.Bitmap.Config; 8 | import android.graphics.BitmapFactory; 9 | import android.graphics.BitmapRegionDecoder; 10 | import android.graphics.Canvas; 11 | import android.graphics.Paint; 12 | import android.graphics.PorterDuff; 13 | import android.graphics.PorterDuffXfermode; 14 | import android.graphics.Rect; 15 | import android.graphics.drawable.Drawable; 16 | import android.net.Uri; 17 | import android.os.Build; 18 | import android.os.Build.VERSION; 19 | import android.provider.MediaStore; 20 | import android.text.TextUtils; 21 | import android.util.Log; 22 | 23 | import java.io.File; 24 | import java.io.FileOutputStream; 25 | import java.io.IOException; 26 | 27 | 28 | public class ImageUtils { 29 | 30 | private static final String TAG = "ImageUtils"; 31 | 32 | private static final int MAX_TEXTURE_SIZE = getOpengl2MaxTextureSize(); 33 | 34 | // A private constructor to hide the the implicit public one 35 | private ImageUtils() { 36 | 37 | } 38 | 39 | public static int getOpengl2MaxTextureSize() { 40 | int[] maxTextureSize = new int[1]; 41 | maxTextureSize[0] = 2048; 42 | android.opengl.GLES20.glGetIntegerv(android.opengl.GLES20.GL_MAX_TEXTURE_SIZE, 43 | maxTextureSize, 0); 44 | return maxTextureSize[0]; 45 | } 46 | 47 | /** 48 | * Get the size in bytes of a bitmap. 49 | * 50 | * @param bitmap 51 | * @return size in bytes 52 | */ 53 | @SuppressLint("NewApi") 54 | public static int getBitmapSize(Bitmap bitmap) { 55 | if (VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { 56 | return bitmap.getByteCount(); 57 | } 58 | // Pre HC-MR1 59 | return bitmap.getRowBytes() * bitmap.getHeight(); 60 | } 61 | 62 | /** 63 | * Decode and sample down a bitmap from resources to the requested width and 64 | * height. 65 | * 66 | * @param res The resources object containing the image data 67 | * @param resId The resource id of the image data 68 | * @param reqWidth The requested width of the resulting bitmap 69 | * @param reqHeight The requested height of the resulting bitmap 70 | * @return A bitmap sampled down from the original with the same aspect 71 | * ratio and dimensions that are equal to or greater than the 72 | * requested width and height(inMutable) 73 | */ 74 | public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, 75 | int reqHeight) { 76 | return decodeSampledBitmapFromResource(res, resId, reqWidth, reqHeight, false); 77 | } 78 | 79 | /** 80 | * Decode and sample down a bitmap from resources to the requested width and 81 | * height. 82 | * 83 | * @param res The resources object containing the image data 84 | * @param resId The resource id of the image data 85 | * @param reqWidth The requested width of the resulting bitmap 86 | * @param reqHeight The requested height of the resulting bitmap 87 | * @param isMutable 可编辑 88 | * @return A bitmap sampled down from the original with the same aspect 89 | * ratio and dimensions that are equal to or greater than the 90 | * requested width and height 91 | */ 92 | public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, 93 | int reqHeight, boolean isMutable) { 94 | 95 | // First decode with inJustDecodeBounds=true to check dimensions 96 | final BitmapFactory.Options options = new BitmapFactory.Options(); 97 | options.inJustDecodeBounds = true; 98 | BitmapFactory.decodeResource(res, resId, options); 99 | 100 | // Calculate inSampleSize 101 | options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); 102 | 103 | // Decode bitmap with inSampleSize set 104 | options.inJustDecodeBounds = false; 105 | 106 | if (isMutable && VERSION.SDK_INT >= 11) { 107 | options.inMutable = true; 108 | } 109 | Bitmap result = BitmapFactory.decodeResource(res, resId, options); 110 | if (isMutable) { 111 | result = createMutableBitmap(result); 112 | } 113 | return result; 114 | } 115 | 116 | public static Bitmap decodeSampledBitmapFromFile(String filePath, int sampledSize) { 117 | final BitmapFactory.Options options = new BitmapFactory.Options(); 118 | options.inJustDecodeBounds = true; 119 | BitmapFactory.decodeFile(filePath, options); 120 | 121 | // Calculate inSampleSize 122 | options.inSampleSize = sampledSize; 123 | 124 | // Decode bitmap with inSampleSize set 125 | options.inJustDecodeBounds = false; 126 | return BitmapFactory.decodeFile(filePath, options); 127 | } 128 | 129 | /** 130 | * Decode and sample down a bitmap from a file to the requested width and 131 | * height. 132 | * 133 | * @param filePath The full path of the file to decode 134 | * @param reqWidth The requested width of the resulting bitmap 135 | * @param reqHeight The requested height of the resulting bitmap 136 | * @return A bitmap sampled down from the original with the same aspect 137 | * ratio and dimensions that are equal to or greater than the 138 | * requested width and height(inmutable) 139 | */ 140 | public static Bitmap decodeSampledBitmapFromFile(String filePath, int reqWidth, int reqHeight) { 141 | return decodeSampledBitmapFromFile(filePath, reqWidth, reqHeight, false); 142 | } 143 | 144 | /** 145 | * Decode and sample down a bitmap from a file to the requested width and 146 | * height. 147 | * 148 | * @param filePath The full path of the file to decode 149 | * @param reqWidth The requested width of the resulting bitmap 150 | * @param reqHeight The requested height of the resulting bitmap 151 | * @param isMutable 可编辑 152 | * @return A bitmap sampled down from the original with the same aspect 153 | * ratio and dimensions that are equal to or greater than the 154 | * requested width and height 155 | */ 156 | public static Bitmap decodeSampledBitmapFromFile(String filePath, int reqWidth, int reqHeight, 157 | boolean isMutable) { 158 | if (filePath == null) { 159 | return null; 160 | } 161 | if (reqHeight == 0) { 162 | reqHeight = MAX_TEXTURE_SIZE; 163 | } 164 | if (reqWidth == 0) { 165 | reqWidth = MAX_TEXTURE_SIZE; 166 | } 167 | 168 | // First decode with inJustDecodeBounds=true to check dimensions 169 | final BitmapFactory.Options options = new BitmapFactory.Options(); 170 | options.inJustDecodeBounds = true; 171 | 172 | BitmapFactory.decodeFile(filePath, options); 173 | 174 | if (options.outWidth == -1 || options.outHeight == -1) { 175 | return null; 176 | } 177 | 178 | // Decode bitmap with inSampleSize set 179 | options.inJustDecodeBounds = false; 180 | options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); 181 | if (isMutable && VERSION.SDK_INT >= 11) { 182 | options.inMutable = true; 183 | } 184 | 185 | Bitmap result = null; 186 | 187 | if (options.outWidth > MAX_TEXTURE_SIZE || options.outHeight > MAX_TEXTURE_SIZE 188 | || (options.outHeight >= options.outWidth * 3)) { 189 | // 长图 190 | try { 191 | result = regionDecode(filePath, reqWidth, reqHeight, options.outWidth, 192 | options.outHeight); 193 | } catch (IOException e) { 194 | Log.e(TAG, Log.getStackTraceString(e)); 195 | } 196 | } else { 197 | result = BitmapFactory.decodeFile(filePath, options); 198 | } 199 | 200 | if (isMutable) { 201 | result = createMutableBitmap(result); 202 | } 203 | 204 | return result; 205 | } 206 | 207 | private static Bitmap regionDecode(String path, int reqWidth, int reqHeight, int outWidth, 208 | int outHeight) throws IOException { 209 | BitmapRegionDecoder regionDecoder = BitmapRegionDecoder.newInstance(path, true); 210 | if (reqWidth > outWidth) { 211 | reqWidth = outWidth; 212 | } 213 | if (reqHeight > outHeight) { 214 | reqHeight = outHeight; 215 | } 216 | 217 | return regionDecoder.decodeRegion(new Rect(0, 0, reqWidth, reqHeight), null); 218 | } 219 | 220 | /** 221 | * Calculate an inSampleSize for use in a 222 | * {@link BitmapFactory.Options} object when decoding 223 | * bitmaps using the decode* methods from 224 | * {@link BitmapFactory}. This implementation calculates 225 | * the closest inSampleSize that will result in the final decoded bitmap 226 | * having a width and height equal to or larger than the requested width and 227 | * height. This implementation does not ensure a power of 2 is returned for 228 | * inSampleSize which can be faster when decoding but results in a larger 229 | * bitmap which isn't as useful for caching purposes. 230 | * 231 | * @param options An options object with out* params already populated (run 232 | * through a decode* method with inJustDecodeBounds==true 233 | * @param reqWidth The requested width of the resulting bitmap 234 | * @param reqHeight The requested height of the resulting bitmap 235 | * @return The value to be used for inSampleSize 236 | */ 237 | public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, 238 | int reqHeight) { 239 | // Raw height and width of image 240 | final int height = options.outHeight; 241 | final int width = options.outWidth; 242 | 243 | int inSampleSize = 1; 244 | 245 | if (height > reqHeight || width > reqWidth) { 246 | int widthSampleSize = 0; 247 | int heightSampleSize = 0; 248 | if (reqWidth < width) { 249 | widthSampleSize = Math.round((float) width / (float) reqWidth); 250 | } 251 | if (reqHeight < height) { 252 | heightSampleSize = Math.round((float) height / (float) reqHeight); 253 | } 254 | inSampleSize = Math.max(widthSampleSize, heightSampleSize); 255 | } 256 | return inSampleSize; 257 | } 258 | 259 | /** 260 | * 通过srcbitmap 创建一个可编辑的bitmap 261 | * 262 | * @param src 263 | * @return 264 | */ 265 | public static Bitmap createMutableBitmap(Bitmap src) { 266 | Bitmap result = null; 267 | if (src == null) { 268 | return null; 269 | } 270 | result = src.copy(Config.ARGB_8888, true); 271 | 272 | return result; 273 | } 274 | 275 | /** 276 | * 将subBmp图像合并到oriBmp中 277 | * 278 | * @param oriBmp 279 | * @param subBmp 280 | * @param oriRect subBmp中取出的bitmap需要填充到oriRect中的区域 281 | * @param subRect 从subBmp中取出的区域 282 | * @return 283 | */ 284 | public static Bitmap mergeBitmap(Bitmap oriBmp, Bitmap subBmp, final Rect oriRect, 285 | final Rect subRect) { 286 | if (subBmp == null) { 287 | return oriBmp; 288 | } 289 | 290 | if (oriBmp == null) { 291 | return null; 292 | } 293 | 294 | if (!oriBmp.isMutable()) { 295 | oriBmp = createMutableBitmap(oriBmp); 296 | } 297 | 298 | Canvas canvas = new Canvas(oriBmp); 299 | canvas.drawBitmap(subBmp, subRect, oriRect, null); 300 | return oriBmp; 301 | } 302 | 303 | /** 304 | * 将subBmp图像合并到oriBmp中 305 | * 306 | * @param oriBmp 307 | * @param subBmp 308 | * @return oriBmp 309 | */ 310 | public static Bitmap mergeBitmap(Bitmap oriBmp, Bitmap subBmp) { 311 | if (subBmp == null) { 312 | return oriBmp; 313 | } 314 | 315 | if (oriBmp == null) { 316 | return null; 317 | } 318 | 319 | return mergeBitmap(oriBmp, subBmp, new Rect(0, 0, oriBmp.getWidth(), oriBmp.getHeight()), 320 | new Rect(0, 0, subBmp.getWidth(), subBmp.getHeight())); 321 | } 322 | 323 | private static final PorterDuffXfermode SRC_IN_MODE = new PorterDuffXfermode( 324 | PorterDuff.Mode.SRC_IN); 325 | 326 | private final static Paint SRC_IN_PAINT = new Paint(); 327 | 328 | static { 329 | SRC_IN_PAINT.setXfermode(SRC_IN_MODE); 330 | } 331 | 332 | /** 333 | * 遮罩图片 334 | * 335 | * @param dstBmp 336 | * @param mask 337 | * @return 遮罩后的图片 338 | */ 339 | public static Bitmap maskBitmap(final Bitmap dstBmp, final Bitmap mask) { 340 | if (dstBmp == null || mask == null) { 341 | return dstBmp; 342 | } 343 | Bitmap result = Bitmap 344 | .createBitmap(dstBmp.getWidth(), dstBmp.getHeight(), Config.ARGB_8888); 345 | Canvas canvas = new Canvas(result); 346 | int sc = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, 347 | Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG 348 | | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG 349 | ); 350 | canvas.drawBitmap(mask, new Rect(0, 0, mask.getWidth(), mask.getHeight()), new Rect(0, 0, 351 | dstBmp.getWidth(), dstBmp.getHeight()), null); 352 | canvas.drawBitmap(dstBmp, 0, 0, SRC_IN_PAINT); 353 | 354 | canvas.restoreToCount(sc); 355 | return result; 356 | } 357 | 358 | public static Bitmap convertToAlphaMask(Bitmap b) { 359 | Bitmap a = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Config.ALPHA_8); 360 | Canvas c = new Canvas(a); 361 | c.drawBitmap(b, 0.0f, 0.0f, null); 362 | return a; 363 | } 364 | 365 | 366 | public static Bitmap getBmpFromUri(Uri uri, ContentResolver resolver) { 367 | try { 368 | return MediaStore.Images.Media.getBitmap(resolver, uri); 369 | } catch (IOException e) { 370 | Log.e(TAG, Log.getStackTraceString(e)); 371 | return null; 372 | } 373 | } 374 | 375 | public static boolean saveBitmap(Bitmap bitmap, String filepath, String fileType) { 376 | if (bitmap == null || TextUtils.isEmpty(filepath) 377 | || TextUtils.isEmpty(fileType)) 378 | return false; 379 | 380 | File file = new File(filepath); 381 | 382 | try { 383 | FileOutputStream fos = new FileOutputStream(file); 384 | if (fileType.equals("jpg") || fileType.equals("jpeg")) { 385 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); 386 | 387 | } else { 388 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); 389 | } 390 | 391 | fos.close(); 392 | 393 | return true; 394 | 395 | } catch (IOException e) { 396 | Log.e(TAG, Log.getStackTraceString(e)); 397 | } 398 | 399 | return false; 400 | } 401 | } 402 | -------------------------------------------------------------------------------- /app/src/main/java/org/quanqi/androidnetworkdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.quanqi.androidnetworkdemo; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | import android.widget.TextView; 8 | 9 | import com.android.volley.Request; 10 | import com.android.volley.Response; 11 | import com.android.volley.VolleyError; 12 | import com.android.volley.toolbox.StringRequest; 13 | 14 | public class MainActivity extends AppCompatActivity { 15 | 16 | private TextView responseContentTextView; 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_main); 22 | 23 | responseContentTextView = (TextView) findViewById(R.id.textViewResponseContent); 24 | 25 | StringRequest request = new StringRequest( 26 | Request.Method.GET, 27 | "https://kyfw.12306.cn/otn/", 28 | new Response.Listener() { 29 | @Override 30 | public void onResponse(String response) { 31 | responseContentTextView.setText(response); 32 | } 33 | }, 34 | new Response.ErrorListener() { 35 | @Override 36 | public void onErrorResponse(VolleyError error) { 37 | responseContentTextView.setText(error.toString()); 38 | } 39 | }); 40 | RequestManager.getInstance(this).addRequest(request, this); 41 | } 42 | 43 | @Override 44 | protected void onDestroy() { 45 | super.onDestroy(); 46 | RequestManager.getInstance(this).cancelAll(this); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/org/quanqi/androidnetworkdemo/OkHttpStack.java: -------------------------------------------------------------------------------- 1 | package org.quanqi.androidnetworkdemo; 2 | 3 | import com.android.volley.toolbox.HurlStack; 4 | import com.squareup.okhttp.OkHttpClient; 5 | import com.squareup.okhttp.OkUrlFactory; 6 | 7 | import java.io.IOException; 8 | import java.net.HttpURLConnection; 9 | import java.net.URL; 10 | 11 | /** 12 | * By cindy on 7/24/15 10:19 AM. 13 | */ 14 | public class OkHttpStack extends HurlStack { 15 | 16 | private OkHttpClient okHttpClient; 17 | 18 | /** 19 | * Create a OkHttpStack with default OkHttpClient. 20 | */ 21 | public OkHttpStack() { 22 | this(new OkHttpClient()); 23 | } 24 | 25 | /** 26 | * Create a OkHttpStack with a custom OkHttpClient 27 | * @param okHttpClient Custom OkHttpClient, NonNull 28 | */ 29 | public OkHttpStack(OkHttpClient okHttpClient) { 30 | this.okHttpClient = okHttpClient; 31 | } 32 | 33 | @Override 34 | protected HttpURLConnection createConnection(URL url) throws IOException { 35 | OkUrlFactory okUrlFactory = new OkUrlFactory(okHttpClient); 36 | return okUrlFactory.open(url); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/org/quanqi/androidnetworkdemo/RequestManager.java: -------------------------------------------------------------------------------- 1 | package org.quanqi.androidnetworkdemo; 2 | 3 | import android.app.ActivityManager; 4 | import android.content.Context; 5 | import android.graphics.Bitmap; 6 | import android.util.Log; 7 | 8 | import com.android.volley.Request; 9 | import com.android.volley.RequestQueue; 10 | import com.android.volley.toolbox.DiskBasedCache; 11 | import com.android.volley.toolbox.HurlStack; 12 | import com.android.volley.toolbox.ImageLoader; 13 | import com.android.volley.toolbox.Volley; 14 | import com.squareup.okhttp.OkHttpClient; 15 | import com.squareup.okhttp.OkUrlFactory; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | import java.net.HttpURLConnection; 21 | import java.net.URL; 22 | import java.security.KeyManagementException; 23 | import java.security.KeyStore; 24 | import java.security.KeyStoreException; 25 | import java.security.NoSuchAlgorithmException; 26 | import java.security.SecureRandom; 27 | import java.security.cert.CertificateException; 28 | import java.util.Hashtable; 29 | import java.util.Map; 30 | 31 | import javax.net.ssl.HttpsURLConnection; 32 | import javax.net.ssl.SSLContext; 33 | import javax.net.ssl.SSLSocketFactory; 34 | import javax.net.ssl.TrustManager; 35 | import javax.net.ssl.TrustManagerFactory; 36 | 37 | /** 38 | * By cindy on 7/24/15 10:08 AM. 39 | */ 40 | public class RequestManager { 41 | private static final String TAG = "RequestManager"; 42 | 43 | private static RequestManager instance; 44 | 45 | private Map socketFactoryMap; 46 | 47 | public static RequestManager getInstance(Context context) { 48 | if (instance == null) { 49 | instance = new RequestManager(context); 50 | } 51 | return instance; 52 | } 53 | 54 | public RequestQueue mRequestQueue; 55 | private OkHttpClient okHttpClient; 56 | private BitmapLruCache mLruCache; 57 | private ImageLoader mImageLoader; 58 | private DiskBasedCache mDiskCache; 59 | 60 | private RequestManager(Context context) { 61 | int MEM_CACHE_SIZE = 1024 * 1024 62 | * ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass() / 3; 63 | okHttpClient = new OkHttpClient(); 64 | mLruCache = new BitmapLruCache(MEM_CACHE_SIZE); 65 | mRequestQueue = newRequestQueue(context.getApplicationContext()); 66 | mImageLoader = new ImageLoader(mRequestQueue, mLruCache); 67 | mDiskCache = (DiskBasedCache) mRequestQueue.getCache(); 68 | } 69 | 70 | private SSLSocketFactory createSSLSocketFactory(Context context, int res, String password) 71 | throws CertificateException, 72 | NoSuchAlgorithmException, 73 | IOException, 74 | KeyStoreException, 75 | KeyManagementException { 76 | InputStream inputStream = context.getResources().openRawResource(res); 77 | KeyStore keyStore = KeyStore.getInstance("BKS"); 78 | keyStore.load(inputStream, password.toCharArray()); 79 | TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 80 | tmf.init(keyStore); 81 | SSLContext sslContext = SSLContext.getInstance("TLS"); 82 | sslContext.init(null, tmf.getTrustManagers(), new SecureRandom()); 83 | return sslContext.getSocketFactory(); 84 | } 85 | 86 | private RequestQueue newRequestQueue(Context context) { 87 | RequestQueue requestQueue; 88 | try { 89 | String[] hosts = {"kyfw.12306.cn"}; 90 | int[] certRes = {R.raw.kyfw}; 91 | String[] certPass = {"asdfqaz"}; 92 | socketFactoryMap = new Hashtable<>(hosts.length); 93 | 94 | for (int i = 0; i < certRes.length; i++) { 95 | int res = certRes[i]; 96 | String password = certPass[i]; 97 | SSLSocketFactory sslSocketFactory = createSSLSocketFactory(context, res, password); 98 | socketFactoryMap.put(hosts[i], sslSocketFactory); 99 | } 100 | 101 | HurlStack stack = new SelfSignSslOkHttpStack(socketFactoryMap); 102 | 103 | requestQueue = Volley.newRequestQueue(context, stack); 104 | requestQueue.start(); 105 | } catch (KeyStoreException 106 | | CertificateException 107 | | NoSuchAlgorithmException 108 | | KeyManagementException 109 | | IOException e) { 110 | throw new RuntimeException(e); 111 | } 112 | return requestQueue; 113 | } 114 | 115 | public void addRequest(Request request, Object tag) { 116 | if (BuildConfig.DEBUG) { 117 | Log.i(TAG, "Add request:" + request.toString()); 118 | } 119 | if (tag != null) { 120 | request.setTag(tag); 121 | } 122 | mRequestQueue.add(request); 123 | } 124 | 125 | public void cancelAll(Object tag) { 126 | mRequestQueue.cancelAll(tag); 127 | } 128 | 129 | public File getCachedImageFile(String url) { 130 | return mDiskCache.getFileForKey(url); 131 | } 132 | 133 | public Bitmap getMemoryBitmap(String key) { 134 | return mLruCache.get(key); 135 | } 136 | 137 | public ImageLoader.ImageContainer loadImage(String requestUrl, 138 | ImageLoader.ImageListener imageListener) { 139 | return loadImage(requestUrl, imageListener, 0, 0); 140 | } 141 | 142 | public ImageLoader.ImageContainer loadImage(String requestUrl, 143 | ImageLoader.ImageListener imageListener, 144 | int maxWidth, 145 | int maxHeight) { 146 | 147 | return mImageLoader.get(requestUrl, imageListener, maxWidth, maxHeight); 148 | } 149 | 150 | 151 | } 152 | -------------------------------------------------------------------------------- /app/src/main/java/org/quanqi/androidnetworkdemo/SelfSignSslOkHttpStack.java: -------------------------------------------------------------------------------- 1 | package org.quanqi.androidnetworkdemo; 2 | 3 | import com.android.volley.toolbox.HurlStack; 4 | import com.squareup.okhttp.OkHttpClient; 5 | import com.squareup.okhttp.OkUrlFactory; 6 | 7 | import java.io.IOException; 8 | import java.net.HttpURLConnection; 9 | import java.net.URL; 10 | import java.util.Map; 11 | 12 | import javax.net.ssl.HttpsURLConnection; 13 | import javax.net.ssl.SSLSocketFactory; 14 | 15 | /** 16 | * A HttpStack implement witch can verify specified self-signed certification. 17 | */ 18 | public class SelfSignSslOkHttpStack extends HurlStack { 19 | 20 | private OkHttpClient okHttpClient; 21 | 22 | private Map socketFactoryMap; 23 | 24 | /** 25 | * Create a OkHttpStack with default OkHttpClient. 26 | */ 27 | public SelfSignSslOkHttpStack(Map factoryMap) { 28 | this(new OkHttpClient(), factoryMap); 29 | } 30 | 31 | /** 32 | * Create a OkHttpStack with a custom OkHttpClient 33 | * @param okHttpClient Custom OkHttpClient, NonNull 34 | */ 35 | public SelfSignSslOkHttpStack(OkHttpClient okHttpClient, Map factoryMap) { 36 | this.okHttpClient = okHttpClient; 37 | this.socketFactoryMap = factoryMap; 38 | } 39 | 40 | @Override 41 | protected HttpURLConnection createConnection(URL url) throws IOException { 42 | if ("https".equals(url.getProtocol()) && socketFactoryMap.containsKey(url.getHost())) { 43 | HttpsURLConnection connection = (HttpsURLConnection) new OkUrlFactory(okHttpClient).open(url); 44 | connection.setSSLSocketFactory(socketFactoryMap.get(url.getHost())); 45 | return connection; 46 | } else { 47 | return new OkUrlFactory(okHttpClient).open(url); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dodocat/AndroidNetworkDemo/86ee9e8f09d1d265f26383fb90c9afe608ff3ed0/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dodocat/AndroidNetworkDemo/86ee9e8f09d1d265f26383fb90c9afe608ff3ed0/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dodocat/AndroidNetworkDemo/86ee9e8f09d1d265f26383fb90c9afe608ff3ed0/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dodocat/AndroidNetworkDemo/86ee9e8f09d1d265f26383fb90c9afe608ff3ed0/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/raw/kyfw.bks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dodocat/AndroidNetworkDemo/86ee9e8f09d1d265f26383fb90c9afe608ff3ed0/app/src/main/res/raw/kyfw.bks -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Android Network Demo 3 | 4 | Hello world! 5 | Settings 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.2.3' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dodocat/AndroidNetworkDemo/86ee9e8f09d1d265f26383fb90c9afe608ff3ed0/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jul 24 09:47:25 CST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /tools/ssl/bcprov-jdk16-1.46.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dodocat/AndroidNetworkDemo/86ee9e8f09d1d265f26383fb90c9afe608ff3ed0/tools/ssl/bcprov-jdk16-1.46.jar -------------------------------------------------------------------------------- /tools/ssl/dump.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Author: jing@quanqi.org 3 | # Created Time: Tue Jan 27 17:31:24 2015 4 | # Description: 5 | # ChangeLog: 6 | 7 | echo | openssl s_client -connect kyfw.12306.cn:443 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > kyfw.12306.cn.pem 8 | 9 | CERTSTORE=../../app/src/main/res/raw/kyfw.bks 10 | 11 | if [ -a $CERTSTORE ]; then 12 | rm $CERTSTORE || exit 1 13 | fi 14 | 15 | keytool -importcert -v \ 16 | -trustcacerts \ 17 | -alias 0 \ 18 | -file <(openssl x509 -in kyfw.12306.cn.pem) \ 19 | -keystore $CERTSTORE -storetype BKS \ 20 | -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \ 21 | -providerpath ./bcprov-jdk16-1.46.jar \ 22 | -storepass asdfqaz 23 | 24 | --------------------------------------------------------------------------------