├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── gui │ │ └── com │ │ └── lgimagecompressor │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── gui │ │ │ └── com │ │ │ └── lgimagecompressor │ │ │ ├── BasicCompressActivity.java │ │ │ ├── CompressServiceListener.java │ │ │ ├── CompressServiceParam.java │ │ │ ├── Constanse.java │ │ │ ├── LGImgCompressor.java │ │ │ ├── LGImgCompressorIntentService.java │ │ │ ├── LGImgCompressorService.java │ │ │ ├── MainActivity.java │ │ │ └── ServiceCompressActivity.java │ └── res │ │ ├── layout │ │ ├── activity_basic_compress.xml │ │ ├── activity_intent_service_compress.xml │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── gui │ └── com │ └── lgimagecompressor │ ├── ExampleUnitTest.java │ └── LGImgCompressorServiceTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | 3 | *.DS_Store 4 | 5 | 6 | # Gradle files 7 | build/ 8 | .gradle/ 9 | */build/ 10 | 11 | 12 | # IDEA 13 | *.iml 14 | .idea/.name 15 | .idea/encodings.xml 16 | .idea/inspectionProfiles/Project_Default.xml 17 | .idea/inspectionProfiles/profiles_settings.xml 18 | .idea/misc.xml 19 | .idea/modules.xml 20 | .idea/scopes/scope_settings.xml 21 | .idea/vcs.xml 22 | .idea/workspace.xml 23 | .idea/libraries 24 | 25 | 26 | # Built application files 27 | *.apk 28 | *.ap_ 29 | 30 | 31 | # Files for the Dalvik VM 32 | *.dex 33 | 34 | 35 | # Java class files 36 | *.class 37 | 38 | # Local configuration file (sdk path, etc) 39 | local.properties 40 | 41 | 42 | # Log Files 43 | *.log -------------------------------------------------------------------------------- /.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/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LGImageCompressor 2 | android图片压缩的处理 3 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "gui.com.lgimagecompressor" 9 | minSdkVersion 14 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.4.0' 26 | } 27 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/guizhigang/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/gui/com/lgimagecompressor/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package gui.com.lgimagecompressor; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/java/gui/com/lgimagecompressor/BasicCompressActivity.java: -------------------------------------------------------------------------------- 1 | package gui.com.lgimagecompressor; 2 | 3 | import android.content.Intent; 4 | import android.content.pm.PackageManager; 5 | import android.graphics.Bitmap; 6 | import android.net.Uri; 7 | import android.os.Environment; 8 | import android.provider.MediaStore; 9 | import android.support.annotation.NonNull; 10 | import android.support.v4.app.ActivityCompat; 11 | import android.support.v4.content.ContextCompat; 12 | import android.support.v7.app.AppCompatActivity; 13 | import android.os.Bundle; 14 | import android.util.Log; 15 | import android.view.View; 16 | import android.widget.ImageView; 17 | import android.widget.TextView; 18 | import android.widget.Toast; 19 | 20 | import java.io.File; 21 | import java.io.IOException; 22 | import java.text.SimpleDateFormat; 23 | import java.util.Date; 24 | 25 | public class BasicCompressActivity extends AppCompatActivity implements LGImgCompressor.CompressListener{ 26 | private final String TAG = MainActivity.class.getSimpleName(); 27 | private ImageView imageView; 28 | private TextView imageInfo; 29 | private final static int CAMERA_REQESTCODE = 100; 30 | 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setContentView(R.layout.activity_basic_compress); 35 | 36 | imageInfo = (TextView) findViewById(R.id.image_info); 37 | imageView = (ImageView) findViewById(R.id.image_view); 38 | imageView.setOnClickListener(new View.OnClickListener() { 39 | @Override 40 | public void onClick(View v) { 41 | requestPermission(); 42 | } 43 | }); 44 | } 45 | 46 | //处理6.0动态权限问题 47 | private void requestPermission() { 48 | if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) 49 | != PackageManager.PERMISSION_GRANTED) { 50 | ActivityCompat.requestPermissions( 51 | this, 52 | new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 53 | CAMERA_REQESTCODE); 54 | } else { 55 | takePictureFormCamera(); 56 | } 57 | } 58 | 59 | @Override 60 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 61 | if (requestCode == CAMERA_REQESTCODE) { 62 | if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 63 | takePictureFormCamera(); 64 | } else { 65 | Toast.makeText(this, "需要允许写入权限来存储图片", Toast.LENGTH_LONG).show(); 66 | } 67 | } 68 | } 69 | 70 | private File imageFile; 71 | 72 | private void takePictureFormCamera() { 73 | Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 74 | 75 | String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); 76 | String fileName = timeStamp + "_"; 77 | File fileDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); 78 | imageFile = null; 79 | try { 80 | imageFile = File.createTempFile(fileName, ".jpg", fileDir); 81 | } catch (IOException e) { 82 | e.printStackTrace(); 83 | } 84 | 85 | intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imageFile)); 86 | startActivityForResult(intent, CAMERA_REQESTCODE); 87 | } 88 | 89 | @Override 90 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 91 | super.onActivityResult(requestCode, resultCode, data); 92 | if (resultCode == RESULT_OK) { 93 | if (requestCode == CAMERA_REQESTCODE) { 94 | LGImgCompressor.getInstance(this).withListener(this). 95 | starCompress(Uri.fromFile(imageFile).toString(), 600, 800, 100); 96 | // LGImgCompressor.getInstance(this).withListener(this). 97 | // starCompressWithDefault(Uri.fromFile(imageFile).toString()); 98 | } 99 | } 100 | } 101 | 102 | @Override 103 | public void onCompressStart() { 104 | Log.d(TAG, "onCompressStart"); 105 | } 106 | 107 | @Override 108 | public void onCompressEnd(LGImgCompressor.CompressResult compressResult) { 109 | Log.d(TAG, "onCompressEnd outPath:" + compressResult.getOutPath()); 110 | if (compressResult.getStatus() == LGImgCompressor.CompressResult.RESULT_ERROR)//压缩失败 111 | return; 112 | 113 | File file = new File(compressResult.getOutPath()); 114 | Bitmap bitmap = null; 115 | try { 116 | bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), Uri.fromFile(file)); 117 | imageView.setImageBitmap(bitmap); 118 | float imageFileSize = file.length() / 1024f; 119 | imageInfo.setText("image info width:" + bitmap.getWidth() + " \nheight:" + bitmap.getHeight() + 120 | " \nsize:" + imageFileSize + "kb" + "\nimagePath:" + file.getAbsolutePath()); 121 | } catch (IOException e) { 122 | e.printStackTrace(); 123 | } 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /app/src/main/java/gui/com/lgimagecompressor/CompressServiceListener.java: -------------------------------------------------------------------------------- 1 | package gui.com.lgimagecompressor; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Created by guizhigang on 16/5/28. 7 | */ 8 | public interface CompressServiceListener { 9 | void onCompressServiceStart(); 10 | void onCompressServiceEnd(ArrayList compressResults); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/gui/com/lgimagecompressor/CompressServiceParam.java: -------------------------------------------------------------------------------- 1 | package gui.com.lgimagecompressor; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | public class CompressServiceParam implements Parcelable { 7 | 8 | private int outWidth; 9 | private int outHeight; 10 | private int maxFileSize; 11 | private String srcImageUri; 12 | 13 | public CompressServiceParam() { 14 | } 15 | 16 | protected CompressServiceParam(Parcel in) { 17 | outWidth = in.readInt(); 18 | outHeight = in.readInt(); 19 | maxFileSize = in.readInt(); 20 | srcImageUri = in.readString(); 21 | } 22 | 23 | public static final Creator CREATOR = new Creator() { 24 | @Override 25 | public CompressServiceParam createFromParcel(Parcel in) { 26 | return new CompressServiceParam(in); 27 | } 28 | 29 | @Override 30 | public CompressServiceParam[] newArray(int size) { 31 | return new CompressServiceParam[size]; 32 | } 33 | }; 34 | 35 | public int getOutWidth() { 36 | return outWidth; 37 | } 38 | 39 | public void setOutWidth(int outWidth) { 40 | this.outWidth = outWidth; 41 | } 42 | 43 | public int getOutHeight() { 44 | return outHeight; 45 | } 46 | 47 | public void setOutHeight(int outHeight) { 48 | this.outHeight = outHeight; 49 | } 50 | 51 | public int getMaxFileSize() { 52 | return maxFileSize; 53 | } 54 | 55 | public void setMaxFileSize(int maxFileSize) { 56 | this.maxFileSize = maxFileSize; 57 | } 58 | 59 | public String getSrcImageUri() { 60 | return srcImageUri; 61 | } 62 | 63 | public void setSrcImageUri(String srcImageUri) { 64 | this.srcImageUri = srcImageUri; 65 | } 66 | 67 | @Override 68 | public int describeContents() { 69 | return 0; 70 | } 71 | 72 | @Override 73 | public void writeToParcel(Parcel dest, int flags) { 74 | dest.writeInt(outWidth); 75 | dest.writeInt(outHeight); 76 | dest.writeInt(maxFileSize); 77 | dest.writeString(srcImageUri); 78 | } 79 | } -------------------------------------------------------------------------------- /app/src/main/java/gui/com/lgimagecompressor/Constanse.java: -------------------------------------------------------------------------------- 1 | package gui.com.lgimagecompressor; 2 | 3 | /** 4 | * Created by guizhigang on 16/5/28. 5 | */ 6 | public abstract class Constanse { 7 | public static final String COMPRESS_PARAM = "gui.com.lgimagecompressor.extra.PARAM"; 8 | public static final String ACTION_COMPRESS_BROADCAST = "gui.com.lgimagecompressor.message.broadcast"; 9 | public static final String KEY_COMPRESS_PROCCESSING = "gui.com.lgimagecompressor.message.proccessing"; 10 | public static final String KEY_COMPRESS_FLAG = "gui.com.lgimagecompressor.message.flag"; 11 | public static final String KEY_COMPRESS_RESULT = "gui.com.lgimagecompressor.message.result"; 12 | 13 | public static final int FLAG_BEGAIIN = 0; 14 | public static final int FLAG_END = 1; 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/gui/com/lgimagecompressor/LGImgCompressor.java: -------------------------------------------------------------------------------- 1 | package gui.com.lgimagecompressor; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.graphics.Bitmap; 6 | import android.graphics.BitmapFactory; 7 | import android.graphics.Matrix; 8 | import android.media.ExifInterface; 9 | import android.net.Uri; 10 | import android.os.AsyncTask; 11 | import android.os.Environment; 12 | import android.os.Parcel; 13 | import android.os.Parcelable; 14 | import android.provider.MediaStore; 15 | 16 | import java.io.BufferedOutputStream; 17 | import java.io.ByteArrayOutputStream; 18 | import java.io.File; 19 | import java.io.FileNotFoundException; 20 | import java.io.FileOutputStream; 21 | import java.io.IOException; 22 | 23 | /** 24 | * Created by guizhigang on 16/5/25. 25 | */ 26 | public class LGImgCompressor { 27 | private static LGImgCompressor instance = null; 28 | private Context context; 29 | private CompressListener compressListener; 30 | private static final int DEFAULT_OUTWIDTH = 720; 31 | private static final int DEFAULT_OUTHEIGHT = 1080; 32 | private static final int DEFAULT_MAXFILESIZE = 1024;//KB 33 | 34 | private LGImgCompressor(Context context) { 35 | this.context = context; 36 | } 37 | 38 | public static LGImgCompressor getInstance(Context context) { 39 | if (instance == null) { 40 | synchronized (LGImgCompressor.class) { 41 | if (instance == null) 42 | instance = new LGImgCompressor(context.getApplicationContext()); 43 | } 44 | } 45 | return instance; 46 | } 47 | 48 | public LGImgCompressor withListener(CompressListener compressListener) { 49 | this.compressListener = compressListener; 50 | return this; 51 | } 52 | 53 | /** 54 | * 通过uri地址获取文件路径 55 | * @param uri 56 | * @return 57 | */ 58 | private String getFilePathFromUri(String uri) { 59 | Uri pathUri = Uri.parse(uri); 60 | Cursor cursor = context.getContentResolver().query(pathUri, null, null, null, null); 61 | if (cursor == null) { 62 | return pathUri.getPath(); 63 | } else { 64 | cursor.moveToFirst(); 65 | int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); 66 | String str = cursor.getString(index); 67 | cursor.close(); 68 | return str; 69 | } 70 | } 71 | 72 | /** 73 | * Can't compress a recycled bitmap 74 | * @param srcImageUri 原始图片的uri路径 75 | * @param outWidth 期望的输出图片的宽度 76 | * @param outHeight 期望的输出图片的高度 77 | * @param maxFileSize 期望的输出图片的最大占用的存储空间 78 | * @return 79 | */ 80 | public String compressImage(String srcImageUri, int outWidth, int outHeight, int maxFileSize) { 81 | String srcImagePath = getFilePathFromUri(srcImageUri); 82 | 83 | //进行大小缩放来达到压缩的目的 84 | BitmapFactory.Options options = new BitmapFactory.Options(); 85 | options.inJustDecodeBounds = true; 86 | BitmapFactory.decodeFile(srcImagePath, options); 87 | //根据原始图片的宽高比和期望的输出图片的宽高比计算最终输出的图片的宽和高 88 | float srcWidth = options.outWidth; 89 | float srcHeight = options.outHeight; 90 | float maxWidth = outWidth; 91 | float maxHeight = outHeight; 92 | float srcRatio = srcWidth / srcHeight; 93 | float outRatio = maxWidth / maxHeight; 94 | float actualOutWidth = srcWidth; 95 | float actualOutHeight = srcHeight; 96 | 97 | if (srcWidth > maxWidth || srcHeight > maxHeight) { 98 | //如果输入比率小于输出比率,则最终输出的宽度以maxHeight为准() 99 | //比如输入比为10:20 输出比是300:10 如果要保证输出图片的宽高比和原始图片的宽高比相同,则最终输出图片的高为10 100 | //宽度为10/20 * 10 = 5 最终输出图片的比率为5:10 和原始输入的比率相同 101 | 102 | //同理如果输入比率大于输出比率,则最终输出的高度以maxHeight为准() 103 | //比如输入比为20:10 输出比是5:100 如果要保证输出图片的宽高比和原始图片的宽高比相同,则最终输出图片的宽为5 104 | //高度需要根据输入图片的比率计算获得 为5 / 20/10= 2.5 最终输出图片的比率为5:2.5 和原始输入的比率相同 105 | if (srcRatio < outRatio) { 106 | actualOutHeight = maxHeight; 107 | actualOutWidth = actualOutHeight * srcRatio; 108 | } else if (srcRatio > outRatio) { 109 | actualOutWidth = maxWidth; 110 | actualOutHeight = actualOutWidth / srcRatio; 111 | } else { 112 | actualOutWidth = maxWidth; 113 | actualOutHeight = maxHeight; 114 | } 115 | } 116 | options.inSampleSize = computSampleSize(options, actualOutWidth, actualOutHeight); 117 | options.inJustDecodeBounds = false; 118 | Bitmap scaledBitmap = null; 119 | try { 120 | scaledBitmap = BitmapFactory.decodeFile(srcImagePath, options); 121 | } catch (OutOfMemoryError e) { 122 | e.printStackTrace(); 123 | } 124 | if (scaledBitmap == null) { 125 | return null;//压缩失败 126 | } 127 | //生成最终输出的bitmap 128 | Bitmap actualOutBitmap = Bitmap.createScaledBitmap(scaledBitmap, (int) actualOutWidth, (int) actualOutHeight, true); 129 | if(actualOutBitmap != scaledBitmap) 130 | scaledBitmap.recycle(); 131 | 132 | //处理图片旋转问题 133 | ExifInterface exif = null; 134 | try { 135 | exif = new ExifInterface(srcImagePath); 136 | int orientation = exif.getAttributeInt( 137 | ExifInterface.TAG_ORIENTATION, 0); 138 | Matrix matrix = new Matrix(); 139 | if (orientation == ExifInterface.ORIENTATION_ROTATE_90) { 140 | matrix.postRotate(90); 141 | } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) { 142 | matrix.postRotate(180); 143 | } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) { 144 | matrix.postRotate(270); 145 | } 146 | actualOutBitmap = Bitmap.createBitmap(actualOutBitmap, 0, 0, 147 | actualOutBitmap.getWidth(), actualOutBitmap.getHeight(), matrix, true); 148 | } catch (IOException e) { 149 | e.printStackTrace(); 150 | return null; 151 | } 152 | 153 | //进行有损压缩 154 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 155 | int options_ = 100; 156 | actualOutBitmap.compress(Bitmap.CompressFormat.JPEG, options_, baos);//质量压缩方法,把压缩后的数据存放到baos中 (100表示不压缩,0表示压缩到最小) 157 | 158 | int baosLength = baos.toByteArray().length; 159 | 160 | while (baosLength / 1024 > maxFileSize) {//循环判断如果压缩后图片是否大于maxMemmorrySize,大于继续压缩 161 | baos.reset();//重置baos即让下一次的写入覆盖之前的内容 162 | options_ = Math.max(0, options_ - 10);//图片质量每次减少10 163 | actualOutBitmap.compress(Bitmap.CompressFormat.JPEG, options_, baos);//将压缩后的图片保存到baos中 164 | baosLength = baos.toByteArray().length; 165 | if (options_ == 0)//如果图片的质量已降到最低则,不再进行压缩 166 | break; 167 | } 168 | actualOutBitmap.recycle(); 169 | 170 | //将bitmap保存到指定路径 171 | FileOutputStream fos = null; 172 | String filePath = getOutputFileName(srcImagePath); 173 | try { 174 | fos = new FileOutputStream(filePath); 175 | //包装缓冲流,提高写入速度 176 | BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fos); 177 | bufferedOutputStream.write(baos.toByteArray()); 178 | bufferedOutputStream.flush(); 179 | } catch (FileNotFoundException e) { 180 | return null; 181 | } catch (IOException e) { 182 | return null; 183 | } finally { 184 | if (baos != null) { 185 | try { 186 | baos.close(); 187 | } catch (IOException e) { 188 | e.printStackTrace(); 189 | } 190 | } 191 | if (fos != null) { 192 | try { 193 | fos.close(); 194 | } catch (IOException e) { 195 | e.printStackTrace(); 196 | } 197 | } 198 | } 199 | 200 | return filePath; 201 | } 202 | 203 | private int computSampleSize(BitmapFactory.Options options, float reqWidth, float reqHeight) { 204 | float srcWidth = options.outWidth;//20 205 | float srcHeight = options.outHeight;//10 206 | int sampleSize = 1; 207 | if (srcWidth > reqWidth || srcHeight > reqHeight) { 208 | int withRatio = Math.round(srcWidth / reqWidth); 209 | int heightRatio = Math.round(srcHeight / reqHeight); 210 | sampleSize = Math.min(withRatio, heightRatio); 211 | } 212 | return sampleSize; 213 | } 214 | 215 | private String getOutputFileName(String srcFilePath) { 216 | File srcFile = new File(srcFilePath); 217 | File file = new File(Environment.getExternalStorageDirectory().getPath(), "LGImgCompressor/Images"); 218 | if (!file.exists()) { 219 | file.mkdirs(); 220 | } 221 | String uriSting = (file.getAbsolutePath() + File.separator + srcFile.getName()); 222 | return uriSting; 223 | } 224 | 225 | public void starCompress(String srcImageUri, int outWidth, int outHeight, int maxFileSize) { 226 | new CompressTask().execute(srcImageUri, "" + outWidth, "" + outHeight, "" + maxFileSize); 227 | } 228 | 229 | public void starCompressWithDefault(String srcImageUri) { 230 | new CompressTask().execute(srcImageUri, "" + DEFAULT_OUTWIDTH, "" + DEFAULT_OUTHEIGHT, "" + DEFAULT_MAXFILESIZE); 231 | } 232 | 233 | public static class CompressResult implements Parcelable{ 234 | public static final int RESULT_OK = 0; 235 | public static final int RESULT_ERROR = 1; 236 | private int status = RESULT_OK; 237 | private String srcPath; 238 | private String outPath; 239 | 240 | public CompressResult(){ 241 | 242 | } 243 | 244 | protected CompressResult(Parcel in) { 245 | status = in.readInt(); 246 | srcPath = in.readString(); 247 | outPath = in.readString(); 248 | } 249 | 250 | public static final Creator CREATOR = new Creator() { 251 | @Override 252 | public CompressResult createFromParcel(Parcel in) { 253 | return new CompressResult(in); 254 | } 255 | 256 | @Override 257 | public CompressResult[] newArray(int size) { 258 | return new CompressResult[size]; 259 | } 260 | }; 261 | 262 | @Override 263 | public int describeContents() { 264 | return 0; 265 | } 266 | 267 | @Override 268 | public void writeToParcel(Parcel dest, int flags) { 269 | dest.writeInt(status); 270 | dest.writeString(srcPath); 271 | dest.writeString(outPath); 272 | } 273 | 274 | public int getStatus() { 275 | return status; 276 | } 277 | 278 | public void setStatus(int status) { 279 | this.status = status; 280 | } 281 | 282 | public String getSrcPath() { 283 | return srcPath; 284 | } 285 | 286 | public void setSrcPath(String srcPath) { 287 | this.srcPath = srcPath; 288 | } 289 | 290 | public String getOutPath() { 291 | return outPath; 292 | } 293 | 294 | public void setOutPath(String outPath) { 295 | this.outPath = outPath; 296 | } 297 | } 298 | /** 299 | * 压缩结果回到监听类 300 | */ 301 | public interface CompressListener { 302 | void onCompressStart(); 303 | 304 | void onCompressEnd(CompressResult imageOutPath); 305 | } 306 | 307 | private class CompressTask extends AsyncTask { 308 | 309 | @Override 310 | protected CompressResult doInBackground(String... params) { 311 | String path = params[0]; 312 | int outWidth = Integer.parseInt(params[1]); 313 | int outHeight = Integer.parseInt(params[2]); 314 | int maxFileSize = Integer.parseInt(params[3]); 315 | CompressResult compressResult = new CompressResult(); 316 | String outPutPath = null; 317 | try { 318 | outPutPath = compressImage(path, outWidth, outHeight, maxFileSize); 319 | }catch (Exception e){ 320 | } 321 | compressResult.setSrcPath(path); 322 | compressResult.setOutPath(outPutPath); 323 | if(outPutPath == null){ 324 | compressResult.setStatus(CompressResult.RESULT_ERROR); 325 | } 326 | return compressResult; 327 | } 328 | 329 | @Override 330 | protected void onPreExecute() { 331 | if (compressListener != null) { 332 | compressListener.onCompressStart(); 333 | } 334 | } 335 | 336 | @Override 337 | protected void onPostExecute(CompressResult compressResult) { 338 | if (compressListener != null) { 339 | compressListener.onCompressEnd(compressResult); 340 | } 341 | } 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /app/src/main/java/gui/com/lgimagecompressor/LGImgCompressorIntentService.java: -------------------------------------------------------------------------------- 1 | package gui.com.lgimagecompressor; 2 | 3 | import android.app.IntentService; 4 | import android.content.Intent; 5 | import android.content.Context; 6 | import android.util.Log; 7 | 8 | import java.util.ArrayList; 9 | 10 | public class LGImgCompressorIntentService extends IntentService { 11 | private final String TAG = LGImgCompressorIntentService.class.getSimpleName(); 12 | 13 | private static final String ACTION_COMPRESS = "gui.com.lgimagecompressor.action.COMPRESS"; 14 | 15 | private ArrayList compressResults = new ArrayList<>();//存储压缩任务的返回结果 16 | 17 | public LGImgCompressorIntentService() { 18 | super("LGImgCompressorIntentService"); 19 | setIntentRedelivery(false);//避免出异常后service重新启动 20 | } 21 | 22 | @Override 23 | public void onCreate() { 24 | super.onCreate(); 25 | Intent intent = new Intent(Constanse.ACTION_COMPRESS_BROADCAST); 26 | intent.putExtra(Constanse.KEY_COMPRESS_FLAG,Constanse.FLAG_BEGAIIN); 27 | sendBroadcast(intent); 28 | Log.d(TAG,"onCreate..."); 29 | } 30 | 31 | @Override 32 | public void onDestroy() { 33 | super.onDestroy(); 34 | Intent intent = new Intent(Constanse.ACTION_COMPRESS_BROADCAST); 35 | intent.putExtra(Constanse.KEY_COMPRESS_FLAG,Constanse.FLAG_END); 36 | intent.putParcelableArrayListExtra(Constanse.KEY_COMPRESS_RESULT,compressResults); 37 | sendBroadcast(intent); 38 | compressResults.clear(); 39 | Log.d(TAG,"onDestroy..."); 40 | } 41 | 42 | public static void startActionCompress(Context context, CompressServiceParam param) { 43 | Intent intent = new Intent(context, LGImgCompressorIntentService.class); 44 | intent.setAction(ACTION_COMPRESS); 45 | intent.putExtra(Constanse.COMPRESS_PARAM, param); 46 | context.startService(intent); 47 | } 48 | 49 | @Override 50 | protected void onHandleIntent(Intent intent) { 51 | if (intent != null) { 52 | final String action = intent.getAction(); 53 | if (ACTION_COMPRESS.equals(action)) { 54 | final CompressServiceParam param1 = intent.getParcelableExtra(Constanse.COMPRESS_PARAM); 55 | handleActionCompress(param1); 56 | } 57 | } 58 | } 59 | 60 | private void handleActionCompress(CompressServiceParam param) { 61 | int outwidth = param.getOutWidth(); 62 | int outHieight = param.getOutHeight(); 63 | int maxFileSize = param.getMaxFileSize(); 64 | String srcImageUri = param.getSrcImageUri(); 65 | LGImgCompressor.CompressResult compressResult = new LGImgCompressor.CompressResult(); 66 | String outPutPath = null; 67 | try { 68 | outPutPath = LGImgCompressor.getInstance(this).compressImage(srcImageUri, outwidth, outHieight, maxFileSize); 69 | } catch (Exception e) { 70 | } 71 | compressResult.setSrcPath(srcImageUri); 72 | compressResult.setOutPath(outPutPath); 73 | if (outPutPath == null) { 74 | compressResult.setStatus(LGImgCompressor.CompressResult.RESULT_ERROR); 75 | } 76 | compressResults.add(compressResult); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/gui/com/lgimagecompressor/LGImgCompressorService.java: -------------------------------------------------------------------------------- 1 | package gui.com.lgimagecompressor; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | import android.util.Log; 7 | 8 | import java.util.ArrayList; 9 | import java.util.concurrent.ExecutorService; 10 | import java.util.concurrent.Executors; 11 | 12 | public class LGImgCompressorService extends Service { 13 | private static final String TAG = "GImgCompressorService"; 14 | 15 | private ArrayList compressResults = new ArrayList<>(); 16 | public LGImgCompressorService() { 17 | } 18 | 19 | @Override 20 | public void onCreate() { 21 | super.onCreate(); 22 | Log.d(TAG,"onCreate..."); 23 | // executorService = Executors.newCachedThreadPool(); 24 | executorService = Executors.newFixedThreadPool(10); 25 | Intent intent = new Intent(Constanse.ACTION_COMPRESS_BROADCAST); 26 | intent.putExtra(Constanse.KEY_COMPRESS_FLAG,Constanse.FLAG_BEGAIIN); 27 | sendBroadcast(intent); 28 | } 29 | 30 | @Override 31 | public void onDestroy() { 32 | super.onDestroy(); 33 | Log.d(TAG,"onDestroy..."); 34 | Intent intent = new Intent(Constanse.ACTION_COMPRESS_BROADCAST); 35 | intent.putExtra(Constanse.KEY_COMPRESS_FLAG,Constanse.FLAG_END); 36 | intent.putParcelableArrayListExtra(Constanse.KEY_COMPRESS_RESULT,compressResults); 37 | sendBroadcast(intent); 38 | compressResults.clear(); 39 | executorService.shutdownNow(); 40 | } 41 | 42 | @Override 43 | public int onStartCommand(Intent intent, int flags, int startId) { 44 | doCompressImages(intent,startId); 45 | return Service.START_NOT_STICKY; 46 | } 47 | 48 | private int taskNumber; 49 | private ExecutorService executorService; 50 | private final Object lock = new Object(); 51 | 52 | private void doCompressImages(final Intent intent,final int taskId){ 53 | final ArrayList paramArrayList = intent.getParcelableArrayListExtra(Constanse.COMPRESS_PARAM); 54 | synchronized (lock){ 55 | taskNumber += paramArrayList.size(); 56 | } 57 | //如果paramArrayList过大,为了避免"The application may be doing too much work on its main thread"的问题,将任务的创建和执行统一放在后台线程中执行 58 | new Thread(new Runnable() { 59 | @Override 60 | public void run() { 61 | for (int i = 0; i < paramArrayList.size(); ++i){ 62 | executorService.execute(new CompressTask(paramArrayList.get(i),taskId)); 63 | } 64 | } 65 | }).start(); 66 | } 67 | 68 | private class CompressTask implements Runnable{ 69 | private CompressServiceParam param; 70 | private int taskId ; 71 | 72 | private CompressTask(CompressServiceParam compressServiceParam,int taskId){ 73 | this.param = compressServiceParam; 74 | this.taskId = taskId; 75 | } 76 | 77 | @Override 78 | public void run() { 79 | Log.d(TAG,taskId + " do compress begain..." + Thread.currentThread().getId()); 80 | int outwidth = param.getOutWidth(); 81 | int outHieight = param.getOutHeight(); 82 | int maxFileSize = param.getMaxFileSize(); 83 | String srcImageUri = param.getSrcImageUri(); 84 | LGImgCompressor.CompressResult compressResult = new LGImgCompressor.CompressResult(); 85 | String outPutPath = null; 86 | try { 87 | outPutPath = LGImgCompressor.getInstance(LGImgCompressorService.this).compressImage( 88 | srcImageUri, outwidth, outHieight, maxFileSize); 89 | } catch (Exception e) { 90 | } 91 | compressResult.setSrcPath(srcImageUri); 92 | compressResult.setOutPath(outPutPath); 93 | if (outPutPath == null) { 94 | compressResult.setStatus(LGImgCompressor.CompressResult.RESULT_ERROR); 95 | } 96 | Log.d(TAG,taskId + " do compress end..." + Thread.currentThread().getId()); 97 | synchronized (lock){ 98 | compressResults.add(compressResult); 99 | taskNumber--; 100 | if(taskNumber <= 0){ 101 | stopSelf(taskId); 102 | } 103 | } 104 | } 105 | } 106 | 107 | @Override 108 | public IBinder onBind(Intent intent) { 109 | throw null; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/gui/com/lgimagecompressor/MainActivity.java: -------------------------------------------------------------------------------- 1 | package gui.com.lgimagecompressor; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | 8 | public class MainActivity extends AppCompatActivity { 9 | private final String TAG = MainActivity.class.getSimpleName(); 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_main); 14 | findViewById(R.id.basic_test).setOnClickListener(new View.OnClickListener() { 15 | @Override 16 | public void onClick(View v) { 17 | Intent intent = new Intent(MainActivity.this,BasicCompressActivity.class); 18 | startActivity(intent); 19 | } 20 | }); 21 | findViewById(R.id.from_service).setOnClickListener(new View.OnClickListener() { 22 | @Override 23 | public void onClick(View v) { 24 | Intent intent = new Intent(MainActivity.this,ServiceCompressActivity.class); 25 | startActivity(intent); 26 | } 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/gui/com/lgimagecompressor/ServiceCompressActivity.java: -------------------------------------------------------------------------------- 1 | package gui.com.lgimagecompressor; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | import android.database.Cursor; 8 | import android.net.Uri; 9 | import android.os.Environment; 10 | import android.provider.MediaStore; 11 | import android.support.v7.app.AppCompatActivity; 12 | import android.os.Bundle; 13 | import android.util.Log; 14 | import android.view.View; 15 | import android.widget.TextView; 16 | 17 | import java.io.File; 18 | import java.util.ArrayList; 19 | 20 | public class ServiceCompressActivity extends AppCompatActivity { 21 | private final String TAG = ServiceCompressActivity.class.getSimpleName(); 22 | 23 | private long serviceStartTime; 24 | private CompressingReciver reciver; 25 | private TextView infoView; 26 | 27 | private class CompressingReciver extends BroadcastReceiver { 28 | @Override 29 | public void onReceive(Context context, Intent intent) { 30 | Log.d(TAG, "onReceive:" + Thread.currentThread().getId()); 31 | int flag = intent.getIntExtra(Constanse.KEY_COMPRESS_FLAG,-1); 32 | Log.d(TAG," flag:" + flag); 33 | if(flag == Constanse.FLAG_BEGAIIN){ 34 | Log.d(TAG, "onCompressServiceStart"); 35 | serviceStartTime = System.currentTimeMillis(); 36 | updateInfo("compress begain..."); 37 | return; 38 | } 39 | 40 | if(flag == Constanse.FLAG_END){ 41 | ArrayList compressResults = 42 | (ArrayList)intent.getSerializableExtra(Constanse.KEY_COMPRESS_RESULT); 43 | Log.d(TAG, compressResults.size() + "compressed done"); 44 | Log.d(TAG, "compress " + compressResults.size() + " files used total time:" + (System.currentTimeMillis() - serviceStartTime)); 45 | updateInfo(compressResults.size() + " files compressed done \nused total time:" + (System.currentTimeMillis() - serviceStartTime) + "ms"); 46 | } 47 | } 48 | } 49 | 50 | private void updateInfo(String message){ 51 | infoView.setText(message); 52 | } 53 | 54 | @Override 55 | protected void onCreate(Bundle savedInstanceState) { 56 | super.onCreate(savedInstanceState); 57 | setContentView(R.layout.activity_intent_service_compress); 58 | 59 | reciver = new CompressingReciver(); 60 | IntentFilter intentFilter = new IntentFilter(Constanse.ACTION_COMPRESS_BROADCAST); 61 | registerReceiver(reciver, intentFilter); 62 | 63 | infoView = (TextView)findViewById(R.id.compress_info); 64 | final int maxSize = 0; 65 | 66 | findViewById(R.id.intent_service).setOnClickListener(new View.OnClickListener() { 67 | @Override 68 | public void onClick(View v) { 69 | ArrayList compressFiles = getImagesPathFormAlbum(); 70 | Log.d(TAG, compressFiles.size() + "compresse begain"); 71 | int size = compressFiles.size() > 10 ? 10:compressFiles.size(); 72 | for (int i = 0; i < compressFiles.size(); ++i) { 73 | Uri uri = compressFiles.get(i); 74 | CompressServiceParam param = new CompressServiceParam(); 75 | param.setOutHeight(800); 76 | param.setOutWidth(600); 77 | param.setMaxFileSize(400); 78 | param.setSrcImageUri(uri.toString()); 79 | LGImgCompressorIntentService.startActionCompress(ServiceCompressActivity.this, param); 80 | } 81 | } 82 | }); 83 | 84 | findViewById(R.id.service).setOnClickListener(new View.OnClickListener() { 85 | @Override 86 | public void onClick(View v) { 87 | ArrayList compressFiles = getImagesPathFormAlbum(); 88 | int size = compressFiles.size() > 10 ? 10:compressFiles.size(); 89 | ArrayList tasks = new ArrayList(compressFiles.size()); 90 | 91 | for (int i = 0; i < compressFiles.size(); ++i) { 92 | Uri uri = compressFiles.get(i); 93 | CompressServiceParam param = new CompressServiceParam(); 94 | param.setOutHeight(800); 95 | param.setOutWidth(600); 96 | param.setMaxFileSize(400); 97 | param.setSrcImageUri(uri.toString()); 98 | tasks.add(param); 99 | } 100 | Log.d(TAG, compressFiles.size() + "compresse begain"); 101 | Intent intent = new Intent(ServiceCompressActivity.this, LGImgCompressorService.class); 102 | intent.putParcelableArrayListExtra(Constanse.COMPRESS_PARAM, tasks); 103 | startService(intent); 104 | } 105 | }); 106 | } 107 | 108 | private ArrayList getImagesPathFormAlbum() { 109 | ArrayList paths = new ArrayList<>(); 110 | //selection: 指定查询条件 111 | String selection = MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?"; 112 | 113 | //定义selection参数匹配值 114 | String[] selectionArgs = {"image/jpeg", "image/png"}; 115 | 116 | Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, 117 | selection, 118 | selectionArgs, 119 | MediaStore.Images.Media.DATE_MODIFIED); 120 | if (cursor != null && cursor.moveToFirst()) { 121 | do { 122 | long id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID)); 123 | String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE)); 124 | String url = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); 125 | long size = (int) cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE)); 126 | long lastModified = (int) cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_MODIFIED)); 127 | //排除size为0的无效文件 128 | if (size != 0) { 129 | Uri uri = Uri.parse(url); 130 | paths.add(uri); 131 | } 132 | } while (cursor.moveToNext()); 133 | cursor.close(); 134 | } 135 | return paths; 136 | } 137 | 138 | @Override 139 | protected void onDestroy() { 140 | super.onDestroy(); 141 | if(reciver != null){ 142 | unregisterReceiver(reciver); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_basic_compress.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 21 | 27 | 28 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_intent_service_compress.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 |