diff --git a/.winboll/winboll_app_build.gradle b/.winboll/winboll_app_build.gradle index ab64932e..54b28bac 100644 --- a/.winboll/winboll_app_build.gradle +++ b/.winboll/winboll_app_build.gradle @@ -28,12 +28,17 @@ android { } } buildTypes { - release { - signingConfig signingConfigs.winboll - } debug { - signingConfig signingConfigs.winboll - } + signingConfig signingConfigs.winboll + } + release { + signingConfig signingConfigs.winboll + minifyEnabled true // 开启混淆(核心开关) + shrinkResources true // 可选:移除无用资源(进一步减小体积) + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), // 官方默认规则(优化版) + 'proguard-rules.pro' // 自定义规则文件 + } + } flavorDimensions "WinBoLLApp" @@ -71,6 +76,7 @@ android { // 2. 配置 Beta Debug 版应用包输出 // if((variant.flavorName == "beta" && variant.buildType.name == "debug") + || (variant.flavorName == "beta" && variant.buildType.name == "release") || (variant.flavorName == "stage" && variant.buildType.name == "debug") || (variant.flavorName == "stage" && variant.buildType.name == "release")) { println "Project root directory: " + project.rootDir.toString() diff --git a/appbase/build.gradle b/appbase/build.gradle index f1adbe8b..0d5af49d 100644 --- a/appbase/build.gradle +++ b/appbase/build.gradle @@ -19,29 +19,31 @@ def genVersionName(def versionName){ android { - compileSdkVersion 32 - buildToolsVersion "32.0.0" + // 1. compileSdkVersion:必须 ≥ targetSdkVersion,建议直接等于 targetSdkVersion(30) + compileSdkVersion 30 + + // 2. buildToolsVersion:需匹配 compileSdkVersion,建议使用 30.x.x 最新稳定版(无需高于 compileSdkVersion) + buildToolsVersion "30.0.3" // 这是 30 对应的最新稳定版,避免使用 beta 版 defaultConfig { applicationId "cc.winboll.studio.appbase" - minSdkVersion 24 + minSdkVersion 23 targetSdkVersion 30 versionCode 1 // versionName 更新后需要手动设置 // .winboll/winbollBuildProps.properties 文件的 stageCount=0 // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0" - versionName "15.10" + versionName "15.11" if(true) { versionName = genVersionName("${versionName}") } } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } + + // 确保 Java 7 兼容性(已适配项目技术栈) + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } } dependencies { diff --git a/appbase/build.properties b/appbase/build.properties index 3b345de0..ca3c7825 100644 --- a/appbase/build.properties +++ b/appbase/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sat Sep 27 21:03:20 HKT 2025 -stageCount=10 +#Fri Nov 21 11:41:04 HKT 2025 +stageCount=2 libraryProject=libappbase -baseVersion=15.10 -publishVersion=15.10.9 +baseVersion=15.11 +publishVersion=15.11.1 buildCount=0 -baseBetaVersion=15.10.10 +baseBetaVersion=15.11.2 diff --git a/appbase/proguard-rules.pro b/appbase/proguard-rules.pro index 233bad20..574eeecc 100644 --- a/appbase/proguard-rules.pro +++ b/appbase/proguard-rules.pro @@ -15,3 +15,112 @@ #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} + +# ============================== 基础通用规则 ============================== +# 保留系统组件 +-keep public class * extends android.app.Activity +-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 + +# 保留 WinBoLL 核心包及子类(适配你的两个包名) +#-keep public class * extends com.winboll.WinBoLLActivity +#-keep public class * extends com.winboll.WinBoLLFragment +# 主包名 +-keep class cc.winboll.studio.*.** { *; } +# beta包名 +-keep class cc.winboll.studio.*.beta.** { *; } +-keepclassmembers class cc.winboll.studio.*.** { *; } +-keepclassmembers class cc.winboll.studio.*.beta.** { *; } + +# 保留所有类中的 public static final String TAG 字段 +-keepclassmembers class * { + public static final java.lang.String TAG; +} + +# 保留序列化类 +-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(); +} + +# 保留 R 文件 +-keepclassmembers class **.R$* { + public static ; +} + +# 保留 native 方法 +-keepclasseswithmembernames class * { + native ; +} + +# 保留注解和泛型 +-keepattributes *Annotation* +-keepattributes Signature + +# 屏蔽 Java 8+ 警告(适配 Java 7) +-dontwarn java.lang.invoke.* +-dontwarn android.support.v8.renderscript.* +-dontwarn java.util.function.** + +# ============================== 第三方框架规则 ============================== +# Retrofit + OkHttp +-keep class retrofit2.** { *; } +-keep interface retrofit2.** { *; } +-keep class okhttp3.** { *; } +-keep interface okhttp3.** { *; } +-keep class okio.** { *; } +-keepclasseswithmembers class * { + @retrofit2.http.* ; +} + +# Glide 4.x +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public class * extends com.bumptech.glide.module.AppGlideModule +-keep public enum com.bumptech.glide.load.ImageHeaderParser$ImageType { + **[] $VALUES; + public *; +} +-dontwarn com.bumptech.glide.load.resource.bitmap.VideoDecoder + +# GreenDAO 3.x +-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao { + public static java.lang.String TABLENAME; +} +-keep class **$Properties +# 实体类包名(按实际调整) +#-keep class cc.winboll.studio.appbase.model.** { *; } + +# ButterKnife 8.x +-keep class butterknife.** { *; } +-dontwarn butterknife.internal.** +-keep class **$$ViewBinder { *; } +-keepclasseswithmembernames class * { + @butterknife.BindView ; + @butterknife.OnClick ; +} + +# EventBus 3.x +-keepclassmembers class ** { + @org.greenrobot.eventbus.Subscribe ; +} +-keep enum org.greenrobot.eventbus.ThreadMode { *; } + +# ============================== 优化与调试 ============================== +-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* +-optimizationpasses 5 +-verbose +-dontpreverify +-dontusemixedcaseclassnames +# 保留行号(便于崩溃定位) +-keepattributes SourceFile,LineNumberTable + diff --git a/appbase/src/main/AndroidManifest.xml b/appbase/src/main/AndroidManifest.xml index ccecd360..284d7afd 100644 --- a/appbase/src/main/AndroidManifest.xml +++ b/appbase/src/main/AndroidManifest.xml @@ -3,9 +3,6 @@ xmlns:android="http://schemas.android.com/apk/res/android" package="cc.winboll.studio.appbase"> - - - * @Date 2025/01/05 09:54:42 - * @Describe APPbase 应用类 + * @Describe 应用全局入口类(继承基础库 GlobalApplication) + * 负责应用初始化、全局资源管理与生命周期回调处理,是整个应用的核心入口 */ -import android.content.IntentFilter; -import cc.winboll.studio.libappbase.GlobalApplication; - public class App extends GlobalApplication { + /** 当前应用类的日志 TAG(用于调试输出,标识日志来源) */ public static final String TAG = "App"; - + + /** + * 应用创建时回调(全局初始化入口) + * 在应用进程启动时执行,仅调用一次,用于初始化全局工具类、第三方库等 + */ @Override public void onCreate() { - super.onCreate(); + super.onCreate(); // 调用父类初始化逻辑(如基础库配置、全局上下文设置) + // 初始化 Toast 工具类(传入应用全局上下文,确保 Toast 可在任意地方调用) + ToastUtils.init(getApplicationContext()); + } + + /** + * 应用终止时回调(资源释放入口) + * 仅在模拟环境(如 Android Studio 模拟器)中可靠触发,真机上可能因系统回收进程不执行 + * 用于释放全局资源,避免内存泄漏 + */ + @Override + public void onTerminate() { + super.onTerminate(); // 调用父类终止逻辑(如基础库资源释放) + // 释放 Toast 工具类资源(销毁全局 Toast 实例,避免内存泄漏) + ToastUtils.release(); } } + diff --git a/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java b/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java index 0a45ba63..387fab3f 100644 --- a/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java +++ b/appbase/src/main/java/cc/winboll/studio/appbase/MainActivity.java @@ -8,67 +8,133 @@ import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.Toast; import android.widget.Toolbar; import cc.winboll.studio.appbase.R; import cc.winboll.studio.libappbase.LogActivity; +import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.ToastUtils; +/** + * @Author ZhanGSKen + * @Date 未标注(建议补充创建日期) + * @Describe 应用主界面 Activity(入口界面) + * 包含功能测试按钮(崩溃测试、日志查看、Toast测试)、顶部工具栏(菜单功能),是应用交互的核心入口 + */ public class MainActivity extends Activity { + /** 当前 Activity 的日志 TAG(用于调试输出,标识日志来源) */ public static final String TAG = "MainActivity"; - Toolbar mToolbar; + /** 顶部工具栏(用于展示标题、菜单,绑定布局中的 Toolbar 控件) */ + private Toolbar mToolbar; + /** + * Activity 创建时回调(初始化界面) + * 在 Activity 首次创建时执行,用于加载布局、初始化控件、设置事件监听 + * @param savedInstanceState 保存 Activity 状态的 Bundle(如屏幕旋转时的数据恢复) + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ToastUtils.show("onCreate"); - setContentView(R.layout.activity_main); + ToastUtils.show("onCreate"); // 显示 Activity 创建提示(调试用) + setContentView(R.layout.activity_main); // 加载主界面布局 + // 初始化 Toolbar 并设置为 ActionBar mToolbar = findViewById(R.id.toolbar); - setActionBar(mToolbar); + setActionBar(mToolbar); // 将 Toolbar 替代系统默认 ActionBar } + /** + * 创建菜单时回调(加载工具栏菜单) + * 初始化 ActionBar 菜单,加载自定义菜单布局 + * @param menu 菜单对象(用于承载菜单项) + * @return true:显示菜单;false:不显示菜单 + */ @Override public boolean onCreateOptionsMenu(Menu menu) { + // 加载菜单布局(R.menu.toolbar_main 为自定义菜单文件) getMenuInflater().inflate(R.menu.toolbar_main, menu); return super.onCreateOptionsMenu(menu); } + /** + * 菜单 item 点击时回调(处理菜单事件) + * 响应 Toolbar 菜单项的点击事件,执行对应业务逻辑 + * @param item 被点击的菜单项 + * @return true:消费点击事件;false:不消费(传递给父类) + */ @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.item_home : { - openWebsiteInBrowser(this); - } - } - // 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。 + switch (item.getItemId()) { + case R.id.item_home: + // 点击 "首页/官网" 菜单项,唤起浏览器打开指定网站 + openWebsiteInBrowser(this); + break; + // 可扩展其他菜单项(如设置、关于等)的处理逻辑 + } return super.onOptionsItemSelected(item); } + /** + * 崩溃测试按钮点击事件(触发应用崩溃,用于调试异常捕获) + * 故意执行非法操作(循环获取不存在的字符串资源),强制应用崩溃 + * @param view 触发事件的 View(对应布局中的崩溃测试按钮) + */ public void onCrashTest(View view) { - for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) { - getString(i); - } + // 循环从 Integer.MIN_VALUE 到 Integer.MAX_VALUE,获取不存在的字符串资源 ID,触发崩溃 + for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) { + getString(i); // i 超出资源 ID 范围,抛出 Resources.NotFoundException 导致崩溃 + } } + /** + * 日志测试按钮点击事件(打开日志查看界面) + * 启动 LogActivity,用于查看应用运行日志 + * @param view 触发事件的 View(对应布局中的日志测试按钮) + */ public void onLogTest(View view) { + // 启动日志查看 Activity(通过静态方法传入上下文,简化跳转逻辑) LogActivity.startLogActivity(this); } - /** - * 唤起默认浏览器打开指定网站 - * @param context 上下文(如 Activity.this) - */ - public void openWebsiteInBrowser(Context context) { - // 目标网站地址 - String url = "https://www.winboll.cc"; - // 构建打开浏览器的意图 - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - // 设置标志:避免创建新的任务栈(可选,按需求调整) - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - - } + /** + * Toast 工具测试按钮点击事件(测试全局 Toast 功能) + * 测试主线程、子线程中 Toast 的显示效果,验证 ToastUtils 的可用性 + * @param view 触发事件的 View(对应布局中的 Toast 测试按钮) + */ + public void onToastUtilsTest(View view) { + LogUtils.d(TAG, "onToastUtilsTest"); // 打印调试日志,标识进入 Toast 测试 + ToastUtils.show("Hello, WinBoLL!"); // 主线程显示 Toast + + // 开启子线程,延迟 2 秒后显示 Toast(测试子线程 Toast 兼容性) + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(2000); // 线程休眠 2 秒 + // 若 ToastUtils 已处理主线程切换,此处可直接调用;否则需通过 Handler 切换到主线程 + ToastUtils.show("Thread.sleep(2000);ToastUtils.show..."); + } catch (InterruptedException e) { + // 捕获线程中断异常(如线程被销毁时),不做处理(测试场景) + e.printStackTrace(); + } + } + }).start(); + } + + /** + * 唤起系统默认浏览器打开指定网站(跳转至应用官网) + * 通过 Intent.ACTION_VIEW 隐式意图,触发浏览器打开目标 URL + * @param context 上下文对象(如 Activity、Application,此处为 MainActivity) + */ + public void openWebsiteInBrowser(Context context) { + String url = "https://www.winboll.cc"; // 目标网站 URL(应用官网) + // 构建隐式意图:ACTION_VIEW 表示查看指定数据(Uri 为网站地址) + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + // 设置标志:在新的任务栈中启动 Activity(避免与当前应用任务栈混淆) + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // 启动意图(唤起浏览器) + context.startActivity(intent); + } } + diff --git a/appbase/src/main/res/layout/activity_main.xml b/appbase/src/main/res/layout/activity_main.xml index 9552e3ad..5ba647d7 100644 --- a/appbase/src/main/res/layout/activity_main.xml +++ b/appbase/src/main/res/layout/activity_main.xml @@ -11,37 +11,57 @@ android:layout_height="wrap_content" android:id="@+id/toolbar"/> - + android:layout_weight="1.0"> -