目录
优化过程:
- 少使用第三方jar包
- APK体积缩小
- APK打包去掉无用资源
- 启动优化
- 线程任务共用
- 后台任务处理
- 白屏处理
- 混淆
- ViewPager+fragment优化
- 单/多线程优化(线程池管理)
- 压缩图片地址:TinyPNG – Compress WebP, PNG and JPEG images intelligently,图片压缩也是个很重要的事噢
1.apk文件占用情况查看
在android studio中双击生成的apk文件,查看apk占用的主要情况,下图为优化后的效果
2.混淆模板
使用:直接复制到app/proguard-rules.pro文件中即可,处理混淆时根据第三方提供的混淆代码不从到其中,以及实体类保持等
- 减少apk体积
- 避免无用的资源编译到apk文件中
- 安全性
- 去掉日志对优化有一定帮助
#-------------------------------------------定制化区域----------------------------------------------
#---------------------1.实体类(注意此处的实体类可按照自己的包名修改)-----------------------------------
-keep class com.face.factoryageing.entity.** { *; }
#-------------------------------------------------------------------------
#---------------------------------2.第三方包-------------------------------
#eventBus
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
#glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
-keep public class cn.jzvd.JZMediaSystem {*; }
-keep public class cn.jzvd.demo.CustomMedia.CustomMedia {*; }
-keep public class cn.jzvd.demo.CustomMedia.JZMediaIjk {*; }
-keep public class cn.jzvd.demo.CustomMedia.JZMediaSystemAssertFolder {*; }
-keep class tv.danmaku.ijk.media.player.** {*; }
-dontwarn tv.danmaku.ijk.media.player.*
-keep interface tv.danmaku.ijk.media.player.** { *; }
#log4j
#-------------------------------------------------------------------------
#---------------------------------3.与js互相调用的类------------------------
-keepclasseswithmembers class com.demo.login.bean.ui.MainActivity$JSInterface {
<methods>;
}
#-------------------------------------------------------------------------
#---------------------------------4.反射相关的类和方法-----------------------
# 有
#----------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------
#-------------------------------------------基本不用动区域--------------------------------------------
#---------------------------------基本指令区----------------------------------
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
-printmapping proguardMapping.txt
-optimizations !code/simplification/cast,!field/*,!class/merging/*
-keepattributes *Annotation*,InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
#----------------------------------------------------------------------------
#---------------------------------默认保留区---------------------------------
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
-keep class android.support.** {*;}
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
-keep class **.R$* {
*;
}
-keepclassmembers class * {
void *(**On*Event);
}
#----------------------------------------------------------------------------
#---------------------------------webview------------------------------------
-keepclassmembers class fqcn.of.javascript.interface.for.Webview {
public *;
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, jav.lang.String);
}
#----------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------
# 删除代码中Log相关的代码
#-assumenosideeffects class android.util.Log {
# public static boolean isLoggable(java.lang.String, int);
# public static int v(...);
# public static int i(...);
# public static int w(...);
# public static int d(...);
# public static int e(...);
#}
app/build.gradle中配置混淆是否启用
android{
buildTypes {
release {
signingConfig signingConfigs.release
//这一句是开启混淆
minifyEnabled true
// Zipalign优化
zipAlignEnabled true
// 移除无用的resource文件
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
signingConfig signingConfigs.debug
//这一句是关闭混淆
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
3.系统framework.jar优化
在app/build.gradle文件中修改
- 参与编译,但是不把framework.jar的内容加入到APK中
- 使用场景(开发系统apk,framework.jar通常都比较大,大致在8M以上)使用以下方式可避免编译到apk中
- 标签中注释下方代码
dependencies {
//注意注释的这一句
//implementation fileTree(dir: "libs", include: ["*.jar"])
//只参与编译
compileOnly files('libs\\framework.jar')
}
根目录下的build.gradle
allprojects {
repositories {
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << '-Xbootclasspath/p:app/libs/classes.jar'
}
}
}
}
4.后台处理耗时任务,初始化操作
耗时任务与主线程同步进行,InitializeService 负责处理复杂耗时的初始化操作
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//这里主要处理启动app时的初始化,异步任务等
InitializeService.start(this);
}
@Override
protected void attachBaseContext(android.content.Context base) {
super.attachBaseContext(base);
//分包
MultiDex.install(this);
}
}
/**
* Create on 2019/10/16
* author chtj
* desc 启动app时的优化
*/
public class InitializeService extends IntentService {
private static final String TAG="InitializeService";
private static final String ACTION_INIT_WHEN_APP_CREATE = "com.anly.githubapp.service.action.INIT";
public InitializeService() {
super("InitializeService");
}
//在继承Application的类中调用了此方法
public static void start(Context context) {
Intent intent = new Intent(context, InitializeService.class);
intent.setAction(ACTION_INIT_WHEN_APP_CREATE);
context.startService(intent);
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_INIT_WHEN_APP_CREATE.equals(action)) {
performInit();
}
}
}
private void performInit() {
//在这里进行初始化的相关代码
KLog.init(true);
KLog.d(TAG,"performInit");
//CrashHandler.getInstance().init(getApplication());
//KLog.d("performInit begin:" + System.currentTimeMillis());
//需要在 Application 的 onCreate() 中调用一次 BaseIotTools.instance()....
//1080,1920是为了适配而去设置相关的值
//设置宽度|高度布局尺寸 layout 布局文件以pt为单位 setBaseScreenParam(1080,1920,true)
BaseIotUtils.instance().
setBaseScreenParam(1080,1920,true).
setCreenType(SCREEN_TYPE.WIDTH).//按照宽度适配
create(getApplication());
}
}
5.Activity启动优化
Activity只负责界面基础view初始化操作,耗时任务在异步任务中操作,互不干扰,避免UI产生卡顿
/**
* Create on 2019/10/16
* author chtj
* desc $ 启动页
*/
public class StartPageAty extends BaseActivity {
private static final String TAG="StartPageAty";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//先进入启动页,什么都不做,只是做一个跳转
startActivity(new Intent(StartPageAty.this, MainActivity.class));
}
@Override
protected void onDestroy() {
super.onDestroy();
try{
boolean isFrist= SPUtils.getBoolean("isFirst",true);
if(isFrist){
//如果是第一次进来
SPUtils.putBoolean("isFirst",false);
}else{
//如果是第二次及以后进来
}
}catch(Exception e){
e.printStackTrace();
KLog.e(TAG,"errMeg:"+e.getMessage());
}
}
}
public class MainActivity extends BaseActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//启动完毕之后结束开始页
AppManager.getAppManager().finishActivity(StartPageAty.class);
//耗时任务放到线程中执行
new Handler().post(new Runnable() {
@Override
public void run() {
//举例:这里异步执行耗时任务
//可以自己定义一个任务栈处理自己的数据获取,网络访问等,访问完毕后再刷新view,一开始只负责显示一些框架,避免耗时
List<File> fileList=getFileList();
}
});
}
}
drawable/logo_splash.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 底层白色 -->
<item android:drawable="@color/white" />
<!-- 顶层Logo居中 -->
<item>
<bitmap
android:gravity="center"
android:src="@mipmap/app_img" />
</item>
</layer-list>
res/values/styles.xml中添加,StartPageAty将会显示这里面的style(drawable/logo_splash.xml 主要是@mipmap/app_img这张图片)
<!--
一:AndroidManifest.xml application中的theme使用该主题
二:启动成功后再Aty中设置为正常的主题
三:其他Aty设置为正常的AppTheme
这样打开桌面图标会马上显示logo,不会出现黑/白屏,
直到Activity启动完成,替换主题,logo消失,
-->
<style name="SplashTheme" parent="AppTheme">
<item name="android:windowBackground">@drawable/logo_splash</item>
<item name="android:windowFullscreen">true</item>
</style>
6.内存申请及启动页配置
activity_main布局文件推荐使用RelativeLayout,可对耗时有一部分优化,尽量减少布局层次,嵌套等复杂布局。
AndroidManifest.xml中 android:largeHeap="true"分配更多内存提供给apk使用
<manifest>
<application
android:largeHeap="true"
android:persistent="true">
<!--启动页-->
<activity
android:name=".startpage.StartPageAty"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--首页-->
<activity android:name=".startpage.MainActivity"/>
<!--在服务里面进行一些application应该执行的初始化操作-->
<service android:name=".myapplication.InitializeService" />
</application>
</manfiest>
7.Fragment+ViewPager优化
将项目中的Fragment继承LazyLoadFragment实现懒加载
/**
* Create on 2020/8/13
* author chtj
* desc
*/
public abstract class LazyLoadFragment extends Fragment {
/**
* 视图是否已经初初始化
*/
protected boolean isInit = false;
protected boolean isLoad = false;
protected final String TAG = "LazyLoadFragment";
private View view;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = inflater.inflate(setContentView(), container, false);
isInit = true;
/**初始化的时候去加载数据**/
isCanLoadData();
return view;
}
/**
* 视图是否已经对用户可见,系统的方法
*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
isCanLoadData();
}
/**
* 是否可以加载数据
* 可以加载数据的条件:
* 1.视图已经初始化
* 2.视图对用户可见
*/
private void isCanLoadData() {
if (!isInit) {
return;
}
if (getUserVisibleHint()) {
lazyLoad();
isLoad = true;
} else {
if (isLoad) {
stopLoad();
}
}
}
/**
* 视图销毁的时候讲Fragment是否初始化的状态变为false
*/
@Override
public void onDestroyView() {
super.onDestroyView();
isInit = false;
isLoad = false;
}
/**
* 设置Fragment要显示的布局
*
* @return 布局的layoutId
*/
protected abstract int setContentView();
/**
* 获取设置的布局
*
* @return
*/
protected View getContentView() {
return view;
}
/**
* 找出对应的控件
*
* @param id
* @param <T>
* @return
*/
protected <T extends View> T findViewById(int id) {
return (T) getContentView().findViewById(id);
}
/**
* 当视图初始化并且对用户可见的时候去真正的加载数据
*/
protected abstract void lazyLoad();
/**
* 当视图已经对用户不可见并且加载过数据,如果需要在切换到其他页面时停止加载数据,可以覆写此方法
*/
protected void stopLoad() {
}
}
8.其他推荐库
这里推荐一些好用的第三方库,具体版本使用,请点击库名跳转:
BaseIotUtils
串口工具,屏幕适配,通知工具类,多文件断点下载,xls,xlsx操作,文件处理,crash控制,音视频播放,usb设备检测,线程管理,app管理(获取列表卸载,安装等),adb工具等...
implementation 'com.github.wave-chtj:BaseIotUtils:1.0.1'
BaseFramework
OTA升级,应用流量查询,应用网络启/禁用,静态/动态IP设置,TF/sdcard/rom/ram查询
implementation 'com.github.wave-chtj:BaseFramework:1.0.1'
BaseKeepAlive
保活服务(Aty|Service)
implementation 'com.github.wave-chtj:BaseKeepAlive:1.0.1'
BaseSocket
socket网络通讯,TCP,UDP通信
implementation 'com.github.wave-chtj:BaseSocket:1.0.1'