├── .gitignore ├── .gradle └── 2.4 │ └── taskArtifacts │ ├── cache.properties │ ├── cache.properties.lock │ ├── fileHashes.bin │ ├── fileSnapshots.bin │ ├── outputFileStates.bin │ └── taskArtifacts.bin ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── dictionaries │ └── lizhiyun.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml ├── vcs.xml └── workspace.xml ├── README.md ├── app ├── app.iml ├── build.gradle ├── libs │ └── gson-2.0.jar └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── a.gif │ ├── anim.gif │ ├── c.gif │ ├── d.gif │ ├── e.gif │ ├── f.gif │ ├── g.gif │ └── h.gif │ ├── java │ ├── com │ │ ├── example │ │ │ └── ui │ │ │ │ ├── App.java │ │ │ │ ├── AppEntry.java │ │ │ │ ├── CrashHandler.java │ │ │ │ ├── FileDownloadActivity.java │ │ │ │ ├── ImageloaderActivity.java │ │ │ │ └── MyUtils.java │ │ └── litesuits │ │ │ └── go │ │ │ ├── OverloadPolicy.java │ │ │ ├── PriorityRunnable.java │ │ │ ├── SchedulePolicy.java │ │ │ ├── SmartExecutor.java │ │ │ └── utils │ │ │ └── GoUtil.java │ └── download │ │ ├── http │ │ ├── core │ │ │ ├── Http.java │ │ │ ├── HttpManager.java │ │ │ └── HttpTask.java │ │ ├── entity │ │ │ ├── CustomJsonReader.java │ │ │ ├── FileEntity.java │ │ │ ├── ResultData.java │ │ │ ├── SimpleJsonReader.java │ │ │ └── User.java │ │ ├── exception │ │ │ ├── AppException.java │ │ │ └── IfNeedLoginGlobalException.java │ │ ├── listener │ │ │ ├── AbsCallback.java │ │ │ ├── CustomJsonReaderCallback.java │ │ │ ├── FileCallback.java │ │ │ ├── ICallback.java │ │ │ ├── JsonCallback.java │ │ │ ├── JsonListCallback.java │ │ │ ├── JsonReaderCallback.java │ │ │ ├── JsonReaderListCallback.java │ │ │ ├── OnGlobalExceptionListener.java │ │ │ ├── OnProgressDownloadListener.java │ │ │ ├── OnProgressUpdateListener.java │ │ │ └── StringCallback.java │ │ ├── request │ │ │ ├── Request.java │ │ │ └── RequestBuilder.java │ │ └── util │ │ │ ├── HttpUrlConnectionUtil.java │ │ │ └── UploadUtil.java │ │ ├── imageLoader │ │ ├── cache │ │ │ ├── BitmapCache.java │ │ │ └── DiskLruCache.java │ │ ├── config │ │ │ ├── FailedDrawable.java │ │ │ ├── ImageConfig.java │ │ │ ├── LoadOrder.java │ │ │ └── LoadingDrawable.java │ │ ├── core │ │ │ ├── BmManager.java │ │ │ ├── Image.java │ │ │ ├── ImageLoader.java │ │ │ ├── LoadTask.java │ │ │ └── RunningTasksManager.java │ │ ├── listener │ │ │ ├── CustomDisplayMethod.java │ │ │ └── OnProgressUpdatedListener.java │ │ ├── loader │ │ │ ├── AssetsLoader.java │ │ │ ├── DrawableLoader.java │ │ │ ├── FileLoader.java │ │ │ ├── HttpLoader.java │ │ │ ├── Load.java │ │ │ └── LoadInterface.java │ │ ├── request │ │ │ ├── BitmapRequest.java │ │ │ └── BitmapRequestBuilder.java │ │ ├── util │ │ │ ├── BitmapOperate.java │ │ │ ├── FaceCropper.java │ │ │ ├── GaussianBlur.java │ │ │ └── ImageSizeUtil.java │ │ └── view │ │ │ └── PowerImageView.java │ │ ├── otherFileLoader │ │ ├── core │ │ │ ├── ConnectRunnable.java │ │ │ ├── ConnectionChangeReceiver.java │ │ │ ├── Constants.java │ │ │ ├── DownloadService.java │ │ │ ├── DownloadTask.java │ │ │ └── DownloadThread.java │ │ ├── db │ │ │ ├── DLDBHelper.java │ │ │ ├── DLDBManager.java │ │ │ └── DownFileManager.java │ │ ├── listener │ │ │ └── DownloadListener.java │ │ ├── request │ │ │ └── DownFile.java │ │ └── util │ │ │ └── ToastUtils.java │ │ └── utils │ │ └── Util.java │ ├── res │ ├── drawable-hdpi │ │ ├── anim.gif │ │ ├── face.jpg │ │ └── ic_launcher.png │ ├── drawable-mdpi │ │ └── ic_launcher.png │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ ├── layout │ │ ├── activity_applist_item.xml │ │ ├── activity_filedownload.xml │ │ ├── activity_imageloader.xml │ │ └── image_list_item.xml │ ├── raw │ │ └── anim.gif │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── resources │ └── download │ └── imageLoader │ └── config │ └── image │ ├── loadfailed.png │ └── loading.png ├── build.gradle ├── down.gif ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── import-summary.txt ├── lib_download.iml ├── local.properties └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Java class files 2 | *.class 3 | 4 | # Local configuration file (sdk path, etc) 5 | local.properties 6 | 7 | #IntelliJ IDEA 8 | .idea 9 | *.iml 10 | 11 | #Gradle 12 | .gradle 13 | build 14 | 15 | signing.properties 16 | CMakeLists.txt 17 | src/main/obj 18 | src/main/libs -------------------------------------------------------------------------------- /.gradle/2.4/taskArtifacts/cache.properties: -------------------------------------------------------------------------------- 1 | #Wed May 18 16:31:46 CST 2016 2 | -------------------------------------------------------------------------------- /.gradle/2.4/taskArtifacts/cache.properties.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/.gradle/2.4/taskArtifacts/cache.properties.lock -------------------------------------------------------------------------------- /.gradle/2.4/taskArtifacts/fileHashes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/.gradle/2.4/taskArtifacts/fileHashes.bin -------------------------------------------------------------------------------- /.gradle/2.4/taskArtifacts/fileSnapshots.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/.gradle/2.4/taskArtifacts/fileSnapshots.bin -------------------------------------------------------------------------------- /.gradle/2.4/taskArtifacts/outputFileStates.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/.gradle/2.4/taskArtifacts/outputFileStates.bin -------------------------------------------------------------------------------- /.gradle/2.4/taskArtifacts/taskArtifacts.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/.gradle/2.4/taskArtifacts/taskArtifacts.bin -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | lib_download -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/dictionaries/lizhiyun.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lib_download 2 | ## 效果图 3 | 4 | 5 | 这是一个处理异步下载的库,包含图片加载,多文件多线程断点下载,http和ormgo(马天宇的开源),特点如下: 6 | 7 | 全局公用一个线程池 8 | 9 | 非ImageView的view也支持哦 10 | 11 | 支持预加载: 12 | ```java 13 | BmLoader.preLoad(uri); 14 | ``` 15 | 16 | 支持本地和网络图片,径格式示例为: 17 | ```xml 18 | "http://img.blog.csdn.net/20160114230048304", 19 | "assets://anim.gif", 20 | "drawable://"+R.drawable.anim, 21 | "file:///mnt/sdcard/paint.png", 22 | ``` 23 | 优化了listview等快速滑动时的图片加载 24 | 25 | 圆角图采用了性能最优的方案 26 | 27 | 可设置模糊效果和头像识别自动剪裁功能 28 | 29 | 通过各种手段保障gridview等在有圆角和大量gif的情况下快速滑动时也能依旧极度流畅。 30 | 31 | 通过自定义的方式确保默认的加载中和加载失败的图片在任何形状的view中都能显示完整并且大小适当。 32 | 33 | 如果view使用或者继承download.imageLoader.view.GifMovieView这个类的话支持gif图,否则只能用回调自己自定义view实现。 34 | 35 | 设置自定义显示方法这样就可以实现各种功能如给textviw设置上下左右的图,给子view设置网络图片,给remoteview设置网络图片等等。 36 | ```java 37 | Image.with(this).load("http://img.my.csdn.net/uploads/201407/26/1406383265_8550.jpg") 38 | .size(130, 130).blur(false) 39 | .customDisplay(new CustomDisplayMethod() { 40 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) 41 | @Override 42 | public void display(Drawable bitmap, Movie movie) { 43 | mTv.setCompoundDrawablesRelativeWithIntrinsicBounds(bitmap, null, null, null); 44 | } 45 | }).into(mTv); 46 | ``` 47 | 48 | 如果使用类download.imageLoader.view.GifMovieView,调用方法更简单了: 49 | ```java 50 | view.rectangle().face(true).blur(false).setBorder(Color.BLUE, 15f).bind(uri); 51 | view.circle().blur(false).setBorder(Color.BLACK, 0f).bind(uri); 52 | view.round(50).blur(false).setBorder(Color.GREEN, 0f).bind(uri); 53 | view.bind(uri) 54 | 55 | ``` 56 | 57 | 断点下载 :可多界面监听同一下载,恢复网络时自动恢复之前失败的下载,可指定各自的下载目录,带md5校验 58 | ```java 59 | DownFileManager.getInstance(FileDownloadActivity.this).download(entry.url,new DownloadListener() { 60 | @Override 61 | public void success(String path) { 62 | 63 | } 64 | 65 | @Override 66 | public void progress(int currentLen, int totalLen) { 67 | 68 | } 69 | 70 | @Override 71 | public void error(String errror) { 72 | 73 | } 74 | 75 | @Override 76 | public void pause() { 77 | 78 | } 79 | 80 | @Override 81 | public void cancel() { 82 | 83 | } 84 | }); 85 | 86 | ``` 87 | 88 | HTTP模块: 89 | ```java 90 | Http.with(this).url(url).progressDownload(new OnProgressDownloadListener() { 91 | @Override 92 | public void onProgressDownload(int curLength, int totalLength) { 93 | 94 | } 95 | }).progressUpdate(new OnProgressUpdateListener() { 96 | @Override 97 | public void onProgressUpdate(int curLength, int totalLength) { 98 | 99 | } 100 | }).callback(new JsonReaderListCallback("data") { 101 | @Override 102 | public void onSuccess(ArrayList result) { 103 | Log.e("test",""+result.size()); 104 | for (int i = 0;i 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | android { 3 | compileSdkVersion 19 4 | buildToolsVersion "23.0.3" 5 | 6 | defaultConfig { 7 | applicationId "com.example.ui" 8 | minSdkVersion 8 9 | targetSdkVersion 17 10 | } 11 | 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 16 | } 17 | } 18 | } 19 | 20 | dependencies { 21 | compile files('libs/gson-2.0.jar') 22 | } -------------------------------------------------------------------------------- /app/libs/gson-2.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/libs/gson-2.0.jar -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/assets/a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/assets/a.gif -------------------------------------------------------------------------------- /app/src/main/assets/anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/assets/anim.gif -------------------------------------------------------------------------------- /app/src/main/assets/c.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/assets/c.gif -------------------------------------------------------------------------------- /app/src/main/assets/d.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/assets/d.gif -------------------------------------------------------------------------------- /app/src/main/assets/e.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/assets/e.gif -------------------------------------------------------------------------------- /app/src/main/assets/f.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/assets/f.gif -------------------------------------------------------------------------------- /app/src/main/assets/g.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/assets/g.gif -------------------------------------------------------------------------------- /app/src/main/assets/h.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/assets/h.gif -------------------------------------------------------------------------------- /app/src/main/java/com/example/ui/App.java: -------------------------------------------------------------------------------- 1 | package com.example.ui; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.app.Application; 6 | import android.content.ComponentCallbacks; 7 | import android.content.res.Configuration; 8 | import android.os.Build; 9 | import android.os.Bundle; 10 | 11 | /** 12 | * Created by lizhiyun on 16/6/15. 13 | */ 14 | public class App extends Application { 15 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 16 | @Override 17 | public void onCreate() { 18 | super.onCreate(); 19 | 20 | 21 | //在这里为应用设置异常处理程序,然后我们的程序才能捕获未处理的异常 22 | CrashHandler crashHandler = CrashHandler.getInstance(); 23 | crashHandler.init(this); 24 | this.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { 25 | @Override 26 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 27 | 28 | } 29 | 30 | @Override 31 | public void onActivityStarted(Activity activity) { 32 | 33 | } 34 | 35 | @Override 36 | public void onActivityResumed(Activity activity) { 37 | 38 | } 39 | 40 | @Override 41 | public void onActivityPaused(Activity activity) { 42 | 43 | } 44 | 45 | @Override 46 | public void onActivityStopped(Activity activity) { 47 | 48 | } 49 | 50 | @Override 51 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) { 52 | 53 | } 54 | 55 | @Override 56 | public void onActivityDestroyed(Activity activity) { 57 | 58 | } 59 | }); 60 | this.registerComponentCallbacks(new ComponentCallbacks() { 61 | @Override 62 | public void onConfigurationChanged(Configuration newConfig) { 63 | 64 | } 65 | 66 | @Override 67 | public void onLowMemory() { 68 | 69 | } 70 | }); 71 | 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/ui/AppEntry.java: -------------------------------------------------------------------------------- 1 | package com.example.ui; 2 | 3 | import java.io.Serializable; 4 | 5 | import download.http.entity.SimpleJsonReader; 6 | import download.otherFileLoader.request.DownFile; 7 | 8 | /** 9 | * Created by Stay on 18/8/15. 10 | * Powered by www.stay4it.com 11 | */ 12 | public class AppEntry extends SimpleJsonReader implements Serializable { 13 | public String name; 14 | public String icon; 15 | public String size; 16 | public String desc; 17 | public String url; 18 | public DownFile.DownloadStatus state = DownFile.DownloadStatus.IDLE; 19 | public int downLength; 20 | public int totalLength; 21 | 22 | 23 | @Override 24 | public String toString() { 25 | return name + "-----" + desc + "-----" + url; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/ui/CrashHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.ui; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.File; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | import java.lang.Thread.UncaughtExceptionHandler; 9 | import java.text.SimpleDateFormat; 10 | import java.util.Date; 11 | 12 | import android.content.Context; 13 | import android.content.pm.PackageInfo; 14 | import android.content.pm.PackageManager; 15 | import android.content.pm.PackageManager.NameNotFoundException; 16 | import android.os.Build; 17 | import android.os.Environment; 18 | import android.os.Process; 19 | import android.util.Log; 20 | 21 | public class CrashHandler implements UncaughtExceptionHandler { 22 | private static final String TAG = "CrashHandler"; 23 | private static final boolean DEBUG = true; 24 | 25 | private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/CrashTest/log/"; 26 | private static final String FILE_NAME = "crash"; 27 | private static final String FILE_NAME_SUFFIX = ".trace"; 28 | 29 | private static CrashHandler sInstance = new CrashHandler(); 30 | private UncaughtExceptionHandler mDefaultCrashHandler; 31 | private Context mContext; 32 | 33 | private CrashHandler() { 34 | } 35 | 36 | public static CrashHandler getInstance() { 37 | return sInstance; 38 | } 39 | 40 | public void init(Context context) { 41 | mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler(); 42 | Thread.setDefaultUncaughtExceptionHandler(this); 43 | mContext = context.getApplicationContext(); 44 | } 45 | 46 | /** 47 | * 这个是最关键的函数,当程序中有未被捕获的异常,系统将会自动调用#uncaughtException方法 48 | * thread为出现未捕获异常的线程,ex为未捕获的异常,有了这个ex,我们就可以得到异常信息。 49 | */ 50 | @Override 51 | public void uncaughtException(Thread thread, Throwable ex) { 52 | try { 53 | //导出异常信息到SD卡中 54 | dumpExceptionToSDCard(ex); 55 | uploadExceptionToServer(); 56 | //这里可以通过网络上传异常信息到服务器,便于开发人员分析日志从而解决bug 57 | } catch (IOException e) { 58 | e.printStackTrace(); 59 | } 60 | 61 | ex.printStackTrace(); 62 | 63 | //如果系统提供了默认的异常处理器,则交给系统去结束我们的程序,否则就由我们自己结束自己 64 | if (mDefaultCrashHandler != null) { 65 | mDefaultCrashHandler.uncaughtException(thread, ex); 66 | } else { 67 | Process.killProcess(Process.myPid()); 68 | } 69 | 70 | } 71 | 72 | private void dumpExceptionToSDCard(Throwable ex) throws IOException { 73 | //如果SD卡不存在或无法使用,则无法把异常信息写入SD卡 74 | if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 75 | if (DEBUG) { 76 | Log.w(TAG, "sdcard unmounted,skip dump exception"); 77 | return; 78 | } 79 | } 80 | 81 | File dir = new File(PATH); 82 | if (!dir.exists()) { 83 | dir.mkdirs(); 84 | } 85 | long current = System.currentTimeMillis(); 86 | String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current)); 87 | File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX); 88 | 89 | try { 90 | PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file))); 91 | pw.println(time); 92 | dumpPhoneInfo(pw); 93 | pw.println(); 94 | ex.printStackTrace(pw); 95 | pw.close(); 96 | } catch (Exception e) { 97 | Log.e(TAG, "dump crash info failed"); 98 | } 99 | } 100 | 101 | private void dumpPhoneInfo(PrintWriter pw) throws NameNotFoundException { 102 | PackageManager pm = mContext.getPackageManager(); 103 | PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES); 104 | pw.print("App Version: "); 105 | pw.print(pi.versionName); 106 | pw.print('_'); 107 | pw.println(pi.versionCode); 108 | 109 | //android版本号 110 | pw.print("OS Version: "); 111 | pw.print(Build.VERSION.RELEASE); 112 | pw.print("_"); 113 | pw.println(Build.VERSION.SDK_INT); 114 | 115 | //手机制造商 116 | pw.print("Vendor: "); 117 | pw.println(Build.MANUFACTURER); 118 | 119 | //手机型号 120 | pw.print("Model: "); 121 | pw.println(Build.MODEL); 122 | 123 | //cpu架构 124 | pw.print("CPU ABI: "); 125 | pw.println(Build.CPU_ABI); 126 | } 127 | 128 | private void uploadExceptionToServer() { 129 | //TODO Upload Exception Message To Your Web Server 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/ui/FileDownloadActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.ui; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.text.format.Formatter; 6 | import android.util.Log; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.AdapterView; 11 | import android.widget.BaseAdapter; 12 | import android.widget.Button; 13 | import android.widget.ListView; 14 | import android.widget.TextView; 15 | import java.util.ArrayList; 16 | import download.http.core.Http; 17 | import download.http.listener.JsonReaderListCallback; 18 | import download.otherFileLoader.db.DownFileManager; 19 | import download.otherFileLoader.listener.DownloadListener; 20 | import download.otherFileLoader.request.DownFile; 21 | import download.otherFileLoader.util.ToastUtils; 22 | 23 | public class FileDownloadActivity extends Activity implements View.OnClickListener{ 24 | 25 | 26 | DownFileManager mDownloadManager; 27 | 28 | Button pauseall; 29 | 30 | 31 | private ListView mDownloadLsv; 32 | private DownloadAdapter adapter; 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | super.onCreate(savedInstanceState); 37 | 38 | setContentView(R.layout.activity_filedownload); 39 | mDownloadManager = DownFileManager.getInstance(this); 40 | 41 | pauseall = (Button) findViewById(R.id.pauseall); 42 | pauseall.setOnClickListener(this); 43 | 44 | mDownloadLsv = (ListView) findViewById(R.id.mDownloadLsv); 45 | mDownloadLsv.setOnItemClickListener(new AdapterView.OnItemClickListener() { 46 | @Override 47 | public void onItemClick(AdapterView parent, View view, int position, long id) { 48 | 49 | } 50 | }); 51 | 52 | String url = "http://api.stay4it.com/v1/public/core/?service=downloader.applist"; 53 | Http.with(this).url(url).callback(new JsonReaderListCallback("data") { 54 | @Override 55 | public ArrayList onPost(ArrayList appEntries) { 56 | for (int i = 0; i < appEntries.size(); i++) { 57 | 58 | DownFile downFile = DownFileManager.getInstance(FileDownloadActivity.this).initData 59 | (appEntries.get(i).url, null); 60 | if (downFile != null){ 61 | appEntries.get(i).downLength = downFile.downLength; 62 | appEntries.get(i).totalLength = downFile.totalLength; 63 | appEntries.get(i).state = downFile.state; 64 | } 65 | } 66 | return super.onPost(appEntries); 67 | } 68 | 69 | @Override 70 | public void onSuccess(ArrayList result) { 71 | Log.e("test", "" + result.size()); 72 | 73 | adapter = new DownloadAdapter(result); 74 | mDownloadLsv.setAdapter(adapter); 75 | 76 | for (final AppEntry entry:result 77 | ) { 78 | if (entry.state == DownFile.DownloadStatus.DOWNLOADING || entry.state == DownFile.DownloadStatus.WAITING || entry.state == DownFile.DownloadStatus.ERROR){ 79 | DownFileManager.getInstance(FileDownloadActivity.this).download(entry.url,getDownloadListener(entry)); 80 | } 81 | } 82 | } 83 | }).get(); 84 | } 85 | Boolean isVisiable = false; 86 | 87 | @Override 88 | protected void onPause() { 89 | super.onPause(); 90 | isVisiable = false; 91 | } 92 | 93 | @Override 94 | protected void onResume() { 95 | super.onResume(); 96 | isVisiable = true; 97 | } 98 | 99 | 100 | @Override 101 | public void onClick(View v) { 102 | switch (v.getId()){ 103 | case R.id.pauseall: 104 | if (pauseall.getText().equals("pauseall")){ 105 | pauseall.setText("recoverall"); 106 | mDownloadManager.pauseAll(); 107 | }else { 108 | pauseall.setText("pauseall"); 109 | mDownloadManager.recoverAll(); 110 | } 111 | break; 112 | } 113 | 114 | } 115 | class DownloadAdapter extends BaseAdapter { 116 | 117 | public ArrayList applist; 118 | public DownloadAdapter(ArrayList list){ 119 | this.applist = list; 120 | } 121 | 122 | private ViewHolder holder; 123 | 124 | @Override 125 | public int getCount() { 126 | return applist != null ? applist.size() : 0; 127 | } 128 | 129 | @Override 130 | public Object getItem(int position) { 131 | return applist.get(position); 132 | } 133 | 134 | @Override 135 | public long getItemId(int position) { 136 | return position; 137 | } 138 | 139 | @Override 140 | public View getView(int position, View convertView, ViewGroup parent) { 141 | if (convertView == null || convertView.getTag() == null) { 142 | convertView = LayoutInflater.from(FileDownloadActivity.this).inflate(R.layout.activity_applist_item, null); 143 | holder = new ViewHolder(); 144 | holder.mDownloadBtn = (Button) convertView.findViewById(R.id.mDownloadBtn); 145 | holder.mDownloadLabel = (TextView) convertView.findViewById(R.id.mDownloadLabel); 146 | holder.mDownloadStatusLabel = (TextView) convertView.findViewById(R.id.mDownloadStatusLabel); 147 | convertView.setTag(holder); 148 | } else { 149 | holder = (ViewHolder) convertView.getTag(); 150 | } 151 | final AppEntry entry = applist.get(position); 152 | 153 | 154 | 155 | holder.mDownloadLabel.setText(entry.name + " " + entry.size + "\n" + entry.desc); 156 | 157 | holder.mDownloadStatusLabel.setText(entry.state + "\n" 158 | + Formatter.formatShortFileSize(getApplicationContext(), entry.downLength) 159 | + "/" + Formatter.formatShortFileSize(getApplicationContext(), entry.totalLength)); 160 | holder.mDownloadBtn.setOnClickListener(new View.OnClickListener() { 161 | @Override 162 | public void onClick(View v) { 163 | if (entry.state != DownFile.DownloadStatus.DOWNLOADING && entry.state != DownFile.DownloadStatus.FINISH && entry.state != DownFile.DownloadStatus.WAITING) { 164 | DownFileManager.getInstance(FileDownloadActivity.this).download(entry.url,getDownloadListener(entry)); 165 | } else if (entry.state == DownFile.DownloadStatus.FINISH) { 166 | //完成 167 | } else if (entry.state == DownFile.DownloadStatus.DOWNLOADING || entry.state == DownFile.DownloadStatus.WAITING) { 168 | DownFileManager.getInstance(FileDownloadActivity.this).pause(entry.url); 169 | } 170 | } 171 | }); 172 | return convertView; 173 | } 174 | } 175 | public DownloadListener getDownloadListener(final AppEntry entry){ 176 | 177 | return new DownloadListener() { 178 | @Override 179 | public void success(String path) { 180 | entry.state = DownFile.DownloadStatus.FINISH; 181 | adapter.notifyDataSetChanged(); 182 | ToastUtils.showToast(FileDownloadActivity.this,"已完成"+path); 183 | } 184 | 185 | @Override 186 | public void progress(int currentLen, int totalLen) { 187 | if (!isVisiable){ 188 | return; 189 | } 190 | entry.downLength = currentLen; 191 | entry.totalLength = totalLen; 192 | entry.state = DownFile.DownloadStatus.DOWNLOADING; 193 | adapter.notifyDataSetChanged(); 194 | } 195 | 196 | @Override 197 | public void error() { 198 | entry.state = DownFile.DownloadStatus.ERROR; 199 | adapter.notifyDataSetChanged(); 200 | } 201 | 202 | @Override 203 | public void waiting() { 204 | entry.state = DownFile.DownloadStatus.WAITING; 205 | adapter.notifyDataSetChanged(); 206 | } 207 | 208 | @Override 209 | public void pause() { 210 | entry.state = DownFile.DownloadStatus.PAUSE; 211 | adapter.notifyDataSetChanged(); 212 | } 213 | 214 | @Override 215 | public void cancel() { 216 | entry.state = DownFile.DownloadStatus.CANCEL; 217 | adapter.notifyDataSetChanged(); 218 | } 219 | }; 220 | } 221 | 222 | static class ViewHolder { 223 | TextView mDownloadLabel; 224 | TextView mDownloadStatusLabel; 225 | Button mDownloadBtn; 226 | } 227 | 228 | } 229 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/ui/MyUtils.java: -------------------------------------------------------------------------------- 1 | package com.example.ui; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | import android.app.ActivityManager; 8 | import android.app.ActivityManager.RunningAppProcessInfo; 9 | import android.content.Context; 10 | import android.content.res.TypedArray; 11 | import android.net.ConnectivityManager; 12 | import android.net.NetworkInfo; 13 | import android.util.DisplayMetrics; 14 | import android.util.TypedValue; 15 | import android.view.WindowManager; 16 | 17 | public class MyUtils { 18 | 19 | public static String getProcessName(Context cxt, int pid) { 20 | ActivityManager am = (ActivityManager) cxt 21 | .getSystemService(Context.ACTIVITY_SERVICE); 22 | List runningApps = am.getRunningAppProcesses(); 23 | if (runningApps == null) { 24 | return null; 25 | } 26 | for (RunningAppProcessInfo procInfo : runningApps) { 27 | if (procInfo.pid == pid) { 28 | return procInfo.processName; 29 | } 30 | } 31 | return null; 32 | } 33 | 34 | public static void close(Closeable closeable) { 35 | try { 36 | if (closeable != null) { 37 | closeable.close(); 38 | } 39 | } catch (IOException e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | 44 | public static DisplayMetrics getScreenMetrics(Context context) { 45 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 46 | DisplayMetrics dm = new DisplayMetrics(); 47 | wm.getDefaultDisplay().getMetrics(dm); 48 | return dm; 49 | } 50 | 51 | public static float dp2px(Context context, float dp) { 52 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, 53 | context.getResources().getDisplayMetrics()); 54 | } 55 | 56 | public static boolean isWifi(Context context) { 57 | ConnectivityManager connectivityManager = (ConnectivityManager) context 58 | .getSystemService(Context.CONNECTIVITY_SERVICE); 59 | NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo(); 60 | if (activeNetInfo != null 61 | && activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI) { 62 | return true; 63 | } 64 | return false; 65 | } 66 | 67 | public static void executeInThread(Runnable runnable) { 68 | new Thread(runnable).start(); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/litesuits/go/OverloadPolicy.java: -------------------------------------------------------------------------------- 1 | package com.litesuits.go; 2 | 3 | 4 | /** 5 | * Policy of thread-pool-executor overload. 6 | * 7 | * @author MaTianyu 8 | * @date 2015-04-23 9 | */ 10 | public enum OverloadPolicy { 11 | DiscardNewTaskInQueue, 12 | DiscardOldTaskInQueue, 13 | DiscardCurrentTask, 14 | CallerRuns, 15 | ThrowExecption 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/litesuits/go/PriorityRunnable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 litesuits.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.litesuits.go; 17 | 18 | /** 19 | * @author MaTianyu 20 | * @date 2015-04-23 21 | */ 22 | public abstract class PriorityRunnable implements Runnable { 23 | 24 | int priority; 25 | 26 | protected PriorityRunnable(int priority) { 27 | this.priority = priority; 28 | } 29 | 30 | public int getPriority() { 31 | return priority; 32 | } 33 | 34 | public void setPriority(int priority) { 35 | this.priority = priority; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/litesuits/go/SchedulePolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 litesuits.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.litesuits.go; 17 | 18 | /** 19 | * @author MaTianyu 20 | * @date 2015-04-23 21 | */ 22 | public enum SchedulePolicy { 23 | LastInFirstRun, 24 | FirstInFistRun 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/litesuits/go/utils/GoUtil.java: -------------------------------------------------------------------------------- 1 | package com.litesuits.go.utils; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.Dialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | import android.util.Log; 8 | 9 | import java.io.File; 10 | import java.io.FileFilter; 11 | import java.text.SimpleDateFormat; 12 | import java.util.Date; 13 | import java.util.regex.Pattern; 14 | 15 | /** 16 | * @author MaTianyu 17 | * @date 2015-04-21 18 | */ 19 | public class GoUtil { 20 | private static final String TAG = GoUtil.class.getSimpleName(); 21 | 22 | private static final String PATH_CPU = "/sys/devices/system/cpu/"; 23 | private static final String CPU_FILTER = "cpu[0-9]+"; 24 | private static int CPU_CORES = 0; 25 | 26 | public static String formatDate(long millis) { 27 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 28 | return format.format(new Date(millis)); 29 | } 30 | 31 | /** 32 | * Get available processors. 33 | */ 34 | public static int getProcessorsCount() { 35 | return Runtime.getRuntime().availableProcessors(); 36 | } 37 | 38 | /** 39 | * Gets the number of cores available in this device, across all processors. 40 | * Requires: Ability to peruse the filesystem at "/sys/devices/system/cpu" 41 | * 42 | * @return The number of cores, or available processors if failed to get result 43 | */ 44 | public static int getCoresNumbers() { 45 | if (CPU_CORES > 0) { 46 | return CPU_CORES; 47 | } 48 | //Private Class to display only CPU devices in the directory listing 49 | class CpuFilter implements FileFilter { 50 | @Override 51 | public boolean accept(File pathname) { 52 | //Check if filename is "cpu", followed by a single digit number 53 | if (Pattern.matches(CPU_FILTER, pathname.getName())) { 54 | return true; 55 | } 56 | return false; 57 | } 58 | } 59 | try { 60 | //Get directory containing CPU info 61 | File dir = new File(PATH_CPU); 62 | //Filter to only list the devices we care about 63 | File[] files = dir.listFiles(new CpuFilter()); 64 | //Return the number of cores (virtual CPU devices) 65 | CPU_CORES = files.length; 66 | } catch (Exception e) { 67 | e.printStackTrace(); 68 | } 69 | if (CPU_CORES < 1) { 70 | CPU_CORES = Runtime.getRuntime().availableProcessors(); 71 | } 72 | if (CPU_CORES < 1) { 73 | CPU_CORES = 1; 74 | } 75 | Log.i(TAG, "CPU cores: " + CPU_CORES); 76 | return CPU_CORES; 77 | } 78 | 79 | public static AlertDialog.Builder dialogBuilder(Context context, String title, String msg) { 80 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 81 | if (msg != null) { 82 | builder.setMessage(msg); 83 | } 84 | if (title != null) { 85 | builder.setTitle(title); 86 | } 87 | return builder; 88 | } 89 | 90 | 91 | public static Dialog showTips(Context context, String title, String des) { 92 | return showTips(context, title, des, null, null); 93 | } 94 | 95 | public static Dialog showTips(Context context, String title, String des, String btn, 96 | DialogInterface.OnDismissListener dismissListener) { 97 | AlertDialog.Builder builder = dialogBuilder(context, title, des); 98 | builder.setCancelable(true); 99 | builder.setPositiveButton(btn, null); 100 | Dialog dialog = builder.show(); 101 | dialog.setCanceledOnTouchOutside(true); 102 | dialog.setOnDismissListener(dismissListener); 103 | return dialog; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/core/Http.java: -------------------------------------------------------------------------------- 1 | package download.http.core; 2 | 3 | import android.content.Context; 4 | 5 | import download.http.request.RequestBuilder; 6 | import download.utils.Util; 7 | 8 | /** 9 | * Created by lizhiyun on 16/6/7. 10 | */ 11 | public class Http { 12 | public static String tempFileRootPath; 13 | public static RequestBuilder with(Context context){ 14 | if (tempFileRootPath == null){ 15 | tempFileRootPath = Util.getDiskCacheDir(context,"temp").getAbsolutePath(); 16 | } 17 | return new RequestBuilder(context); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/core/HttpManager.java: -------------------------------------------------------------------------------- 1 | package download.http.core; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.os.Build; 6 | 7 | import com.litesuits.go.OverloadPolicy; 8 | import com.litesuits.go.SchedulePolicy; 9 | import com.litesuits.go.SmartExecutor; 10 | 11 | import download.http.request.Request; 12 | import download.http.request.RequestBuilder; 13 | 14 | public class HttpManager { 15 | private SmartExecutor executor; 16 | private static class InstanceHoler { 17 | private static final HttpManager instance = new HttpManager(); 18 | } 19 | public static HttpManager getInstance() { 20 | return InstanceHoler.instance; 21 | } 22 | 23 | private final int threadCount = 3; 24 | 25 | private HttpManager() { 26 | executor = new SmartExecutor(threadCount, 200); 27 | executor.setSchedulePolicy(SchedulePolicy.FirstInFistRun); 28 | executor.setOverloadPolicy(OverloadPolicy.DiscardOldTaskInQueue); 29 | executor.setDebug(false); 30 | } 31 | 32 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 33 | public void request(final Request request) { 34 | executor.execute(new HttpTask(request)); 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/download/http/core/HttpTask.java: -------------------------------------------------------------------------------- 1 | package download.http.core; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.os.Message; 6 | import android.util.Log; 7 | 8 | import java.net.HttpURLConnection; 9 | 10 | import download.http.exception.AppException; 11 | import download.http.listener.OnProgressDownloadListener; 12 | import download.http.request.Request; 13 | import download.http.util.HttpUrlConnectionUtil; 14 | import download.imageLoader.listener.OnProgressUpdatedListener; 15 | 16 | /** 17 | * Created by lizhiyun on 16/5/23. 18 | */ 19 | public class HttpTask implements Runnable { 20 | public final static int UPDATEPROGRESS = 0; 21 | public final static int DOWNLOADPROGRESS = 1; 22 | public final static int RESULT = 2; 23 | public Request mRequest; 24 | int retryTime; 25 | private HttpTask(){} 26 | public HttpTask(Request request){ 27 | super(); 28 | this.mRequest = request; 29 | } 30 | 31 | @Override 32 | public void run() { 33 | notifyFinish(request(retryTime)); 34 | } 35 | 36 | private Object request(int retryTime) { 37 | try { 38 | HttpURLConnection connection = HttpUrlConnectionUtil.execute(mRequest, new OnProgressUpdatedListener() { 39 | @Override 40 | public void onProgressUpdated(int curLen, int totalLen) { 41 | if (mRequest.onProgressUpdatedListener != null){ 42 | updateProgress(curLen,totalLen); 43 | } 44 | } 45 | }); 46 | if (mRequest.onProgressDownloadListener != null){ 47 | return mRequest.getCallback().parse(mRequest, connection, new OnProgressDownloadListener() { 48 | @Override 49 | public void onProgressDownload(int curLength, int totalLength) { 50 | downloadProgress(curLength, totalLength); 51 | } 52 | }); 53 | }else { 54 | return mRequest.getCallback().parse(mRequest, connection, null); 55 | } 56 | } catch (AppException e) { 57 | if (e.errorType == AppException.ErrorType.IO){ 58 | if (retryTime < mRequest.getMaxRetryTime()){ 59 | retryTime++; 60 | return request(retryTime); 61 | } 62 | } 63 | return e; 64 | } 65 | } 66 | 67 | private void updateProgress(int curLength, int totalLength) { 68 | Message message = Message.obtain(); 69 | message.what = UPDATEPROGRESS; 70 | message.obj = mRequest; 71 | message.arg1 = curLength; 72 | message.arg2 = totalLength; 73 | if (sUIHandler != null){ 74 | sUIHandler.sendMessage(message); 75 | } 76 | } 77 | private void downloadProgress(int curLength, int totalLength) { 78 | Message message = Message.obtain(); 79 | message.what = DOWNLOADPROGRESS; 80 | message.obj = mRequest; 81 | message.arg1 = curLength; 82 | message.arg2 = totalLength; 83 | if (sUIHandler != null){ 84 | sUIHandler.sendMessage(message); 85 | } 86 | } 87 | 88 | private void notifyFinish(Object object) { 89 | Message message = Message.obtain(); 90 | message.what = RESULT; 91 | mRequest.setReturnObject(object); 92 | message.obj = mRequest; 93 | if (sUIHandler != null){ 94 | sUIHandler.sendMessage(message); 95 | } 96 | } 97 | private static Handler sUIHandler = new Handler(Looper.getMainLooper()) { 98 | public void handleMessage(Message msg) { 99 | final Request request = (Request) msg.obj; 100 | switch (msg.what) { 101 | case HttpTask.UPDATEPROGRESS: 102 | request.onProgressUpdatedListener.onProgressUpdate(msg.arg1, msg.arg2); 103 | break; 104 | case HttpTask.DOWNLOADPROGRESS: 105 | request.onProgressDownloadListener.onProgressDownload(msg.arg1, msg.arg2); 106 | break; 107 | case HttpTask.RESULT: 108 | if (request.getReturnObject() instanceof AppException){ 109 | if (request.isCanceled()){ 110 | request.getCallback().onCancel(); 111 | return; 112 | } 113 | if (request.getGlobalExceptionListener() != null){ 114 | if (!request.getGlobalExceptionListener().handleException((AppException) request.getReturnObject())){ 115 | request.getCallback().onFailure((AppException) request.getReturnObject()); 116 | } 117 | } 118 | }else { 119 | request.getCallback().onSuccess(request.getReturnObject()); 120 | } 121 | break; 122 | 123 | default: 124 | break; 125 | } 126 | 127 | } 128 | 129 | }; 130 | 131 | } -------------------------------------------------------------------------------- /app/src/main/java/download/http/entity/CustomJsonReader.java: -------------------------------------------------------------------------------- 1 | package download.http.entity; 2 | 3 | import com.google.gson.stream.JsonReader; 4 | 5 | import download.http.exception.AppException; 6 | 7 | /** 8 | * Created by lizhiyun on 16/6/10. 9 | */ 10 | public interface CustomJsonReader { 11 | public abstract void readJsonReader(JsonReader jsonReader) throws AppException; 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/entity/FileEntity.java: -------------------------------------------------------------------------------- 1 | package download.http.entity; 2 | 3 | /** 4 | * @author Stay 5 | * @version create time:Mar 11, 2014 9:06:17 PM 6 | */ 7 | public class FileEntity { 8 | 9 | private String fileName; 10 | private String fileType; 11 | private String filePath; 12 | 13 | public String getFileName() { 14 | return fileName; 15 | } 16 | 17 | public void setFileName(String fileName) { 18 | this.fileName = fileName; 19 | } 20 | 21 | public String getFileType() { 22 | return fileType; 23 | } 24 | 25 | public void setFileType(String fileType) { 26 | this.fileType = fileType; 27 | } 28 | 29 | public String getFilePath() { 30 | return filePath; 31 | } 32 | 33 | public void setFilePath(String filePath) { 34 | this.filePath = filePath; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/entity/ResultData.java: -------------------------------------------------------------------------------- 1 | package download.http.entity; 2 | 3 | /** 4 | * Created by lizhiyun on 16/6/6. 5 | */ 6 | public class ResultData extends SimpleJsonReader { 7 | public int ret; 8 | public User data; 9 | public String msg; 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/entity/SimpleJsonReader.java: -------------------------------------------------------------------------------- 1 | package download.http.entity; 2 | 3 | 4 | import com.google.gson.stream.JsonReader; 5 | 6 | import java.io.IOException; 7 | import java.lang.reflect.Field; 8 | 9 | import download.http.exception.AppException; 10 | 11 | /** 12 | * Created by lizhiyun on 16/6/10. 13 | */ 14 | public class SimpleJsonReader { 15 | Field[] fields = null; 16 | public void readFromJsonReader(JsonReader reader) throws AppException{ 17 | try { 18 | reader.beginObject(); 19 | String node; 20 | while (reader.hasNext()){ 21 | node = reader.nextName(); 22 | Boolean isReaded = false; 23 | if (fields == null){ 24 | fields = this.getClass().getDeclaredFields(); 25 | } 26 | for (Field field: fields 27 | ) { 28 | if (field.getName().equals(node)){ 29 | field.setAccessible(true); 30 | Class field_class = field.getType(); 31 | if (field_class == Integer.TYPE){ 32 | field.set(this, reader.nextInt()); 33 | isReaded = true; 34 | }else if (field_class == Long.TYPE){ 35 | field.set(this, reader.nextLong()); 36 | isReaded = true; 37 | }else if (field_class == Boolean.TYPE){ 38 | field.set(this, reader.nextBoolean()); 39 | isReaded = true; 40 | }else if (field_class == String.class){ 41 | field.set(this, reader.nextString()); 42 | isReaded = true; 43 | }else if (field_class == Double.TYPE){ 44 | field.set(this, reader.nextDouble()); 45 | isReaded = true; 46 | } 47 | else if (field_class.getSuperclass() == SimpleJsonReader.class){ 48 | SimpleJsonReader baseJsonReader = (SimpleJsonReader) field_class.newInstance(); 49 | baseJsonReader.readFromJsonReader(reader); 50 | field.set(this, baseJsonReader); 51 | isReaded = true; 52 | } 53 | } 54 | } 55 | if (!isReaded){ 56 | reader.skipValue(); 57 | } 58 | } 59 | reader.endObject(); 60 | }catch (IOException e){ 61 | throw new AppException(AppException.ErrorType.JSON,e.getMessage()); 62 | } catch (IllegalAccessException e) { 63 | throw new AppException(AppException.ErrorType.JSON,e.getMessage()); 64 | } 65 | catch (InstantiationException e) { 66 | throw new AppException(AppException.ErrorType.JSON,e.getMessage()); 67 | } 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/entity/User.java: -------------------------------------------------------------------------------- 1 | package download.http.entity; 2 | 3 | 4 | /** 5 | * Created by lizhiyun on 16/6/3. 6 | */ 7 | public class User extends SimpleJsonReader { 8 | public String id; 9 | public String account; 10 | public String email; 11 | public String username; 12 | public String token; 13 | 14 | @Override 15 | public String toString() { 16 | return username+" "+email; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/exception/AppException.java: -------------------------------------------------------------------------------- 1 | package download.http.exception; 2 | 3 | /** 4 | * Created by lizhiyun on 16/6/5. 5 | */ 6 | public class AppException extends Exception { 7 | 8 | public int statusCode; 9 | public String responseMessage; 10 | public enum ErrorType{TIMEOUT,SERVER,JSON,IO,FILENOTFOUND, UPLOAD, MANUAL, CANCEL} 11 | 12 | public ErrorType errorType; 13 | public AppException(int statusCode, String responseMessage){ 14 | super(); 15 | this.statusCode = statusCode; 16 | this.responseMessage = responseMessage; 17 | this.errorType = ErrorType.SERVER; 18 | } 19 | public AppException(ErrorType errorType, String responseMessage){ 20 | super(responseMessage); 21 | this.errorType = errorType; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/exception/IfNeedLoginGlobalException.java: -------------------------------------------------------------------------------- 1 | package download.http.exception; 2 | 3 | 4 | import download.http.listener.OnGlobalExceptionListener; 5 | 6 | /** 7 | * Created by lizhiyun on 16/6/7. 8 | */ 9 | public class IfNeedLoginGlobalException implements OnGlobalExceptionListener{ 10 | @Override 11 | public boolean handleException(AppException e) { 12 | if (e.statusCode == 403){ 13 | //TODO ... 14 | 15 | return true; 16 | } 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/listener/AbsCallback.java: -------------------------------------------------------------------------------- 1 | package download.http.listener; 2 | 3 | import org.apache.http.HttpStatus; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.FileOutputStream; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.io.OutputStream; 11 | import java.net.HttpURLConnection; 12 | import java.util.zip.GZIPInputStream; 13 | import java.util.zip.InflaterInputStream; 14 | 15 | import download.http.exception.AppException; 16 | import download.http.request.Request; 17 | import download.utils.Util; 18 | 19 | /** 20 | * Created by lizhiyun on 16/6/3. 21 | */ 22 | public abstract class AbsCallback implements ICallback { 23 | 24 | public void onCancel(){} 25 | public T onPost(T t){return t;} 26 | 27 | public void onFailure(AppException exception){} 28 | 29 | private String path; 30 | 31 | @Override 32 | public T parse(Request request,HttpURLConnection connection, download.http.listener.OnProgressDownloadListener listener) throws AppException { 33 | try { 34 | request.checkIfCancelled(); 35 | int status = connection.getResponseCode(); 36 | InputStream is = null; 37 | BufferedReader reader = null; 38 | String encode = connection.getContentEncoding(); 39 | if (encode != null && "gzip".equalsIgnoreCase(encode)){ 40 | is = new GZIPInputStream(connection.getInputStream()); 41 | }else if (encode != null && "deflate".equalsIgnoreCase(encode)){ 42 | is = new InflaterInputStream(connection.getInputStream()); 43 | }else { 44 | reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); 45 | } 46 | OutputStream out; 47 | if (status == HttpStatus.SC_OK){ 48 | if (path == null){ 49 | out = new ByteArrayOutputStream(); 50 | }else { 51 | out = new FileOutputStream(path); 52 | } 53 | int totalLength = connection.getContentLength(); 54 | int curLength = 0; 55 | if (reader == null){ 56 | byte[] buffer = new byte[2048]; 57 | int len; 58 | while ((len = is.read(buffer)) != -1){ 59 | request.checkIfCancelled(); 60 | out.write(buffer, 0, len); 61 | curLength += len; 62 | if (listener != null){ 63 | listener.onProgressDownload(curLength, totalLength); 64 | } 65 | } 66 | }else { 67 | String s; 68 | while ((s = reader.readLine()) != null){ 69 | request.checkIfCancelled(); 70 | byte[] bytes = s.getBytes(); 71 | out.write(bytes,0,bytes.length); 72 | curLength += bytes.length; 73 | if (listener != null){ 74 | listener.onProgressDownload(curLength, totalLength); 75 | } 76 | } 77 | } 78 | if (is != null){ 79 | is.close(); 80 | } 81 | if (out != null){ 82 | out.flush(); 83 | out.close(); 84 | } 85 | if (reader != null){ 86 | reader.close(); 87 | } 88 | if (path == null){ 89 | String result = new String(((ByteArrayOutputStream)out).toByteArray()).trim(); 90 | return onPost(parseData(result)); 91 | }else { 92 | return onPost(parseData(path)); 93 | } 94 | 95 | }else { 96 | throw new AppException(AppException.ErrorType.SERVER,connection.getResponseMessage()); 97 | } 98 | }catch (Exception e){ 99 | throw new AppException(AppException.ErrorType.IO,e.getMessage()); 100 | } 101 | } 102 | 103 | 104 | protected abstract T parseData(String result) throws AppException; 105 | 106 | public ICallback setCachePath(String path){ 107 | this.path = path; 108 | return this; 109 | }; 110 | 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/listener/CustomJsonReaderCallback.java: -------------------------------------------------------------------------------- 1 | package download.http.listener; 2 | 3 | 4 | import android.annotation.TargetApi; 5 | import android.os.Build; 6 | 7 | import com.google.gson.stream.JsonReader; 8 | 9 | import java.io.File; 10 | import java.io.FileReader; 11 | import java.lang.reflect.ParameterizedType; 12 | import java.lang.reflect.Type; 13 | import java.util.UUID; 14 | 15 | import download.http.core.Http; 16 | import download.http.entity.CustomJsonReader; 17 | import download.http.exception.AppException; 18 | 19 | 20 | /** 21 | * 数据很多的时自己处理数据以防oom 22 | * Created by lizhiyun on 16/6/3. 23 | */ 24 | public abstract class CustomJsonReaderCallback extends download.http.listener.AbsCallback { 25 | public CustomJsonReaderCallback(){ 26 | setCachePath(Http.tempFileRootPath + File.separator + UUID.randomUUID()); 27 | } 28 | 29 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 30 | @Override 31 | protected T parseData(String result) throws AppException{ 32 | try{ 33 | ParameterizedType p = (ParameterizedType) this.getClass().getGenericSuperclass(); 34 | Type type = p.getActualTypeArguments()[0]; 35 | Class clazz = (Class) type; 36 | T t = clazz.newInstance(); 37 | FileReader in = new FileReader(result); 38 | JsonReader reader = new JsonReader(in); 39 | t.readJsonReader(reader); 40 | return t; 41 | }catch (Exception e){ 42 | throw new AppException(AppException.ErrorType.JSON,e.getMessage()); 43 | }finally { 44 | File file = new File(result); 45 | if (file != null && file.exists()){ 46 | file.delete(); 47 | } 48 | } 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/listener/FileCallback.java: -------------------------------------------------------------------------------- 1 | package download.http.listener; 2 | 3 | 4 | 5 | /** 6 | * Created by lizhiyun on 16/6/3. 7 | */ 8 | public abstract class FileCallback extends AbsCallback { 9 | 10 | @Override 11 | protected String parseData(String result) { 12 | return result; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/listener/ICallback.java: -------------------------------------------------------------------------------- 1 | package download.http.listener; 2 | 3 | 4 | import java.net.HttpURLConnection; 5 | 6 | import download.http.exception.AppException; 7 | import download.http.request.Request; 8 | 9 | /** 10 | * Created by lizhiyun on 16/6/3. 11 | */ 12 | public interface ICallback { 13 | 14 | /** 15 | * 获取数据后的耗时预处理,在子线程 16 | * @param t 17 | * @return 18 | */ 19 | T onPost(T t); 20 | void onSuccess(T result); 21 | void onFailure(AppException exception); 22 | void onCancel(); 23 | T parse(Request request, HttpURLConnection connection, OnProgressDownloadListener listener) throws AppException; 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/listener/JsonCallback.java: -------------------------------------------------------------------------------- 1 | package download.http.listener; 2 | 3 | 4 | import com.google.gson.Gson; 5 | 6 | import java.lang.reflect.ParameterizedType; 7 | 8 | 9 | /** 10 | * Created by lizhiyun on 16/6/3. 11 | */ 12 | public abstract class JsonCallback extends AbsCallback { 13 | 14 | @Override 15 | protected T parseData(String result) { 16 | ParameterizedType p = (ParameterizedType) this.getClass().getGenericSuperclass(); 17 | Class c = (Class) p.getActualTypeArguments()[0]; 18 | return (T) new Gson().fromJson(result, c); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/listener/JsonListCallback.java: -------------------------------------------------------------------------------- 1 | package download.http.listener; 2 | 3 | 4 | import android.annotation.TargetApi; 5 | import android.os.Build; 6 | 7 | import com.google.gson.Gson; 8 | import com.google.gson.stream.JsonReader; 9 | 10 | import org.json.JSONArray; 11 | import org.json.JSONObject; 12 | 13 | import java.io.File; 14 | import java.io.FileReader; 15 | import java.lang.reflect.ParameterizedType; 16 | import java.lang.reflect.Type; 17 | import java.util.ArrayList; 18 | import java.util.UUID; 19 | 20 | import download.http.core.Http; 21 | import download.http.entity.SimpleJsonReader; 22 | import download.http.exception.AppException; 23 | 24 | /** 25 | * Created by lizhiyun on 16/6/3. 26 | */ 27 | public abstract class JsonListCallback extends AbsCallback> { 28 | String key=""; 29 | public JsonListCallback(String key){ 30 | this.key = key; 31 | } 32 | 33 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 34 | @Override 35 | protected ArrayList parseData(String result) throws AppException{ 36 | ParameterizedType p = (ParameterizedType) this.getClass().getGenericSuperclass(); 37 | Class c = (Class) p.getActualTypeArguments()[0]; 38 | ArrayList ts = new ArrayList(); 39 | 40 | T t; 41 | try{ 42 | JSONObject jsonObject = new JSONObject(result); 43 | JSONArray jsonArray = jsonObject.getJSONArray(key); 44 | if (jsonArray.length() > 0){ 45 | for (int i = 0;i < jsonArray.length();i++){ 46 | JSONObject object = jsonArray.getJSONObject(i); 47 | t = (T) new Gson().fromJson(object.toString(), c); 48 | ts.add(t); 49 | } 50 | } 51 | return ts; 52 | }catch (Exception e){ 53 | throw new AppException(AppException.ErrorType.JSON,e.getMessage()); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/listener/JsonReaderCallback.java: -------------------------------------------------------------------------------- 1 | package download.http.listener; 2 | 3 | 4 | 5 | import android.annotation.TargetApi; 6 | import android.os.Build; 7 | import com.google.gson.stream.JsonReader; 8 | 9 | import java.io.File; 10 | import java.io.FileReader; 11 | import java.lang.reflect.ParameterizedType; 12 | import java.lang.reflect.Type; 13 | import java.util.UUID; 14 | 15 | import download.http.core.Http; 16 | import download.http.entity.SimpleJsonReader; 17 | import download.http.exception.AppException; 18 | 19 | /** 20 | * Created by lizhiyun on 16/6/3. 21 | */ 22 | public abstract class JsonReaderCallback extends AbsCallback { 23 | public JsonReaderCallback(){ 24 | setCachePath(Http.tempFileRootPath + File.separator + UUID.randomUUID()); 25 | } 26 | 27 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 28 | @Override 29 | protected T parseData(String result) throws AppException{ 30 | try{ 31 | ParameterizedType p = (ParameterizedType) this.getClass().getGenericSuperclass(); 32 | Type type = p.getActualTypeArguments()[0]; 33 | Class clazz = (Class) type; 34 | T t = clazz.newInstance(); 35 | FileReader in = new FileReader(result); 36 | JsonReader reader = new JsonReader(in); 37 | t.readFromJsonReader(reader); 38 | return t; 39 | }catch (Exception e){ 40 | throw new AppException(AppException.ErrorType.JSON,e.getMessage()); 41 | }finally { 42 | File file = new File(result); 43 | if (file != null && file.exists()){ 44 | file.delete(); 45 | } 46 | } 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/listener/JsonReaderListCallback.java: -------------------------------------------------------------------------------- 1 | package download.http.listener; 2 | 3 | 4 | import android.annotation.TargetApi; 5 | import android.os.Build; 6 | 7 | import com.google.gson.stream.JsonReader; 8 | 9 | import java.io.File; 10 | import java.io.FileReader; 11 | import java.lang.reflect.ParameterizedType; 12 | import java.lang.reflect.Type; 13 | import java.util.ArrayList; 14 | import java.util.UUID; 15 | 16 | import download.http.core.Http; 17 | import download.http.entity.SimpleJsonReader; 18 | import download.http.exception.AppException; 19 | 20 | /** 21 | * Created by lizhiyun on 16/6/3. 22 | */ 23 | public abstract class JsonReaderListCallback extends AbsCallback> { 24 | String key="data"; 25 | public JsonReaderListCallback(String key){ 26 | this.key = key; 27 | setCachePath(Http.tempFileRootPath + File.separator + UUID.randomUUID()); 28 | } 29 | 30 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 31 | @Override 32 | protected ArrayList parseData(String result) throws AppException{ 33 | ArrayList ts = new ArrayList(); 34 | T t; 35 | try{ 36 | ParameterizedType p = (ParameterizedType) this.getClass().getGenericSuperclass(); 37 | Type type = p.getActualTypeArguments()[0]; 38 | Class clazz = (Class) type; 39 | FileReader in = new FileReader(result); 40 | JsonReader reader = new JsonReader(in); 41 | String node = ""; 42 | reader.beginObject(); 43 | while (reader.hasNext()){ 44 | node = reader.nextName(); 45 | if (node.equals(key)){ 46 | reader.beginArray(); 47 | while (reader.hasNext()){ 48 | t = clazz.newInstance(); 49 | t.readFromJsonReader(reader); 50 | ts.add(t); 51 | } 52 | reader.endArray(); 53 | }else { 54 | reader.skipValue(); 55 | } 56 | } 57 | reader.endObject(); 58 | return ts; 59 | }catch (Exception e){ 60 | throw new AppException(AppException.ErrorType.JSON,e.getMessage()); 61 | }finally { 62 | try { 63 | File file = new File(result); 64 | if (file != null && file.exists()){ 65 | file.delete(); 66 | } 67 | }catch (Exception e){ 68 | e.printStackTrace(); 69 | } 70 | } 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/listener/OnGlobalExceptionListener.java: -------------------------------------------------------------------------------- 1 | package download.http.listener; 2 | 3 | import download.http.exception.AppException; 4 | 5 | /** 6 | * Created by lizhiyun on 16/6/5. 7 | */ 8 | public interface OnGlobalExceptionListener { 9 | boolean handleException(AppException e); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/listener/OnProgressDownloadListener.java: -------------------------------------------------------------------------------- 1 | package download.http.listener; 2 | 3 | /** 4 | * Created by lizhiyun on 16/6/4. 5 | */ 6 | public interface OnProgressDownloadListener { 7 | void onProgressDownload(int curLength, int totalLength); 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/listener/OnProgressUpdateListener.java: -------------------------------------------------------------------------------- 1 | package download.http.listener; 2 | 3 | /** 4 | * Created by lizhiyun on 16/6/4. 5 | */ 6 | public interface OnProgressUpdateListener { 7 | void onProgressUpdate(int curLength, int totalLength); 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/listener/StringCallback.java: -------------------------------------------------------------------------------- 1 | package download.http.listener; 2 | 3 | 4 | 5 | /** 6 | * Created by lizhiyun on 16/6/3. 7 | */ 8 | public abstract class StringCallback extends AbsCallback { 9 | 10 | @Override 11 | protected String parseData(String result) { 12 | return result; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/request/Request.java: -------------------------------------------------------------------------------- 1 | package download.http.request; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Map; 5 | 6 | import download.http.entity.FileEntity; 7 | import download.http.exception.AppException; 8 | import download.http.listener.ICallback; 9 | import download.http.listener.OnGlobalExceptionListener; 10 | import download.http.listener.OnProgressDownloadListener; 11 | import download.http.listener.OnProgressUpdateListener; 12 | 13 | import static download.http.request.RequestBuilder.*; 14 | 15 | /** 16 | * Created by lizhiyun on 16/6/3. 17 | */ 18 | public class Request { 19 | 20 | private Object returnObject; 21 | private String tag; 22 | private ICallback callback; 23 | public OnProgressUpdateListener onProgressUpdatedListener; 24 | public OnProgressDownloadListener onProgressDownloadListener; 25 | private int maxRetryTime = 0; 26 | private volatile boolean isCanceled = false; 27 | public String filePath; 28 | public ArrayList fileEntities; 29 | 30 | private OnGlobalExceptionListener globalExceptionListener; 31 | 32 | 33 | public void checkIfCancelled() throws AppException { 34 | if (isCanceled){ 35 | throw new AppException(AppException.ErrorType.CANCEL,"canceled"); 36 | } 37 | } 38 | 39 | public void cancel() { 40 | isCanceled = true; 41 | } 42 | 43 | private RequestMethod method; 44 | 45 | private String url; 46 | private Map headers; 47 | private String content; 48 | 49 | public Request(){ 50 | } 51 | 52 | public Object getReturnObject() { 53 | return returnObject; 54 | } 55 | 56 | public void setReturnObject(Object returnObject) { 57 | this.returnObject = returnObject; 58 | } 59 | 60 | public String getTag() { 61 | return tag; 62 | } 63 | 64 | public void setTag(String tag) { 65 | this.tag = tag; 66 | } 67 | 68 | public ICallback getCallback() { 69 | return callback; 70 | } 71 | 72 | protected void setCallback(ICallback callback) { 73 | this.callback = callback; 74 | } 75 | 76 | public int getMaxRetryTime() { 77 | return maxRetryTime; 78 | } 79 | 80 | public void setMaxRetryTime(int maxRetryTime) { 81 | this.maxRetryTime = maxRetryTime; 82 | } 83 | 84 | public boolean isCanceled() { 85 | return isCanceled; 86 | } 87 | 88 | public void setIsCanceled(boolean isCanceled) { 89 | this.isCanceled = isCanceled; 90 | } 91 | 92 | public OnGlobalExceptionListener getGlobalExceptionListener() { 93 | return globalExceptionListener; 94 | } 95 | 96 | protected void setGlobalExceptionListener(OnGlobalExceptionListener globalExceptionListener) { 97 | this.globalExceptionListener = globalExceptionListener; 98 | } 99 | 100 | public String getFilePath() { 101 | return filePath; 102 | } 103 | 104 | public void setFilePath(String filePath) { 105 | this.filePath = filePath; 106 | } 107 | 108 | public ArrayList getFileEntities() { 109 | return fileEntities; 110 | } 111 | 112 | public void setFileEntities(ArrayList fileEntities) { 113 | this.fileEntities = fileEntities; 114 | } 115 | 116 | public RequestMethod getMethod() { 117 | return method; 118 | } 119 | 120 | protected void setMethod(RequestMethod method) { 121 | this.method = method; 122 | } 123 | 124 | public String getUrl() { 125 | return url; 126 | } 127 | 128 | protected void setUrl(String url) { 129 | this.url = url; 130 | } 131 | 132 | public Map getHeaders() { 133 | return headers; 134 | } 135 | 136 | protected void setHeaders(Map headers) { 137 | this.headers = headers; 138 | } 139 | 140 | public String getContent() { 141 | return content; 142 | } 143 | 144 | protected void setContent(String content) { 145 | this.content = content; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/request/RequestBuilder.java: -------------------------------------------------------------------------------- 1 | package download.http.request; 2 | 3 | import android.content.Context; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Map; 7 | 8 | import download.http.core.HttpManager; 9 | import download.http.entity.FileEntity; 10 | import download.http.listener.ICallback; 11 | import download.http.listener.OnGlobalExceptionListener; 12 | import download.http.listener.OnProgressDownloadListener; 13 | import download.http.listener.OnProgressUpdateListener; 14 | 15 | /** 16 | * Created by lizhiyun on 16/6/3. 17 | */ 18 | public class RequestBuilder { 19 | private ICallback callback; 20 | public OnProgressUpdateListener onProgressUpdatedListener; 21 | public OnProgressDownloadListener onProgressDownloadListener; 22 | private int maxRetryTime = 3; 23 | private OnGlobalExceptionListener globalExceptionListener; 24 | public enum RequestMethod{GET,POST,PUT,DELETE} 25 | private RequestMethod method; 26 | public String filePath; 27 | public ArrayList fileEntities; 28 | 29 | private String url; 30 | private Map headers; 31 | private String content; 32 | 33 | 34 | public RequestBuilder(Context context){ 35 | this.method = RequestMethod.GET; 36 | this.maxRetryTime = 2; 37 | } 38 | public RequestBuilder url(String url){ 39 | this.url = url; 40 | return this; 41 | } 42 | public RequestBuilder method(RequestMethod method){ 43 | this.method = method; 44 | return this; 45 | } 46 | public RequestBuilder content(String content){ 47 | this.content = content; 48 | return this; 49 | } 50 | public RequestBuilder headers(Map headers){ 51 | this.headers = headers; 52 | return this; 53 | } 54 | 55 | public RequestBuilder maxRetryTime(int maxRetryTime){ 56 | this.maxRetryTime = maxRetryTime; 57 | return this; 58 | } 59 | public RequestBuilder callback(ICallback callback){ 60 | this.callback = callback; 61 | return this; 62 | } 63 | 64 | public RequestBuilder globalException(OnGlobalExceptionListener globalExceptionListener){ 65 | this.globalExceptionListener = globalExceptionListener; 66 | return this; 67 | } 68 | 69 | 70 | public void post(){ 71 | this.method = RequestMethod.POST; 72 | execute(); 73 | } 74 | public void get(){ 75 | this.method = RequestMethod.GET; 76 | execute(); 77 | } 78 | public void delete(){ 79 | this.method = RequestMethod.DELETE; 80 | execute(); 81 | } 82 | public void put(){ 83 | this.method = RequestMethod.PUT; 84 | put(); 85 | } 86 | 87 | public RequestBuilder progressUpdate(OnProgressUpdateListener listener){ 88 | this.onProgressUpdatedListener = listener; 89 | return this; 90 | } 91 | public RequestBuilder progressDownload(OnProgressDownloadListener listener){ 92 | this.onProgressDownloadListener = listener; 93 | return this; 94 | } 95 | public RequestBuilder filePath(String filePath){ 96 | this.filePath = filePath; 97 | return this; 98 | } 99 | 100 | 101 | public RequestBuilder fileEntities(ArrayList fileEntities){ 102 | this.fileEntities = fileEntities; 103 | return this; 104 | } 105 | private void execute(){ 106 | Request request = new Request(); 107 | request.setUrl(url); 108 | request.setCallback(callback); 109 | request.setContent(content); 110 | request.setHeaders(headers); 111 | request.setMethod(method); 112 | request.setGlobalExceptionListener(globalExceptionListener); 113 | request.setMaxRetryTime(maxRetryTime); 114 | request.onProgressDownloadListener = onProgressDownloadListener; 115 | request.onProgressUpdatedListener = onProgressUpdatedListener; 116 | request.setFilePath(filePath); 117 | request.setFileEntities(fileEntities); 118 | HttpManager.getInstance().request(request); 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/util/HttpUrlConnectionUtil.java: -------------------------------------------------------------------------------- 1 | package download.http.util; 2 | 3 | import android.webkit.URLUtil; 4 | 5 | import java.io.IOException; 6 | import java.io.InterruptedIOException; 7 | import java.io.OutputStream; 8 | import java.net.HttpURLConnection; 9 | import java.net.URL; 10 | import java.util.Map; 11 | 12 | import download.http.exception.AppException; 13 | import download.http.request.Request; 14 | import download.imageLoader.listener.OnProgressUpdatedListener; 15 | 16 | /** 17 | * Created by lizhiyun on 16/6/3. 18 | */ 19 | public class HttpUrlConnectionUtil { 20 | public static HttpURLConnection execute(Request request,OnProgressUpdatedListener listener) throws AppException { 21 | if (!URLUtil.isNetworkUrl(request.getUrl())){ 22 | throw new AppException(AppException.ErrorType.IO,"the url :"+request.getUrl() + "is not valid"); 23 | } 24 | switch (request.getMethod()){ 25 | case GET: 26 | case DELETE: 27 | return get(request); 28 | case POST: 29 | case PUT: 30 | return post(request,listener); 31 | 32 | } 33 | return null; 34 | 35 | } 36 | private static HttpURLConnection get(Request request) throws AppException { 37 | try { 38 | request.checkIfCancelled(); 39 | HttpURLConnection connection = (HttpURLConnection) new URL(request.getUrl()).openConnection(); 40 | connection.setRequestMethod(request.getMethod().name()); 41 | connection.setConnectTimeout(15 * 1000); 42 | connection.setReadTimeout(10 * 1000); 43 | addHeader(connection, request.getHeaders()); 44 | request.checkIfCancelled(); 45 | return connection; 46 | 47 | } 48 | catch(InterruptedIOException e) { 49 | throw new AppException(AppException.ErrorType.TIMEOUT,e.getMessage()); 50 | } 51 | catch(IOException e){ 52 | throw new AppException(AppException.ErrorType.IO,e.getMessage()); 53 | } 54 | 55 | } 56 | 57 | private static HttpURLConnection post(Request request,OnProgressUpdatedListener listener) throws AppException { 58 | OutputStream os = null; 59 | try { 60 | request.checkIfCancelled(); 61 | HttpURLConnection connection = (HttpURLConnection) new URL(request.getUrl()).openConnection(); 62 | connection.setRequestMethod(request.getMethod().name()); 63 | connection.setConnectTimeout(15 * 1000); 64 | connection.setReadTimeout(10 * 1000); 65 | connection.setDoOutput(true); 66 | addHeader(connection, request.getHeaders()); 67 | request.checkIfCancelled(); 68 | os = connection.getOutputStream(); 69 | if (request.filePath != null){ 70 | UploadUtil.upload(os, request.filePath); 71 | }else if (request.fileEntities != null){ 72 | UploadUtil.upload(os, request.getContent(), request.getFileEntities(), listener); 73 | }else if (request.getContent() != null){ 74 | os.write(request.getContent().getBytes()); 75 | }else { 76 | throw new AppException(AppException.ErrorType.MANUAL,"the post request has no post content"); 77 | } 78 | os.write(request.getContent().getBytes()); 79 | request.checkIfCancelled(); 80 | return connection; 81 | } 82 | catch(InterruptedIOException e) { 83 | throw new AppException(AppException.ErrorType.TIMEOUT,e.getMessage()); 84 | }catch (IOException e) { 85 | throw new AppException(AppException.ErrorType.IO,e.getMessage()); 86 | }finally { 87 | try { 88 | if (os != null){ 89 | os.flush(); 90 | os.close(); 91 | } 92 | }catch (IOException e){ 93 | throw new AppException(AppException.ErrorType.IO,"the post outputstream cannot close"); 94 | } 95 | } 96 | } 97 | private static void addHeader(HttpURLConnection connection, Map headers) { 98 | if (headers == null || headers.size() == 0) 99 | return; 100 | for (Map.Entry entry: headers.entrySet()){ 101 | connection.addRequestProperty(entry.getKey(),entry.getValue()); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/download/http/util/UploadUtil.java: -------------------------------------------------------------------------------- 1 | package download.http.util; 2 | 3 | 4 | 5 | import java.io.DataOutputStream; 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.InputStream; 9 | import java.io.OutputStream; 10 | import java.util.ArrayList; 11 | 12 | import download.http.entity.FileEntity; 13 | import download.http.exception.AppException; 14 | import download.imageLoader.listener.OnProgressUpdatedListener; 15 | 16 | 17 | /** 18 | * @author Stay 19 | * @version create time:Mar 11, 2014 9:01:28 PM 20 | */ 21 | public class UploadUtil { 22 | /** 23 | * @param out 24 | * @param filePath 25 | * @throws AppException 26 | */ 27 | public static void upload(OutputStream out, String filePath) throws AppException { 28 | String BOUNDARY = "7d4a6d158c9"; // 数据分隔线 29 | DataOutputStream outStream = new DataOutputStream(out); 30 | try { 31 | outStream.writeBytes("--" + BOUNDARY + "\r\n"); 32 | outStream.writeBytes("Content-Disposition: form-data; name=\"file0\"; filename=\"" 33 | + filePath.substring(filePath.lastIndexOf("/") + 1) + "\"" + "\r\n"); 34 | outStream.writeBytes("\r\n"); 35 | byte[] buffer = new byte[1024]; 36 | FileInputStream fis = new FileInputStream(filePath); 37 | while (fis.read(buffer, 0, 1024) != -1) { 38 | outStream.write(buffer, 0, buffer.length); 39 | } 40 | fis.close(); 41 | outStream.write("\r\n".getBytes()); 42 | byte[] end_data = ("--" + BOUNDARY + "--\r\n").getBytes();// 数据结束标志 43 | outStream.write(end_data); 44 | outStream.flush(); 45 | } catch (Exception e) { 46 | throw new AppException(AppException.ErrorType.UPLOAD, e.getMessage()); 47 | } 48 | } 49 | 50 | /** 51 | * @param out 52 | * @param postContent 53 | * @param entities 54 | */ 55 | public static void upload(OutputStream out, String postContent, ArrayList entities) throws AppException { 56 | String BOUNDARY = "7d4a6d158c9"; // 数据分隔线 57 | String PREFIX = "--", LINEND = "\r\n"; 58 | String CHARSET = "UTF-8"; 59 | DataOutputStream outStream = new DataOutputStream(out); 60 | try { 61 | StringBuilder sb = new StringBuilder(); 62 | sb.append(PREFIX); 63 | sb.append(BOUNDARY); 64 | sb.append(LINEND); 65 | sb.append("Content-Disposition: form-data; name=\"" + "data" + "\"" + LINEND); 66 | sb.append("Content-Type: text/plain; charset=" + CHARSET + LINEND); 67 | sb.append("Content-Transfer-Encoding: 8bit" + LINEND); 68 | sb.append(LINEND); 69 | // post content 70 | sb.append(postContent); 71 | sb.append(LINEND); 72 | outStream.write(sb.toString().getBytes()); 73 | int i = 0; 74 | for (FileEntity entity : entities) { 75 | StringBuilder sb1 = new StringBuilder(); 76 | sb1.append(PREFIX); 77 | sb1.append(BOUNDARY); 78 | sb1.append(LINEND); 79 | sb1.append("Content-Disposition: form-data; name=\"file" + (i++) + "\"; filename=\"" + entity.getFileName() + "\"" 80 | + LINEND); 81 | sb1.append("Content-Type: " + entity.getFileType() + LINEND); 82 | sb1.append(LINEND); 83 | outStream.write(sb1.toString().getBytes()); 84 | 85 | InputStream is = new FileInputStream(entity.getFilePath()); 86 | byte[] buffer = new byte[1024]; 87 | int len = 0; 88 | while ((len = is.read(buffer)) != -1) { 89 | 90 | outStream.write(buffer, 0, len); 91 | } 92 | 93 | is.close(); 94 | outStream.write(LINEND.getBytes()); 95 | } 96 | byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes(); 97 | outStream.write(end_data); 98 | outStream.flush(); 99 | } catch (Exception e) { 100 | throw new AppException(AppException.ErrorType.UPLOAD, e.getMessage()); 101 | } 102 | } 103 | 104 | /** 105 | * @param out 106 | * @param postContent 107 | * @param entities 108 | */ 109 | public static void upload(OutputStream out, String postContent, ArrayList entities, OnProgressUpdatedListener listener) throws AppException { 110 | String BOUNDARY = "7d4a6d158c9"; // 数据分隔线 111 | String PREFIX = "--", LINEND = "\r\n"; 112 | String CHARSET = "UTF-8"; 113 | DataOutputStream outStream = new DataOutputStream(out); 114 | try { 115 | StringBuilder sb = new StringBuilder(); 116 | sb.append(PREFIX); 117 | sb.append(BOUNDARY); 118 | sb.append(LINEND); 119 | sb.append("Content-Disposition: form-data; name=\"" + "data" + "\"" + LINEND); 120 | sb.append("Content-Type: text/plain; charset=" + CHARSET + LINEND); 121 | sb.append("Content-Transfer-Encoding: 8bit" + LINEND); 122 | sb.append(LINEND); 123 | // post content 124 | sb.append(postContent); 125 | sb.append(LINEND); 126 | outStream.write(sb.toString().getBytes()); 127 | int i = 0; 128 | int totalLen = 0; 129 | int percent = 0; 130 | if (listener != null) { 131 | // TODO compute total file length 132 | File file = null; 133 | for (FileEntity entity : entities) { 134 | file = new File(entity.getFilePath()); 135 | if (file.length() > 0) { 136 | totalLen += file.length(); 137 | } 138 | } 139 | } 140 | for (FileEntity entity : entities) { 141 | StringBuilder sb1 = new StringBuilder(); 142 | sb1.append(PREFIX); 143 | sb1.append(BOUNDARY); 144 | sb1.append(LINEND); 145 | sb1.append("Content-Disposition: form-data; name=\"file" + (i++) + "\"; filename=\"" + entity.getFileName() + "\"" 146 | + LINEND); 147 | sb1.append("Content-Type: " + entity.getFileType() + LINEND); 148 | sb1.append(LINEND); 149 | outStream.write(sb1.toString().getBytes()); 150 | 151 | InputStream is = new FileInputStream(entity.getFilePath()); 152 | byte[] buffer = new byte[1024]; 153 | int len = 0; 154 | int curLen = 0; 155 | while ((len = is.read(buffer)) != -1) { 156 | outStream.write(buffer, 0, len); 157 | if (listener != null){ 158 | curLen += len; 159 | if (curLen * 100l / totalLen > percent) { 160 | listener.onProgressUpdated(curLen, totalLen); 161 | percent = (int) (curLen * 100l / totalLen); 162 | } 163 | } 164 | } 165 | 166 | is.close(); 167 | outStream.write(LINEND.getBytes()); 168 | } 169 | byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes(); 170 | outStream.write(end_data); 171 | outStream.flush(); 172 | } catch (Exception e) { 173 | throw new AppException(AppException.ErrorType.UPLOAD, e.getMessage()); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/cache/BitmapCache.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.cache; 2 | 3 | 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.IOException; 7 | import java.util.HashMap; 8 | 9 | import download.imageLoader.request.BitmapRequest; 10 | import download.imageLoader.util.ImageSizeUtil; 11 | import download.utils.Util; 12 | 13 | import android.annotation.SuppressLint; 14 | import android.annotation.TargetApi; 15 | import android.content.Context; 16 | import android.graphics.Bitmap; 17 | import android.graphics.BitmapFactory; 18 | import android.graphics.Movie; 19 | import android.graphics.drawable.BitmapDrawable; 20 | import android.os.Build; 21 | import android.os.StatFs; 22 | import android.os.Build.VERSION_CODES; 23 | import android.util.LruCache; 24 | 25 | 26 | @SuppressLint("NewApi") 27 | public class BitmapCache { 28 | private LruCache mMemoryBitmapLruCache; 29 | private LruCache mMemoryMovieLruCache; 30 | private DiskLruCache mDiskLruCache = null; 31 | private static final long DISK_CACHE_SIZE = 1024 * 1024 * 60; 32 | private class Size{ 33 | public int width; 34 | public int height; 35 | public Size(int w,int h){ 36 | this.width = w; 37 | this.height = h; 38 | } 39 | } 40 | private HashMap mHistoryMaxSize = new HashMap(); 41 | 42 | 43 | 44 | @SuppressLint("NewApi") 45 | public BitmapCache() { 46 | super(); 47 | // 获取我们应用的最大可用内存 48 | int maxMemory = Math.min( 49 | (int) Runtime.getRuntime().maxMemory() / 8, 30 * 1024 * 1024); 50 | mMemoryBitmapLruCache = new LruCache(maxMemory) { 51 | @Override 52 | protected int sizeOf(String key, Bitmap value) { 53 | return getBitmapByteSize(value); 54 | } 55 | }; 56 | mMemoryMovieLruCache = new LruCache(10){ 57 | @Override 58 | protected int sizeOf(String key, Movie value) { 59 | return 1; 60 | } 61 | }; 62 | } 63 | 64 | private Boolean isSetted = false; 65 | private File diskCacheDir; 66 | 67 | public void setDiskLruCache(Context context) { 68 | if (isSetted) { 69 | isSetted = true; 70 | return; 71 | } 72 | if (mDiskLruCache == null) { 73 | try { 74 | diskCacheDir = Util.getDiskCacheDir(context, "bitmap"); 75 | if (!diskCacheDir.exists()) { 76 | diskCacheDir.mkdirs(); 77 | } 78 | if (getUsableSpace(diskCacheDir) > DISK_CACHE_SIZE) { 79 | mDiskLruCache = DiskLruCache.open(diskCacheDir, 80 | 1, 1, DISK_CACHE_SIZE); 81 | } 82 | } catch (IOException e) { 83 | e.printStackTrace(); 84 | } 85 | } 86 | } 87 | 88 | @SuppressWarnings("deprecation") 89 | @TargetApi(VERSION_CODES.GINGERBREAD) 90 | private long getUsableSpace(File path) { 91 | if (Build.VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { 92 | return path.getUsableSpace(); 93 | } 94 | final StatFs stats = new StatFs(path.getPath()); 95 | return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks(); 96 | } 97 | 98 | public void addMemoryBitmap(BitmapRequest request) { 99 | String key = Util.md5(request.path); 100 | if (request.bitmap != null){ 101 | //保存使用过的尽可能的最大值 102 | Size oldSize = mHistoryMaxSize.get(request.path); 103 | Size newSize = new Size(request.width,request.height); 104 | if (oldSize == null || ( newSize.width * newSize.height > oldSize.width * oldSize.height )){ 105 | mHistoryMaxSize.put(request.path,newSize); 106 | } 107 | mMemoryBitmapLruCache.put(request.getKey(), request.bitmap); 108 | } 109 | if (request.movie != null){ 110 | mMemoryMovieLruCache.put(key,request.movie); 111 | } 112 | } 113 | 114 | 115 | public void getMemoryCache(BitmapRequest request) { 116 | String key = Util.md5(request.path); 117 | ImageSizeUtil.getImageViewSize(request); 118 | request.movie = mMemoryMovieLruCache.get(key); 119 | if (request.checkIfNeedAsyncLoad()){ 120 | request.bitmap = mMemoryBitmapLruCache.get(request.getKey()); 121 | 122 | Size oldSize = mHistoryMaxSize.get(request.path); 123 | if (oldSize != null && request.view != null && request.bitmap != null && (request.width > oldSize.width || request.height > oldSize.height)) { 124 | request.bitmap = null; 125 | } 126 | } 127 | 128 | } 129 | 130 | public DiskLruCache getmDiskLruCacheBitmap() { 131 | return mDiskLruCache; 132 | } 133 | 134 | 135 | public void getDiskCacheBitmap(BitmapRequest request) { 136 | if (mDiskLruCache == null) { 137 | return ; 138 | } 139 | String key = Util.md5(request.path); 140 | try { 141 | DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key); 142 | if (snapShot != null) { 143 | loadImageFromLocal(diskCacheDir.getAbsolutePath() 144 | + File.separator + key + ".0", request); 145 | 146 | } 147 | } catch (IOException e) { 148 | e.printStackTrace(); 149 | } 150 | } 151 | 152 | private void loadImageFromLocal(String path, BitmapRequest request) { 153 | if (request.view == null || request.view.get() == null){ 154 | return; 155 | } 156 | if (!new File(path).exists()) { 157 | return ; 158 | } 159 | ImageSizeUtil.getImageViewSize(request); 160 | BitmapFactory.Options options = new BitmapFactory.Options(); 161 | options.inJustDecodeBounds = true; 162 | BitmapFactory.decodeFile(path, options); 163 | options.inSampleSize = ImageSizeUtil.caculateInSampleSize(options, 164 | request.width, request.height); 165 | options.inJustDecodeBounds = false; 166 | try{ 167 | request.movie = Movie.decodeStream(new FileInputStream(new File(path))); 168 | }catch (Exception e){ 169 | 170 | } 171 | if (request.checkIfNeedAsyncLoad()){ 172 | request.bitmap = BitmapFactory.decodeFile(path, options); 173 | } 174 | 175 | } 176 | 177 | public Boolean hasDiskBm(String path) { 178 | String key = Util.md5(path); 179 | File file = new File(diskCacheDir.getAbsolutePath()+ File.separator + key + ".0"); 180 | return file.exists() && file.length() > 0; 181 | } 182 | 183 | public void clearMemory(){ 184 | mMemoryBitmapLruCache.evictAll(); 185 | mMemoryMovieLruCache.evictAll(); 186 | } 187 | public void clearDiskMemory(){ 188 | try { 189 | if (mDiskLruCache != null){ 190 | mDiskLruCache.delete(); 191 | } 192 | }catch (Exception e){ 193 | e.printStackTrace(); 194 | } 195 | } 196 | private int getBitmapByteSize(Bitmap bitmap) { 197 | // The return value of getAllocationByteCount silently changes for recycled bitmaps from the 198 | // internal buffer size to row bytes * height. To avoid random inconsistencies in caches, we 199 | // instead assert here. 200 | if (bitmap.isRecycled()) { 201 | throw new IllegalStateException("Cannot obtain size for recycled Bitmap: " + bitmap 202 | + "[" + bitmap.getWidth() + "x" + bitmap.getHeight() + "] " + bitmap.getConfig()); 203 | } 204 | if (Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT) { 205 | // Workaround for KitKat initial release NPE in Bitmap, fixed in MR1. See issue #148. 206 | try { 207 | return bitmap.getAllocationByteCount(); 208 | } catch (NullPointerException e) { 209 | // Do nothing. 210 | } 211 | } 212 | return bitmap.getHeight() * bitmap.getRowBytes(); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/config/FailedDrawable.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.config; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Color; 5 | import android.graphics.ColorFilter; 6 | import android.graphics.Paint; 7 | import android.graphics.PixelFormat; 8 | import android.graphics.Rect; 9 | import android.graphics.drawable.Drawable; 10 | 11 | public class FailedDrawable extends Drawable { 12 | private Paint mPaint; 13 | private int txsize; 14 | private int txcolor; 15 | 16 | public FailedDrawable(int color) { 17 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 18 | this.txcolor = color; 19 | } 20 | 21 | @Override 22 | public void draw(Canvas canvas) { 23 | final Rect r = getBounds(); 24 | float cx = r.exactCenterX(); 25 | float cy = r.exactCenterY(); 26 | txsize = (int) Math.min((cx/2/2),cy * 0.8); 27 | mPaint.setTextSize(txsize); 28 | mPaint.setColor(txcolor); 29 | canvas.drawText("加载失败", cx - mPaint.measureText("加载失败") / 2, cy + txsize / 2, mPaint); 30 | } 31 | 32 | @Override 33 | public void setAlpha(int alpha) { 34 | mPaint.setAlpha(alpha); 35 | invalidateSelf(); 36 | 37 | } 38 | 39 | @Override 40 | public void setColorFilter(ColorFilter cf) { 41 | mPaint.setColorFilter(cf); 42 | invalidateSelf(); 43 | } 44 | 45 | @Override 46 | public int getOpacity() { 47 | // not sure, so be safe 48 | return PixelFormat.TRANSLUCENT; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/config/ImageConfig.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.config; 2 | 3 | import java.io.InputStream; 4 | 5 | import download.imageLoader.cache.BitmapCache; 6 | import android.annotation.SuppressLint; 7 | import android.content.Context; 8 | import android.content.res.Resources; 9 | import android.graphics.Bitmap; 10 | import android.graphics.BitmapFactory; 11 | import android.graphics.Color; 12 | import android.graphics.drawable.BitmapDrawable; 13 | import android.graphics.drawable.Drawable; 14 | 15 | 16 | @SuppressLint("NewApi") 17 | public class ImageConfig { 18 | public BitmapCache cache; 19 | private Drawable loadingBm = null; 20 | private Drawable failedBm; 21 | /** 22 | *只在wifi下下载 23 | */ 24 | private Boolean onlyWifiMode = false; 25 | /** 26 | * 仅使用内存缓存模式,针对图片名不变图片经常变动的商城项目这样的需求。 27 | */ 28 | private Boolean onlyMemoryMode = false; 29 | 30 | public void setOnlyWifiMode(Boolean b){ 31 | this.onlyWifiMode = b; 32 | } 33 | 34 | public void setOnlyMemoryMode(Boolean b){ 35 | this.onlyMemoryMode = b; 36 | } 37 | 38 | public Boolean getOnlyWifiMode(){ 39 | return onlyWifiMode; 40 | } 41 | 42 | public Boolean getOnlyMemoryMode(){ 43 | return onlyMemoryMode; 44 | } 45 | 46 | public void setFailedIdAndLoadingId(Resources res,int failedId,int loadingId) { 47 | ifIint = true; 48 | this.failedBm = new BitmapDrawable(res,BitmapFactory.decodeResource(res, failedId)); 49 | this.loadingBm = new BitmapDrawable(res,BitmapFactory.decodeResource(res, loadingId)); 50 | } 51 | 52 | 53 | private Boolean ifIint = false; 54 | public void initDefault(Context context){ 55 | if (!ifIint){ 56 | ifIint = true; 57 | loadingBm = new LoadingDrawable(); 58 | failedBm = new FailedDrawable(Color.RED); 59 | } 60 | } 61 | public Drawable getLoadingBm() { 62 | return loadingBm; 63 | } 64 | 65 | public Drawable getFailedBm() { 66 | return failedBm; 67 | } 68 | 69 | 70 | public ImageConfig() { 71 | super(); 72 | cache = new BitmapCache(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/config/LoadOrder.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.config; 2 | 3 | public enum LoadOrder { 4 | FIFO, LIFO; 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/config/LoadingDrawable.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.config; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | import android.graphics.Canvas; 6 | import android.graphics.ColorFilter; 7 | import android.graphics.Paint; 8 | import android.graphics.PixelFormat; 9 | import android.graphics.Rect; 10 | import android.graphics.RectF; 11 | import android.graphics.drawable.Drawable; 12 | 13 | 14 | import java.io.InputStream; 15 | 16 | public class LoadingDrawable extends Drawable { 17 | private Paint mPaint; 18 | private int txsize; 19 | private Bitmap bitmap; 20 | 21 | final Rect r = null; 22 | float cx; 23 | float cy; 24 | int size; 25 | Rect src; 26 | RectF dst; 27 | 28 | public LoadingDrawable() { 29 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 30 | bitmap = getBitmapFormSrc("image/loading.png"); 31 | } 32 | 33 | @Override 34 | public void draw(Canvas canvas) { 35 | Rect r = getBounds(); 36 | cx = r.exactCenterX(); 37 | cy = r.exactCenterY(); 38 | size = (int) Math.min(cx,cy); 39 | src = new Rect(0,0, bitmap.getWidth(),bitmap.getHeight()); 40 | dst = new RectF(cx - size / 2, cy - size / 2,cx + size / 2, cy + size / 2); 41 | canvas.drawBitmap(bitmap,src,dst,mPaint); 42 | } 43 | 44 | @Override 45 | public void setAlpha(int alpha) { 46 | mPaint.setAlpha(alpha); 47 | invalidateSelf(); 48 | 49 | } 50 | 51 | @Override 52 | public void setColorFilter(ColorFilter cf) { 53 | mPaint.setColorFilter(cf); 54 | invalidateSelf(); 55 | } 56 | 57 | @Override 58 | public int getOpacity() { 59 | // not sure, so be safe 60 | return PixelFormat.TRANSLUCENT; 61 | } 62 | public static Bitmap getBitmapFormSrc(String src){ 63 | Bitmap bit = null; 64 | try { 65 | InputStream is = ImageConfig.class.getResourceAsStream(src); 66 | bit = BitmapFactory.decodeStream(is); 67 | } catch (Exception e) { 68 | } 69 | return bit; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/core/BmManager.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.core; 2 | 3 | import android.graphics.Bitmap; 4 | import android.util.Log; 5 | 6 | import download.imageLoader.util.FaceCropper; 7 | 8 | /** 9 | * 为了调用更简单添加此门面 10 | * path示例:"http://img.blog.csdn.net/20160114230048304",//gif图 11 | * "assets//:test.png", 12 | * "drawable//:"+R.drawable.common_logo, 13 | * "file:///mnt/sdcard/paint.png", 14 | * @author lizhiyun 15 | * 16 | */ 17 | public class BmManager { 18 | 19 | /** 20 | * preload 21 | * 22 | * @param path 23 | */ 24 | public static void preLoad(String path) { 25 | download.imageLoader.core.ImageLoader.getInstance().preLoad(path); 26 | } 27 | 28 | 29 | /** 30 | * 设置是否仅wifi下下载模式 31 | * @param b 32 | */ 33 | public static void setOnlyWifiMode(Boolean b){ 34 | ImageLoader.getInstance().getConfig().setOnlyWifiMode(b); 35 | } 36 | 37 | /** 38 | * 设置是否仅使用缓存模式 39 | * @param b 40 | */ 41 | public static void setOnlyMemoryMode(Boolean b){ 42 | ImageLoader.getInstance().getConfig().setOnlyMemoryMode(b); 43 | } 44 | 45 | /** 46 | * clear all memory 47 | */ 48 | public static void clearAllMemory(){ 49 | ImageLoader.getInstance().getConfig().cache.clearMemory(); 50 | ImageLoader.getInstance().getConfig().cache.clearDiskMemory(); 51 | } 52 | 53 | 54 | static FaceCropper fc = null; 55 | public static synchronized Bitmap face(Bitmap bitmap){ 56 | try{ 57 | if (fc == null){ 58 | fc = new FaceCropper(); 59 | fc.setDebug(false); 60 | } 61 | Log.e("test", "face"); 62 | Bitmap faceBitmap = fc.cropFace(bitmap); 63 | return faceBitmap; 64 | }catch (Exception e){ 65 | Log.e("test","face exception "+e.getMessage()); 66 | return null; 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/core/Image.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.core; 2 | 3 | import android.content.Context; 4 | 5 | import download.imageLoader.request.BitmapRequestBuilder; 6 | 7 | /** 8 | * Created by lizhiyun on 16/6/7. 9 | */ 10 | public class Image { 11 | public static BitmapRequestBuilder with(){ 12 | return new BitmapRequestBuilder(); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/core/ImageLoader.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.core; 2 | 3 | import com.litesuits.go.OverloadPolicy; 4 | import com.litesuits.go.SchedulePolicy; 5 | import com.litesuits.go.SmartExecutor; 6 | import download.imageLoader.config.ImageConfig; 7 | import download.imageLoader.request.BitmapRequest; 8 | import download.imageLoader.view.PowerImageView; 9 | 10 | import android.annotation.TargetApi; 11 | import android.content.Context; 12 | import android.os.Build; 13 | import android.os.Looper; 14 | import android.util.Log; 15 | 16 | public class ImageLoader { 17 | private SmartExecutor executor; 18 | 19 | 20 | private static class InstanceHoler { 21 | private static final ImageLoader instance = new ImageLoader(); 22 | } 23 | public static ImageLoader getInstance() { 24 | return InstanceHoler.instance; 25 | } 26 | 27 | private RunningTasksManager mRunningTasksManager; 28 | private ImageConfig config = null; 29 | private final int threadCount = 3; 30 | 31 | public RunningTasksManager getmRunningTasksManager(){ 32 | if (mRunningTasksManager == null) 33 | mRunningTasksManager = new RunningTasksManager(threadCount); 34 | return mRunningTasksManager; 35 | } 36 | public ImageConfig getConfig(){ 37 | return config; 38 | } 39 | private ImageLoader() { 40 | mRunningTasksManager = new RunningTasksManager(threadCount); 41 | executor = new SmartExecutor(threadCount, 400); 42 | executor.setSchedulePolicy(SchedulePolicy.FirstInFistRun); 43 | executor.setOverloadPolicy(OverloadPolicy.DiscardOldTaskInQueue); 44 | config = new ImageConfig(); 45 | } 46 | 47 | protected void setLoadingAndFailedId(Context context, int loadingId, 48 | int failedId) { 49 | config.setFailedIdAndLoadingId(context.getResources(), failedId, 50 | loadingId); 51 | } 52 | 53 | 54 | protected void preLoad(String path) { 55 | BitmapRequest request = new BitmapRequest(); 56 | request.path = path; 57 | loadImage(request); 58 | } 59 | 60 | public void cancelOldTask(PowerImageView powerImageView) { 61 | executor.remove(powerImageView); 62 | } 63 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 64 | public void loadImage(final BitmapRequest request) { 65 | if (Looper.myLooper() == Looper.getMainLooper()) { 66 | if (request.view == null || request.view.get() == null){ 67 | final LoadTask task = new LoadTask(request, ImageLoader.this); 68 | executor.execute(task); 69 | }else { 70 | getInstance(); 71 | config.cache.setDiskLruCache(request.view.get().getContext() 72 | .getApplicationContext()); 73 | config.initDefault(request.view.get().getContext()); 74 | config.cache.getMemoryCache(request); 75 | request.view.get().setTag(request.getKey()); 76 | if (!request.checkIfNeedAsyncLoad()) { 77 | request.display(); 78 | } else { 79 | executor.remove(request.view.get()); 80 | final LoadTask task = new LoadTask(request, ImageLoader.this); 81 | request.displayLoading(config.getLoadingBm()); 82 | executor.execute(task); 83 | } 84 | } 85 | } else { 86 | throw new RuntimeException("only run on ui thread"); 87 | } 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/core/LoadTask.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.core; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | import android.graphics.drawable.BitmapDrawable; 6 | import android.os.Handler; 7 | import android.os.Looper; 8 | import android.os.Message; 9 | import android.util.Log; 10 | 11 | 12 | import com.example.ui.R; 13 | 14 | import java.lang.ref.WeakReference; 15 | import java.util.concurrent.atomic.AtomicBoolean; 16 | 17 | import download.imageLoader.loader.Load; 18 | import download.imageLoader.request.BitmapRequest; 19 | import download.imageLoader.util.FaceCropper; 20 | import download.imageLoader.util.GaussianBlur; 21 | 22 | /** 23 | * Created by lizhiyun on 16/5/23. 24 | */ 25 | public class LoadTask implements Runnable { 26 | public final static int REFRESH = 1; 27 | public BitmapRequest mRequest; 28 | ImageLoader mImageLoader; 29 | private final AtomicBoolean mCancel = new AtomicBoolean(); 30 | private LoadTask(){} 31 | public LoadTask(BitmapRequest request,ImageLoader loader){ 32 | super(); 33 | this.mRequest = request; 34 | this.mImageLoader = loader; 35 | this.mCancel.set(false); 36 | } 37 | 38 | public void cancel(){ 39 | mCancel.set(true); 40 | } 41 | 42 | @Override 43 | public void run() { 44 | if (mCancel.get()){ 45 | return; 46 | } 47 | while (mImageLoader.getmRunningTasksManager().hasDoingTask(mRequest)) { 48 | try { 49 | if (mCancel.get()){ 50 | return; 51 | } 52 | Thread.sleep(50); 53 | } catch (InterruptedException e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | mImageLoader.getmRunningTasksManager().addDoingTask(mRequest); 58 | 59 | mImageLoader.getConfig().cache.getMemoryCache(mRequest); 60 | if (mRequest.checkIfNeedAsyncLoad()){ 61 | mImageLoader.getConfig().cache.getDiskCacheBitmap(mRequest); 62 | } 63 | if (mRequest.checkIfNeedAsyncLoad()) { 64 | Load.loadBitmap(mRequest, mImageLoader.getConfig()); 65 | if (mRequest.checkIfNeedAsyncLoad()){ 66 | mImageLoader.getConfig().cache.getDiskCacheBitmap(mRequest); 67 | } 68 | 69 | } 70 | if (mCancel.get()){ 71 | return; 72 | } 73 | face(); 74 | blur(); 75 | if (mCancel.get()){ 76 | return; 77 | } 78 | refreashBitmap(); 79 | mImageLoader.getConfig().cache.addMemoryBitmap(mRequest); 80 | mImageLoader.getmRunningTasksManager().removeDoingTask(mRequest); 81 | } 82 | 83 | private void face() { 84 | if (mRequest.isFace && mRequest.movie == null && mRequest.bitmap != null) { 85 | Bitmap face = BmManager.face(mRequest.bitmap); 86 | if (face != null){ 87 | mRequest.bitmap = face; 88 | } 89 | } 90 | } 91 | 92 | private void blur() { 93 | if (mRequest.isBlur && mRequest.movie == null && mRequest.bitmap != null) { 94 | Bitmap blurBitmap = new GaussianBlur().blur(mRequest.bitmap, 3); 95 | mRequest.bitmap = blurBitmap; 96 | } 97 | } 98 | 99 | 100 | private void refreashBitmap() { 101 | if (mCancel.get()){ 102 | return; 103 | } 104 | //使用消息缓存 105 | Message message = Message.obtain(); 106 | message.what = REFRESH; 107 | message.obj = mRequest; 108 | if (sUIHandler != null){ 109 | sUIHandler.sendMessage(message); 110 | } 111 | } 112 | private static Handler sUIHandler = new Handler(Looper.getMainLooper()) { 113 | public void handleMessage(Message msg) { 114 | final BitmapRequest request = (BitmapRequest) msg.obj; 115 | switch (msg.what) { 116 | case LoadTask.REFRESH: 117 | if (!request.checkEffective()) { 118 | return; 119 | } 120 | if (request.view == null || request.view.get() == null){ 121 | return; 122 | } 123 | request.display(); 124 | break; 125 | 126 | default: 127 | break; 128 | } 129 | 130 | } 131 | 132 | }; 133 | 134 | } -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/core/RunningTasksManager.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.core; 2 | 3 | import java.util.LinkedHashMap; 4 | 5 | import download.imageLoader.request.BitmapRequest; 6 | 7 | /** 8 | * 该类用于解决较近的2个相同uri下载造成的可能的bug。 9 | * Created by lizhiyun on 16/5/23. 10 | */ 11 | public class RunningTasksManager { 12 | 13 | private LinkedHashMap doingMap = null; 14 | private RunningTasksManager(){} 15 | public RunningTasksManager(int threadCount){ 16 | doingMap = new LinkedHashMap( 17 | threadCount); 18 | } 19 | public Boolean hasDoingTask(BitmapRequest request) { 20 | synchronized (doingMap) { 21 | if (!request.path.contains("http:") && !request.path.contains("https:")){ 22 | return false; 23 | } 24 | return doingMap.containsKey(request.path); 25 | } 26 | } 27 | 28 | public void addDoingTask(BitmapRequest request) { 29 | synchronized (doingMap) { 30 | if (!request.path.contains("http:") && !request.path.contains("https:")){ 31 | return ; 32 | } 33 | doingMap.put(request.path, request.path); 34 | } 35 | } 36 | 37 | public void removeDoingTask(BitmapRequest request) { 38 | synchronized (doingMap) { 39 | if (!request.path.contains("http:") && !request.path.contains("https:")){ 40 | return ; 41 | } 42 | doingMap.remove(request.path); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/listener/CustomDisplayMethod.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.listener; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Movie; 5 | import android.graphics.drawable.BitmapDrawable; 6 | import android.graphics.drawable.Drawable; 7 | import android.view.View; 8 | 9 | /** 10 | * Created by lizhiyun on 16/5/29. 11 | */ 12 | public abstract class CustomDisplayMethod { 13 | public abstract void display(Bitmap bitmap,Movie movie); 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/listener/OnProgressUpdatedListener.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.listener; 2 | 3 | /** 4 | * Created by lizhiyun on 16/6/10. 5 | */ 6 | public interface OnProgressUpdatedListener { 7 | 8 | void onProgressUpdated(int curLen, int totalLen); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/loader/AssetsLoader.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.loader; 2 | 3 | import android.graphics.BitmapFactory; 4 | import android.graphics.Movie; 5 | import android.graphics.drawable.BitmapDrawable; 6 | 7 | 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | 11 | import download.imageLoader.config.ImageConfig; 12 | import download.imageLoader.request.BitmapRequest; 13 | import download.imageLoader.util.ImageSizeUtil; 14 | 15 | /** 16 | * Created by lizhiyun on 16/6/2. 17 | */ 18 | public class AssetsLoader implements LoadInterface { 19 | 20 | @Override 21 | public void load(BitmapRequest request, ImageConfig config) { 22 | String name = request.path.substring(9); 23 | InputStream is = null; 24 | try { 25 | if (request.view == null || request.view.get() == null){ 26 | return; 27 | } 28 | is = request.view.get().getContext().getAssets().open(name); 29 | } catch (IOException e) { 30 | e.printStackTrace(); 31 | } 32 | if (is == null) { 33 | return; 34 | } 35 | ImageSizeUtil.getImageViewSize(request); 36 | request.movie = Movie.decodeStream(is); 37 | if (request.checkIfNeedAsyncLoad()){ 38 | BitmapFactory.Options options = new BitmapFactory.Options(); 39 | options.inJustDecodeBounds = true; 40 | BitmapFactory.decodeStream(is, null, options); 41 | options.inSampleSize = ImageSizeUtil.caculateInSampleSize(options, 42 | request.width, request.height); 43 | options.inJustDecodeBounds = false; 44 | request.bitmap = BitmapFactory.decodeStream(is, null, options); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/loader/DrawableLoader.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.loader; 2 | 3 | import android.graphics.BitmapFactory; 4 | import android.graphics.Movie; 5 | import android.graphics.drawable.BitmapDrawable; 6 | 7 | 8 | import download.imageLoader.config.ImageConfig; 9 | import download.imageLoader.request.BitmapRequest; 10 | import download.imageLoader.util.ImageSizeUtil; 11 | 12 | /** 13 | * Created by lizhiyun on 16/6/2. 14 | */ 15 | public class DrawableLoader implements LoadInterface { 16 | 17 | @Override 18 | public void load(BitmapRequest request, ImageConfig config) { 19 | if (request.view == null || request.view.get() == null){ 20 | return; 21 | } 22 | int id = 0; 23 | try { 24 | id = Integer.parseInt(request.path.substring(11)); 25 | } catch (NumberFormatException e) { 26 | e.printStackTrace(); 27 | } 28 | if (id == 0) { 29 | return; 30 | } 31 | ImageSizeUtil.getImageViewSize(request); 32 | BitmapFactory.Options options = new BitmapFactory.Options(); 33 | options.inJustDecodeBounds = true; 34 | 35 | BitmapFactory.decodeResource(request.view.get().getResources(), id, options); 36 | options.inSampleSize = ImageSizeUtil.caculateInSampleSize(options, 37 | request.width, request.height); 38 | options.inJustDecodeBounds = false; 39 | try{ 40 | request.movie = Movie.decodeStream(request.view.get().getResources().openRawResource(id)); 41 | }catch (Exception e){ 42 | 43 | } 44 | if (request.checkIfNeedAsyncLoad()){ 45 | request.bitmap = BitmapFactory.decodeResource(request.view.get().getResources(), id, options); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/loader/FileLoader.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.loader; 2 | 3 | import android.graphics.BitmapFactory; 4 | import android.graphics.Movie; 5 | import android.graphics.drawable.BitmapDrawable; 6 | 7 | 8 | import java.io.File; 9 | import java.io.FileInputStream; 10 | import java.net.URI; 11 | 12 | import download.imageLoader.config.ImageConfig; 13 | import download.imageLoader.request.BitmapRequest; 14 | import download.imageLoader.util.ImageSizeUtil; 15 | 16 | /** 17 | * Created by lizhiyun on 16/6/2. 18 | */ 19 | public class FileLoader implements download.imageLoader.loader.LoadInterface { 20 | @Override 21 | public void load(BitmapRequest request, ImageConfig config) { 22 | 23 | if (request.view == null || request.view.get() == null){ 24 | return; 25 | } 26 | String path = new File(URI.create(request.path)).getAbsolutePath().substring(1); 27 | if (!new File(path).exists()) { 28 | return ; 29 | } 30 | ImageSizeUtil.getImageViewSize(request); 31 | BitmapFactory.Options options = new BitmapFactory.Options(); 32 | options.inJustDecodeBounds = true; 33 | BitmapFactory.decodeFile(path, options); 34 | options.inSampleSize = ImageSizeUtil.caculateInSampleSize(options, 35 | request.width, request.height); 36 | options.inJustDecodeBounds = false; 37 | 38 | try{ 39 | request.movie = Movie.decodeStream(new FileInputStream(new File(path))); 40 | }catch (Exception e){ 41 | 42 | } 43 | if (request.checkIfNeedAsyncLoad()){ 44 | request.bitmap = BitmapFactory.decodeFile(path, options); 45 | } 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/loader/HttpLoader.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.loader; 2 | 3 | import android.graphics.BitmapFactory; 4 | import android.graphics.Movie; 5 | import android.graphics.drawable.BitmapDrawable; 6 | 7 | 8 | import java.io.BufferedInputStream; 9 | import java.io.BufferedOutputStream; 10 | import java.io.FileOutputStream; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.OutputStream; 14 | import java.net.HttpURLConnection; 15 | import java.net.URL; 16 | 17 | import download.imageLoader.cache.DiskLruCache; 18 | import download.imageLoader.config.ImageConfig; 19 | import download.imageLoader.request.BitmapRequest; 20 | import download.imageLoader.util.ImageSizeUtil; 21 | import download.utils.Util; 22 | 23 | /** 24 | * Created by lizhiyun on 16/6/2. 25 | */ 26 | public class HttpLoader implements LoadInterface { 27 | 28 | @Override 29 | public void load(BitmapRequest request, ImageConfig config) { 30 | request.isFirstDown = true; 31 | if (!config.getOnlyMemoryMode()){ 32 | if (!config.getOnlyWifiMode()){ 33 | downloadBitmapToDisk(request, config.cache.getmDiskLruCacheBitmap()); 34 | } 35 | config.cache.getDiskCacheBitmap(request); 36 | } 37 | if (request.checkIfNeedAsyncLoad() && !config.getOnlyWifiMode()) { 38 | downloadImgByUrl(request); 39 | } 40 | } 41 | public void downloadBitmapToDisk(BitmapRequest request, 42 | DiskLruCache diskLruCache) { 43 | if (diskLruCache == null) { 44 | return ; 45 | } 46 | final String key = Util.md5(request.path); 47 | DiskLruCache.Editor editor; 48 | try { 49 | editor = diskLruCache.edit(key); 50 | if (editor != null) { 51 | OutputStream outputStream = editor.newOutputStream(0); 52 | BufferedOutputStream out = null; 53 | BufferedInputStream in = null; 54 | HttpURLConnection conn = null; 55 | try { 56 | URL url = new URL(request.path); 57 | conn = (HttpURLConnection) url.openConnection(); 58 | conn.setConnectTimeout(10*1000); 59 | request.totalSize = conn.getContentLength(); 60 | in = new BufferedInputStream(conn.getInputStream(), 8 * 1024); 61 | out = new BufferedOutputStream(outputStream, 8 * 1024); 62 | int b = 0; 63 | while ((b = in.read()) != -1) { 64 | out.write(b); 65 | } 66 | editor.commit(); 67 | } catch (Exception e) { 68 | e.printStackTrace(); 69 | editor.abort(); 70 | } finally { 71 | Util.close(conn, out, in); 72 | } 73 | } 74 | } catch (IOException e1) { 75 | e1.printStackTrace(); 76 | } 77 | 78 | try { 79 | diskLruCache.flush(); 80 | } catch (IOException e) { 81 | e.printStackTrace(); 82 | } 83 | 84 | } 85 | 86 | 87 | public static void downloadImgByUrl(BitmapRequest request) { 88 | FileOutputStream fos = null; 89 | InputStream is = null; 90 | HttpURLConnection conn = null; 91 | try { 92 | URL url = new URL(request.path); 93 | conn = (HttpURLConnection) url.openConnection(); 94 | conn.setConnectTimeout(10*1000); 95 | is = new BufferedInputStream(conn.getInputStream()); 96 | is.mark(is.available()); 97 | BitmapFactory.Options options = new BitmapFactory.Options(); 98 | options.inJustDecodeBounds = true; 99 | BitmapFactory.decodeStream(is, null, options); 100 | ImageSizeUtil.getImageViewSize(request); 101 | options.inSampleSize = ImageSizeUtil.caculateInSampleSize(options, 102 | request.width, request.height); 103 | options.inJustDecodeBounds = false; 104 | request.movie = Movie.decodeStream(is); 105 | if (request.checkIfNeedAsyncLoad()){ 106 | request.bitmap = BitmapFactory.decodeStream(is, null, options); 107 | } 108 | } catch (Exception e) { 109 | e.printStackTrace(); 110 | } finally { 111 | Util.close(conn, fos, is); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/loader/Load.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.loader; 2 | 3 | // _ooOoo_ 4 | // o8888888o 5 | // 88" . "88 6 | // (| -_- |) 7 | // O\ = /O 8 | // ____/`---'\____ 9 | // . ' \\| |// `. 10 | // / \\||| : |||// \ 11 | // / _||||| -:- |||||- \ 12 | // | | \\\ - /// | | 13 | // | \_| ''\---/'' | | 14 | // \ .-\__ `-` ___/-. / 15 | // ___`. .' /--.--\ `. . __ 16 | // ."" '< `.___\_<|>_/___.' >'"". 17 | // | | : `- \`.;`\ _ /`;.`/ - ` : | | 18 | // \ \ `-. \_ __\ /__ _/ .-` / / 19 | // ======`-.____`-.___\_____/___.-`____.-'====== 20 | // `=---=' 21 | // 22 | // ............................................. 23 | // 佛祖镇楼 BUG辟易 24 | // 佛曰: 25 | // 写字楼里写字间,写字间里程序员; 26 | // 程序人员写程序,又拿程序换酒钱。 27 | // 酒醒只在网上坐,酒醉还来网下眠; 28 | // 酒醉酒醒日复日,网上网下年复年。 29 | // 但愿老死电脑间,不愿鞠躬老板前; 30 | // 奔驰宝马贵者趣,公交自行程序员。 31 | // 别人笑我忒疯癫,我笑自己命太贱; 32 | // 不见满街漂亮妹,哪个归得程序员? 33 | 34 | 35 | import java.util.HashMap; 36 | import java.util.Map; 37 | 38 | import download.imageLoader.config.ImageConfig; 39 | import download.imageLoader.request.BitmapRequest; 40 | 41 | /** 42 | * 图片加载器 43 | * 44 | * @author Administrator 45 | * 46 | */ 47 | public class Load { 48 | private static HashMap map; 49 | private static HashMap classHashMap; 50 | static { 51 | classHashMap = new HashMap(4); 52 | classHashMap.put("http:",HttpLoader.class); 53 | classHashMap.put("https:",HttpLoader.class); 54 | classHashMap.put("assets:",AssetsLoader.class); 55 | classHashMap.put("drawable:",DrawableLoader.class); 56 | classHashMap.put("file:",FileLoader.class); 57 | map = new HashMap(); 58 | } 59 | public static void loadBitmap(BitmapRequest request, ImageConfig config) { 60 | for (Map.Entry entry: classHashMap.entrySet() 61 | ) { 62 | if (request.path.contains(entry.getKey())){ 63 | try { 64 | map.put(entry.getKey(),((LoadInterface) entry.getValue().newInstance())); 65 | 66 | } catch (InstantiationException e) { 67 | e.printStackTrace(); 68 | } catch (IllegalAccessException e) { 69 | e.printStackTrace(); 70 | } 71 | if (map.get(entry.getKey()) != null){ 72 | map.get(entry.getKey()).load(request,config); 73 | } 74 | break; 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/loader/LoadInterface.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.loader; 2 | 3 | import download.imageLoader.config.ImageConfig; 4 | import download.imageLoader.request.BitmapRequest; 5 | 6 | /** 7 | * Created by lizhiyun on 16/6/2. 8 | */ 9 | public interface LoadInterface { 10 | public abstract void load(BitmapRequest request, ImageConfig config); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/request/BitmapRequest.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.request; 2 | 3 | import download.imageLoader.core.ImageLoader; 4 | import download.imageLoader.core.LoadTask; 5 | import download.imageLoader.listener.CustomDisplayMethod; 6 | import download.imageLoader.view.PowerImageView; 7 | import download.utils.Util; 8 | 9 | import android.animation.ObjectAnimator; 10 | import android.annotation.TargetApi; 11 | import android.graphics.Bitmap; 12 | import android.graphics.Movie; 13 | import android.graphics.drawable.BitmapDrawable; 14 | import android.graphics.drawable.Drawable; 15 | import android.os.Build; 16 | import android.view.View; 17 | import android.widget.ImageSwitcher; 18 | import android.widget.ImageView; 19 | 20 | import java.lang.ref.WeakReference; 21 | 22 | public class BitmapRequest{ 23 | public WeakReference view; 24 | public Bitmap bitmap; 25 | public Movie movie; 26 | public int width; 27 | public int height; 28 | public Boolean isFirstDown; 29 | public Boolean isBlur; 30 | public Boolean isFace; 31 | public String path; 32 | public long totalSize; 33 | public CustomDisplayMethod customDisplayMethod; 34 | 35 | 36 | public BitmapRequest(){ 37 | isFirstDown = false; 38 | isBlur = false; 39 | } 40 | public String getKey(){ 41 | return Util.md5(this.path) + this.isBlur + this.isFace; 42 | } 43 | /** 44 | * 检测是否需要异步获取 45 | * @return 46 | */ 47 | public Boolean checkIfNeedAsyncLoad(){ 48 | return isNoresult() || (bitmap == null && view.get() != null && !(view.get() instanceof PowerImageView)) ; 49 | } 50 | 51 | public Boolean isNoresult(){ 52 | return (bitmap == null && movie == null); 53 | } 54 | 55 | /** 56 | * 检测是否能用于显示 57 | * @return 58 | */ 59 | public Boolean checkEffective(){ 60 | if (this.view.get() != null && ((String)this.view.get().getTag()).equals(getKey())){ 61 | return true; 62 | } 63 | return false; 64 | } 65 | public Boolean isNetWork() { 66 | return path.contains("http:") || path.contains("https:"); 67 | } 68 | 69 | 70 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 71 | public void displayLoading(Drawable b) { 72 | if (view == null || view.get() == null){ 73 | return; 74 | } 75 | if (customDisplayMethod != null){ 76 | customDisplayMethod.display(bitmap, movie); 77 | return; 78 | } 79 | if (view.get() instanceof PowerImageView){ 80 | ((PowerImageView) view.get()).setImageDrawable(b); 81 | }else{ 82 | setBitmap(view.get(), b); 83 | } 84 | } 85 | 86 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 87 | public void display() { 88 | if (view == null || view.get() == null){ 89 | return; 90 | } 91 | if (customDisplayMethod != null){ 92 | customDisplayMethod.display(bitmap,movie); 93 | return; 94 | } 95 | if (view.get() instanceof PowerImageView){ 96 | if (movie != null){ 97 | ((PowerImageView) view.get()).setMovie(movie); 98 | }else { 99 | if (bitmap != null){ 100 | ((PowerImageView) view.get()).setImageBitmap(bitmap); 101 | }else { 102 | ((PowerImageView) view.get()).setImageDrawable(ImageLoader.getInstance().getConfig().getFailedBm()); 103 | } 104 | } 105 | }else{ 106 | if (bitmap != null){ 107 | setBitmap(view.get(),bitmap); 108 | }else { 109 | setBitmap(view.get(),ImageLoader.getInstance().getConfig().getFailedBm()); 110 | } 111 | } 112 | if (this != null && isFirstDown != null && isFirstDown && bitmap != null) { 113 | ObjectAnimator.ofFloat(view.get(),"alpha",0,1f).setDuration(500).start(); 114 | } 115 | } 116 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 117 | public void setBitmap(View view,Drawable bitmap) { 118 | if (view == null) { 119 | return; 120 | } 121 | if (view instanceof ImageView) { 122 | ((ImageView) view).setImageDrawable(bitmap); 123 | } else if (view instanceof ImageSwitcher) { 124 | ((ImageSwitcher) view).setImageDrawable(bitmap); 125 | } else {// 对于其他view设置背景 126 | view.setBackground(bitmap); 127 | } 128 | } 129 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 130 | public void setBitmap(View view,Bitmap bitmap) { 131 | if (view == null) { 132 | return; 133 | } 134 | if (view instanceof ImageView) { 135 | ((ImageView) view).setImageBitmap(bitmap); 136 | } else if (view instanceof ImageSwitcher) { 137 | ((ImageSwitcher) view).setImageDrawable(new BitmapDrawable(bitmap)); 138 | } else {// 对于其他view设置背景 139 | view.setBackground(new BitmapDrawable(bitmap)); 140 | } 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/request/BitmapRequestBuilder.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.request; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | import java.lang.ref.WeakReference; 7 | import download.imageLoader.core.ImageLoader; 8 | import download.imageLoader.core.LoadTask; 9 | import download.imageLoader.listener.CustomDisplayMethod; 10 | 11 | /** 12 | * Created by lizhiyun on 16/6/3. 13 | */ 14 | public class BitmapRequestBuilder { 15 | public WeakReference view; 16 | public int width; 17 | public int height; 18 | public String path; 19 | public CustomDisplayMethod customDisplayMethod; 20 | public Boolean isBlur; 21 | public Boolean isFace; 22 | 23 | public BitmapRequestBuilder(){ 24 | this.isBlur = false; 25 | this.isFace = false; 26 | } 27 | 28 | public void into(View view){ 29 | if (isBlur == null){ 30 | isBlur = false; 31 | } 32 | BitmapRequest bitmapRequest = new BitmapRequest(); 33 | bitmapRequest.path = path; 34 | bitmapRequest.width = width; 35 | bitmapRequest.height = height; 36 | bitmapRequest.customDisplayMethod = customDisplayMethod; 37 | bitmapRequest.view = new WeakReference(view); 38 | bitmapRequest.isBlur = isBlur; 39 | bitmapRequest.isFace = isFace; 40 | ImageLoader.getInstance().loadImage(bitmapRequest); 41 | 42 | 43 | 44 | } 45 | public BitmapRequestBuilder blur(Boolean b){ 46 | this.isBlur = b; 47 | return this; 48 | } 49 | public BitmapRequestBuilder face(Boolean b){ 50 | this.isFace = b; 51 | return this; 52 | } 53 | 54 | public BitmapRequestBuilder size(int width,int height){ 55 | this.width = width; 56 | this.height = height; 57 | return this; 58 | } 59 | public BitmapRequestBuilder load(String path){ 60 | this.path = path; 61 | return this; 62 | } 63 | public BitmapRequestBuilder customDisplay(CustomDisplayMethod customDisplayMethod){ 64 | this.customDisplayMethod = customDisplayMethod; 65 | return this; 66 | } 67 | 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/util/BitmapOperate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014, kymjs 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package download.imageLoader.util; 17 | 18 | import android.graphics.Bitmap; 19 | import android.graphics.Canvas; 20 | import android.graphics.Color; 21 | import android.graphics.Paint; 22 | 23 | /** 24 | * 格式化图片的工具类 25 | * 26 | * @author kymjs (http://www.kymjs.com) 27 | * 28 | */ 29 | final class BitmapOperate { 30 | /** 31 | * 格式化图片宽高,使之可以被识别 32 | * 33 | * @param bitmap 34 | * 待格式化的图片 35 | * @return 36 | */ 37 | public static Bitmap formatBitmap(Bitmap bitmap) { 38 | Bitmap aimBitmap = null; 39 | 40 | int width = bitmap.getWidth(); 41 | int height = bitmap.getHeight(); 42 | boolean needChange = false; 43 | if (width % 2 == 1) { 44 | width++; 45 | needChange = true; 46 | } 47 | if (height % 2 == 1) { 48 | height++; 49 | needChange = true; 50 | } 51 | // 如果发生了改变,则做形变 52 | if (needChange) { 53 | aimBitmap = Bitmap.createScaledBitmap(bitmap, width, height, false); 54 | bitmap = null; 55 | } else { 56 | aimBitmap = bitmap; 57 | } 58 | return aimBitmap; 59 | } 60 | 61 | /** 62 | * 格式化图片为565格式,使之可以裁剪 63 | * 64 | * @param bitmap 65 | * 待处理的图片 66 | * @return 67 | */ 68 | public static Bitmap formatBitmapTo565(Bitmap bitmap) { 69 | Bitmap aimBitmap = null; 70 | if (bitmap.getConfig() != Bitmap.Config.RGB_565) { 71 | aimBitmap = Bitmap.createBitmap(bitmap.getWidth(), 72 | bitmap.getHeight(), Bitmap.Config.RGB_565); 73 | // 将创建的565格式作为画布,把bitmap重新画到565画布上 74 | Paint paint = new Paint(); 75 | Canvas canvas = new Canvas(aimBitmap); 76 | paint.setColor(Color.BLACK); 77 | canvas.drawBitmap(bitmap, 0, 0, paint); 78 | bitmap = null; 79 | } 80 | return aimBitmap; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/util/FaceCropper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014, kymjs 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package download.imageLoader.util; 17 | 18 | import android.content.Context; 19 | import android.graphics.Bitmap; 20 | import android.graphics.BitmapFactory; 21 | import android.graphics.Canvas; 22 | import android.graphics.Color; 23 | import android.graphics.Matrix; 24 | import android.graphics.Paint; 25 | import android.graphics.Point; 26 | import android.graphics.PointF; 27 | import android.media.FaceDetector; 28 | import android.util.Log; 29 | 30 | /** 31 | * 从Bitmap获取脸部位置的工具类.
32 | * 它支持多个人脸的识别并剪裁全部识别出来的脸(默认最多8个,效率问题)支持任何格式的图片 33 | * 34 | * @author kymjs (http://www.kymjs.com) 35 | */ 36 | public final class FaceCropper { 37 | 38 | public enum SizeMode { 39 | FACE_MARGIN, EYE_MARGIN 40 | }; 41 | 42 | // default constant 43 | private static final int MAX_FACES = 8; // 最多识别几张脸 44 | private static final int FACE_MARGIN_PX = 100; // 脸与脸间隔 45 | private static final float EYE_MARGIN_PX = 2F; // 双眼间距 46 | private static final int MIN_FACE_SIZE = 200; // 人脸最小直径 47 | 48 | // default value 49 | private int mFaceMinSize = MIN_FACE_SIZE; 50 | private int mMaxFaces = MAX_FACES; 51 | private int mFaceMarginPx = FACE_MARGIN_PX; 52 | private float mEyeMarginPx = EYE_MARGIN_PX; 53 | private SizeMode mSizeMode = SizeMode.EYE_MARGIN; 54 | 55 | private boolean mDebug = true; 56 | private Paint mDebugPainter; // 用作调试阶段显示人脸识别的圈 57 | private Paint mDebugAreaPainter; // 用作调试阶段显示人脸识别的圈 58 | 59 | public FaceCropper() { 60 | initPaints(); 61 | } 62 | 63 | public FaceCropper(int faceMarginPx) { 64 | setFaceMarginPx(faceMarginPx); 65 | initPaints(); 66 | } 67 | 68 | public FaceCropper(float eyesDistanceFactorMargin) { 69 | setEyeDistanceFactorMargin(eyesDistanceFactorMargin); 70 | initPaints(); 71 | } 72 | 73 | private void initPaints() { 74 | mDebugPainter = new Paint(); 75 | mDebugPainter.setColor(Color.RED); 76 | mDebugPainter.setAlpha(80); 77 | 78 | mDebugAreaPainter = new Paint(); 79 | mDebugAreaPainter.setColor(Color.GREEN); 80 | mDebugAreaPainter.setAlpha(80); 81 | } 82 | 83 | /********************* core method **********************************/ 84 | 85 | /** 86 | * 获取debug模式下的图片 87 | * 88 | * @param ctx 89 | * @param res 90 | * @return 91 | */ 92 | public Bitmap getDebugImage(Context ctx, int res) { 93 | // 以565格式通过resId创建bitmap 94 | BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); 95 | bitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565; 96 | return getDebugImage(BitmapFactory.decodeResource(ctx.getResources(), 97 | res, bitmapOptions)); 98 | } 99 | 100 | /** 101 | * 获取debug模式下的图片 102 | * 103 | * @param bitmap 104 | * @return 105 | */ 106 | public Bitmap getDebugImage(Bitmap bitmap) { 107 | FaceResult result = cropFace(bitmap, true); 108 | Canvas canvas = new Canvas(result.getBitmap()); 109 | 110 | canvas.drawBitmap(result.getBitmap(), new Matrix(), null); 111 | canvas.drawRect(result.getInit().x, result.getInit().y, 112 | result.getEnd().x, result.getEnd().y, mDebugAreaPainter); 113 | 114 | return result.getBitmap(); 115 | } 116 | 117 | /** 118 | * 剪裁指定图片中的脸 119 | * 120 | * @param ctx 121 | * @param res 122 | * @return 123 | */ 124 | public Bitmap cropFace(Context ctx, int res) { 125 | return getCroppedImage(ctx, res); 126 | } 127 | 128 | /** 129 | * 剪裁指定图片中的脸 130 | * 131 | * @param bitmap 132 | * @return 133 | */ 134 | public Bitmap cropFace(Bitmap bitmap) { 135 | return getCroppedImage(bitmap); 136 | } 137 | 138 | /** 139 | * 获取裁剪以后的人脸 140 | * 141 | * @param ctx 142 | * @param res 143 | * @return 144 | */ 145 | private Bitmap getCroppedImage(Context ctx, int res) { 146 | // 以565格式通过resId创建bitmap 147 | BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); 148 | bitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565; 149 | return getCroppedImage(BitmapFactory.decodeResource(ctx.getResources(), 150 | res, bitmapOptions)); 151 | } 152 | 153 | /** 154 | * 获取裁剪以后的人脸 155 | * 156 | * @param bitmap 157 | * @return 158 | */ 159 | private Bitmap getCroppedImage(Bitmap bitmap) { 160 | FaceResult result = cropFace(bitmap, mDebug); 161 | Bitmap croppedBitmap = Bitmap.createBitmap(result.getBitmap(), 162 | result.getInit().x, result.getInit().y, result.getEnd().x 163 | - result.getInit().x, 164 | result.getEnd().y - result.getInit().y); 165 | if (result.getBitmap() != croppedBitmap) { 166 | result.getBitmap().recycle(); 167 | } 168 | return croppedBitmap; 169 | } 170 | 171 | /** 172 | * 剪裁脸部的核心方法 173 | * 174 | * @param bitmap 175 | * 待识别的图片 176 | * @param debug 177 | * 是否开启调试模式 178 | * @return 179 | */ 180 | private FaceResult cropFace(Bitmap bitmap, boolean debug) { 181 | Bitmap formatBitmap = BitmapOperate.formatBitmap(bitmap); 182 | formatBitmap = BitmapOperate.formatBitmapTo565(formatBitmap); 183 | Bitmap aimBitmap = formatBitmap.copy(Bitmap.Config.RGB_565, true); 184 | if (formatBitmap != aimBitmap) { 185 | formatBitmap.recycle(); 186 | } 187 | // 创建一个人脸识别器 188 | FaceDetector faceDetector = new FaceDetector(aimBitmap.getWidth(), 189 | aimBitmap.getHeight(), mMaxFaces); 190 | // 人脸数组 191 | FaceDetector.Face[] faces = new FaceDetector.Face[mMaxFaces]; 192 | // Bitmap必须是565格式 193 | int faceCount = faceDetector.findFaces(aimBitmap, faces); 194 | 195 | if (debug) { 196 | Log.d("kymjs", faceCount + "张脸被找到"); 197 | } 198 | 199 | if (faceCount == 0) { 200 | return new FaceResult(aimBitmap); 201 | } 202 | 203 | int initX = aimBitmap.getWidth(); 204 | int initY = aimBitmap.getHeight(); 205 | int endX = 0; 206 | int endY = 0; 207 | 208 | PointF centerFace = new PointF(); 209 | 210 | Canvas canvas = new Canvas(aimBitmap); 211 | canvas.drawBitmap(aimBitmap, new Matrix(), null); 212 | 213 | // 计算每张脸的最小外接圆 214 | for (int i = 0; i < faceCount; i++) { 215 | FaceDetector.Face face = faces[i]; 216 | // 通常采用眼睛间距乘以三作为脸的外接圆直径 217 | int faceSize = (int) (face.eyesDistance() * 3); 218 | if (SizeMode.FACE_MARGIN.equals(mSizeMode)) { 219 | faceSize += mFaceMarginPx * 2; 220 | } else if (SizeMode.EYE_MARGIN.equals(mSizeMode)) { 221 | faceSize += face.eyesDistance() * mEyeMarginPx; 222 | } 223 | faceSize = Math.max(faceSize, mFaceMinSize); 224 | face.getMidPoint(centerFace); 225 | 226 | if (debug) { // 显示调试人脸识别圈 227 | canvas.drawPoint(centerFace.x, centerFace.y, mDebugPainter); 228 | canvas.drawCircle(centerFace.x, centerFace.y, 229 | face.eyesDistance() * 1.5f, mDebugPainter); 230 | } 231 | 232 | int tInitX = (int) (centerFace.x - faceSize / 2); 233 | int tInitY = (int) (centerFace.y - faceSize / 2); 234 | tInitX = Math.max(0, tInitX); 235 | tInitY = Math.max(0, tInitY); 236 | 237 | int tEndX = tInitX + faceSize; 238 | int tEndY = tInitY + faceSize; 239 | tEndX = Math.min(tEndX, aimBitmap.getWidth()); 240 | tEndY = Math.min(tEndY, aimBitmap.getHeight()); 241 | 242 | initX = Math.min(initX, tInitX); 243 | initY = Math.min(initY, tInitY); 244 | endX = Math.max(endX, tEndX); 245 | endY = Math.max(endY, tEndY); 246 | } 247 | 248 | int sizeX = endX - initX; 249 | int sizeY = endY - initY; 250 | 251 | if (sizeX + initX > aimBitmap.getWidth()) { 252 | sizeX = aimBitmap.getWidth() - initX; 253 | } 254 | if (sizeY + initY > aimBitmap.getHeight()) { 255 | sizeY = aimBitmap.getHeight() - initY; 256 | } 257 | Point init = new Point(initX, initY); 258 | Point end = new Point(initX + sizeX, initY + sizeY); 259 | return new FaceResult(aimBitmap, init, end); 260 | } 261 | 262 | /********************** config method *******************************/ 263 | 264 | public int getMaxFaces() { 265 | return mMaxFaces; 266 | } 267 | 268 | public void setMaxFaces(int maxFaces) { 269 | this.mMaxFaces = maxFaces; 270 | } 271 | 272 | public int getFaceMinSize() { 273 | return mFaceMinSize; 274 | } 275 | 276 | public void setFaceMinSize(int faceMinSize) { 277 | mFaceMinSize = faceMinSize; 278 | } 279 | 280 | public int getFaceMarginPx() { 281 | return mFaceMarginPx; 282 | } 283 | 284 | public void setFaceMarginPx(int faceMarginPx) { 285 | mFaceMarginPx = faceMarginPx; 286 | mSizeMode = SizeMode.FACE_MARGIN; 287 | } 288 | 289 | public SizeMode getSizeMode() { 290 | return mSizeMode; 291 | } 292 | 293 | public float getEyeDistanceFactorMargin() { 294 | return mEyeMarginPx; 295 | } 296 | 297 | public void setEyeDistanceFactorMargin(float eyeDistanceFactorMargin) { 298 | mEyeMarginPx = eyeDistanceFactorMargin; 299 | mSizeMode = SizeMode.EYE_MARGIN; 300 | } 301 | 302 | public boolean isDebug() { 303 | return mDebug; 304 | } 305 | 306 | public void setDebug(boolean debug) { 307 | mDebug = debug; 308 | } 309 | 310 | private class FaceResult { 311 | Bitmap mBitmap; 312 | Point mInit; 313 | Point mEnd; 314 | 315 | public FaceResult(Bitmap bitmap, Point init, Point end) { 316 | mBitmap = bitmap; 317 | mInit = init; 318 | mEnd = end; 319 | } 320 | 321 | public FaceResult(Bitmap bitmap) { 322 | mBitmap = bitmap; 323 | mInit = new Point(0, 0); 324 | mEnd = new Point(bitmap.getWidth(), bitmap.getHeight()); 325 | } 326 | 327 | public Bitmap getBitmap() { 328 | return mBitmap; 329 | } 330 | 331 | public Point getInit() { 332 | return mInit; 333 | } 334 | 335 | public Point getEnd() { 336 | return mEnd; 337 | } 338 | } 339 | 340 | } 341 | -------------------------------------------------------------------------------- /app/src/main/java/download/imageLoader/util/ImageSizeUtil.java: -------------------------------------------------------------------------------- 1 | package download.imageLoader.util; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | import download.imageLoader.request.BitmapRequest; 6 | import android.graphics.BitmapFactory.Options; 7 | import android.util.DisplayMetrics; 8 | import android.util.Log; 9 | import android.view.View; 10 | import android.view.ViewGroup.LayoutParams; 11 | import android.widget.ImageView; 12 | 13 | /** 14 | * http://blog.csdn.net/lmj623565791/article/details/41874561 15 | * 16 | * @author zhy 17 | * 18 | */ 19 | public class ImageSizeUtil { 20 | /** 21 | * 根据需求的宽和高以及图片实际的宽和高计算SampleSize 22 | * 23 | */ 24 | public static int caculateInSampleSize(Options options, int reqWidth, 25 | int reqHeight) { 26 | int width = options.outWidth; 27 | int height = options.outHeight; 28 | 29 | int inSampleSize = 1; 30 | 31 | if (width > reqWidth || height > reqHeight) { 32 | int widthRadio = Math.round(width * 1.0f / reqWidth); 33 | int heightRadio = Math.round(height * 1.0f / reqHeight); 34 | 35 | inSampleSize = Math.max(widthRadio, heightRadio); 36 | } 37 | 38 | return inSampleSize; 39 | } 40 | 41 | /** 42 | * 根据ImageView获适当的压缩的宽和高 43 | * 44 | */ 45 | public static void getImageViewSize(BitmapRequest request) { 46 | if (request.width > 0 && request.height > 0) { 47 | return; 48 | } 49 | if (request.view == null || request.view.get() == null) { 50 | request.width = 300; 51 | request.height = 300; 52 | return; 53 | } 54 | 55 | LayoutParams lp = request.view.get().getLayoutParams(); 56 | 57 | int width = request.view.get().getWidth() - request.view.get().getPaddingLeft() - request.view.get().getPaddingRight();// 获取imageview的实际宽度 58 | if (width <= 0) { 59 | width = lp.width - request.view.get().getPaddingLeft() - request.view.get().getPaddingRight();// 获取imageview在layout中声明的宽度 60 | } 61 | if (width <= 0) { 62 | // width = imageView.getMaxWidth();// 检查最大值 63 | width = getImageViewFieldValue(request.view, "mMaxWidth") - request.view.get().getPaddingTop() - request.view.get().getPaddingBottom(); 64 | } 65 | if (width <= 0) { 66 | width = 300; 67 | } 68 | 69 | int height = request.view.get().getHeight() - request.view.get().getPaddingTop() - request.view.get().getPaddingBottom();// 获取imageview的实际高度 70 | if (height <= 0) { 71 | height = lp.height;// 获取imageview在layout中声明的宽度 72 | } 73 | if (height <= 0) { 74 | height = getImageViewFieldValue(request.view, "mMaxHeight") - request.view.get().getPaddingTop() - request.view.get().getPaddingBottom();// 检查最大值 75 | } 76 | if (height <= 0) { 77 | height = 300; 78 | } 79 | request.width = width; 80 | request.height = height; 81 | 82 | } 83 | 84 | public static class ImageSize { 85 | public int width; 86 | public int height; 87 | } 88 | 89 | /** 90 | * 通过反射获取imageview的某个属性值 91 | * 92 | * @param object 93 | * @param fieldName 94 | * @return 95 | */ 96 | private static int getImageViewFieldValue(Object object, String fieldName) { 97 | int value = 0; 98 | try { 99 | Field field = ImageView.class.getDeclaredField(fieldName); 100 | field.setAccessible(true); 101 | int fieldValue = field.getInt(object); 102 | if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) { 103 | value = fieldValue; 104 | } 105 | } catch (Exception e) { 106 | } 107 | return value; 108 | 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/download/otherFileLoader/core/ConnectRunnable.java: -------------------------------------------------------------------------------- 1 | package download.otherFileLoader.core; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.net.HttpURLConnection; 6 | import java.net.URL; 7 | 8 | /** 9 | * Created by lizhiyun on 16/6/15. 10 | */ 11 | public class ConnectRunnable implements Runnable { 12 | private String url; 13 | public ConnectRunnable(String url, ConnectListener listener){ 14 | this.url = url; 15 | this.listener = listener; 16 | } 17 | private Boolean isRunning = false; 18 | ConnectListener listener; 19 | public Boolean isRunning (){ 20 | return isRunning; 21 | } 22 | 23 | @Override 24 | public void run() { 25 | isRunning = true; 26 | HttpURLConnection connection = null; 27 | try { 28 | connection = (HttpURLConnection) new URL(url).openConnection(); 29 | connection.setRequestMethod("GET"); 30 | connection.setConnectTimeout(5 * 1000); 31 | connection.setReadTimeout(5*1000); 32 | int responseCode = connection.getResponseCode(); 33 | int contentLength = connection.getContentLength(); 34 | boolean isSupport = false; 35 | if (responseCode == HttpURLConnection.HTTP_OK){ 36 | String ranges = connection.getHeaderField("Accept-Ranges"); 37 | if ("bytes".equals(ranges)){ 38 | isSupport = true; 39 | } 40 | listener.onConnected(contentLength,isSupport); 41 | String content_md5 = connection.getHeaderField("Content-MD5"); 42 | if (TextUtils.isEmpty(content_md5)){ 43 | listener.onGetContentMd5(content_md5); 44 | } 45 | }else { 46 | listener.onError("server error:"+responseCode); 47 | } 48 | isRunning = false; 49 | }catch (Exception e){ 50 | isRunning = false; 51 | listener.onError(e.getMessage()); 52 | e.printStackTrace(); 53 | }finally { 54 | if (connection != null){ 55 | connection.disconnect(); 56 | } 57 | } 58 | 59 | } 60 | interface ConnectListener{ 61 | void onConnected(int totalLength, Boolean isSupport); 62 | void onError(String message); 63 | void onGetContentMd5(String md5); 64 | } 65 | public void cancel(){ 66 | Thread.currentThread().interrupt(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/download/otherFileLoader/core/ConnectionChangeReceiver.java: -------------------------------------------------------------------------------- 1 | package download.otherFileLoader.core; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.net.ConnectivityManager; 7 | import android.net.NetworkInfo; 8 | 9 | import download.otherFileLoader.db.DownFileManager; 10 | 11 | /** 12 | * Created by lizhiyun on 16/7/6. 13 | */ 14 | public class ConnectionChangeReceiver extends BroadcastReceiver { 15 | @Override 16 | public void onReceive(Context context, Intent intent) { 17 | ConnectivityManager connectivityManager=(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 18 | NetworkInfo mobNetInfo=connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); 19 | NetworkInfo wifiNetInfo=connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 20 | if (!mobNetInfo.isConnected() && !wifiNetInfo.isConnected()) { 21 | 22 | }else { 23 | DownFileManager.getInstance().recoverAllNetError(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/download/otherFileLoader/core/Constants.java: -------------------------------------------------------------------------------- 1 | package download.otherFileLoader.core; 2 | 3 | /** 4 | * Created by lizhiyun on 16/6/12. 5 | */ 6 | public class Constants { 7 | 8 | public static final int WHAT_DOWNLOADING = 10; 9 | public static final int WHAT_FINISH = 12; 10 | public static final int WHAT_ERROR = 13; 11 | 12 | 13 | public static final int CONNECT_TIME = 10000; 14 | public static final int READ_TIME = 10000; 15 | public static final int BLOB_COUNT = 3; 16 | 17 | 18 | public volatile static long lastNotifyTime; 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/download/otherFileLoader/core/DownloadService.java: -------------------------------------------------------------------------------- 1 | package download.otherFileLoader.core; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.content.IntentFilter; 6 | import android.net.ConnectivityManager; 7 | import android.os.IBinder; 8 | 9 | import download.otherFileLoader.db.DownFileManager; 10 | import download.otherFileLoader.request.DownFile; 11 | 12 | /** 13 | * Created by lizhiyun on 16/7/6. 14 | */ 15 | public class DownloadService extends Service { 16 | private ConnectionChangeReceiver connectionChangeReceiver; 17 | 18 | @Override 19 | public void onCreate() { 20 | super.onCreate(); 21 | connectionChangeReceiver = new ConnectionChangeReceiver(); 22 | registerReceiver(connectionChangeReceiver,new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); 23 | } 24 | 25 | @Override 26 | public void onDestroy() { 27 | super.onDestroy(); 28 | unregisterReceiver(connectionChangeReceiver); 29 | } 30 | 31 | @Override 32 | public IBinder onBind(Intent intent) { 33 | return null; 34 | } 35 | 36 | @Override 37 | public int onStartCommand(Intent intent, int flags, int startId) { 38 | String url = intent.getStringExtra("url"); 39 | String path = intent.getStringExtra("path"); 40 | DownFile downFile = new DownFile(url,path); 41 | DownFileManager.getInstance(this).addTask(downFile); 42 | return super.onStartCommand(intent, flags, startId); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/download/otherFileLoader/core/DownloadTask.java: -------------------------------------------------------------------------------- 1 | package download.otherFileLoader.core; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.os.Message; 6 | import android.text.TextUtils; 7 | import android.util.Log; 8 | 9 | import com.litesuits.go.SmartExecutor; 10 | import java.io.File; 11 | import java.util.HashMap; 12 | 13 | import download.otherFileLoader.db.DLDBManager; 14 | import download.otherFileLoader.db.DownFileManager; 15 | import download.otherFileLoader.request.DownFile; 16 | import download.utils.Util; 17 | 18 | 19 | public class DownloadTask implements DownloadThread.DownListener{ 20 | public DownFile downFile; 21 | private ConnectRunnable mConnectThread; 22 | private DownloadThread[] mDownloadThreads; 23 | private DownFile.DownloadStatus[] mDownloadStatus; 24 | private long mLastStamp; 25 | private File destFile; 26 | 27 | 28 | public DownloadTask(DownFile entry) { 29 | this.downFile = entry; 30 | this.destFile = new File(entry.downPath,entry.name); 31 | } 32 | 33 | public void pause() { 34 | downFile.state = DownFile.DownloadStatus.PAUSE; 35 | if (mConnectThread != null && mConnectThread.isRunning()) { 36 | mConnectThread.cancel(); 37 | } 38 | if (mDownloadThreads != null && mDownloadThreads.length > 0) { 39 | for (int i = 0; i < mDownloadThreads.length; i++) { 40 | if (mDownloadThreads[i] != null && mDownloadThreads[i].isRunning()) { 41 | if (downFile.isSuppurtRanger) { 42 | mDownloadThreads[i].pause(); 43 | } else { 44 | mDownloadThreads[i].cancel(); 45 | } 46 | } 47 | } 48 | } 49 | } 50 | 51 | public void error() { 52 | downFile.state = DownFile.DownloadStatus.ERROR; 53 | if (mConnectThread != null && mConnectThread.isRunning()) { 54 | mConnectThread.cancel(); 55 | } 56 | if (mDownloadThreads != null && mDownloadThreads.length > 0) { 57 | for (int i = 0; i < mDownloadThreads.length; i++) { 58 | if (mDownloadThreads[i] != null && mDownloadThreads[i].isRunning()) { 59 | mDownloadThreads[i].error(); 60 | } 61 | } 62 | } 63 | } 64 | 65 | public void cancel() { 66 | downFile.state = DownFile.DownloadStatus.CANCEL; 67 | if (mConnectThread != null && mConnectThread.isRunning()) { 68 | mConnectThread.cancel(); 69 | } 70 | if (mDownloadThreads != null && mDownloadThreads.length > 0) { 71 | for (int i = 0; i < mDownloadThreads.length; i++) { 72 | if (mDownloadThreads[i] != null && mDownloadThreads[i].isRunning()) { 73 | mDownloadThreads[i].cancel(); 74 | } 75 | } 76 | } 77 | } 78 | 79 | public void start() { 80 | downFile.isError = false; 81 | downFile.isCanceled = false; 82 | downFile.isPaused = false; 83 | if (downFile.totalLength == 0){ 84 | DownFileManager.sExecutor.execute(new ConnectRunnable(downFile.url, new ConnectRunnable.ConnectListener() { 85 | @Override 86 | public void onConnected(int totalLength, Boolean isSupport) { 87 | downFile.totalLength = totalLength; 88 | downFile.isSuppurtRanger = isSupport; 89 | if (downFile.totalLength <= 0) { 90 | downFile.isSuppurtRanger = false; 91 | } 92 | startDownload(); 93 | 94 | } 95 | 96 | @Override 97 | public void onError(String message) { 98 | downFile.isError = true; 99 | notifyUpdate(downFile, Constants.WHAT_ERROR); 100 | } 101 | 102 | @Override 103 | public void onGetContentMd5(String md5) { 104 | downFile.content_md5 = md5; 105 | } 106 | })); 107 | }else{ 108 | startDownload(); 109 | } 110 | } 111 | 112 | 113 | private void startDownload() { 114 | downFile.state = DownFile.DownloadStatus.DOWNLOADING; 115 | if (downFile.isSuppurtRanger) { 116 | startMultiDownload(); 117 | } else { 118 | startSingleDownload(); 119 | } 120 | } 121 | 122 | private void startMultiDownload() { 123 | int block = downFile.totalLength / Constants.BLOB_COUNT; 124 | int startPos = 0; 125 | int endPos = 0; 126 | if (downFile.ranges == null) { 127 | downFile.ranges = new HashMap(); 128 | for (int i = 0; i < Constants.BLOB_COUNT; i++) { 129 | downFile.ranges.put(i, 0); 130 | } 131 | } 132 | mDownloadThreads = new DownloadThread[Constants.BLOB_COUNT]; 133 | mDownloadStatus = new DownFile.DownloadStatus[Constants.BLOB_COUNT]; 134 | for (int i = 0; i < Constants.BLOB_COUNT; i++) { 135 | startPos = i * block + downFile.ranges.get(i); 136 | if (i == Constants.BLOB_COUNT - 1) { 137 | endPos = downFile.totalLength; 138 | } else { 139 | endPos = (i + 1) * block - 1; 140 | } 141 | if (startPos < endPos) { 142 | mDownloadThreads[i] = new DownloadThread(downFile.url,destFile, i, startPos, endPos, this); 143 | mDownloadStatus[i] = DownFile.DownloadStatus.DOWNLOADING; 144 | DownFileManager.sExecutor.execute(mDownloadThreads[i]); 145 | }else { 146 | mDownloadStatus[i] = DownFile.DownloadStatus.FINISH; 147 | } 148 | } 149 | } 150 | 151 | private void startSingleDownload() { 152 | mDownloadThreads = new DownloadThread[1]; 153 | mDownloadStatus = new DownFile.DownloadStatus[1]; 154 | mDownloadStatus[0] = DownFile.DownloadStatus.DOWNLOADING; 155 | mDownloadThreads[0] = new DownloadThread(downFile.url,destFile, 0, 0, 0, this); 156 | DownFileManager.sExecutor.execute(mDownloadThreads[0]); 157 | } 158 | 159 | private void notifyUpdate(DownFile entry, int what) { 160 | if (what == Constants.WHAT_DOWNLOADING){ 161 | if (System.currentTimeMillis() - Constants.lastNotifyTime > 1000 || downFile.downLength >= downFile.totalLength){ 162 | Constants.lastNotifyTime = System.currentTimeMillis(); 163 | }else { 164 | return; 165 | } 166 | } 167 | Message msg = DownFileManager.sHandler.obtainMessage(); 168 | msg.what = what; 169 | msg.obj = entry; 170 | DownFileManager.sHandler.sendMessage(msg); 171 | if (what == Constants.WHAT_ERROR){ 172 | DownFileManager.dldbManager.deleteTaskInfo(downFile); 173 | }else { 174 | DownFileManager.dldbManager.insertOrUpdate(downFile); 175 | } 176 | } 177 | 178 | @Override 179 | public void onProgressChanged(int index, int progress) { 180 | if (downFile.isSuppurtRanger) { 181 | int range = downFile.ranges.get(index) + progress; 182 | downFile.ranges.put(index, range); 183 | } 184 | downFile.downLength += progress; 185 | notifyUpdate(downFile, Constants.WHAT_DOWNLOADING); 186 | } 187 | 188 | @Override 189 | public void onDownloadCompleted(int index) { 190 | Log.e("test","onDownloadCompleted "+index); 191 | mDownloadStatus[index] = DownFile.DownloadStatus.FINISH; 192 | for (int i = 0; i < mDownloadStatus.length;i++){ 193 | if (mDownloadStatus[i] != DownFile.DownloadStatus.FINISH){ 194 | return; 195 | } 196 | } 197 | if (!TextUtils.isEmpty(downFile.content_md5)){ 198 | if (!downFile.content_md5.equals(Util.md5sum(downFile.downPath))){ 199 | downFile.state = DownFile.DownloadStatus.ERROR; 200 | notifyUpdate(downFile,Constants.WHAT_ERROR); 201 | return; 202 | } 203 | } 204 | downFile.state = DownFile.DownloadStatus.FINISH; 205 | Log.v("test","finish"+downFile.state.name+downFile.downLength+" "+downFile.totalLength); 206 | notifyUpdate(downFile, Constants.WHAT_FINISH); 207 | } 208 | 209 | @Override 210 | public void onDownloadError(int index, String message) { 211 | for (int i = 0; i < mDownloadStatus.length; i++) { 212 | if (mDownloadStatus[i] != DownFile.DownloadStatus.FINISH && mDownloadStatus[i] != DownFile.DownloadStatus.ERROR) { 213 | mDownloadThreads[i].error(); 214 | } 215 | } 216 | downFile.isError = true; 217 | downFile.state = DownFile.DownloadStatus.ERROR; 218 | notifyUpdate(downFile, Constants.WHAT_ERROR); 219 | } 220 | 221 | } 222 | -------------------------------------------------------------------------------- /app/src/main/java/download/otherFileLoader/core/DownloadThread.java: -------------------------------------------------------------------------------- 1 | package download.otherFileLoader.core; 2 | 3 | import android.util.Log; 4 | 5 | import java.io.BufferedInputStream; 6 | import java.io.BufferedOutputStream; 7 | import java.io.BufferedReader; 8 | import java.io.File; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.InputStreamReader; 13 | import java.io.RandomAccessFile; 14 | import java.net.HttpURLConnection; 15 | import java.net.URL; 16 | 17 | import download.otherFileLoader.listener.DownloadListener; 18 | import download.otherFileLoader.request.DownFile; 19 | 20 | /** 21 | * Created by Stay on 4/8/15. 22 | * Powered by www.stay4it.com 23 | */ 24 | public class DownloadThread implements Runnable { 25 | private final String url; 26 | private final int startPos; 27 | private final int endPos; 28 | private final File destFile; 29 | private final DownListener listener; 30 | private final int index; 31 | private final boolean isSingleDownload; 32 | private volatile boolean isPaused; 33 | private volatile DownFile.DownloadStatus state = DownFile.DownloadStatus.IDLE; 34 | 35 | private volatile boolean isCancelled; 36 | private volatile boolean isError; 37 | 38 | public DownloadThread(String url, File destFile, int index, int startPos, int endPos, DownListener listener) { 39 | this.url = url; 40 | this.index = index; 41 | this.startPos = startPos; 42 | this.endPos = endPos; 43 | this.destFile = destFile; 44 | if (startPos == 0 && endPos == 0) { 45 | isSingleDownload = true; 46 | } else { 47 | isSingleDownload = false; 48 | } 49 | 50 | this.listener = listener; 51 | } 52 | 53 | @Override 54 | public void run() { 55 | state = DownFile.DownloadStatus.DOWNLOADING; 56 | HttpURLConnection connection = null; 57 | try { 58 | connection = (HttpURLConnection) new URL(url).openConnection(); 59 | connection.setRequestMethod("GET"); 60 | if (!isSingleDownload) { 61 | connection.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos); 62 | } 63 | connection.setConnectTimeout(Constants.CONNECT_TIME); 64 | connection.setReadTimeout(Constants.READ_TIME); 65 | int responseCode = connection.getResponseCode(); 66 | int contentLength = connection.getContentLength(); 67 | RandomAccessFile raf = null; 68 | BufferedOutputStream bos = null; 69 | BufferedInputStream is ; 70 | byte[] bytes; 71 | if (responseCode == HttpURLConnection.HTTP_PARTIAL) { 72 | raf = new RandomAccessFile(destFile, "rw"); 73 | raf.seek(startPos); 74 | } else if (responseCode == HttpURLConnection.HTTP_OK) { 75 | bos = new BufferedOutputStream(new FileOutputStream(destFile)); 76 | } else { 77 | state = DownFile.DownloadStatus.ERROR; 78 | listener.onDownloadError(index, "server error:" + responseCode); 79 | return; 80 | } 81 | String s; 82 | is = new BufferedInputStream(connection.getInputStream(),8 *1024); 83 | bytes = new byte[2048]; 84 | int len; 85 | while ((len = is.read(bytes)) != -1) { 86 | if (isPaused || isCancelled || isError) { 87 | break; 88 | } 89 | if (raf != null){ 90 | raf.write(bytes,0,len); 91 | }else { 92 | bos.write(bytes,0,len); 93 | } 94 | listener.onProgressChanged(index, len); 95 | } 96 | if (raf != null) 97 | raf.close(); 98 | if (bos != null) 99 | bos.close(); 100 | is.close(); 101 | 102 | if (!isPaused && !isCancelled && !isError){ 103 | listener.onDownloadCompleted(index); 104 | } 105 | } catch (IOException e) { 106 | e.printStackTrace(); 107 | if (!isPaused && !isCancelled && !isError){ 108 | listener.onDownloadError(index, e.getMessage()); 109 | } 110 | } finally { 111 | if (connection != null) { 112 | connection.disconnect(); 113 | } 114 | } 115 | } 116 | 117 | public boolean isRunning() { 118 | return state == DownFile.DownloadStatus.DOWNLOADING; 119 | } 120 | 121 | public void pause() { 122 | state = DownFile.DownloadStatus.PAUSE; 123 | isPaused = true; 124 | Thread.currentThread().interrupt(); 125 | } 126 | 127 | public void cancel() { 128 | state = DownFile.DownloadStatus.CANCEL; 129 | isCancelled = true; 130 | Thread.currentThread().interrupt(); 131 | } 132 | 133 | public void error() { 134 | state = DownFile.DownloadStatus.ERROR; 135 | isError = true; 136 | Thread.currentThread().interrupt(); 137 | } 138 | interface DownListener { 139 | void onProgressChanged(int index, int progress); 140 | 141 | void onDownloadCompleted(int index); 142 | 143 | void onDownloadError(int index, String message); 144 | 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /app/src/main/java/download/otherFileLoader/db/DLDBHelper.java: -------------------------------------------------------------------------------- 1 | package download.otherFileLoader.db; 2 | 3 | import android.content.Context; 4 | import android.database.DatabaseErrorHandler; 5 | import android.database.sqlite.SQLiteDatabase; 6 | import android.database.sqlite.SQLiteOpenHelper; 7 | 8 | final class DLDBHelper extends SQLiteOpenHelper { 9 | private static final String DB_NAME = "df.db"; 10 | private static final int DB_VERSION = 3; 11 | public static final String TABLENAME = "downloadfile"; 12 | private static final String TB_CREATE = 13 | "CREATE TABLE "+ 14 | TABLENAME+"(id INTEGER PRIMARY KEY AUTOINCREMENT, url CHAR, path CHAR NOT NULL," + 15 | " name CHAR,"+ 16 | " issupportrange INTEGER, downlength INTEGER, totallength INTEGER, rangers CHAR, state INTEGER" + 17 | ")"; 18 | 19 | public DLDBHelper(Context context) { 20 | super(context,DB_NAME,null,DB_VERSION); 21 | } 22 | 23 | 24 | @Override 25 | public void onCreate(SQLiteDatabase db) { 26 | db.execSQL(TB_CREATE); 27 | } 28 | 29 | @Override 30 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 31 | db.execSQL("DROP TABLE IF EXISTS "+TABLENAME); 32 | onCreate(db); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/download/otherFileLoader/db/DLDBManager.java: -------------------------------------------------------------------------------- 1 | package download.otherFileLoader.db; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.database.sqlite.SQLiteDatabase; 7 | import android.text.TextUtils; 8 | 9 | import java.util.HashMap; 10 | import java.util.List; 11 | 12 | import download.otherFileLoader.request.DownFile; 13 | 14 | public final class DLDBManager { 15 | private static DLDBManager sManager; 16 | private DLDBHelper helper; 17 | private Context context; 18 | 19 | private DLDBManager(Context context) { 20 | helper = new DLDBHelper(context); 21 | } 22 | 23 | static DLDBManager getInstance(Context context) { 24 | if (null == sManager) { 25 | sManager = new DLDBManager(context); 26 | } 27 | return sManager; 28 | } 29 | 30 | public synchronized void insertTaskInfo(DownFile info) { 31 | SQLiteDatabase db = helper.getWritableDatabase(); 32 | ContentValues values = new ContentValues(); 33 | values.put("url",info.url); 34 | values.put("path",info.downPath); 35 | values.put("name",info.name); 36 | values.put("downlength",info.downLength); 37 | values.put("totallength", info.totalLength); 38 | values.put("state",info.state.value); 39 | 40 | if (info.ranges != null && info.ranges.size() > 0){ 41 | StringBuilder sb = new StringBuilder(); 42 | for (int i = 0;i < info.ranges.size();i++){ 43 | if (i > 0){ 44 | sb.append(","); 45 | } 46 | sb.append(info.ranges.get(i)); 47 | } 48 | values.put("rangers",sb.toString()); 49 | } 50 | db.insert(DLDBHelper.TABLENAME,null,values); 51 | } 52 | 53 | public synchronized void insertOrUpdate(DownFile info){ 54 | if (queryTaskInfo(info) == null){ 55 | insertTaskInfo(info); 56 | }else { 57 | updateTaskInfo(info); 58 | } 59 | } 60 | 61 | public synchronized void deleteTaskInfo(DownFile info) { 62 | SQLiteDatabase db = helper.getWritableDatabase(); 63 | db.delete(DLDBHelper.TABLENAME,"url=? and path=?",new String[]{info.url,info.downPath}); 64 | db.close(); 65 | } 66 | 67 | public synchronized void updateTaskInfo(DownFile info) { 68 | SQLiteDatabase db = helper.getWritableDatabase(); 69 | ContentValues values = new ContentValues(); 70 | values.put("url",info.url); 71 | values.put("path",info.downPath); 72 | values.put("name",info.name); 73 | values.put("downlength",info.downLength); 74 | values.put("totallength", info.totalLength); 75 | values.put("state",info.state.value); 76 | if (info.ranges != null && info.ranges.size() > 0){ 77 | StringBuilder sb = new StringBuilder(); 78 | if (info.ranges != null){ 79 | for (int i = 0;i < info.ranges.size();i++){ 80 | if (i > 0){ 81 | sb.append(","); 82 | } 83 | sb.append(info.ranges.get(i)); 84 | } 85 | } 86 | values.put("rangers",sb.toString()); 87 | } 88 | db.update(DLDBHelper.TABLENAME,values,"url = ? and path = ?",new String[]{info.url,info.downPath}); 89 | } 90 | 91 | public synchronized DownFile queryTaskInfo(DownFile info) { 92 | DownFile downFile = null; 93 | SQLiteDatabase db = helper.getReadableDatabase(); 94 | Cursor cursor = db.query(DLDBHelper.TABLENAME,new String[]{"name,issupportrange,downlength,totallength,rangers,state"},"url = ? and path = ?",new String[]{info.url,info.downPath},null,null,"url desc","1,2"); 95 | 96 | while (cursor.moveToNext()){ 97 | if (downFile == null){ 98 | downFile = new DownFile(info.url); 99 | downFile.downPath = info.downPath; 100 | } 101 | downFile.name = cursor.getString(0); 102 | downFile.isSuppurtRanger = cursor.getInt(1) > 0; 103 | downFile.downLength = cursor.getInt(2); 104 | downFile.totalLength = cursor.getInt(3); 105 | String rangers = cursor.getString(4); 106 | if (!TextUtils.isEmpty(rangers.trim())){ 107 | String[] strings = rangers.split(","); 108 | if (strings.length > 0){ 109 | downFile.ranges = new HashMap(); 110 | } 111 | for (int i = 0;i ranges; 55 | public enum DownloadStatus{ 56 | IDLE(0,"空闲"),DOWNLOADING(2,"下载中"),FINISH(1,"完成"),ERROR(3,"异常"),PAUSE(4,"暂停"),CANCEL(5,"取消"),WAITING(6,"等待"); 57 | public int value; 58 | public String name; 59 | private DownloadStatus(int value,String name){ 60 | this.name = name; 61 | this.value = value; 62 | } 63 | } 64 | 65 | @Override 66 | public boolean equals(Object o) { 67 | if (o == null){ 68 | return false; 69 | } 70 | if (!(o instanceof DownFile)){ 71 | return false; 72 | } 73 | DownFile other = (DownFile) o; 74 | return this.url.equals(other.url) && this.downPath.equals(other.downPath); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/download/otherFileLoader/util/ToastUtils.java: -------------------------------------------------------------------------------- 1 | package download.otherFileLoader.util; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.os.Build.VERSION; 6 | import android.util.Log; 7 | import android.view.Gravity; 8 | import android.view.View; 9 | import android.widget.TextView; 10 | import android.widget.Toast; 11 | 12 | public class ToastUtils { 13 | 14 | private static Toast toast; 15 | 16 | /** 17 | * 日记的输出方法 18 | * 19 | * @param msg 20 | */ 21 | public static void log(Object msg) { 22 | Log.d("test", String.valueOf(msg)); 23 | } 24 | private static int color = Color.parseColor("#3EA32B"); 25 | /** 26 | * 显示常规的Toast 27 | * 28 | * @param context 29 | * 上下文对象 30 | * @param text 31 | * 显示的内容 32 | */ 33 | public static void showToast(Context context, String text) { 34 | showMessage(context, text, 15,color); 35 | } 36 | public static void showToast(Context context, int text) { 37 | showMessage(context, context.getResources().getString(text), 15,color); 38 | } 39 | public static void showToast(Context context, String text,int size) { 40 | showMessage(context, text, size,color); 41 | } 42 | public static void showToast(Context context, String text,int size,int color) { 43 | showMessage(context, text, size,color); 44 | } 45 | 46 | /** 47 | * 根据方位显示toast 48 | * 49 | * @param context 50 | * 上下文对象 51 | * @param text 52 | * 显示的内容 53 | * @param gravity 54 | * 对齐方式 55 | */ 56 | public static void makeToast(Context context, String text, int gravity) { 57 | Toast t = Toast.makeText(context, text, Toast.LENGTH_SHORT); 58 | t.setGravity(gravity, 0, 0); 59 | t.show(); 60 | 61 | } 62 | 63 | /** 64 | * 自定义Toast的View的显示样式 65 | * 66 | * @param context 67 | * 上下文对象 68 | * @param view 69 | * 需要显示的布局视图 70 | */ 71 | public static void makeToast(Context context, View view) { 72 | Toast t = Toast.makeText(context, "", Toast.LENGTH_SHORT); 73 | t.setView(view); 74 | t.setGravity(Gravity.CENTER, 0, 0); 75 | t.show(); 76 | } 77 | 78 | public static void showMessage(Context context ,String text,int size,int color){ 79 | if (toast == null) { 80 | toast=new Toast(context); 81 | } 82 | if (toast != null && VERSION.SDK_INT < 14) { 83 | toast.cancel(); 84 | } 85 | toast.setDuration(Toast.LENGTH_SHORT); 86 | TextView textView = new TextView(context); 87 | textView.setText(text); 88 | textView.setTextSize(size); 89 | textView.setTextColor(color); 90 | textView.setGravity(Gravity.CENTER); 91 | textView.setBackgroundResource(android.R.drawable.toast_frame); 92 | toast.setView(textView); 93 | toast.setGravity(Gravity.CENTER, 0, 0); 94 | toast.show(); 95 | } 96 | public static void showFailedToast(Context context) { 97 | showMessage(context, "获取数据失败", 15,Color.parseColor("#0000C0")); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/java/download/utils/Util.java: -------------------------------------------------------------------------------- 1 | package download.utils; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager.NameNotFoundException; 6 | import android.graphics.drawable.BitmapDrawable; 7 | import android.os.Build; 8 | import android.os.Environment; 9 | 10 | import java.io.Closeable; 11 | import java.io.File; 12 | import java.io.FileInputStream; 13 | import java.io.InputStream; 14 | import java.net.HttpURLConnection; 15 | import java.security.MessageDigest; 16 | import java.security.NoSuchAlgorithmException; 17 | 18 | public class Util { 19 | /** 20 | * 利用签名辅助类,将字符串字节数组 21 | * 22 | * @param str 23 | * @return 24 | */ 25 | public static String md5(String str) { 26 | byte[] digest = null; 27 | try { 28 | MessageDigest md = MessageDigest.getInstance("md5"); 29 | digest = md.digest(str.getBytes()); 30 | return bytes2hex02(digest); 31 | 32 | } catch (NoSuchAlgorithmException e) { 33 | e.printStackTrace(); 34 | } 35 | return null; 36 | } 37 | 38 | 39 | /** 40 | * 方式二 41 | * 42 | * @param bytes 43 | * @return 44 | */ 45 | public static String bytes2hex02(byte[] bytes) { 46 | StringBuilder sb = new StringBuilder(); 47 | String tmp = null; 48 | for (byte b : bytes) { 49 | // 将每个字节与0xFF进行与运算,然后转化为10进制,然后借助于Integer再转化为16进制 50 | tmp = Integer.toHexString(0xFF & b); 51 | if (tmp.length() == 1)// 每个字节8为,转为16进制标志,2个16进制位 52 | { 53 | tmp = "0" + tmp; 54 | } 55 | sb.append(tmp); 56 | } 57 | 58 | return sb.toString(); 59 | 60 | } 61 | 62 | public static File getDiskCacheDir(Context context, String uniqueName) { 63 | String cachePath = null; 64 | if (Environment.MEDIA_MOUNTED.equals(Environment 65 | .getExternalStorageState())) { 66 | File cacheDir = context.getExternalCacheDir(); 67 | if (cacheDir != null) { 68 | cachePath = cacheDir.getPath(); 69 | } 70 | } else { 71 | cachePath = context.getCacheDir().getPath(); 72 | } 73 | if (cachePath == null) { 74 | cachePath = Environment.getExternalStorageDirectory().getAbsolutePath(); 75 | } 76 | File file = new File(cachePath + File.separator + uniqueName); 77 | if (!file.exists()){ 78 | file.mkdirs(); 79 | } 80 | return file; 81 | } 82 | public static String getNameFromUrl(String url){ 83 | return url.substring(url.lastIndexOf("/")+1); 84 | } 85 | 86 | public static int getAppVersion(Context context) { 87 | try { 88 | PackageInfo info = context.getPackageManager().getPackageInfo( 89 | context.getPackageName(), 0); 90 | return info.versionCode; 91 | } catch (NameNotFoundException e) { 92 | e.printStackTrace(); 93 | } 94 | return 1; 95 | } 96 | public static String getFileSize(long size){ 97 | if (size < 1024) { 98 | return size + "B"; 99 | }else if (size < 1024*1024) { 100 | return new java.text.DecimalFormat("#.##").format(size / 1024.0f) + "KB"; 101 | }else { 102 | return new java.text.DecimalFormat("#.##").format(size / (1024 * 1024.0f)) + "M"; 103 | } 104 | } 105 | 106 | public static void close(Closeable c) { 107 | try { 108 | if (c != null) { 109 | c.close(); 110 | } 111 | } catch (Exception e) { 112 | } 113 | } 114 | 115 | public static void close(HttpURLConnection c) { 116 | if (c != null) { 117 | c.disconnect(); 118 | } 119 | } 120 | 121 | public static void close(HttpURLConnection conn, Closeable out, Closeable in) { 122 | close(conn); 123 | close(out); 124 | close(in); 125 | } 126 | private static final char HEX_DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 127 | 'A', 'B', 'C', 'D', 'E', 'F' }; 128 | public static String toHexString(byte[] b) { 129 | StringBuilder sb = new StringBuilder(b.length * 2); 130 | for (int i = 0; i < b.length; i++) { 131 | sb.append(HEX_DIGITS[(b[i] & 0xf0) >>> 4]); 132 | sb.append(HEX_DIGITS[b[i] & 0x0f]); 133 | } 134 | return sb.toString(); 135 | } 136 | 137 | public static String md5sum(String filePath) { 138 | InputStream fis; 139 | byte[] buffer = new byte[1024]; 140 | int numRead = 0; 141 | MessageDigest md5; 142 | try{ 143 | fis = new FileInputStream(filePath); 144 | md5 = MessageDigest.getInstance("MD5"); 145 | while((numRead=fis.read(buffer)) > 0) { 146 | md5.update(buffer,0,numRead); 147 | } 148 | fis.close(); 149 | return toHexString(md5.digest()); 150 | } catch (Exception e) { 151 | System.out.println("error"); 152 | return ""; 153 | } 154 | } 155 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/res/drawable-hdpi/anim.gif -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/face.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/res/drawable-hdpi/face.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdanlizhiyun/lib_download/50a26a9e406221b85a4238de853a876e9e79ae6d/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_applist_item.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 18 | 19 |