Repository: guizhigang/LGImageCompressor
Branch: master
Commit: 3080b2d1cb42
Files: 37
Total size: 54.1 KB
Directory structure:
gitextract_la4y5atw/
├── .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
│ │ ├── values/
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── values-w820dp/
│ │ └── dimens.xml
│ └── test/
│ └── java/
│ └── gui/
│ └── com/
│ └── lgimagecompressor/
│ ├── ExampleUnitTest.java
│ └── LGImgCompressorServiceTest.java
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# OSX
*.DS_Store
# Gradle files
build/
.gradle/
*/build/
# IDEA
*.iml
.idea/.name
.idea/encodings.xml
.idea/inspectionProfiles/Project_Default.xml
.idea/inspectionProfiles/profiles_settings.xml
.idea/misc.xml
.idea/modules.xml
.idea/scopes/scope_settings.xml
.idea/vcs.xml
.idea/workspace.xml
.idea/libraries
# Built application files
*.apk
*.ap_
# Files for the Dalvik VM
*.dex
# Java class files
*.class
# Local configuration file (sdk path, etc)
local.properties
# Log Files
*.log
================================================
FILE: .idea/compiler.xml
================================================
================================================
FILE: .idea/copyright/profiles_settings.xml
================================================
================================================
FILE: .idea/gradle.xml
================================================
================================================
FILE: .idea/runConfigurations.xml
================================================
================================================
FILE: README.md
================================================
# LGImageCompressor
android图片压缩的处理
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "gui.com.lgimagecompressor"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/guizhigang/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: app/src/androidTest/java/gui/com/lgimagecompressor/ApplicationTest.java
================================================
package gui.com.lgimagecompressor;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* Testing Fundamentals
*/
public class ApplicationTest extends ApplicationTestCase {
public ApplicationTest() {
super(Application.class);
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/gui/com/lgimagecompressor/BasicCompressActivity.java
================================================
package gui.com.lgimagecompressor;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class BasicCompressActivity extends AppCompatActivity implements LGImgCompressor.CompressListener{
private final String TAG = MainActivity.class.getSimpleName();
private ImageView imageView;
private TextView imageInfo;
private final static int CAMERA_REQESTCODE = 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_basic_compress);
imageInfo = (TextView) findViewById(R.id.image_info);
imageView = (ImageView) findViewById(R.id.image_view);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
requestPermission();
}
});
}
//处理6.0动态权限问题
private void requestPermission() {
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE},
CAMERA_REQESTCODE);
} else {
takePictureFormCamera();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == CAMERA_REQESTCODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
takePictureFormCamera();
} else {
Toast.makeText(this, "需要允许写入权限来存储图片", Toast.LENGTH_LONG).show();
}
}
}
private File imageFile;
private void takePictureFormCamera() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String fileName = timeStamp + "_";
File fileDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
imageFile = null;
try {
imageFile = File.createTempFile(fileName, ".jpg", fileDir);
} catch (IOException e) {
e.printStackTrace();
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imageFile));
startActivityForResult(intent, CAMERA_REQESTCODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == CAMERA_REQESTCODE) {
LGImgCompressor.getInstance(this).withListener(this).
starCompress(Uri.fromFile(imageFile).toString(), 600, 800, 100);
// LGImgCompressor.getInstance(this).withListener(this).
// starCompressWithDefault(Uri.fromFile(imageFile).toString());
}
}
}
@Override
public void onCompressStart() {
Log.d(TAG, "onCompressStart");
}
@Override
public void onCompressEnd(LGImgCompressor.CompressResult compressResult) {
Log.d(TAG, "onCompressEnd outPath:" + compressResult.getOutPath());
if (compressResult.getStatus() == LGImgCompressor.CompressResult.RESULT_ERROR)//压缩失败
return;
File file = new File(compressResult.getOutPath());
Bitmap bitmap = null;
try {
bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), Uri.fromFile(file));
imageView.setImageBitmap(bitmap);
float imageFileSize = file.length() / 1024f;
imageInfo.setText("image info width:" + bitmap.getWidth() + " \nheight:" + bitmap.getHeight() +
" \nsize:" + imageFileSize + "kb" + "\nimagePath:" + file.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
================================================
FILE: app/src/main/java/gui/com/lgimagecompressor/CompressServiceListener.java
================================================
package gui.com.lgimagecompressor;
import java.util.ArrayList;
/**
* Created by guizhigang on 16/5/28.
*/
public interface CompressServiceListener {
void onCompressServiceStart();
void onCompressServiceEnd(ArrayList compressResults);
}
================================================
FILE: app/src/main/java/gui/com/lgimagecompressor/CompressServiceParam.java
================================================
package gui.com.lgimagecompressor;
import android.os.Parcel;
import android.os.Parcelable;
public class CompressServiceParam implements Parcelable {
private int outWidth;
private int outHeight;
private int maxFileSize;
private String srcImageUri;
public CompressServiceParam() {
}
protected CompressServiceParam(Parcel in) {
outWidth = in.readInt();
outHeight = in.readInt();
maxFileSize = in.readInt();
srcImageUri = in.readString();
}
public static final Creator CREATOR = new Creator() {
@Override
public CompressServiceParam createFromParcel(Parcel in) {
return new CompressServiceParam(in);
}
@Override
public CompressServiceParam[] newArray(int size) {
return new CompressServiceParam[size];
}
};
public int getOutWidth() {
return outWidth;
}
public void setOutWidth(int outWidth) {
this.outWidth = outWidth;
}
public int getOutHeight() {
return outHeight;
}
public void setOutHeight(int outHeight) {
this.outHeight = outHeight;
}
public int getMaxFileSize() {
return maxFileSize;
}
public void setMaxFileSize(int maxFileSize) {
this.maxFileSize = maxFileSize;
}
public String getSrcImageUri() {
return srcImageUri;
}
public void setSrcImageUri(String srcImageUri) {
this.srcImageUri = srcImageUri;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(outWidth);
dest.writeInt(outHeight);
dest.writeInt(maxFileSize);
dest.writeString(srcImageUri);
}
}
================================================
FILE: app/src/main/java/gui/com/lgimagecompressor/Constanse.java
================================================
package gui.com.lgimagecompressor;
/**
* Created by guizhigang on 16/5/28.
*/
public abstract class Constanse {
public static final String COMPRESS_PARAM = "gui.com.lgimagecompressor.extra.PARAM";
public static final String ACTION_COMPRESS_BROADCAST = "gui.com.lgimagecompressor.message.broadcast";
public static final String KEY_COMPRESS_PROCCESSING = "gui.com.lgimagecompressor.message.proccessing";
public static final String KEY_COMPRESS_FLAG = "gui.com.lgimagecompressor.message.flag";
public static final String KEY_COMPRESS_RESULT = "gui.com.lgimagecompressor.message.result";
public static final int FLAG_BEGAIIN = 0;
public static final int FLAG_END = 1;
}
================================================
FILE: app/src/main/java/gui/com/lgimagecompressor/LGImgCompressor.java
================================================
package gui.com.lgimagecompressor;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.MediaStore;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Created by guizhigang on 16/5/25.
*/
public class LGImgCompressor {
private static LGImgCompressor instance = null;
private Context context;
private CompressListener compressListener;
private static final int DEFAULT_OUTWIDTH = 720;
private static final int DEFAULT_OUTHEIGHT = 1080;
private static final int DEFAULT_MAXFILESIZE = 1024;//KB
private LGImgCompressor(Context context) {
this.context = context;
}
public static LGImgCompressor getInstance(Context context) {
if (instance == null) {
synchronized (LGImgCompressor.class) {
if (instance == null)
instance = new LGImgCompressor(context.getApplicationContext());
}
}
return instance;
}
public LGImgCompressor withListener(CompressListener compressListener) {
this.compressListener = compressListener;
return this;
}
/**
* 通过uri地址获取文件路径
* @param uri
* @return
*/
private String getFilePathFromUri(String uri) {
Uri pathUri = Uri.parse(uri);
Cursor cursor = context.getContentResolver().query(pathUri, null, null, null, null);
if (cursor == null) {
return pathUri.getPath();
} else {
cursor.moveToFirst();
int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
String str = cursor.getString(index);
cursor.close();
return str;
}
}
/**
* Can't compress a recycled bitmap
* @param srcImageUri 原始图片的uri路径
* @param outWidth 期望的输出图片的宽度
* @param outHeight 期望的输出图片的高度
* @param maxFileSize 期望的输出图片的最大占用的存储空间
* @return
*/
public String compressImage(String srcImageUri, int outWidth, int outHeight, int maxFileSize) {
String srcImagePath = getFilePathFromUri(srcImageUri);
//进行大小缩放来达到压缩的目的
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(srcImagePath, options);
//根据原始图片的宽高比和期望的输出图片的宽高比计算最终输出的图片的宽和高
float srcWidth = options.outWidth;
float srcHeight = options.outHeight;
float maxWidth = outWidth;
float maxHeight = outHeight;
float srcRatio = srcWidth / srcHeight;
float outRatio = maxWidth / maxHeight;
float actualOutWidth = srcWidth;
float actualOutHeight = srcHeight;
if (srcWidth > maxWidth || srcHeight > maxHeight) {
//如果输入比率小于输出比率,则最终输出的宽度以maxHeight为准()
//比如输入比为10:20 输出比是300:10 如果要保证输出图片的宽高比和原始图片的宽高比相同,则最终输出图片的高为10
//宽度为10/20 * 10 = 5 最终输出图片的比率为5:10 和原始输入的比率相同
//同理如果输入比率大于输出比率,则最终输出的高度以maxHeight为准()
//比如输入比为20:10 输出比是5:100 如果要保证输出图片的宽高比和原始图片的宽高比相同,则最终输出图片的宽为5
//高度需要根据输入图片的比率计算获得 为5 / 20/10= 2.5 最终输出图片的比率为5:2.5 和原始输入的比率相同
if (srcRatio < outRatio) {
actualOutHeight = maxHeight;
actualOutWidth = actualOutHeight * srcRatio;
} else if (srcRatio > outRatio) {
actualOutWidth = maxWidth;
actualOutHeight = actualOutWidth / srcRatio;
} else {
actualOutWidth = maxWidth;
actualOutHeight = maxHeight;
}
}
options.inSampleSize = computSampleSize(options, actualOutWidth, actualOutHeight);
options.inJustDecodeBounds = false;
Bitmap scaledBitmap = null;
try {
scaledBitmap = BitmapFactory.decodeFile(srcImagePath, options);
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
if (scaledBitmap == null) {
return null;//压缩失败
}
//生成最终输出的bitmap
Bitmap actualOutBitmap = Bitmap.createScaledBitmap(scaledBitmap, (int) actualOutWidth, (int) actualOutHeight, true);
if(actualOutBitmap != scaledBitmap)
scaledBitmap.recycle();
//处理图片旋转问题
ExifInterface exif = null;
try {
exif = new ExifInterface(srcImagePath);
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION, 0);
Matrix matrix = new Matrix();
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
matrix.postRotate(90);
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
matrix.postRotate(180);
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
matrix.postRotate(270);
}
actualOutBitmap = Bitmap.createBitmap(actualOutBitmap, 0, 0,
actualOutBitmap.getWidth(), actualOutBitmap.getHeight(), matrix, true);
} catch (IOException e) {
e.printStackTrace();
return null;
}
//进行有损压缩
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int options_ = 100;
actualOutBitmap.compress(Bitmap.CompressFormat.JPEG, options_, baos);//质量压缩方法,把压缩后的数据存放到baos中 (100表示不压缩,0表示压缩到最小)
int baosLength = baos.toByteArray().length;
while (baosLength / 1024 > maxFileSize) {//循环判断如果压缩后图片是否大于maxMemmorrySize,大于继续压缩
baos.reset();//重置baos即让下一次的写入覆盖之前的内容
options_ = Math.max(0, options_ - 10);//图片质量每次减少10
actualOutBitmap.compress(Bitmap.CompressFormat.JPEG, options_, baos);//将压缩后的图片保存到baos中
baosLength = baos.toByteArray().length;
if (options_ == 0)//如果图片的质量已降到最低则,不再进行压缩
break;
}
actualOutBitmap.recycle();
//将bitmap保存到指定路径
FileOutputStream fos = null;
String filePath = getOutputFileName(srcImagePath);
try {
fos = new FileOutputStream(filePath);
//包装缓冲流,提高写入速度
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fos);
bufferedOutputStream.write(baos.toByteArray());
bufferedOutputStream.flush();
} catch (FileNotFoundException e) {
return null;
} catch (IOException e) {
return null;
} finally {
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return filePath;
}
private int computSampleSize(BitmapFactory.Options options, float reqWidth, float reqHeight) {
float srcWidth = options.outWidth;//20
float srcHeight = options.outHeight;//10
int sampleSize = 1;
if (srcWidth > reqWidth || srcHeight > reqHeight) {
int withRatio = Math.round(srcWidth / reqWidth);
int heightRatio = Math.round(srcHeight / reqHeight);
sampleSize = Math.min(withRatio, heightRatio);
}
return sampleSize;
}
private String getOutputFileName(String srcFilePath) {
File srcFile = new File(srcFilePath);
File file = new File(Environment.getExternalStorageDirectory().getPath(), "LGImgCompressor/Images");
if (!file.exists()) {
file.mkdirs();
}
String uriSting = (file.getAbsolutePath() + File.separator + srcFile.getName());
return uriSting;
}
public void starCompress(String srcImageUri, int outWidth, int outHeight, int maxFileSize) {
new CompressTask().execute(srcImageUri, "" + outWidth, "" + outHeight, "" + maxFileSize);
}
public void starCompressWithDefault(String srcImageUri) {
new CompressTask().execute(srcImageUri, "" + DEFAULT_OUTWIDTH, "" + DEFAULT_OUTHEIGHT, "" + DEFAULT_MAXFILESIZE);
}
public static class CompressResult implements Parcelable{
public static final int RESULT_OK = 0;
public static final int RESULT_ERROR = 1;
private int status = RESULT_OK;
private String srcPath;
private String outPath;
public CompressResult(){
}
protected CompressResult(Parcel in) {
status = in.readInt();
srcPath = in.readString();
outPath = in.readString();
}
public static final Creator CREATOR = new Creator() {
@Override
public CompressResult createFromParcel(Parcel in) {
return new CompressResult(in);
}
@Override
public CompressResult[] newArray(int size) {
return new CompressResult[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(status);
dest.writeString(srcPath);
dest.writeString(outPath);
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getSrcPath() {
return srcPath;
}
public void setSrcPath(String srcPath) {
this.srcPath = srcPath;
}
public String getOutPath() {
return outPath;
}
public void setOutPath(String outPath) {
this.outPath = outPath;
}
}
/**
* 压缩结果回到监听类
*/
public interface CompressListener {
void onCompressStart();
void onCompressEnd(CompressResult imageOutPath);
}
private class CompressTask extends AsyncTask {
@Override
protected CompressResult doInBackground(String... params) {
String path = params[0];
int outWidth = Integer.parseInt(params[1]);
int outHeight = Integer.parseInt(params[2]);
int maxFileSize = Integer.parseInt(params[3]);
CompressResult compressResult = new CompressResult();
String outPutPath = null;
try {
outPutPath = compressImage(path, outWidth, outHeight, maxFileSize);
}catch (Exception e){
}
compressResult.setSrcPath(path);
compressResult.setOutPath(outPutPath);
if(outPutPath == null){
compressResult.setStatus(CompressResult.RESULT_ERROR);
}
return compressResult;
}
@Override
protected void onPreExecute() {
if (compressListener != null) {
compressListener.onCompressStart();
}
}
@Override
protected void onPostExecute(CompressResult compressResult) {
if (compressListener != null) {
compressListener.onCompressEnd(compressResult);
}
}
}
}
================================================
FILE: app/src/main/java/gui/com/lgimagecompressor/LGImgCompressorIntentService.java
================================================
package gui.com.lgimagecompressor;
import android.app.IntentService;
import android.content.Intent;
import android.content.Context;
import android.util.Log;
import java.util.ArrayList;
public class LGImgCompressorIntentService extends IntentService {
private final String TAG = LGImgCompressorIntentService.class.getSimpleName();
private static final String ACTION_COMPRESS = "gui.com.lgimagecompressor.action.COMPRESS";
private ArrayList compressResults = new ArrayList<>();//存储压缩任务的返回结果
public LGImgCompressorIntentService() {
super("LGImgCompressorIntentService");
setIntentRedelivery(false);//避免出异常后service重新启动
}
@Override
public void onCreate() {
super.onCreate();
Intent intent = new Intent(Constanse.ACTION_COMPRESS_BROADCAST);
intent.putExtra(Constanse.KEY_COMPRESS_FLAG,Constanse.FLAG_BEGAIIN);
sendBroadcast(intent);
Log.d(TAG,"onCreate...");
}
@Override
public void onDestroy() {
super.onDestroy();
Intent intent = new Intent(Constanse.ACTION_COMPRESS_BROADCAST);
intent.putExtra(Constanse.KEY_COMPRESS_FLAG,Constanse.FLAG_END);
intent.putParcelableArrayListExtra(Constanse.KEY_COMPRESS_RESULT,compressResults);
sendBroadcast(intent);
compressResults.clear();
Log.d(TAG,"onDestroy...");
}
public static void startActionCompress(Context context, CompressServiceParam param) {
Intent intent = new Intent(context, LGImgCompressorIntentService.class);
intent.setAction(ACTION_COMPRESS);
intent.putExtra(Constanse.COMPRESS_PARAM, param);
context.startService(intent);
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_COMPRESS.equals(action)) {
final CompressServiceParam param1 = intent.getParcelableExtra(Constanse.COMPRESS_PARAM);
handleActionCompress(param1);
}
}
}
private void handleActionCompress(CompressServiceParam param) {
int outwidth = param.getOutWidth();
int outHieight = param.getOutHeight();
int maxFileSize = param.getMaxFileSize();
String srcImageUri = param.getSrcImageUri();
LGImgCompressor.CompressResult compressResult = new LGImgCompressor.CompressResult();
String outPutPath = null;
try {
outPutPath = LGImgCompressor.getInstance(this).compressImage(srcImageUri, outwidth, outHieight, maxFileSize);
} catch (Exception e) {
}
compressResult.setSrcPath(srcImageUri);
compressResult.setOutPath(outPutPath);
if (outPutPath == null) {
compressResult.setStatus(LGImgCompressor.CompressResult.RESULT_ERROR);
}
compressResults.add(compressResult);
}
}
================================================
FILE: app/src/main/java/gui/com/lgimagecompressor/LGImgCompressorService.java
================================================
package gui.com.lgimagecompressor;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class LGImgCompressorService extends Service {
private static final String TAG = "GImgCompressorService";
private ArrayList compressResults = new ArrayList<>();
public LGImgCompressorService() {
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"onCreate...");
// executorService = Executors.newCachedThreadPool();
executorService = Executors.newFixedThreadPool(10);
Intent intent = new Intent(Constanse.ACTION_COMPRESS_BROADCAST);
intent.putExtra(Constanse.KEY_COMPRESS_FLAG,Constanse.FLAG_BEGAIIN);
sendBroadcast(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy...");
Intent intent = new Intent(Constanse.ACTION_COMPRESS_BROADCAST);
intent.putExtra(Constanse.KEY_COMPRESS_FLAG,Constanse.FLAG_END);
intent.putParcelableArrayListExtra(Constanse.KEY_COMPRESS_RESULT,compressResults);
sendBroadcast(intent);
compressResults.clear();
executorService.shutdownNow();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
doCompressImages(intent,startId);
return Service.START_NOT_STICKY;
}
private int taskNumber;
private ExecutorService executorService;
private final Object lock = new Object();
private void doCompressImages(final Intent intent,final int taskId){
final ArrayList paramArrayList = intent.getParcelableArrayListExtra(Constanse.COMPRESS_PARAM);
synchronized (lock){
taskNumber += paramArrayList.size();
}
//如果paramArrayList过大,为了避免"The application may be doing too much work on its main thread"的问题,将任务的创建和执行统一放在后台线程中执行
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < paramArrayList.size(); ++i){
executorService.execute(new CompressTask(paramArrayList.get(i),taskId));
}
}
}).start();
}
private class CompressTask implements Runnable{
private CompressServiceParam param;
private int taskId ;
private CompressTask(CompressServiceParam compressServiceParam,int taskId){
this.param = compressServiceParam;
this.taskId = taskId;
}
@Override
public void run() {
Log.d(TAG,taskId + " do compress begain..." + Thread.currentThread().getId());
int outwidth = param.getOutWidth();
int outHieight = param.getOutHeight();
int maxFileSize = param.getMaxFileSize();
String srcImageUri = param.getSrcImageUri();
LGImgCompressor.CompressResult compressResult = new LGImgCompressor.CompressResult();
String outPutPath = null;
try {
outPutPath = LGImgCompressor.getInstance(LGImgCompressorService.this).compressImage(
srcImageUri, outwidth, outHieight, maxFileSize);
} catch (Exception e) {
}
compressResult.setSrcPath(srcImageUri);
compressResult.setOutPath(outPutPath);
if (outPutPath == null) {
compressResult.setStatus(LGImgCompressor.CompressResult.RESULT_ERROR);
}
Log.d(TAG,taskId + " do compress end..." + Thread.currentThread().getId());
synchronized (lock){
compressResults.add(compressResult);
taskNumber--;
if(taskNumber <= 0){
stopSelf(taskId);
}
}
}
}
@Override
public IBinder onBind(Intent intent) {
throw null;
}
}
================================================
FILE: app/src/main/java/gui/com/lgimagecompressor/MainActivity.java
================================================
package gui.com.lgimagecompressor;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.basic_test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,BasicCompressActivity.class);
startActivity(intent);
}
});
findViewById(R.id.from_service).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,ServiceCompressActivity.class);
startActivity(intent);
}
});
}
}
================================================
FILE: app/src/main/java/gui/com/lgimagecompressor/ServiceCompressActivity.java
================================================
package gui.com.lgimagecompressor;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import java.io.File;
import java.util.ArrayList;
public class ServiceCompressActivity extends AppCompatActivity {
private final String TAG = ServiceCompressActivity.class.getSimpleName();
private long serviceStartTime;
private CompressingReciver reciver;
private TextView infoView;
private class CompressingReciver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive:" + Thread.currentThread().getId());
int flag = intent.getIntExtra(Constanse.KEY_COMPRESS_FLAG,-1);
Log.d(TAG," flag:" + flag);
if(flag == Constanse.FLAG_BEGAIIN){
Log.d(TAG, "onCompressServiceStart");
serviceStartTime = System.currentTimeMillis();
updateInfo("compress begain...");
return;
}
if(flag == Constanse.FLAG_END){
ArrayList compressResults =
(ArrayList)intent.getSerializableExtra(Constanse.KEY_COMPRESS_RESULT);
Log.d(TAG, compressResults.size() + "compressed done");
Log.d(TAG, "compress " + compressResults.size() + " files used total time:" + (System.currentTimeMillis() - serviceStartTime));
updateInfo(compressResults.size() + " files compressed done \nused total time:" + (System.currentTimeMillis() - serviceStartTime) + "ms");
}
}
}
private void updateInfo(String message){
infoView.setText(message);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intent_service_compress);
reciver = new CompressingReciver();
IntentFilter intentFilter = new IntentFilter(Constanse.ACTION_COMPRESS_BROADCAST);
registerReceiver(reciver, intentFilter);
infoView = (TextView)findViewById(R.id.compress_info);
final int maxSize = 0;
findViewById(R.id.intent_service).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ArrayList compressFiles = getImagesPathFormAlbum();
Log.d(TAG, compressFiles.size() + "compresse begain");
int size = compressFiles.size() > 10 ? 10:compressFiles.size();
for (int i = 0; i < compressFiles.size(); ++i) {
Uri uri = compressFiles.get(i);
CompressServiceParam param = new CompressServiceParam();
param.setOutHeight(800);
param.setOutWidth(600);
param.setMaxFileSize(400);
param.setSrcImageUri(uri.toString());
LGImgCompressorIntentService.startActionCompress(ServiceCompressActivity.this, param);
}
}
});
findViewById(R.id.service).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ArrayList compressFiles = getImagesPathFormAlbum();
int size = compressFiles.size() > 10 ? 10:compressFiles.size();
ArrayList tasks = new ArrayList(compressFiles.size());
for (int i = 0; i < compressFiles.size(); ++i) {
Uri uri = compressFiles.get(i);
CompressServiceParam param = new CompressServiceParam();
param.setOutHeight(800);
param.setOutWidth(600);
param.setMaxFileSize(400);
param.setSrcImageUri(uri.toString());
tasks.add(param);
}
Log.d(TAG, compressFiles.size() + "compresse begain");
Intent intent = new Intent(ServiceCompressActivity.this, LGImgCompressorService.class);
intent.putParcelableArrayListExtra(Constanse.COMPRESS_PARAM, tasks);
startService(intent);
}
});
}
private ArrayList getImagesPathFormAlbum() {
ArrayList paths = new ArrayList<>();
//selection: 指定查询条件
String selection = MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?";
//定义selection参数匹配值
String[] selectionArgs = {"image/jpeg", "image/png"};
Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null,
selection,
selectionArgs,
MediaStore.Images.Media.DATE_MODIFIED);
if (cursor != null && cursor.moveToFirst()) {
do {
long id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID));
String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE));
String url = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
long size = (int) cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE));
long lastModified = (int) cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_MODIFIED));
//排除size为0的无效文件
if (size != 0) {
Uri uri = Uri.parse(url);
paths.add(uri);
}
} while (cursor.moveToNext());
cursor.close();
}
return paths;
}
@Override
protected void onDestroy() {
super.onDestroy();
if(reciver != null){
unregisterReceiver(reciver);
}
}
}
================================================
FILE: app/src/main/res/layout/activity_basic_compress.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_intent_service_compress.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#3F51B5
#303F9F
#FF4081
================================================
FILE: app/src/main/res/values/dimens.xml
================================================
16dp
16dp
================================================
FILE: app/src/main/res/values/strings.xml
================================================
LGImageCompressor
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/main/res/values-w820dp/dimens.xml
================================================
64dp
================================================
FILE: app/src/test/java/gui/com/lgimagecompressor/ExampleUnitTest.java
================================================
package gui.com.lgimagecompressor;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* To work on unit tests, switch the Test Artifact in the Build Variants view.
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: app/src/test/java/gui/com/lgimagecompressor/LGImgCompressorServiceTest.java
================================================
package gui.com.lgimagecompressor;
import android.provider.Settings;
import android.util.Log;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import static org.junit.Assert.*;
/**
* Created by guizhigang on 16/5/29.
*/
public class LGImgCompressorServiceTest {
private Object lock = new Object();
private int taskNumber;
private ArrayList results = new ArrayList<>();
private class CompressTask implements Runnable{
private int taskId ;
private CompressTask(int taskId){
this.taskId = taskId;
}
@Override
public void run() {
System.out.println(taskId + " do compress begain..." + Thread.currentThread().getId());
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(taskId + " do compress end..." + Thread.currentThread().getId());
synchronized (lock){
System.out.println(taskId + " make taskNumber release before " + taskNumber);
results.add(taskId);
taskNumber--;
System.out.println(taskId + " make taskNumber release end " + taskNumber);
if(taskNumber <= 0){
System.out.println("all task done...");
}
}
}
}
@Test
public void startServer(){
taskNumber = 8;
int count = taskNumber;
for (int i = 1; i <= count; i++){
new Thread(new CompressTask(i)).start();
}
synchronized (lock){
try {
Thread.sleep(taskNumber * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
}
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Mon Dec 28 10:00:20 PST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
include ':app'