├── .gitignore ├── .metadata ├── README.md ├── alipwechat.png ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ ├── AndroidManifest.xml │ │ └── lib │ │ │ └── l10n │ │ │ └── intl_zh.arb │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── lijiaqi │ │ │ │ └── bedrock │ │ │ │ ├── AppCrashHandler.java │ │ │ │ ├── BaseApp.java │ │ │ │ ├── MainActivity.java │ │ │ │ ├── plugin │ │ │ │ └── BedrockPlugin.java │ │ │ │ ├── protect │ │ │ │ ├── ActivityStackManager.java │ │ │ │ ├── AndroidPlatformProtect.java │ │ │ │ ├── IProtect.java │ │ │ │ ├── handler │ │ │ │ │ ├── ActivityExceptionHandler.java │ │ │ │ │ └── DefaultActivityExceptionHandler.java │ │ │ │ └── zone │ │ │ │ │ ├── ActivityStartProtect.java │ │ │ │ │ ├── ChildThreadProtect.java │ │ │ │ │ └── UIThreadProtect.java │ │ │ │ ├── test │ │ │ │ ├── TestPage1.java │ │ │ │ ├── TestPage2.java │ │ │ │ └── readme │ │ │ │ └── util │ │ │ │ └── ReflexUtil.java │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── lijiaqi │ │ │ │ └── flutter_bedrock │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── layout │ │ │ ├── activity_test_page1.xml │ │ │ └── activity_test_page2.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 │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ │ └── xml │ │ │ └── provider_paths.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── settings_aar.gradle ├── assets ├── images │ ├── bg.png │ ├── car.png │ ├── icons │ │ ├── icon_back_black.png │ │ └── suggestion_camera.png │ └── sds.png └── test_icon.png ├── build.yaml ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── base_framework │ ├── config │ │ ├── app_config.dart │ │ ├── frame_constant.dart │ │ ├── global_provider_manager.dart │ │ ├── net │ │ │ ├── base_http.dart │ │ │ └── bedrock_http.dart │ │ ├── router_manager.dart │ │ └── storage_manager.dart │ ├── constant.dart │ ├── empty.dart │ ├── exception │ │ ├── base_exception.dart │ │ ├── data_missing_exception.dart │ │ ├── un_authorized_exception.dart │ │ ├── un_handle_exception.dart │ │ ├── user_unbind_exception.dart │ │ └── verification_code_exception.dart │ ├── extension │ │ ├── list_extension.dart │ │ └── size_adapter_extension.dart │ ├── factory │ │ ├── page │ │ │ └── page_animation_builder.dart │ │ └── useless.dart │ ├── native │ │ └── native_method_manager.dart │ ├── observe │ │ ├── app_status │ │ │ ├── app_status_observe.dart │ │ │ ├── net_observer.dart │ │ │ └── server_notify_observer.dart │ │ ├── own_navigator_observe.dart │ │ └── route │ │ │ └── router_binding.dart │ ├── ui │ │ ├── anim │ │ │ ├── a.dart │ │ │ └── page_route_anim │ │ │ │ ├── fade_animation.dart │ │ │ │ ├── no_animation.dart │ │ │ │ ├── size_scale_animation.dart │ │ │ │ └── slide_animation.dart │ │ ├── behavior │ │ │ └── over_scroll_behavior.dart │ │ ├── empty.dart │ │ └── widget │ │ │ ├── activity_indicator.dart │ │ │ ├── bottom_nav_bar_no_ink.dart │ │ │ ├── circle_border_container.dart │ │ │ ├── detail_image_widget.dart │ │ │ ├── ffloat.dart │ │ │ ├── float_container_widget.dart │ │ │ ├── image │ │ │ └── image_editor.dart │ │ │ ├── no_ink_well_factory.dart │ │ │ ├── notification │ │ │ ├── common_notification_widget.dart │ │ │ └── notification_handler.dart │ │ │ ├── popup_window.dart │ │ │ ├── progress_widget.dart │ │ │ ├── provider_widget.dart │ │ │ ├── route │ │ │ ├── pop_window_builder.dart │ │ │ └── route_aware_widget.dart │ │ │ ├── skeleton │ │ │ ├── base_skeleton │ │ │ │ └── base_skeleton_widget.dart │ │ │ └── skeleton_rect.dart │ │ │ ├── view_state_widget.dart │ │ │ └── web │ │ │ ├── html_page.dart │ │ │ └── web_page.dart │ ├── utils │ │ ├── colors_util.dart │ │ ├── exception_pitcher.dart │ │ ├── image_helper.dart │ │ ├── isolate │ │ │ ├── app_private_ioslate.dart │ │ │ └── factory │ │ │ │ ├── proxy_isolate.dart │ │ │ │ ├── task_warpper.dart │ │ │ │ ├── work_isolate_wrapper.dart │ │ │ │ └── worker_isolate.dart │ │ ├── little_util.dart │ │ ├── mmkv_flutter.dart │ │ ├── notification_util.dart │ │ ├── platform_utils.dart │ │ ├── refresh_helper.dart │ │ ├── show_image_util.dart │ │ └── string_helper.dart │ ├── view_model │ │ ├── app_model │ │ │ ├── app_cache_model.dart │ │ │ ├── app_status_model.dart │ │ │ ├── device_model.dart │ │ │ ├── locale_model.dart │ │ │ └── user_view_model.dart │ │ ├── custome_view_state_model.dart │ │ ├── handle │ │ │ └── exception_handler.dart │ │ ├── interface │ │ │ └── cache_data_factory.dart │ │ ├── list_view_state_model.dart │ │ ├── refresh_list_view_state_model.dart │ │ ├── single_view_state_model.dart │ │ ├── view_state.dart │ │ └── view_state_model.dart │ └── widget_state │ │ ├── base_state.dart │ │ ├── base_stateless_widget.dart │ │ ├── binding │ │ └── manipulate_widget_binding.dart │ │ ├── page_state.dart │ │ └── widget_state.dart ├── generated │ ├── intl │ │ ├── messages_all.dart │ │ ├── messages_en.dart │ │ └── messages_zh.dart │ └── l10n.dart ├── l10n │ ├── intl_en.arb │ └── intl_zh.arb ├── main.dart ├── main.reflectable.dart ├── page │ ├── a.dart │ ├── demo_page │ │ ├── demo_page.dart │ │ ├── exception │ │ │ ├── exception_main_page.dart │ │ │ ├── exception_notify_page.dart │ │ │ ├── exception_view_model.dart │ │ │ ├── handle_exception_page.dart │ │ │ └── unknow_page.dart │ │ ├── image │ │ │ ├── image_main_demo.dart │ │ │ ├── nine_image │ │ │ │ ├── nine_flow_delegate.dart │ │ │ │ ├── nine_image_state.dart │ │ │ │ └── nine_image_vm.dart │ │ │ ├── nine_image_page.dart │ │ │ └── pick_image_page.dart │ │ ├── isolate │ │ │ └── isolate_page.dart │ │ ├── local_i10l │ │ │ └── local_page.dart │ │ ├── main │ │ │ ├── fake_constant.dart │ │ │ ├── first │ │ │ │ ├── cache_data_page.dart │ │ │ │ ├── entity │ │ │ │ │ ├── cache_entity.dart │ │ │ │ │ ├── first_card_entity.dart │ │ │ │ │ └── first_entity.dart │ │ │ │ ├── ffloat_page.dart │ │ │ │ ├── update_page.dart │ │ │ │ ├── view_model │ │ │ │ │ ├── cache_page_vm.dart │ │ │ │ │ ├── first_view_model.dart │ │ │ │ │ └── update_view_model.dart │ │ │ │ └── widget │ │ │ │ │ ├── first_banner.dart │ │ │ │ │ └── first_skeleton_page.dart │ │ │ ├── first_page.dart │ │ │ ├── login │ │ │ │ ├── login_page.dart │ │ │ │ └── login_view_model.dart │ │ │ ├── second │ │ │ │ ├── entity │ │ │ │ │ └── second_entity.dart │ │ │ │ └── view_model │ │ │ │ │ └── second_view_model.dart │ │ │ ├── second_page.dart │ │ │ └── third_page.dart │ │ ├── main_page.dart │ │ ├── notification │ │ │ ├── notification_page.dart │ │ │ └── notify_target_page.dart │ │ ├── other │ │ │ ├── cross_list │ │ │ │ ├── body_item_widget.dart │ │ │ │ ├── cross_list_page.dart │ │ │ │ ├── cross_list_vm.dart │ │ │ │ └── tab_item_widget.dart │ │ │ ├── custom_dialog_page.dart │ │ │ ├── eye_3d.dart │ │ │ ├── float_layer_widget │ │ │ │ └── shake_float_widget.dart │ │ │ ├── little_util_page.dart │ │ │ ├── part_refresh_page.dart │ │ │ ├── request_permission_page.dart │ │ │ ├── scroll_page.dart │ │ │ ├── selector_demo_page.dart │ │ │ ├── simple_list │ │ │ │ └── simple_list_page.dart │ │ │ ├── test_android_page.dart │ │ │ └── timer_page.dart │ │ ├── other_demo_page.dart │ │ ├── route_anim │ │ │ ├── fade_page.dart │ │ │ ├── route_animation_page.dart │ │ │ ├── scale_page.dart │ │ │ └── slide_page.dart │ │ ├── slide_out_page.dart │ │ └── start │ │ │ ├── pages │ │ │ ├── page_1.dart │ │ │ ├── page_2.dart │ │ │ └── page_3.dart │ │ │ ├── start_page.dart │ │ │ └── with_value_page.dart │ ├── demo_widget │ │ └── custom_drawer.dart │ ├── exception │ │ └── exception_page.dart │ └── mine │ │ └── entity │ │ └── user_entity.dart └── service_api │ ├── bedrock_repository_proxy.dart │ └── section_one_api.dart ├── pubspec.lock ├── pubspec.yaml ├── test └── widget_test.dart └── update_log.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Exceptions to above rules. 37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 38 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 27321ebbad34b0a3fafe99fac037102196d655ff 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /alipwechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/alipwechat.png -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 31 30 | buildToolsVersion '30.0.2' 31 | 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/kotlin' 34 | } 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "com.lijiaqi.flutter_bedrock" 43 | minSdkVersion 21 44 | targetSdkVersion 31 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 48 | } 49 | 50 | buildTypes { 51 | release { 52 | // TODO: Add your own signing config for the release build. 53 | // Signing with the debug keys for now, so `flutter run --release` works. 54 | signingConfig signingConfigs.debug 55 | } 56 | } 57 | 58 | } 59 | 60 | flutter { 61 | source '../..' 62 | } 63 | 64 | dependencies { 65 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 66 | implementation 'androidx.appcompat:appcompat:1.2.0' 67 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 68 | testImplementation 'junit:junit:4.12' 69 | androidTestImplementation 'androidx.test:runner:1.1.1' 70 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 71 | } 72 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/debug/lib/l10n/intl_zh.arb: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /android/app/src/main/java/com/lijiaqi/bedrock/AppCrashHandler.java: -------------------------------------------------------------------------------- 1 | package com.lijiaqi.bedrock; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | /** 7 | * @author LiJiaqi 8 | * @date 2020/12/12 9 | * Description: 10 | * handle the thread exception,and avoid app crash. 11 | * in fact,this Singleton doesn't make much sense...but,easy to use. 12 | */ 13 | class AppCrashHandler implements Thread.UncaughtExceptionHandler { 14 | 15 | static final String TAG = "Bedrock_Crash_Handler:"; 16 | final String slash = "------------------"; 17 | 18 | static private volatile AppCrashHandler singleton; 19 | 20 | public static AppCrashHandler getInstance(Context context){ 21 | if(singleton == null){ 22 | synchronized (AppCrashHandler.class){ 23 | if(singleton == null){ 24 | singleton = new AppCrashHandler(context); 25 | } 26 | } 27 | } 28 | return singleton; 29 | } 30 | private Context context; 31 | AppCrashHandler(Context context){ 32 | this.context = context; 33 | } 34 | 35 | ///处理子线程异常 36 | public void initChildThreadProtect(){ 37 | Thread.setDefaultUncaughtExceptionHandler(this); 38 | } 39 | ///处理子线程异常 40 | @Override 41 | public void uncaughtException(Thread t, Throwable e) { 42 | Log.d(TAG, slash+TAG+slash); 43 | Log.d(TAG,t.getName()); 44 | e.printStackTrace(); 45 | Log.d(TAG, slash+TAG+slash); 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/lijiaqi/bedrock/BaseApp.java: -------------------------------------------------------------------------------- 1 | package com.lijiaqi.bedrock; 2 | 3 | import com.lijiaqi.bedrock.protect.AndroidPlatformProtect; 4 | import com.lijiaqi.bedrock.protect.handler.DefaultActivityExceptionHandler; 5 | 6 | import io.flutter.app.FlutterApplication; 7 | 8 | /** 9 | * @author LiJiaqi 10 | * @date 2020/12/12 11 | * Description: 12 | * 13 | * Tip 1: 14 | * 如果你有闪屏页优化需求,可以参考这篇文章,也许对你有所帮助。 15 | * https://juejin.cn/post/6913360134429376525 16 | */ 17 | public class BaseApp extends FlutterApplication { 18 | @Override 19 | public void onCreate() { 20 | super.onCreate(); 21 | 22 | // 如果是纯flutter项目, 23 | // 可以考虑注释掉这两个保护[protectUIThread]和[protectActivityStart] 24 | // 混合项目的话,可以考虑开启 25 | AndroidPlatformProtect.initProtect(new DefaultActivityExceptionHandler()) 26 | ///处理UI线程的异常 27 | //.protectUIThread() 28 | ///处理 activity生命周期的异常 29 | //注释原因: 功能,使用了反射。 30 | // 在混淆下可能会无效或者出现无法预知的问题,建议开启后反复测试 31 | //.protectActivityStart() 32 | ///处理子线程异常 33 | .protectChildThread() 34 | .init(this); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/lijiaqi/bedrock/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.lijiaqi.bedrock; 2 | 3 | 4 | /* 5 | * Author : LiJiqqi 6 | * Date : 2020/7/6 7 | */ 8 | 9 | import androidx.annotation.NonNull; 10 | 11 | import com.lijiaqi.bedrock.plugin.BedrockPlugin; 12 | 13 | import io.flutter.embedding.android.FlutterActivity; 14 | import io.flutter.embedding.engine.FlutterEngine; 15 | import io.flutter.plugins.GeneratedPluginRegistrant; 16 | 17 | public class MainActivity extends FlutterActivity { 18 | @Override 19 | public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { 20 | GeneratedPluginRegistrant.registerWith(flutterEngine); 21 | flutterEngine.getPlugins().add(new BedrockPlugin(this)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/lijiaqi/bedrock/protect/ActivityStackManager.java: -------------------------------------------------------------------------------- 1 | package com.lijiaqi.bedrock.protect; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.os.Bundle; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.annotation.Nullable; 9 | 10 | import java.util.LinkedList; 11 | 12 | /** 13 | * @author LiJiaqi 14 | * @date 2020/12/13 15 | * Description: 16 | */ 17 | public class ActivityStackManager{ 18 | 19 | private static volatile ActivityStackManager singleton; 20 | public static ActivityStackManager getInstance(){ 21 | if(singleton == null){ 22 | synchronized (ActivityStackManager.class){ 23 | if (singleton == null){ 24 | singleton = new ActivityStackManager(); 25 | } 26 | } 27 | } 28 | return singleton; 29 | } 30 | 31 | final private LinkedList activities = new LinkedList<>(); 32 | 33 | public void init(Application app){ 34 | app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() { 35 | @Override 36 | public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle bundle) { 37 | activities.add(activity); 38 | } 39 | 40 | @Override 41 | public void onActivityStarted(@NonNull Activity activity) { 42 | 43 | } 44 | 45 | @Override 46 | public void onActivityResumed(@NonNull Activity activity) { 47 | 48 | } 49 | 50 | @Override 51 | public void onActivityPaused(@NonNull Activity activity) { 52 | 53 | } 54 | 55 | @Override 56 | public void onActivityStopped(@NonNull Activity activity) { 57 | 58 | } 59 | 60 | @Override 61 | public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle bundle) { 62 | 63 | } 64 | 65 | @Override 66 | public void onActivityDestroyed(@NonNull Activity activity) { 67 | activities.remove(activity); 68 | 69 | } 70 | }); 71 | } 72 | 73 | public Activity exceptionBirthplaceActivity(Exception e){ 74 | if(activities.isEmpty()) return null; 75 | String className = activities.getLast().getClass().getName(); 76 | if(e.getStackTrace()[0].getClassName().contains(className)){ 77 | return activities.getLast(); 78 | } 79 | return null; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/lijiaqi/bedrock/protect/AndroidPlatformProtect.java: -------------------------------------------------------------------------------- 1 | package com.lijiaqi.bedrock.protect; 2 | 3 | import android.app.Application; 4 | 5 | import com.lijiaqi.bedrock.protect.handler.ActivityExceptionHandler; 6 | import com.lijiaqi.bedrock.protect.zone.ActivityStartProtect; 7 | import com.lijiaqi.bedrock.protect.zone.ChildThreadProtect; 8 | import com.lijiaqi.bedrock.protect.zone.UIThreadProtect; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * @author LiJiaqi 15 | * @date 2020/12/13 16 | * Description: 17 | * 确保android 平台的异常不会导致应用崩溃(除了系统异常,或连续性异常) 18 | */ 19 | 20 | 21 | 22 | public class AndroidPlatformProtect { 23 | 24 | private final List protectStrategyList = new ArrayList<>(); 25 | 26 | private static volatile AndroidPlatformProtect singleton; 27 | 28 | public static AndroidPlatformProtect initProtect(ActivityExceptionHandler exceptionHandler){ 29 | if(singleton == null){ 30 | synchronized (AndroidPlatformProtect.class){ 31 | if(singleton == null){ 32 | singleton = new AndroidPlatformProtect(exceptionHandler); 33 | } 34 | } 35 | } 36 | return singleton; 37 | } 38 | private AndroidPlatformProtect(ActivityExceptionHandler exceptionHandler){ 39 | if(exceptionHandler == null) throw new RuntimeException("ActivityExceptionHandler is null," + 40 | " you must set a exceptionHandler, that subclass of the ActivityExceptionHandler."); 41 | this.exceptionHandler = exceptionHandler; 42 | } 43 | 44 | public static AndroidPlatformProtect getInstance(){ 45 | if(singleton == null) throw new RuntimeException("you need call method 'initProtect' first than get a Instance."); 46 | return singleton; 47 | } 48 | 49 | ///activity 异常善后 50 | private final ActivityExceptionHandler exceptionHandler; 51 | public ActivityExceptionHandler getExceptionHandler(){ 52 | return exceptionHandler; 53 | } 54 | 55 | public AndroidPlatformProtect protectActivityStart(){ 56 | protectStrategyList.add(new ActivityStartProtect()); 57 | return singleton; 58 | } 59 | 60 | public AndroidPlatformProtect protectUIThread(){ 61 | protectStrategyList.add(new UIThreadProtect()); 62 | return singleton; 63 | } 64 | 65 | 66 | public AndroidPlatformProtect protectChildThread(){ 67 | protectStrategyList.add(new ChildThreadProtect()); 68 | return singleton; 69 | } 70 | 71 | 72 | public void init(Application app){ 73 | if(protectStrategyList.isEmpty()){ 74 | throw new RuntimeException("You must add protect strategy by method \"protectXXX\" before \"init()\""); 75 | } 76 | for(IProtect protect : protectStrategyList){ 77 | protect.protect(app); 78 | } 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/lijiaqi/bedrock/protect/IProtect.java: -------------------------------------------------------------------------------- 1 | package com.lijiaqi.bedrock.protect; 2 | 3 | import android.app.Application; 4 | 5 | /** 6 | * @author LiJiaqi 7 | * @date 2020/12/13 8 | * Description: 9 | */ 10 | public interface IProtect { 11 | void protect(Application application); 12 | } 13 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/lijiaqi/bedrock/protect/handler/ActivityExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.lijiaqi.bedrock.protect.handler; 2 | 3 | 4 | /* 5 | * Author : LiJiqqi 6 | * Date : 2020/12/16 7 | * [protectUIThread]和[protectActivityStart] 异常善后类 8 | */ 9 | 10 | import android.app.Activity; 11 | 12 | public abstract class ActivityExceptionHandler { 13 | 14 | public abstract void onException(Activity activity,Exception e); 15 | public abstract void onChildThreadException(Thread t, Throwable e); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/lijiaqi/bedrock/protect/handler/DefaultActivityExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.lijiaqi.bedrock.protect.handler; 2 | 3 | 4 | /* 5 | * Author : LiJiqqi 6 | * Date : 2020/12/16 7 | */ 8 | 9 | import android.app.Activity; 10 | import android.util.Log; 11 | 12 | 13 | 14 | public class DefaultActivityExceptionHandler extends ActivityExceptionHandler { 15 | static private final String TAG = "Exception_Handler:"; 16 | private final String slash = "------------------"; 17 | /// 18 | 19 | /** 20 | *[protectUIThread]和[protectActivityStart]异常将会调用这个方法 21 | * 22 | * {@link com.lijiaqi.bedrock.protect.zone.ActivityStartProtect} 23 | * {@link com.lijiaqi.bedrock.protect.zone.UIThreadProtect} 24 | */ 25 | 26 | @Override 27 | public void onException(Activity activity, Exception e) { 28 | Log.i(TAG,"==========Activity(UI thread) 发生了一个异常========="); 29 | if(e != null)Log.d(TAG,""+e.getMessage()); 30 | if(activity != null) activity.finish(); 31 | } 32 | 33 | 34 | 35 | /** 36 | *子线程异常会调用此方法 , 37 | * 注意,此方法内的线程切换 38 | * {@link com.lijiaqi.bedrock.protect.zone.ChildThreadProtect} 39 | */ 40 | @Override 41 | public void onChildThreadException(Thread t, Throwable e) { 42 | Log.d(TAG, slash+TAG+slash); 43 | Log.d(TAG,t.getName()); 44 | e.printStackTrace(); 45 | Log.d(TAG, slash+TAG+slash); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/lijiaqi/bedrock/protect/zone/ChildThreadProtect.java: -------------------------------------------------------------------------------- 1 | package com.lijiaqi.bedrock.protect.zone; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.util.Log; 6 | 7 | import com.lijiaqi.bedrock.protect.AndroidPlatformProtect; 8 | import com.lijiaqi.bedrock.protect.IProtect; 9 | import com.lijiaqi.bedrock.protect.handler.ActivityExceptionHandler; 10 | 11 | 12 | /** 13 | * @author LiJiaqi 14 | * @date 2020/12/13 15 | * Description: 16 | * 处理子线程触发的异常 17 | */ 18 | 19 | public class ChildThreadProtect implements IProtect { 20 | @Override 21 | public void protect(Application application) { 22 | ChildThreadHandler.getInstance(application).initChildThreadProtect(); 23 | } 24 | } 25 | 26 | /// handle the thread exception,and avoid app crash. 27 | /// in fact,this Singleton doesn't make much sense...but,easy to use. 28 | 29 | class ChildThreadHandler implements Thread.UncaughtExceptionHandler{ 30 | 31 | static private volatile ChildThreadHandler singleton; 32 | 33 | public static ChildThreadHandler getInstance(Context context){ 34 | if(singleton == null){ 35 | synchronized (ChildThreadHandler.class){ 36 | if(singleton == null){ 37 | singleton = new ChildThreadHandler(context); 38 | } 39 | } 40 | } 41 | return singleton; 42 | } 43 | private final ActivityExceptionHandler exceptionHandler; 44 | private final Context context; 45 | ChildThreadHandler(Context context){ 46 | this.context = context; 47 | this.exceptionHandler = AndroidPlatformProtect.getInstance().getExceptionHandler(); 48 | if(exceptionHandler == null) throw new RuntimeException("ActivityExceptionHandler is null," + 49 | " you must set a exceptionHandler, that subclass of the ActivityExceptionHandler."); 50 | } 51 | 52 | ///处理子线程异常 53 | public void initChildThreadProtect(){ 54 | Thread.setDefaultUncaughtExceptionHandler(this); 55 | } 56 | ///处理子线程异常 57 | @Override 58 | public void uncaughtException(Thread t, Throwable e) { 59 | exceptionHandler.onChildThreadException(t, e); 60 | } 61 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/lijiaqi/bedrock/test/TestPage1.java: -------------------------------------------------------------------------------- 1 | package com.lijiaqi.bedrock.test; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.app.Activity; 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | 10 | import com.lijiaqi.flutter_bedrock.R; 11 | 12 | public class TestPage1 extends Activity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_test_page1); 18 | findViewById(R.id.btn_1).setOnClickListener(new View.OnClickListener() { 19 | @Override 20 | public void onClick(View v) { 21 | throw new RuntimeException("点击事件触发了一个UI异常"); 22 | } 23 | }); 24 | findViewById(R.id.btn_2).setOnClickListener(new View.OnClickListener() { 25 | @Override 26 | public void onClick(View v) { 27 | startActivity(new Intent(TestPage1.this,TestPage2.class)); 28 | } 29 | }); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/lijiaqi/bedrock/test/TestPage2.java: -------------------------------------------------------------------------------- 1 | package com.lijiaqi.bedrock.test; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.app.Activity; 6 | import android.os.Bundle; 7 | 8 | import com.lijiaqi.flutter_bedrock.R; 9 | 10 | public class TestPage2 extends Activity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_test_page2); 16 | } 17 | 18 | @Override 19 | protected void onResume() { 20 | super.onResume(); 21 | throw new RuntimeException("TestPage2 : onResume 发生了一个异常"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/lijiaqi/bedrock/test/readme: -------------------------------------------------------------------------------- 1 | test为测试包,可以直接删除。 2 | This a test package(.test), you can just delete it. -------------------------------------------------------------------------------- /android/app/src/main/java/com/lijiaqi/bedrock/util/ReflexUtil.java: -------------------------------------------------------------------------------- 1 | package com.lijiaqi.bedrock.util; 2 | 3 | import java.lang.reflect.Field; 4 | import java.lang.reflect.Method; 5 | 6 | /** 7 | * @author LiJiaqi 8 | * @date 2020/12/13 9 | * Description: 10 | */ 11 | 12 | public class ReflexUtil { 13 | 14 | public static Field getField(Class clazz, String fieldName){ 15 | try { 16 | Field targetField = clazz.getDeclaredField(fieldName); 17 | targetField.setAccessible(true); 18 | return targetField; 19 | 20 | } catch (NoSuchFieldException e) { 21 | e.printStackTrace(); 22 | } 23 | return null; 24 | } 25 | 26 | 27 | 28 | public static Method getMethod(Class clazz, String methodName, String ... params){ 29 | try{ 30 | Method targetMethod = clazz.getMethod(methodName); 31 | targetMethod.setAccessible(true); 32 | return targetMethod; 33 | } catch (NoSuchMethodException e) { 34 | e.printStackTrace(); 35 | } 36 | return null; 37 | } 38 | 39 | private static Field[] getFields(Class clazz){ 40 | try { 41 | 42 | Field[] targetFields = clazz.getDeclaredFields(); 43 | for(Field field : targetFields){ 44 | field.setAccessible(true); 45 | } 46 | return targetFields; 47 | } catch (Exception e) { 48 | e.printStackTrace(); 49 | } 50 | return null; 51 | } 52 | 53 | 54 | 55 | public static void copyTo(T fromObj,T toObj)throws Exception{ 56 | if(fromObj == null || toObj == null)throw new NullPointerException(); 57 | Field[] fields = getFields(fromObj.getClass()); 58 | for(Field field : fields){ 59 | field.setAccessible(true); 60 | field.set(toObj, field.get(fromObj)); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/lijiaqi/flutter_bedrock/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.lijiaqi.flutter_bedrock 2 | 3 | import androidx.annotation.NonNull; 4 | import io.flutter.embedding.android.FlutterActivity 5 | import io.flutter.embedding.engine.FlutterEngine 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterActivity() { 9 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 10 | GeneratedPluginRegistrant.registerWith(flutterEngine); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/activity_test_page1.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 17 |