1.網絡框架接口創建
1.1 使用的是第三方的框架
【第三方框架】Retrofit,封裝一個通用的框架,可以使用rxJava和rxAndroid進行封裝,比較難,這里不做講解;
1.2 restful 請求
【參考文章】http://www.ruanyifeng.com/blog/2014/05/restful_api
1.3網絡請求的具體實現類
【網絡請求的具體實現類】
【添加依賴】添加依賴可以使用兩種方式:【1】直接復制粘貼;【2】projectStucter尋找添加;
1 //網絡請求依賴 2 compile 'com.squareup.okio:okio:1.13.0' 3 compile 'com.squareup.okhttp3:okhttp:3.8.1' 4 compile 'com.squareup.retrofit2:retrofit:2.3.0' 5 compile 'com.squareup.retrofit2:converter-scalars:2.3.0' //以string 直接轉化來的,最直接的
【框架的搭建需要考慮的問題】明白需要使用什么模式,來什么要什么的是建造者模式是最好的。
【源碼】com.flj.latte.net.RestService接口的封裝
1 package com.flj.latte.net; 2 3 import java.util.WeakHashMap; 4 5 import okhttp3.MultipartBody; 6 import okhttp3.RequestBody; 7 import okhttp3.ResponseBody; 8 import retrofit2.Call; 9 import retrofit2.http.Body; 10 import retrofit2.http.DELETE; 11 import retrofit2.http.FieldMap; 12 import retrofit2.http.FormUrlEncoded; 13 import retrofit2.http.GET; 14 import retrofit2.http.Multipart; 15 import retrofit2.http.POST; 16 import retrofit2.http.PUT; 17 import retrofit2.http.Part; 18 import retrofit2.http.QueryMap; 19 import retrofit2.http.Streaming; 20 import retrofit2.http.Url; 21 22 /** 23 * Created by 傅令傑 on 2017/4/2 24 */ 25 public interface RestService { 26 27 @GET //不傳遞任何的路由信息 28 Call<String> get(@Url String url, @QueryMap WeakHashMap<String, Object> params); //QueryMap是以鍵值對的形式進行存儲的; 29 30 /** 31 * 32 * @param url 33 * @param params 34 * @return 35 * FieldMap:請求體中包含的內容; 36 */ 37 @FormUrlEncoded 38 @POST //不傳遞任何的路由信息 39 Call<String> post(@Url String url, @FieldMap WeakHashMap<String, Object> params); 40 41 @POST 42 Call<String> postRaw(@Url String url, @Body RequestBody body); 43 44 @FormUrlEncoded 45 @PUT 46 Call<String> put(@Url String url, @FieldMap WeakHashMap<String, Object> params); 47 48 @PUT 49 Call<String> putRaw(@Url String url, @Body RequestBody body); 50 51 @DELETE 52 Call<String> delete(@Url String url, @QueryMap WeakHashMap<String, Object> params); 53 54 @Streaming //避免一次性將所有的文件下載下來,導致內存的溢出;但是在寫的時候,仍然需要將文件放在單獨的線程,否則在主線程操作任然會報錯。 55 @GET 56 Call<ResponseBody> download(@Url String url, @QueryMap WeakHashMap<String, Object> params); 57 58 @Multipart 59 @POST 60 Call<String> upload(@Url String url, @Part MultipartBody.Part file); 61 }
【封裝枚舉類】
【傳入ip地址】
【RetrofitHolder創建成功】構建OkHttp請求
1.5 【restService的創建】
1.6 創建get方法
2.Restful請求的處理-框架
【說明】首先要考慮網絡請求的參數(url傳入的值、文件、回調、及loder加載圈)
【說明】使用建造者模式,將建造者類和宿主類分開;
2.1【新建建造者類】
2.2【restClient類的參數的定義】
【restClient類的參數的定義】restClient類在每次Builder的時候會生成全新的實例,而里面的參數一次更改完畢,不允許二次更改;
2.3 【回調類】
【回調類】在網路請求之后,會存在網絡請求之后的回調,比如:請求失敗、請求異常、請求成功等;
[新建CallBack包,書寫需要調用的接口]
2.4 完善RestClient
【完善com.flj.latte.net.RestClient】以Builder的形式構造出來了;
2.5 RestClientBuilder 對數據的設置
【說明】主要完成的數據的傳遞
2.6 RestClient的調用
2.7 RestClientBuilder的改進
2.7.1【改進1】【mParams】參數每次都會構建,比較繁瑣;
【優化1】
【優化方法2】
【client修改】
2.8 requset請求
【新建callBack類】新建類並實現實現接口, 復寫方法;
【部分源碼】com.flj.latte.net.callback.RequestCallbacks
1 package com.flj.latte.net.callback; 2 3 import android.os.Handler; 4 5 import com.flj.latte.app.ConfigKeys; 6 import com.flj.latte.app.Latte; 7 import com.flj.latte.net.RestCreator; 8 import com.flj.latte.ui.loader.LatteLoader; 9 import com.flj.latte.ui.loader.LoaderStyle; 10 11 import retrofit2.Call; 12 import retrofit2.Callback; 13 import retrofit2.Response; 18 19 public final class RequestCallbacks implements Callback<String> { 20 21 private final IRequest REQUEST; 22 private final ISuccess SUCCESS; 23 private final IFailure FAILURE; 24 private final IError ERROR; 25 private final LoaderStyle LOADER_STYLE; 26 private static final Handler HANDLER = Latte.getHandler(); 27 28 public RequestCallbacks(IRequest request, ISuccess success, IFailure failure, IError error, LoaderStyle style) { 29 this.REQUEST = request; 30 this.SUCCESS = success; 31 this.FAILURE = failure; 32 this.ERROR = error; 33 this.LOADER_STYLE = style; 34 } 35 36 @Override 37 public void onResponse(Call<String> call, Response<String> response) { 38 if (response.isSuccessful()) { 39 if (call.isExecuted()) { 40 if (SUCCESS != null) { 41 SUCCESS.onSuccess(response.body()); 42 } 43 } 44 } else { 45 if (ERROR != null) { 46 ERROR.onError(response.code(), response.message()); 47 } 48 } 49 50 onRequestFinish(); 51 } 52 53 @Override 54 public void onFailure(Call<String> call, Throwable t) { 55 if (FAILURE != null) { 56 FAILURE.onFailure(); 57 } 58 if (REQUEST != null) { 59 REQUEST.onRequestEnd(); 60 } 61 62 onRequestFinish(); 63 }
【完善RestClient】
2.9 使用方法
【測試】
【增加權限】
【測試】
【效果】通過get請求返回了數據
3. oading框架集成與完善AVLoadingIndicatorView
3.1 第三方框架的效果
【地址】https://github.com/81813780/AVLoadingIndicatorView
【說明】在該地址中已經存在怎樣使用的步驟;
3.2 集成封裝獲取某種類型的View
【添加依賴】
【說明】各種的效果的獲取使用過的是反射的技術,但是反復使用反射會影響設備的性能;因此做了一個機制的封裝;
【原理】以一種緩存的方式創建loader,不需要每次使用loader的時候進行反射,這樣性能會有很大幅度的提高。
3.3 不同的style的枚舉的封裝
【對不同的類型進行封裝】com.flj.latte.ui.loader.LoaderStyle
1 package com.flj.latte.ui.loader; 2 3 4 @SuppressWarnings("unused") 5 public enum LoaderStyle { 6 BallPulseIndicator, 7 BallGridPulseIndicator, 8 BallClipRotateIndicator, 9 BallClipRotatePulseIndicator, 10 SquareSpinIndicator, 11 BallClipRotateMultipleIndicator, 12 BallPulseRiseIndicator, 13 BallRotateIndicator, 14 CubeTransitionIndicator, 15 BallZigZagIndicator, 16 BallZigZagDeflectIndicator, 17 BallTrianglePathIndicator, 18 BallScaleIndicator, 19 LineScaleIndicator, 20 LineScalePartyIndicator, 21 BallScaleMultipleIndicator, 22 BallPulseSyncIndicator, 23 BallBeatIndicator, 24 LineScalePulseOutIndicator, 25 LineScalePulseOutRapidIndicator, 26 BallScaleRippleIndicator, 27 BallScaleRippleMultipleIndicator, 28 BallSpinFadeLoaderIndicator, 29 LineSpinFadeLoaderIndicator, 30 TriangleSkewSpinIndicator, 31 PacmanIndicator, 32 BallGridBeatIndicator, 33 SemiCircleSpinIndicator, 34 CustomIndicator 35 }
3.4 對傳入的樣式/參數封裝
【樣式的封裝】需要封裝是否需要透明度、顏色等值的傳入;
【傳入樣式參數並作為根布局】
3.5 工具類-填充參數的設置
【工具類】新建工具類的權限一般 放為:public static;
3.6 繼續完善類
【設置縮放比例】為了適應 不同的設備的屏幕的不同的大小,需要對加載的loader進行縮放
【創建集合,統一管理不同類型的loader】【學習思想】在不需要loaders的時候,只要遍歷集合,一一的關閉loaders即可;
【提供默認的loaders樣式】
【關閉和顯示對話框的loader】
【說明】此處使用的dialog.cancle(),因為在關閉dialog之后會調用onCancle()方法,可以響應在關閉對話框之后的一些動作;
dialog.dismiss():直接關閉對話框,沒有響應;
3.7 網絡請求中加入loader
【client】
【builder】
【說明】handler聲明的時候加關鍵字static;可以避免內存泄露;
【測試】
【增加失敗時候關閉loader】
4.網絡框架優化與完善
4.1【支持原始數據的post請求】
4.2【支持原始數據的putRaw】
4.3【增加UPLOAD上傳方法】
【會存在文件的傳遞】
5.文件下載功能設計與實現
【說明】比較復雜;
【增加參數】
【新建downloadhandler】新建下載處理類;
【使用異步的下載方法】
【file工具類】沒有講解,課下編寫的
1 package com.flj.latte.util.file; 2 3 import android.content.ContentResolver; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.content.res.AssetManager; 7 import android.database.Cursor; 8 import android.graphics.Bitmap; 9 import android.graphics.Typeface; 10 import android.media.MediaScannerConnection; 11 import android.net.Uri; 12 import android.os.Build; 13 import android.os.Environment; 14 import android.provider.MediaStore; 15 import android.webkit.MimeTypeMap; 16 import android.widget.TextView; 17 18 import com.flj.latte.app.Latte; 19 20 import java.io.BufferedInputStream; 21 import java.io.BufferedOutputStream; 22 import java.io.BufferedReader; 23 import java.io.File; 24 import java.io.FileNotFoundException; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.InputStreamReader; 29 import java.text.SimpleDateFormat; 30 import java.util.Date; 31 import java.util.Locale; 32 33 34 public final class FileUtil { 35 36 //格式化的模板 37 private static final String TIME_FORMAT = "_yyyyMMdd_HHmmss"; 38 39 private static final String SDCARD_DIR = 40 Environment.getExternalStorageDirectory().getPath(); 41 42 //默認本地上傳圖片目錄 43 public static final String UPLOAD_PHOTO_DIR = 44 Environment.getExternalStorageDirectory().getPath() + "/a_upload_photos/"; 45 46 //網頁緩存地址 47 public static final String WEB_CACHE_DIR = 48 Environment.getExternalStorageDirectory().getPath() + "/app_web_cache/"; 49 50 //系統相機目錄 51 public static final String CAMERA_PHOTO_DIR = 52 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath() + "/Camera/"; 53 54 private static String getTimeFormatName(String timeFormatHeader) { 55 final Date date = new Date(System.currentTimeMillis()); 56 //必須要加上單引號 57 final SimpleDateFormat dateFormat = new SimpleDateFormat("'" + timeFormatHeader + "'" + TIME_FORMAT, Locale.getDefault()); 58 return dateFormat.format(date); 59 } 60 61 /** 62 * @param timeFormatHeader 格式化的頭(除去時間部分) 63 * @param extension 后綴名 64 * @return 返回時間格式化后的文件名 65 */ 66 public static String getFileNameByTime(String timeFormatHeader, String extension) { 67 return getTimeFormatName(timeFormatHeader) + "." + extension; 68 } 69 70 @SuppressWarnings("ResultOfMethodCallIgnored") 71 private static File createDir(String sdcardDirName) { 72 //拼接成SD卡中完整的dir 73 final String dir = SDCARD_DIR + "/" + sdcardDirName + "/"; 74 final File fileDir = new File(dir); 75 if (!fileDir.exists()) { 76 fileDir.mkdirs(); 77 } 78 return fileDir; 79 } 80 81 @SuppressWarnings("ResultOfMethodCallIgnored") 82 public static File createFile(String sdcardDirName, String fileName) { 83 return new File(createDir(sdcardDirName), fileName); 84 } 85 86 private static File createFileByTime(String sdcardDirName, String timeFormatHeader, String extension) { 87 final String fileName = getFileNameByTime(timeFormatHeader, extension); 88 return createFile(sdcardDirName, fileName); 89 } 90 91 //獲取文件的MIME 92 public static String getMimeType(String filePath) { 93 final String extension = getExtension(filePath); 94 return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); 95 } 96 97 //獲取文件的后綴名 98 public static String getExtension(String filePath) { 99 String suffix = ""; 100 final File file = new File(filePath); 101 final String name = file.getName(); 102 final int idx = name.lastIndexOf('.'); 103 if (idx > 0) { 104 suffix = name.substring(idx + 1); 105 } 106 return suffix; 107 } 108 109 /** 110 * 保存Bitmap到SD卡中 111 * 112 * @param dir 目錄名,只需要寫自己的相對目錄名即可 113 * @param compress 壓縮比例 100是不壓縮,值約小壓縮率越高 114 * @return 返回該文件 115 */ 116 public static File saveBitmap(Bitmap mBitmap, String dir, int compress) { 117 118 final String sdStatus = Environment.getExternalStorageState(); 119 // 檢測sd是否可用 120 if (!sdStatus.equals(Environment.MEDIA_MOUNTED)) { 121 return null; 122 } 123 FileOutputStream fos = null; 124 BufferedOutputStream bos = null; 125 File fileName = createFileByTime(dir, "DOWN_LOAD", "jpg"); 126 try { 127 fos = new FileOutputStream(fileName); 128 bos = new BufferedOutputStream(fos); 129 mBitmap.compress(Bitmap.CompressFormat.JPEG, compress, bos);// 把數據寫入文件 130 } catch (FileNotFoundException e) { 131 e.printStackTrace(); 132 } finally { 133 try { 134 135 if (bos != null) { 136 bos.flush(); 137 } 138 if (bos != null) { 139 bos.close(); 140 } 141 //關閉流 142 if (fos != null) { 143 fos.flush(); 144 } 145 if (fos != null) { 146 fos.close(); 147 } 148 } catch (IOException e) { 149 e.printStackTrace(); 150 } 151 } 152 refreshDCIM(); 153 154 return fileName; 155 } 156 157 public static File writeToDisk(InputStream is, String dir, String name) { 158 final File file = FileUtil.createFile(dir, name); 159 BufferedInputStream bis = null; 160 FileOutputStream fos = null; 161 BufferedOutputStream bos = null; 162 163 try { 164 bis = new BufferedInputStream(is); 165 fos = new FileOutputStream(file); 166 bos = new BufferedOutputStream(fos); 167 168 byte data[] = new byte[1024 * 4]; 169 170 int count; 171 while ((count = bis.read(data)) != -1) { 172 bos.write(data, 0, count); 173 } 174 175 bos.flush(); 176 fos.flush(); 177 178 179 } catch (IOException e) { 180 e.printStackTrace(); 181 } finally { 182 try { 183 if (bos != null) { 184 bos.close(); 185 } 186 if (fos != null) { 187 fos.close(); 188 } 189 if (bis != null) { 190 bis.close(); 191 } 192 is.close(); 193 } catch (IOException e) { 194 e.printStackTrace(); 195 } 196 } 197 198 return file; 199 } 200 201 public static File writeToDisk(InputStream is, String dir, String prefix, String extension) { 202 final File file = FileUtil.createFileByTime(dir, prefix, extension); 203 BufferedInputStream bis = null; 204 FileOutputStream fos = null; 205 BufferedOutputStream bos = null; 206 207 try { 208 bis = new BufferedInputStream(is); 209 fos = new FileOutputStream(file); 210 bos = new BufferedOutputStream(fos); 211 212 byte data[] = new byte[1024 * 4]; 213 214 int count; 215 while ((count = bis.read(data)) != -1) { 216 bos.write(data, 0, count); 217 } 218 219 bos.flush(); 220 fos.flush(); 221 222 223 } catch (IOException e) { 224 e.printStackTrace(); 225 } finally { 226 try { 227 if (bos != null) { 228 bos.close(); 229 } 230 if (fos != null) { 231 fos.close(); 232 } 233 if (bis != null) { 234 bis.close(); 235 } 236 is.close(); 237 } catch (IOException e) { 238 e.printStackTrace(); 239 } 240 } 241 242 return file; 243 } 244 245 /** 246 * 通知系統刷新系統相冊,使照片展現出來 247 */ 248 private static void refreshDCIM() { 249 if (Build.VERSION.SDK_INT >= 19) { 250 //兼容android4.4版本,只掃描存放照片的目錄 251 MediaScannerConnection.scanFile(Latte.getApplicationContext(), 252 new String[]{Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath()}, 253 null, null); 254 } else { 255 //掃描整個SD卡來更新系統圖庫,當文件很多時用戶體驗不佳,且不適合4.4以上版本 256 Latte.getApplicationContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + 257 Environment.getExternalStorageDirectory()))); 258 } 259 } 260 261 /** 262 * 讀取raw目錄中的文件,並返回為字符串 263 */ 264 public static String getRawFile(int id) { 265 final InputStream is = Latte.getApplicationContext().getResources().openRawResource(id); 266 final BufferedInputStream bis = new BufferedInputStream(is); 267 final InputStreamReader isr = new InputStreamReader(bis); 268 final BufferedReader br = new BufferedReader(isr); 269 final StringBuilder stringBuilder = new StringBuilder(); 270 String str; 271 try { 272 while ((str = br.readLine()) != null) { 273 stringBuilder.append(str); 274 } 275 } catch (IOException e) { 276 e.printStackTrace(); 277 } finally { 278 try { 279 br.close(); 280 isr.close(); 281 bis.close(); 282 is.close(); 283 } catch (IOException e) { 284 e.printStackTrace(); 285 } 286 } 287 return stringBuilder.toString(); 288 } 289 290 291 public static void setIconFont(String path, TextView textView) { 292 final Typeface typeface = Typeface.createFromAsset(Latte.getApplicationContext().getAssets(), path); 293 textView.setTypeface(typeface); 294 } 295 296 /** 297 * 讀取assets目錄下的文件,並返回字符串 298 */ 299 public static String getAssetsFile(String name) { 300 InputStream is = null; 301 BufferedInputStream bis = null; 302 InputStreamReader isr = null; 303 BufferedReader br = null; 304 StringBuilder stringBuilder = null; 305 final AssetManager assetManager = Latte.getApplicationContext().getAssets(); 306 try { 307 is = assetManager.open(name); 308 bis = new BufferedInputStream(is); 309 isr = new InputStreamReader(bis); 310 br = new BufferedReader(isr); 311 stringBuilder = new StringBuilder(); 312 String str; 313 while ((str = br.readLine()) != null) { 314 stringBuilder.append(str); 315 } 316 } catch (IOException e) { 317 e.printStackTrace(); 318 } finally { 319 try { 320 if (br != null) { 321 br.close(); 322 } 323 if (isr != null) { 324 isr.close(); 325 } 326 if (bis != null) { 327 bis.close(); 328 } 329 if (is != null) { 330 is.close(); 331 } 332 assetManager.close(); 333 } catch (IOException e) { 334 e.printStackTrace(); 335 } 336 } 337 if (stringBuilder != null) { 338 return stringBuilder.toString(); 339 } else { 340 return null; 341 } 342 } 343 344 public static String getRealFilePath(final Context context, final Uri uri) { 345 if (null == uri) return null; 346 final String scheme = uri.getScheme(); 347 String data = null; 348 if (scheme == null) 349 data = uri.getPath(); 350 else if (ContentResolver.SCHEME_FILE.equals(scheme)) { 351 data = uri.getPath(); 352 } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) { 353 final Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null); 354 if (null != cursor) { 355 if (cursor.moveToFirst()) { 356 final int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); 357 if (index > -1) { 358 data = cursor.getString(index); 359 } 360 } 361 cursor.close(); 362 } 363 } 364 return data; 365 } 366 }
【保存文件類的封裝】
【繼續封裝com.flj.latte.net.download.DownloadHandler】
【調用的方法】在后面的使用文件下載然后更新應用程序;
【提交代碼】
6.攔截器功能設計與實現之攔截器的初始化
【說明】沒有搭建服務器,然后使用okhttp庫中的攔截功能,將接收到請求之后做出響應,返回json文件;
6.1【配置文件中的攔截器的配置】
6.2 將配置文件中的interceptors請求配置到okhttp
【接收請求】
7.攔截器功能設計與實現之模擬請求
【說明】模擬服務器:獲取傳入的參數,
【get方法】則:從url獲取參數;
【post方法】:從請求體中獲取參數;
7.1 基類封裝
7.3 調試類封裝
1 package com.flj.latte.net.interceptors; 2 3 import android.support.annotation.NonNull; 4 import android.support.annotation.RawRes; 5 6 import com.flj.latte.util.file.FileUtil; 7 8 import java.io.IOException; 9 10 import okhttp3.MediaType; 11 import okhttp3.Protocol; 12 import okhttp3.Response; 13 import okhttp3.ResponseBody; 14 15 16 public class DebugInterceptor extends BaseInterceptor { 17 18 private final String DEBUG_URL; 19 private final int DEBUG_RAW_ID; 20 21 public DebugInterceptor(String debugUrl, int rawId) { 22 this.DEBUG_URL = debugUrl; 23 this.DEBUG_RAW_ID = rawId; 24 } 25 26 private Response getResponse(Chain chain, String json) { 27 return new Response.Builder() 28 .code(200) 29 .addHeader("Content-Type", "application/json") 30 .body(ResponseBody.create(MediaType.parse("application/json"), json)) 31 .message("OK") 32 .request(chain.request()) 33 .protocol(Protocol.HTTP_1_1) 34 .build(); 35 } 36 37 //debug的封裝,根據rawId查詢獲取json; 38 private Response debugResponse(Chain chain, @RawRes int rawId) { 39 final String json = FileUtil.getRawFile(rawId); //根據rawId取出原始文件; 40 return getResponse(chain, json);//返回Response請求的響應; 41 } 42 43 /** 44 * 說明:此時存在的json文件是存在在單個應用程序的res/raw文件夾下的json; 45 * @param chain 46 * @return 47 * @throws IOException 48 */ 49 @Override 50 public Response intercept(@NonNull Chain chain) throws IOException { 51 final String url = chain.request().url().toString(); //得到攔截的url; 52 if (url.contains(DEBUG_URL)) { //攔截的url包含了DEBUG_URL,返回存在的json文件; 53 return debugResponse(chain, DEBUG_RAW_ID); 54 } 55 return chain.proceed(chain.request()); //否則原樣返回數據; 56 } 57 }
7.4 使用
【效果】就可以不適用服務器的數據,直接可以在本地進行json數據的加載和測試;