├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── app ├── .gitignore ├── build.gradle ├── coolfar_sdk.jks ├── libs │ └── keepalive-1.1.3.aar ├── proguard-rules.pro ├── release │ ├── app-release.apk │ └── output.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sdk │ │ └── coolfar_sdk │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sdk │ │ │ └── coolfar_sdk │ │ │ ├── MainActivity.java │ │ │ └── MyService.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── sdk │ └── coolfar_sdk │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── keepalive ├── .gitignore ├── build.gradle ├── excludeAar.gradle ├── keepalive-1.1.5.aar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sdk │ │ └── keepbackground │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sdk │ │ │ └── keepbackground │ │ │ ├── KeepAliveApplication.java │ │ │ ├── singlepixel │ │ │ ├── ScreenManager.java │ │ │ ├── ScreenReceiverUtil.java │ │ │ └── SinglePixelActivity.java │ │ │ ├── sync │ │ │ ├── Authenticator.java │ │ │ ├── AuthenticatorService.java │ │ │ ├── StubProvider.java │ │ │ ├── SyncAdapter.java │ │ │ └── SyncService.java │ │ │ ├── utils │ │ │ ├── AndroidUtil.java │ │ │ ├── AppUtils.java │ │ │ ├── FileUtils.java │ │ │ ├── ForegroundNotificationUtils.java │ │ │ ├── JumpWindowPemiManagement.java │ │ │ ├── NotificationSetUtil.java │ │ │ ├── RandomUtil.java │ │ │ ├── SPUtils.java │ │ │ ├── SpManager.java │ │ │ └── TimeUtils.java │ │ │ ├── watch │ │ │ ├── AbsServiceConnection.java │ │ │ ├── JobSchedulerService.java │ │ │ ├── PlayMusicService.java │ │ │ ├── WakeUpReceiver.java │ │ │ ├── WatchDogService.java │ │ │ └── WatchProcessPrefHelper.java │ │ │ └── work │ │ │ ├── AbsWorkService.java │ │ │ ├── DaemonEnv.java │ │ │ └── IntentWrapper.java │ └── res │ │ ├── drawable │ │ └── icon1.webp │ │ ├── raw │ │ └── no_notice.mp3 │ │ ├── values │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── net_config.xml │ └── test │ └── java │ └── com │ └── sdk │ └── keepbackground │ └── ExampleUnitTest.java ├── readme.md └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | 5 | compileSdkVersion 29 6 | buildToolsVersion "29.0.1" 7 | defaultConfig { 8 | applicationId "com.sdk.coolfar_sdk" 9 | minSdkVersion 18 10 | targetSdkVersion 29 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 19 | debuggable = true 20 | } 21 | } 22 | repositories { 23 | flatDir { 24 | dirs 'libs' 25 | } 26 | } 27 | } 28 | 29 | dependencies { 30 | implementation fileTree(dir: 'libs', include: ['*.jar']) 31 | // implementation 'androidx.appcompat:appcompat:1.0.2' 32 | // implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 33 | testImplementation 'junit:junit:4.12' 34 | androidTestImplementation 'androidx.test:runner:1.2.0' 35 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 36 | api project(':keepalive') 37 | // compile(name:'keepalive-1.1.3', ext:'aar') 38 | } 39 | -------------------------------------------------------------------------------- /app/coolfar_sdk.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/app/coolfar_sdk.jks -------------------------------------------------------------------------------- /app/libs/keepalive-1.1.3.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/app/libs/keepalive-1.1.3.aar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/release/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/app/release/app-release.apk -------------------------------------------------------------------------------- /app/release/output.json: -------------------------------------------------------------------------------- 1 | [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] -------------------------------------------------------------------------------- /app/src/androidTest/java/com/sdk/coolfar_sdk/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.sdk.coolfar_sdk; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.InstrumentationRegistry; 6 | import androidx.test.runner.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getTargetContext(); 24 | 25 | assertEquals("com.sdk.coolfar_sdk", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/sdk/coolfar_sdk/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.sdk.coolfar_sdk; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.os.Bundle; 6 | 7 | 8 | import com.sdk.keepbackground.work.DaemonEnv; 9 | import com.sdk.keepbackground.work.IntentWrapper; 10 | 11 | 12 | public class MainActivity extends Activity { 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_main); 17 | initLocaService(); 18 | } 19 | 20 | private void initLocaService() { 21 | //初始化 22 | DaemonEnv.init(this); 23 | //請求用戶忽略电池优化 24 | String reason="轨迹跟踪服务的持续运行"; 25 | DaemonEnv.whiteListMatters(this, reason); 26 | //启动work服务 27 | DaemonEnv.startServiceSafelyWithData(MainActivity.this,MyService.class); 28 | } 29 | 30 | //防止华为机型未加入白名单时按返回键回到桌面再锁屏后几秒钟进程被杀 31 | @Override 32 | public void onBackPressed() { 33 | IntentWrapper.onBackPressed(this); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/sdk/coolfar_sdk/MyService.java: -------------------------------------------------------------------------------- 1 | package com.sdk.coolfar_sdk; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.Handler; 6 | import android.os.IBinder; 7 | import android.os.Messenger; 8 | 9 | import com.sdk.keepbackground.work.AbsWorkService; 10 | 11 | public class MyService extends AbsWorkService { 12 | public MyService() { 13 | } 14 | 15 | private boolean mIsRunning; 16 | 17 | @Override 18 | public Boolean needStartWorkService() { 19 | return true; 20 | } 21 | 22 | /** 23 | * 开启具体业务,实际执行与isWorkRunning方法返回值有关, 24 | * 当isWorkRunning返回false才会执行该方法 25 | */ 26 | @Override 27 | public void startWork() { 28 | doWork(); 29 | } 30 | 31 | private void doWork(){ 32 | new Thread(new Runnable() { 33 | @Override 34 | public void run() { 35 | // do something 36 | mIsRunning=true; 37 | } 38 | }).start(); 39 | } 40 | /** 41 | * 业务执行完成需要进行的操作 42 | * 手动停止服务sendStopWorkBroadcast时回调 43 | */ 44 | @Override 45 | public void stopWork() { 46 | 47 | } 48 | 49 | /** 50 | * 任务是否正在运行? 由实现者处理 51 | * @return 任务正在运行, true; 任务当前不在运行, false; 无法判断, 什么也不做, null. 52 | */ 53 | @Override 54 | public Boolean isWorkRunning() { 55 | return mIsRunning; 56 | } 57 | 58 | /** 59 | * 绑定远程service 可根据实际业务情况是否绑定自定义binder或者直接返回默认binder 60 | */ 61 | @Override 62 | public IBinder onBindService(Intent intent, Void aVoid) { 63 | return new Messenger(new Handler()).getBinder(); 64 | } 65 | 66 | /** 67 | * 服务被kill 68 | */ 69 | @Override 70 | public void onServiceKilled() { 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Coolfar_sdk 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/sdk/coolfar_sdk/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.sdk.coolfar_sdk; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.1.3' 11 | classpath'com.github.dcendents:android-maven-gradle-plugin:2.1' 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | maven { url 'https://jitpack.io' } 23 | 24 | } 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | #org.gradle.jvmargs=-XX\:MaxHeapSize\=2048m -Xmx4608M 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | # AndroidX package structure to make it clearer which packages are bundled with the 16 | # Android operating system, and which are packaged with your app's APK 17 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 18 | android.useAndroidX=true 19 | # Automatically convert third-party libraries to use AndroidX 20 | android.enableJetifier=true 21 | android.enableR8=false 22 | org.gradle.configureondemand=false -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Aug 05 17:54:22 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /keepalive/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /keepalive/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | group='shihoo.wang' 4 | 5 | //aar名字 6 | def SDK_NAME = "keepalive-1.1.5.aar" 7 | 8 | //去除多余类配置 9 | configurations.maybeCreate("exclude") 10 | artifacts.add("exclude", file(SDK_NAME)) 11 | //先生成aar后再加上去除配置 12 | apply from: "${project.projectDir.absoluteFile}\\excludeAar.gradle" 13 | 14 | android { 15 | compileSdkVersion 26 16 | buildToolsVersion "29.0.1" 17 | 18 | 19 | defaultConfig { 20 | minSdkVersion 18 21 | targetSdkVersion 26 22 | versionCode 2 23 | versionName "1.1" 24 | 25 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 26 | 27 | } 28 | 29 | buildTypes { 30 | release { 31 | //开启代码混淆 32 | minifyEnabled true 33 | //Zipalign优化 34 | zipAlignEnabled true 35 | // 移除无用的resource文件 36 | // shrinkResources true 37 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 38 | } 39 | } 40 | 41 | //可根据自己需求更改-+.693 gradlew againMakeJar 42 | task againMakeJar(type: Copy) { 43 | delete 'mylib/keeplive.aar' //删除之前的旧jar包 44 | from('build/outputs/aar/') //从这个目录下取出默认jar包 45 | into('/') //将jar包输出到指定目录下 46 | include('keepalive-release.aar') 47 | rename('keepalive-release.aar', SDK_NAME) //自定义jar包的名字 48 | } 49 | againMakeJar.dependsOn(build) 50 | } 51 | ext.rxJavaVersion = '2.1.2' 52 | ext.rxAndroidVersion = '2.0.1' 53 | dependencies { 54 | implementation fileTree(dir: 'libs', include: ['*.jar']) 55 | testImplementation 'junit:junit:4.12' 56 | api 'com.android.support:appcompat-v7:26.1.0' 57 | 58 | // implementation "io.reactivex.rxjava2:rxjava:${rxJavaVersion}" 59 | // implementation "io.reactivex.rxjava2:rxandroid:${rxAndroidVersion}" 60 | } 61 | -------------------------------------------------------------------------------- /keepalive/excludeAar.gradle: -------------------------------------------------------------------------------- 1 | configurations { 2 | exclude 3 | } 4 | 5 | //需要过滤的包名 6 | def excludePackages = [] 7 | //需要过滤的类(需要全类名) 8 | def excludeClasses = [] 9 | 10 | task printMsg { 11 | group "Siy" 12 | 13 | doLast { 14 | println getExcludePackageRegex(excludePackages) 15 | println getExcludeClassRegex(excludeClasses) 16 | } 17 | } 18 | 19 | 20 | def unZipAarFile = new File(buildDir, "unzipaar") 21 | def unZipJarFile = new File(buildDir, 'unzipjar') 22 | def excludeAarFile = new File(buildDir, "excludeaar") 23 | def excludeAarName = "exclude_${getDefaultAar().name.replaceFirst(~/\.[^.]+$/, '')}" 24 | 25 | def getDefaultAar() { 26 | Configuration c = configurations.getByName("exclude") 27 | def files = c.artifacts.files.filter { 28 | it.name ==~ /.*\.aar/ 29 | } 30 | 31 | def file = null 32 | if (!files.empty) { 33 | file = files[0] 34 | } 35 | return file 36 | } 37 | 38 | task deleteDir(type: Delete) { 39 | delete unZipAarFile, unZipJarFile, excludeAarFile 40 | } 41 | 42 | task unZipAar(type: Copy) { 43 | def zipFile = getDefaultAar() 44 | def outputDir = unZipAarFile 45 | from zipTree(zipFile) 46 | into outputDir 47 | } 48 | unZipAar.dependsOn deleteDir 49 | 50 | task unzipJar(type: Copy) { 51 | def zipFile = new File(unZipAarFile, 'classes.jar') 52 | def outputDir = unZipJarFile 53 | from zipTree(zipFile) 54 | into outputDir 55 | } 56 | 57 | unzipJar.dependsOn unZipAar 58 | 59 | task zipJar(type: Jar) { 60 | baseName = 'classes' 61 | from unZipJarFile 62 | destinationDir unZipAarFile 63 | exclude getExcludePackageRegex(excludePackages) 64 | exclude getExcludeClassRegex(excludeClasses) 65 | } 66 | 67 | zipJar.dependsOn unzipJar 68 | 69 | task excludeAar(type: Zip) { 70 | group 'Siy' 71 | description '生成一个过滤之后的aar包' 72 | baseName excludeAarName 73 | extension "aar" 74 | from unZipAarFile 75 | destinationDir excludeAarFile 76 | } 77 | 78 | excludeAar.dependsOn zipJar 79 | 80 | artifacts { 81 | exclude excludeAar 82 | } 83 | 84 | 85 | static def getExcludePackageRegex(def packages) { 86 | packages?.collect { 87 | it?.replace('.', '\\')?.plus("\\**") 88 | } 89 | } 90 | 91 | static def getExcludeClassRegex(def classes) { 92 | classes?.collect { 93 | it?.replace('.', '\\')?.plus(".class") 94 | } 95 | } -------------------------------------------------------------------------------- /keepalive/keepalive-1.1.5.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/keepalive/keepalive-1.1.5.aar -------------------------------------------------------------------------------- /keepalive/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | #-----------基本配置-------------- 2 | # 代码混淆压缩比,在0~7之间,默认为5,一般不需要改 3 | -optimizationpasses 5 4 | 5 | # 混淆时不使用大小写混合,混淆后的类名为小写 6 | -dontusemixedcaseclassnames 7 | 8 | # 指定不去忽略非公共的库的类 9 | -dontskipnonpubliclibraryclasses 10 | 11 | # 指定不去忽略非公共的库的类的成员 12 | -dontskipnonpubliclibraryclassmembers 13 | 14 | # 不做预校验,可加快混淆速度 15 | # preverify是proguard的4个步骤之一 16 | # Android不需要preverify,去掉这一步可以加快混淆速度 17 | -dontpreverify 18 | 19 | # 不优化输入的类文件 20 | -dontoptimize 21 | 22 | # 混淆时生成日志文件,即映射文件 23 | -verbose 24 | 25 | # 指定映射文件的名称 26 | -printmapping proguardMapping.txt 27 | 28 | #混淆时所采用的算法 29 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 30 | 31 | # 保护代码中的Annotation不被混淆 32 | -keepattributes *Annotation* 33 | 34 | # 忽略警告 35 | -ignorewarning 36 | 37 | # 保护泛型不被混淆 38 | -keepattributes Signature 39 | 40 | # 抛出异常时保留代码行号 41 | -keepattributes SourceFile,LineNumberTable 42 | 43 | #-----------需要保留的东西-------------- 44 | # 保留所有的本地native方法不被混淆 45 | -keepclasseswithmembernames class * { 46 | native ; 47 | } 48 | 49 | # 保留了继承自Activity、Application、Fragment这些类的子类 50 | -keep public class * extends android.app.Fragment 51 | -keep public class * extends android.app.Activity 52 | -keep public class * extends android.app.Application 53 | -keep public class * extends android.app.Service 54 | -keep public class * extends android.content.BroadcastReceiver 55 | -keep public class * extends android.content.ContentProvider 56 | -keep public class * extends android.app.backup.BackupAgentHelper 57 | -keep public class * extends android.preference.Preference 58 | -keep public class * extends android.view.View 59 | -keep public class com.android.vending.licensing.ILicensingService 60 | 61 | # support-v4 62 | -dontwarn android.support.v4.** 63 | -keep class android.support.v4.** { *; } 64 | -keep interface android.support.v4.** { *; } 65 | -keep public class * extends android.support.v4.** 66 | # support-v7 67 | -dontwarn android.support.v7.** #去掉警告 68 | -keep class android.support.v7.** { *; } #过滤android.support.v7 69 | -keep interface android.support.v7.app.** { *; } 70 | -keep public class * extends android.support.v7.** 71 | 72 | #----------------保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在------------------------------------ 73 | -keepclasseswithmembernames class * { 74 | public (android.content.Context, android.util.AttributeSet); 75 | } 76 | 77 | -keepclasseswithmembernames class * { 78 | public (android.content.Context, android.util.AttributeSet, int); 79 | } 80 | 81 | # 保持自定义控件类不被混淆,指定格式的构造方法不去混淆 82 | -keepclasseswithmembers class * { 83 | public (android.content.Context); 84 | public (android.content.Context, android.util.AttributeSet); 85 | public (android.content.Context, android.util.AttributeSet, int); 86 | } 87 | 88 | # 保持自定义控件类不被混淆 89 | -keep public class * extends android.view.View { 90 | public (android.content.Context); 91 | public (android.content.Context, android.util.AttributeSet); 92 | public (android.content.Context, android.util.AttributeSet, int); 93 | public void set*(...); 94 | *** get*(); 95 | } 96 | 97 | # 保留在Activity中的方法参数是View的方法 98 | # 从而我们在layout里边编写onClick就不会被影响 99 | -keepclassmembers class * extends android.app.Activity { 100 | public void *(android.view.View); 101 | } 102 | 103 | # 保留枚举 enum 类不被混淆 104 | -keepclassmembers enum * { 105 | public static **[] values(); 106 | public static ** valueOf(java.lang.String); 107 | } 108 | 109 | # 保留 Parcelable 不被混淆 110 | -keep class * implements android.os.Parcelable { 111 | public static final android.os.Parcelable$Creator *; 112 | } 113 | 114 | # 保留 Serializable 不被混淆 115 | -keepnames class * implements java.io.Serializable 116 | -keepclassmembers class * implements java.io.Serializable { 117 | static final long serialVersionUID; 118 | private static final java.io.ObjectStreamField[] serialPersistentFields; 119 | !static !transient ; 120 | !private ; 121 | !private ; 122 | private void writeObject(java.io.ObjectOutputStream); 123 | private void readObject(java.io.ObjectInputStream); 124 | java.lang.Object writeReplace(); 125 | java.lang.Object readResolve(); 126 | } 127 | 128 | # 不混淆资源类 129 | -keepclassmembers class **.R$* { *; } 130 | 131 | # 对于带有回调函数onXXEvent()的,不能被混淆 132 | -keepclassmembers class * { 133 | void *(**On*Event); 134 | } 135 | # WebView 136 | -keepclassmembers class fqcn.of.javascript.interface.for.Webview { 137 | public *; 138 | } 139 | -keepclassmembers class * extends android.webkit.WebViewClient { 140 | public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap); 141 | public boolean *(android.webkit.WebView, java.lang.String); 142 | } 143 | -keepclassmembers class * extends android.webkit.WebViewClient { 144 | public void *(android.webkit.WebView, jav.lang.String); 145 | } 146 | 147 | # 保留实体类和成员不被混淆(根据具体情况修改entity的路径) 148 | -keep class com.smart.tvpos.bean.**{*;} 149 | 150 | 151 | 152 | #高德地图混淆配置 153 | # 3D 地图 V5.0.0之前: 154 | -keep class com.amap.api.maps.**{*;} 155 | -keep class com.autonavi.amap.mapcore.*{*;} 156 | -keep class com.amap.api.trace.**{*;} 157 | 158 | # 3D 地图 V5.0.0之后: 159 | -keep class com.amap.api.maps.**{*;} 160 | -keep class com.autonavi.**{*;} 161 | -keep class com.amap.api.trace.**{*;} 162 | 163 | # 定位 164 | -keep class com.amap.api.location.**{*;} 165 | -keep class com.amap.api.fence.**{*;} 166 | -keep class com.autonavi.aps.amapapi.model.**{*;} 167 | 168 | # 搜索 169 | -keep class com.amap.api.services.**{*;} 170 | 171 | # 2D地图 172 | -keep class com.amap.api.maps2d.**{*;} 173 | -keep class com.amap.api.mapcore2d.**{*;} 174 | 175 | #导航 176 | -keep class com.amap.api.navi.**{*;} 177 | -keep class com.autonavi.**{*;} 178 | 179 | -keep class com.sdk.keepbackground.work.** { *; } 180 | 181 | -------------------------------------------------------------------------------- /keepalive/src/androidTest/java/com/sdk/keepbackground/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground; 2 | 3 | ///** 4 | // * Instrumented test, which will execute on an Android device. 5 | // * 6 | // * @see Testing documentation 7 | // */ 8 | //@RunWith(AndroidJUnit4.class) 9 | public class ExampleInstrumentedTest { 10 | // @Test 11 | // public void useAppContext() { 12 | // // Context of the app under test. 13 | // Context appContext = InstrumentationRegistry.getTargetContext(); 14 | // 15 | // assertEquals("com.sdk.keepbackground.test", appContext.getPackageName()); 16 | // } 17 | } 18 | -------------------------------------------------------------------------------- /keepalive/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 42 | 43 | 44 | 45 | 46 | 47 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 86 | 87 | 90 | 91 | 98 | 99 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/KeepAliveApplication.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground; 2 | 3 | import android.app.ActivityManager; 4 | import android.app.Application; 5 | import android.content.Context; 6 | import android.util.Log; 7 | 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Desc : 13 | * Creator : sj 14 | * Date : 2019/8/13 15 | * Modifier: 16 | */ 17 | public class KeepAliveApplication extends Application { 18 | private static KeepAliveApplication instance; 19 | @Override 20 | public void onCreate() { 21 | super.onCreate(); 22 | instance = this; 23 | 24 | String processName = getProcessName(this.getApplicationContext()); 25 | if ("com.sdk.keepbackground".equals(processName)){ 26 | // 主进程 进行一些其他的操作 27 | Log.d("sj_keep", "启动主进程"); 28 | }else if ("com.sdk.keepbackground:work".equals(processName)){ 29 | Log.d("sj_keep", "启动了工作进程"); 30 | }else if ("com.sdk.keepbackground:watch".equals(processName)){ 31 | // 这里要设置下看护进程所启动的主进程信息 32 | Log.d("sj_keep", "启动了观察进程"); 33 | } 34 | } 35 | 36 | //创建一个静态的方法,以便获取context对象 37 | public static KeepAliveApplication getInstance() { 38 | return instance; 39 | } 40 | 41 | public static String getProcessName(Context context){ 42 | int pid = android.os.Process.myPid(); 43 | ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 44 | if (am == null) { 45 | return null; 46 | } 47 | List processes = am.getRunningAppProcesses(); 48 | if (processes == null){ 49 | return null; 50 | } 51 | for (ActivityManager.RunningAppProcessInfo info : processes){ 52 | if (info.pid == pid){ 53 | return info.processName; 54 | } 55 | } 56 | return null; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/singlepixel/ScreenManager.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.singlepixel; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import java.lang.ref.WeakReference; 8 | 9 | /** 10 | * 对1像素Activity进行防止内存泄露的处理,新建一个ScreenManager类 11 | */ 12 | public class ScreenManager { 13 | 14 | private static final String TAG = ScreenManager.class.getSimpleName(); 15 | private static ScreenManager sInstance; 16 | private Context mContext; 17 | private WeakReference mActivity; 18 | 19 | private ScreenManager(Context mContext) { 20 | this.mContext = mContext; 21 | } 22 | 23 | public static ScreenManager getInstance(Context context) { 24 | if (sInstance == null) { 25 | sInstance = new ScreenManager(context); 26 | } 27 | return sInstance; 28 | } 29 | 30 | /** 获得SinglePixelActivity的引用 31 | * @param activity 32 | */ 33 | public void setSingleActivity(Activity activity) { 34 | mActivity = new WeakReference<>(activity); 35 | } 36 | 37 | /** 38 | * 启动SinglePixelActivity 39 | */ 40 | public void startActivity() { 41 | Intent intent = new Intent(mContext, SinglePixelActivity.class); 42 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 43 | mContext.startActivity(intent); 44 | } 45 | 46 | /** 47 | * 结束SinglePixelActivity 48 | */ 49 | public void finishActivity() { 50 | if (mActivity != null) { 51 | Activity activity = mActivity.get(); 52 | if (activity != null) { 53 | activity.finish(); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/singlepixel/ScreenReceiverUtil.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.singlepixel; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | import android.util.Log; 8 | 9 | /** 10 | * 对广播进行监听,封装为一个ScreenReceiverUtil类,进行锁屏解锁的广播动态注册监听 11 | */ 12 | public class ScreenReceiverUtil { 13 | private Context mContext; 14 | private SreenBroadcastReceiver mScreenReceiver; 15 | private ScreenManager mScreenManager; 16 | 17 | public ScreenReceiverUtil(Context mContext) { 18 | this.mContext = mContext; 19 | } 20 | 21 | public void startScreenReceiverListener() { 22 | // 动态启动广播接收器 23 | this.mScreenReceiver = new SreenBroadcastReceiver(); 24 | IntentFilter filter = new IntentFilter(); 25 | filter.addAction(Intent.ACTION_SCREEN_ON); 26 | filter.addAction(Intent.ACTION_SCREEN_OFF); 27 | filter.addAction(Intent.ACTION_USER_PRESENT); 28 | mContext.registerReceiver(mScreenReceiver, filter); 29 | mScreenManager = ScreenManager.getInstance(mContext); 30 | } 31 | 32 | public void stopScreenReceiverListener() { 33 | if (null != mScreenReceiver) { 34 | mContext.unregisterReceiver(mScreenReceiver); 35 | mScreenReceiver = null; 36 | } 37 | mScreenManager = null; 38 | } 39 | 40 | public class SreenBroadcastReceiver extends BroadcastReceiver { 41 | @Override 42 | public void onReceive(Context context, Intent intent) { 43 | String action = intent.getAction(); 44 | if (Intent.ACTION_SCREEN_ON.equals(action)) { // 开屏 45 | } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 锁屏 46 | if (mScreenManager != null) { 47 | mScreenManager.startActivity(); 48 | } 49 | Log.d("sj_keep", "打开了1像素Activity"); 50 | } else if (Intent.ACTION_USER_PRESENT.equals(action)) { // 解锁 51 | if (mScreenManager != null) { 52 | mScreenManager.finishActivity(); // 解锁 53 | } 54 | Log.d("sj_keep", "关闭了1像素Activity"); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/singlepixel/SinglePixelActivity.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.singlepixel; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.os.PowerManager; 9 | import android.util.Log; 10 | import android.view.Gravity; 11 | import android.view.MotionEvent; 12 | import android.view.Window; 13 | import android.view.WindowManager; 14 | 15 | 16 | import com.sdk.keepbackground.watch.WatchDogService; 17 | 18 | 19 | 20 | /** 21 | * 该Activity的View只要设置为1像素然后设置在Window对象上即可。在Activity的onDestroy周期中进行保活服务的存活判断从而唤醒服务。 22 | * 运行在:watch进程, 为了提高watch进程的优先级 oom_adj值越小,优先级越高。 23 | * copy from shihu wang 24 | */ 25 | public class SinglePixelActivity extends Activity { 26 | 27 | private static final String TAG = SinglePixelActivity.class.getSimpleName(); 28 | 29 | @Override 30 | protected void onCreate( Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | Window mWindow = getWindow(); 33 | mWindow.setGravity(Gravity.LEFT | Gravity.TOP); 34 | WindowManager.LayoutParams attrParams = mWindow.getAttributes(); 35 | attrParams.x = 0; 36 | attrParams.y = 0; 37 | attrParams.height = 1; 38 | attrParams.width = 1; 39 | mWindow.setAttributes(attrParams); 40 | ScreenManager.getInstance(this).setSingleActivity(this); 41 | } 42 | 43 | @Override 44 | protected void onDestroy() { 45 | // if (!SystemUtils.isAppAlive(this, Constant.PACKAGE_NAME)) { 46 | Log.d("sj_keep", " 1 像素Activity --- onDestroy"); 47 | Intent intentAlive = new Intent(this, WatchDogService.class); 48 | startService(intentAlive); 49 | // } 50 | super.onDestroy(); 51 | } 52 | 53 | @Override 54 | protected void onResume() { 55 | super.onResume(); 56 | if (isScreenOn()) { 57 | finishSelf(); 58 | } 59 | } 60 | 61 | @Override 62 | public boolean dispatchTouchEvent(MotionEvent motionEvent) { 63 | finishSelf(); 64 | return super.dispatchTouchEvent(motionEvent); 65 | } 66 | 67 | @Override 68 | public boolean onTouchEvent(MotionEvent motionEvent) { 69 | finishSelf(); 70 | return super.onTouchEvent(motionEvent); 71 | } 72 | 73 | public void finishSelf() { 74 | if (!isFinishing()) { 75 | finish(); 76 | } 77 | } 78 | 79 | 80 | private boolean isScreenOn() { 81 | PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); 82 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { 83 | return powerManager.isInteractive(); 84 | } else { 85 | return powerManager.isScreenOn(); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/sync/Authenticator.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.sync; 2 | 3 | import android.accounts.AbstractAccountAuthenticator; 4 | import android.accounts.Account; 5 | import android.accounts.AccountAuthenticatorResponse; 6 | import android.accounts.AccountManager; 7 | import android.accounts.NetworkErrorException; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.os.Bundle; 11 | 12 | /** 13 | */ 14 | 15 | public class Authenticator extends AbstractAccountAuthenticator { 16 | 17 | final Context context; 18 | 19 | public Authenticator(Context context) { 20 | super(context); 21 | this.context = context; 22 | } 23 | 24 | @Override 25 | public Bundle addAccount(AccountAuthenticatorResponse response, 26 | String accountType, String authTokenType, 27 | String[] requiredFeatures, Bundle options) 28 | throws NetworkErrorException { 29 | Intent intent = new Intent(); 30 | intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, 31 | response); 32 | Bundle bundle = new Bundle(); 33 | bundle.putParcelable(AccountManager.KEY_INTENT, intent); 34 | return bundle; 35 | } 36 | 37 | @Override 38 | public Bundle confirmCredentials(AccountAuthenticatorResponse response, 39 | Account account, Bundle options) throws NetworkErrorException { 40 | return null; 41 | } 42 | 43 | @Override 44 | public Bundle editProperties(AccountAuthenticatorResponse response, 45 | String accountType) { 46 | return null; 47 | } 48 | 49 | @Override 50 | public Bundle getAuthToken(AccountAuthenticatorResponse response, 51 | Account account, String authTokenType, Bundle options) 52 | throws NetworkErrorException { 53 | return null; 54 | } 55 | 56 | @Override 57 | public String getAuthTokenLabel(String authTokenType) { 58 | return null; 59 | } 60 | 61 | @Override 62 | public Bundle hasFeatures(AccountAuthenticatorResponse response, 63 | Account account, String[] features) 64 | throws NetworkErrorException { 65 | return null; 66 | } 67 | 68 | @Override 69 | public Bundle updateCredentials(AccountAuthenticatorResponse response, 70 | Account account, String authTokenType, Bundle options) 71 | throws NetworkErrorException { 72 | return null; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/sync/AuthenticatorService.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.sync; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | 7 | /** 8 | * 授权此服务提供给SyncAdapter framework,用于调用Authenticator的方法 9 | */ 10 | 11 | public class AuthenticatorService extends Service { 12 | 13 | private Authenticator mAuthenticator; 14 | public AuthenticatorService() { 15 | } 16 | 17 | @Override 18 | public void onCreate() { 19 | super.onCreate(); 20 | mAuthenticator = new Authenticator(this); 21 | } 22 | 23 | @Override 24 | public IBinder onBind(Intent intent) { 25 | return mAuthenticator.getIBinder(); 26 | } 27 | } -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/sync/StubProvider.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.sync; 2 | 3 | import android.content.ContentProvider; 4 | import android.content.ContentValues; 5 | import android.database.Cursor; 6 | import android.net.Uri; 7 | 8 | 9 | 10 | /** 11 | * ContentProvider 跨进程 12 | */ 13 | 14 | public class StubProvider extends ContentProvider { 15 | @Override 16 | public boolean onCreate() { 17 | return false; 18 | } 19 | 20 | 21 | @Override 22 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 23 | return null; 24 | } 25 | 26 | 27 | @Override 28 | public String getType(Uri uri) { 29 | return null; 30 | } 31 | 32 | 33 | @Override 34 | public Uri insert(Uri uri, ContentValues values) { 35 | return null; 36 | } 37 | 38 | @Override 39 | public int delete(Uri uri, String selection, String[] selectionArgs) { 40 | return 0; 41 | } 42 | 43 | @Override 44 | public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 45 | return 0; 46 | } 47 | } -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/sync/SyncAdapter.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.sync; 2 | 3 | /** 4 | */ 5 | 6 | import android.accounts.Account; 7 | import android.content.AbstractThreadedSyncAdapter; 8 | import android.content.ContentProviderClient; 9 | import android.content.Context; 10 | import android.content.SyncResult; 11 | import android.os.Bundle; 12 | 13 | import com.sdk.keepbackground.work.DaemonEnv; 14 | import com.sdk.keepbackground.watch.WatchDogService; 15 | 16 | 17 | public class SyncAdapter extends AbstractThreadedSyncAdapter { 18 | 19 | 20 | private Context mContext; 21 | 22 | public SyncAdapter(Context context, boolean autoInitialize) { 23 | super(context, autoInitialize); 24 | this.mContext = context; 25 | } 26 | 27 | @Override 28 | public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { 29 | DaemonEnv.startServiceSafely(mContext, WatchDogService.class); 30 | } 31 | } -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/sync/SyncService.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.sync; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | 7 | /** 8 | * 9 | * 此服务需能交给操作系统使用 10 | */ 11 | 12 | public class SyncService extends Service { 13 | 14 | 15 | // Storage for an instance of the sync adapter 16 | private static SyncAdapter sSyncAdapter = null; 17 | // Object to use as a thread-safe lock 18 | private static final Object sSyncAdapterLock = new Object(); 19 | 20 | public SyncService() { 21 | } 22 | 23 | @Override 24 | public void onCreate() { 25 | super.onCreate(); 26 | synchronized (sSyncAdapterLock) { 27 | sSyncAdapter = new SyncAdapter(getApplicationContext(), true); 28 | } 29 | } 30 | 31 | @Override 32 | public IBinder onBind(Intent intent) { 33 | return sSyncAdapter.getSyncAdapterBinder(); 34 | } 35 | } -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/AndroidUtil.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import android.content.Context; 4 | import android.location.LocationManager; 5 | import android.net.wifi.WifiInfo; 6 | import android.net.wifi.WifiManager; 7 | import android.os.Build; 8 | import android.os.SystemClock; 9 | 10 | 11 | import java.lang.reflect.Method; 12 | import java.net.InetAddress; 13 | import java.net.NetworkInterface; 14 | import java.text.SimpleDateFormat; 15 | import java.util.Collections; 16 | import java.util.Date; 17 | import java.util.List; 18 | 19 | /** 20 | * 手机信息 & MAC地址 & 开机时间等 21 | */ 22 | public class AndroidUtil { 23 | 24 | /** 25 | * 获取mac地址 适用android6.0以上 26 | * 27 | * @return 28 | */ 29 | public static String getMacAddr() { 30 | try { 31 | List all = Collections.list(NetworkInterface.getNetworkInterfaces()); 32 | for (NetworkInterface nif : all) { 33 | if (!nif.getName().equalsIgnoreCase("wlan0")) continue; 34 | 35 | byte[] macBytes = nif.getHardwareAddress(); 36 | if (macBytes == null) { 37 | return ""; 38 | } 39 | 40 | StringBuilder res1 = new StringBuilder(); 41 | for (byte b : macBytes) { 42 | res1.append(String.format("%02X:", b)); 43 | } 44 | 45 | if (res1.length() > 0) { 46 | res1.deleteCharAt(res1.length() - 1); 47 | } 48 | return res1.toString(); 49 | } 50 | } catch (Exception ex) { 51 | 52 | } 53 | return "02:00:00:00:00:00"; 54 | } 55 | 56 | /** 57 | * 获取 MAC 地址 58 | * 59 | */ 60 | public static String getMacAddress(Context context) { 61 | 62 | try { 63 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 64 | return getMacAddr(); 65 | } 66 | //wifi mac地址 67 | WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 68 | WifiInfo info = wifi.getConnectionInfo(); 69 | String mac = info.getMacAddress(); 70 | return info.getMacAddress(); 71 | } catch (Exception e) { 72 | return null; 73 | } 74 | } 75 | 76 | public static String getSerialNumber(){ 77 | 78 | String serial = null; 79 | 80 | try { 81 | 82 | Class c = Class.forName("android.os.SystemProperties"); 83 | 84 | Method get =c.getMethod("get", String.class); 85 | 86 | serial = (String)get.invoke(c, "ro.serialno"); 87 | 88 | } catch (Exception e) { 89 | e.printStackTrace(); 90 | } 91 | 92 | return serial; 93 | 94 | } 95 | 96 | /** 97 | * 获取本地Ip地址 98 | * 99 | * @param context 100 | * @return 101 | */ 102 | public static String getLocalIpAddress(Context context) { 103 | try { 104 | WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 105 | WifiInfo info = wifi.getConnectionInfo(); 106 | int ipAddress = info.getIpAddress(); 107 | String Ipv4Address = InetAddress 108 | .getByName( 109 | String.format("%d.%d.%d.%d", (ipAddress & 0xff), 110 | (ipAddress >> 8 & 0xff), 111 | (ipAddress >> 16 & 0xff), 112 | (ipAddress >> 24 & 0xff))).getHostAddress() 113 | .toString(); 114 | return Ipv4Address; 115 | } catch (Exception e) { 116 | e.printStackTrace(); 117 | } 118 | return null; 119 | } 120 | 121 | /** 122 | * 获取 开机时间 123 | */ 124 | public static String getBootTimeString() { 125 | long ut = SystemClock.elapsedRealtime() / 1000; 126 | int h = (int) ((ut / 3600)); 127 | int m = (int) ((ut / 60) % 60); 128 | return h + ":" + m; 129 | } 130 | 131 | public static String printSystemInfo() { 132 | Date date = new Date(System.currentTimeMillis()); 133 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 134 | String time = dateFormat.format(date); 135 | StringBuilder sb = new StringBuilder(); 136 | sb.append("_______ 系统信息 ").append(time).append(" ______________"); 137 | sb.append("\nID :").append(Build.ID); 138 | sb.append("\nBRAND :").append(Build.BRAND); 139 | sb.append("\nMODEL :").append(Build.MODEL); 140 | sb.append("\nRELEASE :").append(Build.VERSION.RELEASE); 141 | sb.append("\nSDK :").append(Build.VERSION.SDK); 142 | 143 | sb.append("\n_______ OTHER _______"); 144 | sb.append("\nBOARD :").append(Build.BOARD); 145 | sb.append("\nPRODUCT :").append(Build.PRODUCT); 146 | sb.append("\nDEVICE :").append(Build.DEVICE); 147 | sb.append("\nFINGERPRINT :").append(Build.FINGERPRINT); 148 | sb.append("\nHOST :").append(Build.HOST); 149 | sb.append("\nTAGS :").append(Build.TAGS); 150 | sb.append("\nTYPE :").append(Build.TYPE); 151 | sb.append("\nTIME :").append(Build.TIME); 152 | sb.append("\nINCREMENTAL :").append(Build.VERSION.INCREMENTAL); 153 | 154 | sb.append("\n_______ CUPCAKE-3 _______"); 155 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) { 156 | sb.append("\nDISPLAY :").append(Build.DISPLAY); 157 | } 158 | 159 | sb.append("\n_______ DONUT-4 _______"); 160 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.DONUT) { 161 | sb.append("\nSDK_INT :").append(Build.VERSION.SDK_INT); 162 | sb.append("\nMANUFACTURER :").append(Build.MANUFACTURER); 163 | sb.append("\nBOOTLOADER :").append(Build.BOOTLOADER); 164 | sb.append("\nCPU_ABI :").append(Build.CPU_ABI); 165 | sb.append("\nCPU_ABI2 :").append(Build.CPU_ABI2); 166 | sb.append("\nHARDWARE :").append(Build.HARDWARE); 167 | sb.append("\nUNKNOWN :").append(Build.UNKNOWN); 168 | sb.append("\nCODENAME :").append(Build.VERSION.CODENAME); 169 | } 170 | 171 | sb.append("\n_______ GINGERBREAD-9 _______"); 172 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { 173 | sb.append("\nSERIAL :").append(Build.SERIAL); 174 | } 175 | return sb.toString(); 176 | } 177 | 178 | /** 179 | * 判断gps是否开启 180 | * @param context 181 | * @return 182 | */ 183 | public static final boolean gpsOpened(final Context context) { 184 | LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); 185 | return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/AppUtils.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.sdk.keepbackground.KeepAliveApplication; 6 | import com.sdk.keepbackground.work.DaemonEnv; 7 | 8 | /** 9 | *
10 |  *     author: Blankj
11 |  *     blog  : http://blankj.com
12 |  *     time  : 2016/8/2
13 |  *     desc  : App相关工具类
14 |  * 
15 | */ 16 | public class AppUtils { 17 | 18 | // public static void installApk(Context context, File file) { 19 | // Intent intent = new Intent(Intent.ACTION_VIEW); 20 | // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 21 | // Uri data; 22 | // // 判断版本大于等于7.0 23 | // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 24 | // // "net.csdn.blog.ruancoder.fileprovider"即是在清单文件中配置的authorities 25 | // data = FileProvider.getUriForFile(context, "org.sdk.coolfar_sdk.fileprovider", file); 26 | // // 给目标应用一个临时授权 27 | // intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 28 | // } else { 29 | // data = Uri.fromFile(file); 30 | // } 31 | // intent.setDataAndType(data, "application/vnd.android.package-archive"); 32 | // context.startActivity(intent); 33 | // } 34 | 35 | // public static void upDateApp(Context context, File file) { 36 | // installApk(context, file); 37 | // android.os.Process.killProcess(android.os.Process.myPid()); 38 | // } 39 | 40 | private static final String KEY_SERIALNUMBER = "key_serialnumber"; 41 | 42 | /** 43 | * uuid 获取uuid 44 | * @return 45 | */ 46 | public static String getUUID() { 47 | String uuid = AndroidUtil.getMacAddress(DaemonEnv.app); 48 | if(TextUtils.isEmpty(uuid) || "02:00:00:00:00:00".equals(uuid)) { 49 | uuid = AndroidUtil.getSerialNumber(); 50 | } 51 | if(TextUtils.isEmpty(uuid)) { 52 | uuid = SpManager.getInstance().getString(KEY_SERIALNUMBER); 53 | if(TextUtils.isEmpty(uuid)) { 54 | uuid = RandomUtil.getRandom(RandomUtil.NUMBERS_AND_LETTERS, 32); 55 | SpManager.getInstance().putString(KEY_SERIALNUMBER, uuid); 56 | } 57 | } 58 | return uuid; 59 | } 60 | 61 | public static void checkUUID() { 62 | String localUUID = SpManager.getInstance().getString(KEY_SERIALNUMBER); 63 | if(!TextUtils.isEmpty(localUUID) && !localUUID.equals(getUUID())) { 64 | SpManager.getInstance().putString(KEY_SERIALNUMBER, null); 65 | } 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import android.util.Log; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.InputStreamReader; 11 | import java.io.RandomAccessFile; 12 | 13 | /** 14 | * Created by Ray on 2020/1/6. 15 | */ 16 | public class FileUtils { 17 | private static final String TAG = "FileUtils"; 18 | 19 | //包名地址 20 | public static final String FILE_PKG_DIRRECT = "/mnt/sdcard/keepalive/"; 21 | public static final String FILE_SERVICE_NAME = "service.txt"; 22 | public static final String FILE_PKG_PATH = FILE_PKG_DIRRECT + FILE_SERVICE_NAME; 23 | 24 | public static String readTxtFile(String path) { 25 | StringBuilder content = new StringBuilder(); //文件内容字符串 26 | File file = new File(path); //打开文件 27 | if (!file.exists()) { 28 | return content.toString(); 29 | } 30 | if (file.isDirectory()) //如果path是传递过来的参数,可以做一个非目录的判断 31 | { 32 | Log.d("TestFile", "The File doesn't not exist."); 33 | } else { 34 | try { 35 | InputStream instream = new FileInputStream(file); 36 | InputStreamReader inputreader = new InputStreamReader(instream); 37 | BufferedReader buffreader = new BufferedReader(inputreader); 38 | String line; 39 | //分行读取 40 | while ((line = buffreader.readLine()) != null) { 41 | content.append(line); 42 | } 43 | instream.close(); 44 | } catch (java.io.FileNotFoundException e) { 45 | Log.d("TestFile", "The File doesn't not exist."); 46 | } catch (IOException e) { 47 | Log.d("TestFile", e.getMessage()); 48 | } 49 | } 50 | return content.toString(); 51 | } 52 | 53 | 54 | public static void writeTxtToFile(final String strcontent, final String path, final String name) { 55 | new Thread(new Runnable() { 56 | @Override 57 | public void run() { 58 | makeFilePath(path, name); 59 | String strFilePath = path + name; 60 | if (!strFilePath.contains(".txt")) { 61 | strFilePath += ".txt"; 62 | } 63 | // String strContent = TimeUtils.milliseconds2String(System.currentTimeMillis()) + strcontent + "\r\n"; 64 | try { 65 | File file = new File(strFilePath); 66 | if (file.exists()) { 67 | file.delete(); 68 | } 69 | file.getParentFile().mkdirs(); 70 | file.createNewFile(); 71 | RandomAccessFile raf = new RandomAccessFile(file, "rwd"); 72 | raf.seek(file.length()); 73 | raf.write(strcontent.getBytes("UTF-8")); 74 | raf.close(); 75 | } catch (Exception e) { 76 | Log.e("TestFile", "Error on write File:" + e); 77 | } 78 | } 79 | }).start(); 80 | } 81 | 82 | 83 | private static File makeFilePath(String filePath, String fileName) { 84 | File file = null; 85 | makeRootDirectory(filePath); 86 | try { 87 | file = new File(filePath + fileName); 88 | if (!file.exists()) { 89 | file.createNewFile(); 90 | } 91 | } catch (Exception e) { 92 | e.printStackTrace(); 93 | } 94 | return file; 95 | } 96 | 97 | private static void makeRootDirectory(String filePath) { 98 | File file = null; 99 | try { 100 | file = new File(filePath); 101 | if (!file.exists()) { 102 | file.mkdir(); 103 | } 104 | } catch (Exception e) { 105 | Log.i("error:", e + ""); 106 | } 107 | } 108 | 109 | } -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/ForegroundNotificationUtils.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationChannel; 5 | import android.app.NotificationManager; 6 | import android.app.Service; 7 | import android.content.Context; 8 | import android.graphics.BitmapFactory; 9 | import android.graphics.Color; 10 | import android.os.Build; 11 | 12 | import com.sdk.keepbackground.R; 13 | 14 | public class ForegroundNotificationUtils { 15 | // 通知渠道的id 16 | private static final String CHANNEL_ID = "保活图腾"; 17 | private static final int CHANNEL_POSITION = 1; 18 | public static void startForegroundNotification(Service service){ 19 | 20 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ 21 | //启动前台服务而不显示通知的漏洞已在 API Level 25 修复 22 | NotificationManager manager = (NotificationManager)service.getSystemService(Context.NOTIFICATION_SERVICE); 23 | NotificationChannel Channel = new NotificationChannel(CHANNEL_ID,"主服务",NotificationManager.IMPORTANCE_DEFAULT); 24 | Channel.enableLights(true);//设置提示灯 25 | Channel.setLightColor(Color.GREEN);//设置提示灯颜色 26 | Channel.setShowBadge(true);//显示logo 27 | Channel.setDescription("");//设置描述 28 | Channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); //设置锁屏可见 VISIBILITY_PUBLIC=可见 29 | Channel.enableVibration(false); 30 | Channel.setSound(null,null); 31 | manager.createNotificationChannel(Channel); 32 | 33 | Notification notification = new Notification.Builder(service,CHANNEL_ID) 34 | .setContentTitle("主服务")//标题 35 | .setContentText("运行中...")//内容 36 | .setWhen(System.currentTimeMillis()) 37 | .setSmallIcon(R.drawable.icon1)//小图标一定需要设置,否则会报错(如果不设置它启动服务前台化不会报错,但是你会发现这个通知不会启动),如果是普通通知,不设置必然报错 38 | .setLargeIcon(BitmapFactory.decodeResource(service.getResources(),R.drawable.icon1)) 39 | .build(); 40 | service.startForeground(CHANNEL_POSITION,notification);//服务前台化只能使用startForeground()方法,不能使用 notificationManager.notify(1,notification); 这个只是启动通知使用的,使用这个方法你只需要等待几秒就会发现报错了 41 | }else { 42 | //利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知 43 | // service.startForeground(Foreground_ID, new Notification()); 44 | Notification notification = new Notification.Builder(service) 45 | .setContentTitle("主服务")//设置标题 46 | .setContentText("运行中...")//设置内容 47 | .setWhen(System.currentTimeMillis())//设置创建时间 48 | .setSmallIcon(R.drawable.icon1)//设置状态栏图标 49 | .setLargeIcon(BitmapFactory.decodeResource(service.getResources(),R.drawable.icon1))//设置通知栏图标 50 | .build(); 51 | service.startForeground(CHANNEL_POSITION,notification); 52 | } 53 | } 54 | 55 | public static void deleteForegroundNotification(Service service){ 56 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 57 | NotificationManager mNotificationManager = (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE); 58 | NotificationChannel mChannel = mNotificationManager.getNotificationChannel(CHANNEL_ID); 59 | if (null != mChannel) { 60 | mNotificationManager.deleteNotificationChannel(CHANNEL_ID); 61 | } 62 | }else { 63 | NotificationManager notificationManager = (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE); 64 | notificationManager.cancel(CHANNEL_POSITION); 65 | } 66 | } 67 | 68 | 69 | // private void oldStartForegNotify(){ 70 | //启动前台服务而不显示通知的漏洞已在 API Level 25 修复,大快人心! 71 | // if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) { 72 | // //利用漏洞在 API Level 17 及以下的 Android 系统中,启动前台服务而不显示通知 73 | // startForeground(HASH_CODE, new Notification()); 74 | // //利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知 75 | // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 76 | // Boolean needStartWorkService = needStartWorkService(null, 0, 0); 77 | // DaemonEnv.startServiceSafely(AbsWorkService.this, 78 | // WorkNotificationService.class, 79 | // needStartWorkService); 80 | // } 81 | // } 82 | // } 83 | 84 | // public static class WorkNotificationService extends Service { 85 | // 86 | // /** 87 | // * 利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知 88 | // */ 89 | // @Override 90 | // public int onStartCommand(Intent intent, int flags, int startId) { 91 | // startForeground(1, new Notification()); 92 | // stopSelf(); 93 | // return START_STICKY; 94 | // } 95 | // 96 | // @Override 97 | // public IBinder onBind(Intent intent) { 98 | // return null; 99 | // } 100 | // } 101 | } 102 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/JumpWindowPemiManagement.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.ActivityNotFoundException; 6 | import android.content.ComponentName; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.content.pm.PackageManager; 10 | import android.net.Uri; 11 | import android.os.Build; 12 | import android.provider.Settings; 13 | import android.support.v7.appcompat.BuildConfig; 14 | import android.util.Log; 15 | 16 | public class JumpWindowPemiManagement { 17 | private static final String TAG = "JumpPermission"; 18 | 19 | /** 20 | * Build.MANUFACTURER 21 | */ 22 | private static final String MANUFACTURER_HUAWEI = "Huawei";//华为 23 | private static final String MANUFACTURER_MEIZU = "Meizu";//魅族 24 | private static final String MANUFACTURER_XIAOMI = "Xiaomi";//小米 25 | private static final String MANUFACTURER_SONY = "Sony";//索尼 26 | private static final String MANUFACTURER_OPPO = "OPPO"; 27 | private static final String MANUFACTURER_LG = "LG"; 28 | private static final String MANUFACTURER_VIVO = "vivo"; 29 | private static final String MANUFACTURER_SAMSUNG = "samsung";//三星 30 | private static final String MANUFACTURER_LETV = "Letv";//乐视 31 | private static final String MANUFACTURER_ZTE = "ZTE";//中兴 32 | private static final String MANUFACTURER_YULONG = "YuLong";//酷派 33 | private static final String MANUFACTURER_LENOVO = "LENOVO";//联想 34 | 35 | /** 36 | * 此函数可以自己定义 37 | * 38 | * @param activity 39 | */ 40 | public static void GoToSetting(Activity activity) { 41 | try { 42 | switch (Build.MANUFACTURER) { 43 | case MANUFACTURER_HUAWEI: 44 | Huawei(activity); 45 | break; 46 | case MANUFACTURER_MEIZU: 47 | Meizu(activity); 48 | break; 49 | case MANUFACTURER_XIAOMI: 50 | Xiaomi(activity); 51 | break; 52 | case MANUFACTURER_SONY: 53 | Sony(activity); 54 | break; 55 | case MANUFACTURER_OPPO: 56 | OPPO(activity); 57 | break; 58 | case MANUFACTURER_LG: 59 | LG(activity); 60 | break; 61 | case MANUFACTURER_LETV: 62 | Letv(activity); 63 | break; 64 | default: 65 | ApplicationInfo(activity); 66 | Log.e(TAG, "目前暂不支持此系统"); 67 | break; 68 | } 69 | } catch (Exception e) { 70 | Log.v(TAG, e.toString()); 71 | goToWindow(activity); 72 | } 73 | } 74 | 75 | public static boolean hasWindowPei(Context context) { 76 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 77 | if (context.checkSelfPermission(Manifest.permission.SYSTEM_ALERT_WINDOW) == PackageManager.PERMISSION_GRANTED || Settings.canDrawOverlays(context)) { 78 | return true; 79 | } 80 | } 81 | return false; 82 | } 83 | 84 | public static void Huawei(Activity activity) { 85 | Intent intent = new Intent(); 86 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 87 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 88 | ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity"); 89 | intent.setComponent(comp); 90 | activity.startActivity(intent); 91 | } 92 | 93 | public static void Meizu(Activity activity) { 94 | Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); 95 | intent.addCategory(Intent.CATEGORY_DEFAULT); 96 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 97 | activity.startActivity(intent); 98 | } 99 | 100 | public static void Xiaomi(Activity activity) { 101 | try { 102 | Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); 103 | ComponentName componentName = new ComponentName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); 104 | intent.setComponent(componentName); 105 | intent.putExtra("extra_pkgname", BuildConfig.APPLICATION_ID); 106 | activity.startActivity(intent); 107 | } catch (Exception e) { 108 | goToWindow(activity); 109 | } 110 | } 111 | 112 | public static void Sony(Activity activity) { 113 | Intent intent = new Intent(); 114 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 115 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 116 | ComponentName comp = new ComponentName("com.sonymobile.cta", "com.sonymobile.cta.SomcCTAMainActivity"); 117 | intent.setComponent(comp); 118 | activity.startActivity(intent); 119 | } 120 | 121 | public static void OPPO(Activity activity) { 122 | Intent intent = new Intent(); 123 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 124 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 125 | ComponentName comp = new ComponentName("com.color.safecenter", "com.color.safecenter.permission.PermissionManagerActivity"); 126 | intent.setComponent(comp); 127 | activity.startActivity(intent); 128 | } 129 | 130 | public static void LG(Activity activity) { 131 | Intent intent = new Intent("android.intent.action.MAIN"); 132 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 133 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 134 | ComponentName comp = new ComponentName("com.android.settings", "com.android.settings.Settings$AccessLockSummaryActivity"); 135 | intent.setComponent(comp); 136 | activity.startActivity(intent); 137 | } 138 | 139 | public static void Letv(Activity activity) { 140 | Intent intent = new Intent(); 141 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 142 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 143 | ComponentName comp = new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.PermissionAndApps"); 144 | intent.setComponent(comp); 145 | activity.startActivity(intent); 146 | } 147 | 148 | /** 149 | * 只能打开到自带安全软件 150 | * 151 | * @param activity 152 | */ 153 | public static void _360(Activity activity) { 154 | Intent intent = new Intent("android.intent.action.MAIN"); 155 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 156 | intent.putExtra("packageName", BuildConfig.APPLICATION_ID); 157 | ComponentName comp = new ComponentName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.AppEnterActivity"); 158 | intent.setComponent(comp); 159 | activity.startActivity(intent); 160 | } 161 | 162 | public static final int CHECK_PEERISSION_CODE = 1001; 163 | 164 | /** 165 | * 应用信息界面 166 | * 167 | * @param context 168 | */ 169 | public static void ApplicationInfo(Activity context) { 170 | Uri packageURI = Uri.parse("package:" + context.getPackageName()); 171 | Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI); 172 | (context).startActivityForResult(intent, CHECK_PEERISSION_CODE); 173 | } 174 | 175 | /** 176 | * 系统设置界面 177 | * 178 | * @param activity 179 | */ 180 | public static void SystemConfig(Activity activity) { 181 | Intent intent = new Intent(Settings.ACTION_SETTINGS); 182 | activity.startActivity(intent); 183 | } 184 | 185 | public static void goToWindow(Context context) { 186 | try { 187 | Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, 188 | Uri.parse("package:" + context.getPackageName())); 189 | context.startActivity(intent); 190 | } catch (ActivityNotFoundException e) { 191 | goToAppSetting(context); 192 | } 193 | } 194 | 195 | //进入应用设置 196 | public static void goToAppSetting(Context context) { 197 | if (!(context instanceof Activity)) return; 198 | Uri packageURI = Uri.parse("package:" + context.getPackageName()); 199 | Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI); 200 | ((Activity) context).startActivityForResult(intent, CHECK_PEERISSION_CODE); 201 | } 202 | } -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/NotificationSetUtil.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.AppOpsManager; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.pm.ApplicationInfo; 8 | import android.net.Uri; 9 | import android.os.Build; 10 | import android.support.annotation.RequiresApi; 11 | import android.support.v4.app.NotificationManagerCompat; 12 | 13 | import java.lang.reflect.Field; 14 | import java.lang.reflect.Method; 15 | 16 | /** 17 | * Created by HaiyuKing 18 | * Used 判断是否开启消息通知,没有开启的话跳转到手机系统设置界面 19 | * 参考:https://blog.csdn.net/lidayingyy/article/details/81778894 20 | * https://www.jianshu.com/p/205bc87cac29 21 | * https://blog.csdn.net/yrmao9893/article/details/74607402/ 22 | * https://www.meiwen.com.cn/subject/qcklpftx.html 23 | */ 24 | public class NotificationSetUtil { 25 | 26 | //判断是否需要打开设置界面 27 | public static void OpenNotificationSetting(Context context, OnNextLitener mOnNextLitener) { 28 | if (!isNotificationEnabled(context)) { 29 | mOnNextLitener.onNext(false); 30 | } else { 31 | if (mOnNextLitener != null) { 32 | mOnNextLitener.onNext(true); 33 | } 34 | } 35 | } 36 | 37 | //判断该app是否打开了通知 38 | /** 39 | * 可以通过NotificationManagerCompat 中的 areNotificationsEnabled()来判断是否开启通知权限。NotificationManagerCompat 在 android.support.v4.app包中,是API 22.1.0 中加入的。而 areNotificationsEnabled()则是在 API 24.1.0之后加入的。 40 | * areNotificationsEnabled 只对 API 19 及以上版本有效,低于API 19 会一直返回true 41 | * */ 42 | @TargetApi(Build.VERSION_CODES.KITKAT) 43 | public static boolean isNotificationEnabled(Context context) { 44 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){ 45 | NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context); 46 | boolean areNotificationsEnabled = notificationManagerCompat.areNotificationsEnabled(); 47 | return areNotificationsEnabled; 48 | } 49 | 50 | String CHECK_OP_NO_THROW = "checkOpNoThrow"; 51 | String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION"; 52 | 53 | AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 54 | ApplicationInfo appInfo = context.getApplicationInfo(); 55 | String pkg = context.getApplicationContext().getPackageName(); 56 | int uid = appInfo.uid; 57 | 58 | Class appOpsClass = null; 59 | /* Context.APP_OPS_MANAGER */ 60 | try { 61 | appOpsClass = Class.forName(AppOpsManager.class.getName()); 62 | Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, 63 | String.class); 64 | Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION); 65 | 66 | int value = (Integer) opPostNotificationValue.get(Integer.class); 67 | return ((Integer) checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg) == AppOpsManager.MODE_ALLOWED); 68 | 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | } 72 | return false; 73 | 74 | } 75 | 76 | //打开手机设置页面 77 | /** 78 | * 假设没有开启通知权限,点击之后就需要跳转到 APP的通知设置界面,对应的Action是:Settings.ACTION_APP_NOTIFICATION_SETTINGS, 这个Action是 API 26 后增加的 79 | * 如果在部分手机中无法精确的跳转到 APP对应的通知设置界面,那么我们就考虑直接跳转到 APP信息界面,对应的Action是:Settings.ACTION_APPLICATION_DETAILS_SETTINGS*/ 80 | public static void gotoSet(Context context) { 81 | 82 | Intent intent = new Intent(); 83 | if (Build.VERSION.SDK_INT >= 26) { 84 | // android 8.0引导 85 | intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); 86 | intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName()); 87 | } else if (Build.VERSION.SDK_INT >= 21) { 88 | // android 5.0-7.0 89 | intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); 90 | intent.putExtra("app_package", context.getPackageName()); 91 | intent.putExtra("app_uid", context.getApplicationInfo().uid); 92 | } else { 93 | // 其他 94 | intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); 95 | intent.setData(Uri.fromParts("package", context.getPackageName(), null)); 96 | } 97 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 98 | context.startActivity(intent); 99 | } 100 | 101 | /*=====================添加Listener回调================================*/ 102 | public interface OnNextLitener { 103 | /** 104 | * 不需要设置通知的下一步 105 | */ 106 | void onNext(Boolean isOpen); 107 | } 108 | 109 | private OnNextLitener mOnNextLitener; 110 | 111 | public void setOnNextLitener(OnNextLitener mOnNextLitener) { 112 | this.mOnNextLitener = mOnNextLitener; 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/RandomUtil.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * 随机工具类 7 | */ 8 | public class RandomUtil { 9 | public static final String NUMBERS_AND_LETTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 10 | public static final String NUMBERS = "0123456789"; 11 | public static final String LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 12 | public static final String CAPITAL_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 13 | public static final String LOWER_CASE_LETTERS = "abcdefghijklmnopqrstuvwxyz"; 14 | 15 | private RandomUtil() { 16 | throw new AssertionError(); 17 | } 18 | 19 | /** 20 | * get a fixed-length random string, its a mixture of uppercase, lowercase letters and numbers 21 | * 22 | * @param length 23 | * @return 24 | * @see RandomUtil#getRandom(String source, int length) 25 | */ 26 | public static String getRandomNumbersAndLetters(int length) { 27 | return getRandom(NUMBERS_AND_LETTERS, length); 28 | } 29 | 30 | /** 31 | * get a fixed-length random string, its a mixture of numbers 32 | * 33 | * @param length 34 | * @return 35 | * @see RandomUtil#getRandom(String source, int length) 36 | */ 37 | public static String getRandomNumbers(int length) { 38 | return getRandom(NUMBERS, length); 39 | } 40 | 41 | /** 42 | * get a fixed-length random string, its a mixture of uppercase and lowercase letters 43 | * 44 | * @param length 45 | * @return 46 | * @see RandomUtil#getRandom(String source, int length) 47 | */ 48 | public static String getRandomLetters(int length) { 49 | return getRandom(LETTERS, length); 50 | } 51 | 52 | /** 53 | * get a fixed-length random string, its a mixture of uppercase letters 54 | * 55 | * @param length 56 | * @return 57 | * @see RandomUtil#getRandom(String source, int length) 58 | */ 59 | public static String getRandomCapitalLetters(int length) { 60 | return getRandom(CAPITAL_LETTERS, length); 61 | } 62 | 63 | /** 64 | * get a fixed-length random string, its a mixture of lowercase letters 65 | * 66 | * @param length 67 | * @return 68 | * @see RandomUtil#getRandom(String source, int length) 69 | */ 70 | public static String getRandomLowerCaseLetters(int length) { 71 | return getRandom(LOWER_CASE_LETTERS, length); 72 | } 73 | 74 | /** 75 | * get a fixed-length random string, its a mixture of chars in source 76 | * 77 | * @param source 78 | * @param length 79 | * @return
    80 | *
  • if source is null or empty, return null
  • 81 | *
  • else see {@link RandomUtil#getRandom(char[] sourceChar, int length)}
  • 82 | *
83 | */ 84 | public static String getRandom(String source, int length) { 85 | return source == null ? null : getRandom(source.toCharArray(), length); 86 | } 87 | 88 | /** 89 | * get a fixed-length random string, its a mixture of chars in sourceChar 90 | * 91 | * @param sourceChar 92 | * @param length 93 | * @return
    94 | *
  • if sourceChar is null or empty, return null
  • 95 | *
  • if length less than 0, return null
  • 96 | *
97 | */ 98 | public static String getRandom(char[] sourceChar, int length) { 99 | if (sourceChar == null || sourceChar.length == 0 || length < 0) { 100 | return null; 101 | } 102 | 103 | StringBuilder str = new StringBuilder(length); 104 | Random random = new Random(); 105 | for (int i = 0; i < length; i++) { 106 | str.append(sourceChar[random.nextInt(sourceChar.length)]); 107 | } 108 | return str.toString(); 109 | } 110 | 111 | /** 112 | * get random int between 0 and max 113 | * 114 | * @param max 115 | * @return
    116 | *
  • if max <= 0, return 0
  • 117 | *
  • else return random int between 0 and max
  • 118 | *
119 | */ 120 | public static int getRandom(int max) { 121 | return getRandom(0, max); 122 | } 123 | 124 | /** 125 | * get random int between min and max 126 | * 127 | * @param min 128 | * @param max 129 | * @return
    130 | *
  • if min > max, return 0
  • 131 | *
  • if min == max, return min
  • 132 | *
  • else return random int between min and max
  • 133 | *
134 | */ 135 | public static int getRandom(int min, int max) { 136 | if (min > max) { 137 | return 0; 138 | } 139 | if (min == max) { 140 | return min; 141 | } 142 | return min + new Random().nextInt(max - min); 143 | } 144 | 145 | /** 146 | * Shuffling algorithm, Randomly permutes the specified array using a default source of randomness 147 | */ 148 | public static boolean shuffle(Object[] objArray) { 149 | if (objArray == null) { 150 | return false; 151 | } 152 | return shuffle(objArray, getRandom(objArray.length)); 153 | } 154 | 155 | /** 156 | * Shuffling algorithm, Randomly permutes the specified array 157 | */ 158 | public static boolean shuffle(Object[] objArray, int shuffleCount) { 159 | int length; 160 | if (objArray == null || shuffleCount < 0 || (length = objArray.length) < shuffleCount) { 161 | return false; 162 | } 163 | 164 | for (int i = 1; i <= shuffleCount; i++) { 165 | int random = getRandom(length - i); 166 | Object temp = objArray[length - i]; 167 | objArray[length - i] = objArray[random]; 168 | objArray[random] = temp; 169 | } 170 | return true; 171 | } 172 | 173 | /** 174 | * Shuffling algorithm, Randomly permutes the specified int array using a default source of randomness 175 | */ 176 | public static int[] shuffle(int[] intArray) { 177 | if (intArray == null) { 178 | return null; 179 | } 180 | 181 | return shuffle(intArray, getRandom(intArray.length)); 182 | } 183 | 184 | /** 185 | * Shuffling algorithm, Randomly permutes the specified int array 186 | */ 187 | public static int[] shuffle(int[] intArray, int shuffleCount) { 188 | int length; 189 | if (intArray == null || shuffleCount < 0 || (length = intArray.length) < shuffleCount) { 190 | return null; 191 | } 192 | 193 | int[] out = new int[shuffleCount]; 194 | for (int i = 1; i <= shuffleCount; i++) { 195 | int random = getRandom(length - i); 196 | out[i - 1] = intArray[random]; 197 | int temp = intArray[length - i]; 198 | intArray[length - i] = intArray[random]; 199 | intArray[random] = temp; 200 | } 201 | return out; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/SPUtils.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | import com.sdk.keepbackground.KeepAliveApplication; 7 | import com.sdk.keepbackground.work.DaemonEnv; 8 | 9 | import java.util.Map; 10 | 11 | /** 12 | *
 13 |  *     author: Blankj
 14 |  *     blog  : http://blankj.com
 15 |  *     time  : 2016/8/2
 16 |  *     desc  : SP相关工具类
 17 |  * 
18 | */ 19 | public class SPUtils { 20 | 21 | private SharedPreferences sp; 22 | private SharedPreferences.Editor editor; 23 | 24 | /** 25 | * SPUtils构造函数 26 | *

在Application中初始化

27 | * 28 | * @param spName spName 29 | */ 30 | public SPUtils(String spName) { 31 | sp = DaemonEnv.app.getSharedPreferences(spName, Context.MODE_PRIVATE); 32 | editor = sp.edit(); 33 | editor.apply(); 34 | } 35 | 36 | /** 37 | * SP中写入String类型value 38 | * 39 | * @param key 键 40 | * @param value 值 41 | */ 42 | public void putString(String key, String value) { 43 | editor.putString(key, value).apply(); 44 | } 45 | 46 | /** 47 | * SP中读取String 48 | * 49 | * @param key 键 50 | * @return 存在返回对应值,不存在返回默认值{@code null} 51 | */ 52 | public String getString(String key) { 53 | return getString(key, null); 54 | } 55 | 56 | /** 57 | * SP中读取String 58 | * 59 | * @param key 键 60 | * @param defaultValue 默认值 61 | * @return 存在返回对应值,不存在返回默认值{@code defaultValue} 62 | */ 63 | public String getString(String key, String defaultValue) { 64 | return sp.getString(key, defaultValue); 65 | } 66 | 67 | /** 68 | * SP中写入int类型value 69 | * 70 | * @param key 键 71 | * @param value 值 72 | */ 73 | public void putInt(String key, int value) { 74 | editor.putInt(key, value).apply(); 75 | } 76 | 77 | /** 78 | * SP中读取int 79 | * 80 | * @param key 键 81 | * @return 存在返回对应值,不存在返回默认值-1 82 | */ 83 | public int getInt(String key) { 84 | return getInt(key, -1); 85 | } 86 | 87 | /** 88 | * SP中读取int 89 | * 90 | * @param key 键 91 | * @param defaultValue 默认值 92 | * @return 存在返回对应值,不存在返回默认值{@code defaultValue} 93 | */ 94 | public int getInt(String key, int defaultValue) { 95 | return sp.getInt(key, defaultValue); 96 | } 97 | 98 | /** 99 | * SP中写入long类型value 100 | * 101 | * @param key 键 102 | * @param value 值 103 | */ 104 | public void putLong(String key, long value) { 105 | editor.putLong(key, value).apply(); 106 | } 107 | 108 | /** 109 | * SP中读取long 110 | * 111 | * @param key 键 112 | * @return 存在返回对应值,不存在返回默认值-1 113 | */ 114 | public long getLong(String key) { 115 | return getLong(key, -1L); 116 | } 117 | 118 | /** 119 | * SP中读取long 120 | * 121 | * @param key 键 122 | * @param defaultValue 默认值 123 | * @return 存在返回对应值,不存在返回默认值{@code defaultValue} 124 | */ 125 | public long getLong(String key, long defaultValue) { 126 | return sp.getLong(key, defaultValue); 127 | } 128 | 129 | /** 130 | * SP中写入float类型value 131 | * 132 | * @param key 键 133 | * @param value 值 134 | */ 135 | public void putFloat(String key, float value) { 136 | editor.putFloat(key, value).apply(); 137 | } 138 | 139 | /** 140 | * SP中读取float 141 | * 142 | * @param key 键 143 | * @return 存在返回对应值,不存在返回默认值-1 144 | */ 145 | public float getFloat(String key) { 146 | return getFloat(key, -1f); 147 | } 148 | 149 | /** 150 | * SP中读取float 151 | * 152 | * @param key 键 153 | * @param defaultValue 默认值 154 | * @return 存在返回对应值,不存在返回默认值{@code defaultValue} 155 | */ 156 | public float getFloat(String key, float defaultValue) { 157 | return sp.getFloat(key, defaultValue); 158 | } 159 | 160 | /** 161 | * SP中写入boolean类型value 162 | * 163 | * @param key 键 164 | * @param value 值 165 | */ 166 | public void putBoolean(String key, boolean value) { 167 | editor.putBoolean(key, value).apply(); 168 | } 169 | 170 | /** 171 | * SP中读取boolean 172 | * 173 | * @param key 键 174 | * @return 存在返回对应值,不存在返回默认值{@code false} 175 | */ 176 | public boolean getBoolean(String key) { 177 | return getBoolean(key, false); 178 | } 179 | 180 | /** 181 | * SP中读取boolean 182 | * 183 | * @param key 键 184 | * @param defaultValue 默认值 185 | * @return 存在返回对应值,不存在返回默认值{@code defaultValue} 186 | */ 187 | public boolean getBoolean(String key, boolean defaultValue) { 188 | return sp.getBoolean(key, defaultValue); 189 | } 190 | 191 | /** 192 | * SP中获取所有键值对 193 | * 194 | * @return Map对象 195 | */ 196 | public Map getAll() { 197 | return sp.getAll(); 198 | } 199 | 200 | /** 201 | * SP中移除该key 202 | * 203 | * @param key 键 204 | */ 205 | public void remove(String key) { 206 | editor.remove(key).apply(); 207 | } 208 | 209 | /** 210 | * SP中是否存在该key 211 | * 212 | * @param key 键 213 | * @return {@code true}: 存在
{@code false}: 不存在 214 | */ 215 | public boolean contains(String key) { 216 | return sp.contains(key); 217 | } 218 | 219 | /** 220 | * SP中清除所有数据 221 | */ 222 | public void clear() { 223 | editor.clear().apply(); 224 | } 225 | } -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/SpManager.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | 4 | /** 5 | * 6 | * SharedPreferences 对象管理类 7 | * 8 | */ 9 | public class SpManager extends SPUtils { 10 | 11 | private static final String SPNAME = "tourguide_sp"; 12 | 13 | private static SpManager instance; 14 | 15 | private SpManager(String spName) { 16 | super(spName); 17 | } 18 | 19 | /** 20 | * 21 | * 22 | * @return 23 | */ 24 | public static SpManager getInstance(){ 25 | if(instance == null) { 26 | instance = new SpManager(SPNAME); 27 | } 28 | return instance; 29 | } 30 | 31 | /** 32 | * 33 | * 存储的SharedPreferences的键 34 | * 35 | * @author Administrator 36 | * 37 | */ 38 | public static final class Keys { 39 | 40 | //搜索记录 41 | public static final String SEARCH_HISTORY = "search_history"; 42 | 43 | //搜索记录 44 | public static final String SEARCH_CITYSCENIC_HISTORY = "search_cityscenic_history"; 45 | 46 | //是否已经签到 47 | public static final String ISSIGN = "issign"; 48 | 49 | //签到时间 50 | public static final String SIGNTIME = "signtime"; 51 | 52 | //用户编号 53 | public static final String USERNUMBER = "userNumber"; 54 | 55 | //用户token 56 | public static final String USERTOKEN = "userToken"; 57 | 58 | //是否已执行过白名单操作 59 | public static final String SP_IS_ACTION_WHITE_POWER = "isDoPowerWhite"; 60 | //是否开启过悬浮窗权限 61 | public static final String IS_HINT_SYSTEM_WINDOW = "isDoWindowWhite"; 62 | public static final String WORK_SERVICE = "workService"; 63 | 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/utils/TimeUtils.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.utils; 2 | 3 | import java.text.ParseException; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | import java.util.Locale; 8 | 9 | 10 | public class TimeUtils { 11 | /** 12 | * 毫秒与毫秒的倍数 13 | */ 14 | private static final int MSEC = 1; 15 | /** 16 | * 秒与毫秒的倍数 17 | */ 18 | private static final int SEC = 1000; 19 | /** 20 | * 分与毫秒的倍数 21 | */ 22 | public static final int MIN = 60000; 23 | /** 24 | * 时与毫秒的倍数 25 | */ 26 | private static final int HOUR = 3600000; 27 | /** 28 | * 天与毫秒的倍数 29 | */ 30 | public static final int DAY = 86400000; 31 | 32 | private TimeUtils() { 33 | throw new UnsupportedOperationException("u can't instantiate me..."); 34 | } 35 | 36 | public static final SimpleDateFormat DEFAULT_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); 37 | 38 | public static final SimpleDateFormat UTC_SDF = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault()); 39 | 40 | public static final SimpleDateFormat DATE_SDF = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); 41 | 42 | public static final SimpleDateFormat DATE_SDF_POINT = new SimpleDateFormat("yyyy.MM.dd", Locale.getDefault()); 43 | 44 | public static final SimpleDateFormat DATE_NO_YEAR_SDF = new SimpleDateFormat("MM-dd", Locale.getDefault()); 45 | 46 | public static final SimpleDateFormat HOUR_SDF = new SimpleDateFormat("HH:mm", Locale.getDefault()); 47 | 48 | public static final SimpleDateFormat HOURLY_FORECAST_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()); 49 | 50 | public static final SimpleDateFormat API_TIME = new SimpleDateFormat("yyyyMMdd", Locale.getDefault()); 51 | 52 | public static final SimpleDateFormat MONEY_DAY_HH_MM = new SimpleDateFormat("MM-dd HH:mm", Locale.getDefault()); 53 | 54 | /** 55 | * 将时间戳转为时间字符串 56 | *

格式为yyyy-MM-dd HH:mm:ss

57 | * 58 | * @param milliseconds 毫秒时间戳 59 | * @return 时间字符串 60 | */ 61 | public static String milliseconds2String(long milliseconds) { 62 | return milliseconds2String(milliseconds, DEFAULT_SDF); 63 | } 64 | 65 | /** 66 | * 将时间戳转为时间字符串 67 | *

格式为用户自定义

68 | * 69 | * @param milliseconds 毫秒时间戳 70 | * @param format 时间格式 71 | * @return 时间字符串 72 | */ 73 | public synchronized static String milliseconds2String(long milliseconds, SimpleDateFormat format) { 74 | return format.format(new Date(milliseconds)); 75 | } 76 | 77 | /** 78 | * 将时间字符串转为时间戳 79 | *

格式为yyyy-MM-dd HH:mm:ss

80 | * 81 | * @param time 时间字符串 82 | * @return 毫秒时间戳 83 | */ 84 | public static long string2Milliseconds(String time) { 85 | return string2Milliseconds(time, DEFAULT_SDF); 86 | } 87 | 88 | /** 89 | * 将时间字符串转为时间戳 90 | *

格式为用户自定义

91 | * 92 | * @param time 时间字符串 93 | * @param format 时间格式 94 | * @return 毫秒时间戳 95 | */ 96 | public static long string2Milliseconds(String time, SimpleDateFormat format) { 97 | try { 98 | return format.parse(time).getTime(); 99 | } catch (ParseException e) { 100 | e.printStackTrace(); 101 | } 102 | return -1; 103 | } 104 | 105 | /** 106 | * 将时间字符串转为Date类型 107 | *

格式为yyyy-MM-dd HH:mm:ss

108 | * 109 | * @param time 时间字符串 110 | * @return Date类型 111 | */ 112 | public static Date string2Date(String time) { 113 | return string2Date(time, DEFAULT_SDF); 114 | } 115 | 116 | /** 117 | * 将时间字符串转为Date类型 118 | *

格式为用户自定义

119 | * 120 | * @param time 时间字符串 121 | * @param format 时间格式 122 | * @return Date类型 123 | */ 124 | public static Date string2Date(String time, SimpleDateFormat format) { 125 | return new Date(string2Milliseconds(time, format)); 126 | } 127 | 128 | /** 129 | * 将Date类型转为时间字符串 130 | *

格式为yyyy-MM-dd HH:mm:ss

131 | * 132 | * @param time Date类型时间 133 | * @return 时间字符串 134 | */ 135 | public static String date2String(Date time) { 136 | return date2String(time, DEFAULT_SDF); 137 | } 138 | 139 | /** 140 | * 将Date类型转为时间字符串 141 | *

格式为用户自定义

142 | * 143 | * @param time Date类型时间 144 | * @param format 时间格式 145 | * @return 时间字符串 146 | */ 147 | public synchronized static String date2String(Date time, SimpleDateFormat format) { 148 | return format.format(time); 149 | } 150 | 151 | /** 152 | * 将Date类型转为时间戳 153 | * 154 | * @param time Date类型时间 155 | * @return 毫秒时间戳 156 | */ 157 | public static long date2Milliseconds(Date time) { 158 | return time.getTime(); 159 | } 160 | 161 | /** 162 | * 将时间戳转为Date类型 163 | * 164 | * @param milliseconds 毫秒时间戳 165 | * @return Date类型时间 166 | */ 167 | public static Date milliseconds2Date(long milliseconds) { 168 | return new Date(milliseconds); 169 | } 170 | 171 | /** 172 | * 毫秒时间戳单位转换(单位:unit) 173 | * 174 | * @param milliseconds 毫秒时间戳 175 | * @param unit 176 | * @return unit时间戳 177 | */ 178 | private static long milliseconds2Unit(long milliseconds, TimeUnit unit) { 179 | switch (unit) { 180 | case MSEC: 181 | return milliseconds / MSEC; 182 | case SEC: 183 | return milliseconds / SEC; 184 | case MIN: 185 | return milliseconds / MIN; 186 | case HOUR: 187 | return milliseconds / HOUR; 188 | case DAY: 189 | return milliseconds / DAY; 190 | default: 191 | } 192 | return -1; 193 | } 194 | 195 | /** 196 | * 获取两个时间差(单位:unit) 197 | *

time1和time2格式都为yyyy-MM-dd HH:mm:ss

198 | * 199 | * @param time0 时间字符串1 200 | * @param time1 时间字符串2 201 | * @param unit 202 | * @return unit时间戳 203 | */ 204 | public static long getIntervalTime(String time0, String time1, TimeUnit unit) { 205 | return getIntervalTime(time0, time1, unit, DEFAULT_SDF); 206 | } 207 | 208 | /** 209 | * 获取两个时间差(单位:unit) 210 | *

time1和time2格式都为format

211 | * 212 | * @param time0 时间字符串1 213 | * @param time1 时间字符串2 214 | * @param unit 215 | * @param format 时间格式 216 | * @return unit时间戳 217 | */ 218 | public static long getIntervalTime(String time0, String time1, TimeUnit unit, SimpleDateFormat format) { 219 | return milliseconds2Unit(Math.abs(string2Milliseconds(time0, format) 220 | - string2Milliseconds(time1, format)), unit); 221 | } 222 | 223 | /** 224 | * 获取两个时间差(单位:unit) 225 | *

time1和time2都为Date类型

226 | * 227 | * @param time0 Date类型时间1 228 | * @param time1 Date类型时间2 229 | * @param unit 230 | * @return unit时间戳 231 | */ 232 | public static long getIntervalTime(Date time0, Date time1, TimeUnit unit) { 233 | return milliseconds2Unit(Math.abs(date2Milliseconds(time1) 234 | - date2Milliseconds(time0)), unit); 235 | } 236 | 237 | /** 238 | * 获取当前时间 239 | * 240 | * @return 毫秒时间戳 241 | */ 242 | public static long getCurTimeMills() { 243 | return System.currentTimeMillis(); 244 | } 245 | 246 | /** 247 | * 获取当前时间 248 | *

格式为yyyy-MM-dd HH:mm:ss

249 | * 250 | * @return 时间字符串 251 | */ 252 | public static String getCurTimeString() { 253 | return date2String(new Date()); 254 | } 255 | 256 | /** 257 | * 获取当前时间 258 | *

格式为用户自定义

259 | * 260 | * @param format 时间格式 261 | * @return 时间字符串 262 | */ 263 | public static String getCurTimeString(SimpleDateFormat format) { 264 | return date2String(new Date(), format); 265 | } 266 | 267 | /** 268 | * 获取当前时间 269 | *

Date类型

270 | * 271 | * @return Date类型时间 272 | */ 273 | public static Date getCurTimeDate() { 274 | return new Date(); 275 | } 276 | 277 | /** 278 | * 获取与当前时间的差(单位:unit) 279 | *

time格式为yyyy-MM-dd HH:mm:ss

280 | * 281 | * @param time 时间字符串 282 | * @param unit 283 | * @return unit时间戳 284 | */ 285 | public static long getIntervalByNow(String time, TimeUnit unit) { 286 | return getIntervalByNow(time, unit, DEFAULT_SDF); 287 | } 288 | 289 | /** 290 | * 获取与当前时间的差(单位:unit) 291 | *

time格式为format

292 | * 293 | * @param time 时间字符串 294 | * @param unit 295 | * @param format 时间格式 296 | * @return unit时间戳 297 | */ 298 | public static long getIntervalByNow(String time, TimeUnit unit, SimpleDateFormat format) { 299 | return getIntervalTime(getCurTimeString(), time, unit, format); 300 | } 301 | 302 | /** 303 | * 获取与当前时间的差(单位:unit) 304 | *

time为Date类型

305 | * 306 | * @param time Date类型时间 307 | * @param unit 308 | * @return unit时间戳 309 | */ 310 | public static long getIntervalByNow(Date time, TimeUnit unit) { 311 | return getIntervalTime(getCurTimeDate(), time, unit); 312 | } 313 | 314 | /** 315 | * 判断闰年 316 | * 317 | * @param year 年份 318 | * @return {@code true}: 闰年
{@code false}: 平年 319 | */ 320 | public static boolean isLeapYear(int year) { 321 | return year % 4 == 0 && year % 100 != 0 || year % 400 == 0; 322 | } 323 | 324 | /** 325 | * 获取星期 326 | *

time格式为yyyy-MM-dd HH:mm:ss

327 | * 328 | * @param time 时间字符串 329 | * @return 星期 330 | */ 331 | public static String getWeek(String time) { 332 | return new SimpleDateFormat("EEEE", Locale.getDefault()).format(string2Date(time)); 333 | } 334 | 335 | /** 336 | * 获取星期 337 | * 338 | * @param time 时间字符串 339 | * @param format 时间格式 340 | * @return 星期 341 | */ 342 | public static String getWeek(String time, SimpleDateFormat format) { 343 | return new SimpleDateFormat("EEEE", Locale.getDefault()).format(string2Date(time, format)); 344 | } 345 | 346 | /** 347 | * 获取星期 348 | * 349 | * @param time Date类型时间 350 | * @return 星期 351 | */ 352 | public static String getWeek(Date time) { 353 | return new SimpleDateFormat("EEEE", Locale.getDefault()).format(time); 354 | } 355 | 356 | /** 357 | * 获取星期 358 | *

注意:周日的Index才是1,周六为7

359 | *

time格式为yyyy-MM-dd HH:mm:ss

360 | * 361 | * @param time 时间字符串 362 | * @return 1...5 363 | */ 364 | public static int getWeekIndex(String time) { 365 | Date date = string2Date(time); 366 | return getWeekIndex(date); 367 | } 368 | 369 | /** 370 | * 获取星期 371 | *

注意:周日的Index才是1,周六为7

372 | * 373 | * @param time 时间字符串 374 | * @param format 时间格式 375 | * @return 1...7 376 | */ 377 | public static int getWeekIndex(String time, SimpleDateFormat format) { 378 | Date date = string2Date(time, format); 379 | return getWeekIndex(date); 380 | } 381 | 382 | /** 383 | * 获取星期 384 | *

注意:周日的Index才是1,周六为7

385 | * 386 | * @param time Date类型时间 387 | * @return 1...7 388 | */ 389 | public static int getWeekIndex(Date time) { 390 | Calendar cal = Calendar.getInstance(); 391 | cal.setTime(time); 392 | return cal.get(Calendar.DAY_OF_WEEK); 393 | } 394 | 395 | /** 396 | * 获取月份中的第几周 397 | *

注意:国外周日才是新的一周的开始

398 | *

time格式为yyyy-MM-dd HH:mm:ss

399 | * 400 | * @param time 时间字符串 401 | * @return 1...5 402 | */ 403 | public static int getWeekOfMonth(String time) { 404 | Date date = string2Date(time); 405 | return getWeekOfMonth(date); 406 | } 407 | 408 | /** 409 | * 获取月份中的第几周 410 | *

注意:国外周日才是新的一周的开始

411 | * 412 | * @param time 时间字符串 413 | * @param format 时间格式 414 | * @return 1...5 415 | */ 416 | public static int getWeekOfMonth(String time, SimpleDateFormat format) { 417 | Date date = string2Date(time, format); 418 | return getWeekOfMonth(date); 419 | } 420 | 421 | /** 422 | * 获取月份中的第几周 423 | *

注意:国外周日才是新的一周的开始

424 | * 425 | * @param time Date类型时间 426 | * @return 1...5 427 | */ 428 | public static int getWeekOfMonth(Date time) { 429 | Calendar cal = Calendar.getInstance(); 430 | cal.setTime(time); 431 | return cal.get(Calendar.WEEK_OF_MONTH); 432 | } 433 | 434 | /** 435 | * 获取年份中的第几周 436 | *

注意:国外周日才是新的一周的开始

437 | *

time格式为yyyy-MM-dd HH:mm:ss

438 | * 439 | * @param time 时间字符串 440 | * @return 1...54 441 | */ 442 | public static int getWeekOfYear(String time) { 443 | Date date = string2Date(time); 444 | return getWeekOfYear(date); 445 | } 446 | 447 | /** 448 | * 获取年份中的第几周 449 | *

注意:国外周日才是新的一周的开始

450 | * 451 | * @param time 时间字符串 452 | * @param format 时间格式 453 | * @return 1...54 454 | */ 455 | public static int getWeekOfYear(String time, SimpleDateFormat format) { 456 | Date date = string2Date(time, format); 457 | return getWeekOfYear(date); 458 | } 459 | 460 | /** 461 | * 获取年份中的第几周 462 | *

注意:国外周日才是新的一周的开始

463 | * 464 | * @param time Date类型时间 465 | * @return 1...54 466 | */ 467 | public static int getWeekOfYear(Date time) { 468 | Calendar cal = Calendar.getInstance(); 469 | cal.setTime(time); 470 | return cal.get(Calendar.WEEK_OF_YEAR); 471 | } 472 | 473 | public static String string2String(String time, SimpleDateFormat fromFormat, SimpleDateFormat toFormat) { 474 | return TimeUtils.date2String(TimeUtils.string2Date(time, fromFormat), toFormat); 475 | } 476 | 477 | public static boolean isBefore(Date d1, Date d2) { 478 | return d1.before(d2); 479 | } 480 | 481 | public static boolean isBefore(String d1, String d2, SimpleDateFormat sdf) { 482 | try { 483 | return sdf.parse(d1).before(sdf.parse(d2)); 484 | } catch (Exception e) { 485 | return false; 486 | } 487 | } 488 | 489 | /** 490 | * 获取某个日期的前几天的日期 491 | * 492 | * @param dateString 某日期 493 | * @param dayNumber 前面第几天 494 | * @return 495 | */ 496 | public static String getPreviousDay(String dateString, int dayNumber) { 497 | Calendar c = Calendar.getInstance(); 498 | Date date = null; 499 | try { 500 | date = new SimpleDateFormat("yyyy-MM-dd").parse(dateString); 501 | } catch (ParseException e) { 502 | e.printStackTrace(); 503 | } 504 | c.setTime(date); 505 | int day = c.get(Calendar.DATE); 506 | c.set(Calendar.DATE, day - dayNumber); 507 | 508 | String previousDay = new SimpleDateFormat("yyyy-MM-dd").format(c.getTime()); 509 | return previousDay; 510 | } 511 | 512 | public static String getSystemTime() { 513 | return String.valueOf(System.currentTimeMillis()); 514 | } 515 | 516 | /** 517 | * 判断当前时间是否在两个时间段之内 518 | * 519 | * @param beginTime 开始时间 520 | * @param endTime 结束时间 521 | * @return 522 | */ 523 | public static boolean isNowBetween(String beginTime, String endTime) { 524 | SimpleDateFormat df = new SimpleDateFormat("HH:mm"); 525 | Date now = null; 526 | Date begin = null; 527 | Date end = null; 528 | try { 529 | now = df.parse(df.format(new Date())); 530 | begin = df.parse(beginTime); 531 | end = df.parse(endTime); 532 | } catch (Exception e) { 533 | e.printStackTrace(); 534 | } 535 | Calendar nowCal = Calendar.getInstance(); 536 | nowCal.setTime(now); 537 | 538 | Calendar beginCal = Calendar.getInstance(); 539 | beginCal.setTime(begin); 540 | 541 | Calendar endCal = Calendar.getInstance(); 542 | endCal.setTime(end); 543 | 544 | if (nowCal.after(beginCal) && nowCal.before(endCal)) { 545 | return true; 546 | } else { 547 | return false; 548 | } 549 | } 550 | 551 | /** 552 | * 获取当前时间在某段时间内的百分比位置 553 | * 554 | * @param beginTime 开始时间 555 | * @param endTime 结束时间 556 | * @return 557 | */ 558 | public static float getTimeDiffPercent(String beginTime, String endTime) { 559 | SimpleDateFormat df = new SimpleDateFormat("HH:mm"); 560 | Date now = null; 561 | Date begin = null; 562 | Date end = null; 563 | try { 564 | now = df.parse(df.format(new Date())); 565 | begin = df.parse(beginTime); 566 | end = df.parse(endTime); 567 | } catch (Exception e) { 568 | e.printStackTrace(); 569 | } 570 | return (float) (now.getTime() - begin.getTime()) / (float) (end.getTime() - begin.getTime()); 571 | } 572 | public enum TimeUnit { 573 | MSEC, 574 | SEC, 575 | MIN, 576 | HOUR, 577 | DAY 578 | } 579 | } 580 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/watch/AbsServiceConnection.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.watch; 2 | 3 | import android.content.ComponentName; 4 | import android.content.ServiceConnection; 5 | import android.os.IBinder; 6 | public abstract class AbsServiceConnection implements ServiceConnection { 7 | 8 | // 当前绑定的状态 9 | public boolean mConnectedState = false; 10 | 11 | @Override 12 | public void onServiceConnected(ComponentName name, IBinder service) { 13 | mConnectedState = true; 14 | } 15 | 16 | @Override 17 | public void onServiceDisconnected(ComponentName name) { 18 | if (mConnectedState) { 19 | mConnectedState = false; 20 | onDisconnected(name); 21 | } 22 | } 23 | 24 | @Override 25 | public void onBindingDied(ComponentName name) { 26 | onServiceDisconnected(name); 27 | } 28 | 29 | public abstract void onDisconnected(ComponentName name); 30 | } 31 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/watch/JobSchedulerService.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.watch; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.job.JobParameters; 5 | import android.app.job.JobService; 6 | import android.os.Build; 7 | import android.util.Log; 8 | 9 | import com.sdk.keepbackground.work.DaemonEnv; 10 | 11 | /** 12 | * Android 5.0+ 使用的 JobScheduler. 13 | * 运行在 :watch 子进程中. 14 | * 15 | */ 16 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 17 | public class JobSchedulerService extends JobService { 18 | 19 | @Override 20 | public boolean onStartJob(JobParameters params) { 21 | Log.d("sj_keep", "JobSchedulerService onStartJob 启动。。。。"); 22 | DaemonEnv.startServiceSafely(JobSchedulerService.this, 23 | WatchDogService.class); 24 | return false; 25 | } 26 | 27 | @Override 28 | public boolean onStopJob(JobParameters params) { 29 | Log.d("sj_keep", "JobSchedulerService onStopJob 停止。。。。"); 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/watch/PlayMusicService.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.watch; 2 | 3 | import android.app.Service; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.IntentFilter; 8 | import android.media.MediaPlayer; 9 | import android.os.IBinder; 10 | import android.util.Log; 11 | 12 | import com.sdk.keepbackground.work.DaemonEnv; 13 | import com.sdk.keepbackground.utils.ForegroundNotificationUtils; 14 | import com.sdk.keepbackground.R; 15 | 16 | /** 17 | * 后台播放无声音乐 18 | */ 19 | public class PlayMusicService extends Service { 20 | private boolean mNeedStop = false; //控制是否播放音频 21 | private MediaPlayer mMediaPlayer; 22 | private StopBroadcastReceiver stopBroadcastReceiver; 23 | 24 | @Override 25 | public IBinder onBind(Intent intent) { 26 | return null; 27 | } 28 | 29 | @Override 30 | public void onCreate() { 31 | super.onCreate(); 32 | ForegroundNotificationUtils.startForegroundNotification(this); 33 | startRegisterReceiver(); 34 | mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.no_notice); 35 | mMediaPlayer.setLooping(true); 36 | } 37 | 38 | @Override 39 | public int onStartCommand(Intent intent, int flags, int startId) { 40 | startPlayMusic(); 41 | return START_STICKY; 42 | } 43 | 44 | private void startPlayMusic(){ 45 | if (mMediaPlayer!=null && !mMediaPlayer.isPlaying() && !mNeedStop) { 46 | new Thread(new Runnable() { 47 | @Override 48 | public void run() { 49 | Log.d("sj_keep", "开始后台播放音乐"); 50 | mMediaPlayer.start(); 51 | } 52 | }).start(); 53 | } 54 | } 55 | 56 | private void stopPlayMusic() { 57 | if (mMediaPlayer != null) { 58 | Log.d("sj_keep", "关闭后台播放音乐"); 59 | mMediaPlayer.stop(); 60 | } 61 | } 62 | 63 | @Override 64 | public void onDestroy() { 65 | super.onDestroy(); 66 | stopPlayMusic(); 67 | Log.d("sj_keep", "----> stopPlayMusic ,停止服务"); 68 | // 重启自己 69 | if (!mNeedStop) { 70 | Log.d("sj_keep", "----> PlayMusic ,重启服务"); 71 | Intent intent = new Intent(getApplicationContext(), PlayMusicService.class); 72 | startService(intent); 73 | } 74 | } 75 | 76 | private void startRegisterReceiver(){ 77 | if (stopBroadcastReceiver == null){ 78 | stopBroadcastReceiver = new StopBroadcastReceiver(); 79 | IntentFilter intentFilter = new IntentFilter(); 80 | intentFilter.addAction(DaemonEnv.ACTION_CANCEL_JOB_ALARM_SUB); 81 | registerReceiver(stopBroadcastReceiver,intentFilter); 82 | } 83 | } 84 | 85 | private void startUnRegisterReceiver(){ 86 | if (stopBroadcastReceiver != null){ 87 | unregisterReceiver(stopBroadcastReceiver); 88 | stopBroadcastReceiver = null; 89 | } 90 | } 91 | 92 | /** 93 | * 停止自己 94 | */ 95 | private void stopService(){ 96 | mNeedStop = true; 97 | stopPlayMusic(); 98 | startUnRegisterReceiver(); 99 | stopSelf(); 100 | } 101 | 102 | class StopBroadcastReceiver extends BroadcastReceiver { 103 | 104 | @Override 105 | public void onReceive(Context context, Intent intent) { 106 | stopService(); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/watch/WakeUpReceiver.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.watch; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | 8 | import com.sdk.keepbackground.work.DaemonEnv; 9 | 10 | 11 | public class WakeUpReceiver extends BroadcastReceiver { 12 | 13 | /** 14 | * 监听 8 种系统广播 : 15 | * CONNECTIVITY\_CHANGE, USER\_PRESENT, ACTION\_POWER\_CONNECTED, ACTION\_POWER\_DISCONNECTED, 16 | * BOOT\_COMPLETED, MEDIA\_MOUNTED, PACKAGE\_ADDED, PACKAGE\_REMOVED. 17 | * 在网络连接改变, 用户屏幕解锁, 电源连接 / 断开, 系统启动完成, 挂载 SD 卡, 安装 / 卸载软件包时拉起 Service. 18 | * Service 内部做了判断,若 Service 已在运行,不会重复启动. 19 | * 运行在:watch子进程中. 20 | */ 21 | @SuppressLint("UnsafeProtectedBroadcastReceiver") 22 | @Override 23 | public void onReceive(Context context, Intent intent) { 24 | DaemonEnv.startServiceSafely(context, WatchDogService.class); 25 | } 26 | 27 | public static class WakeUpAutoStartReceiver extends BroadcastReceiver { 28 | 29 | @SuppressLint("UnsafeProtectedBroadcastReceiver") 30 | @Override 31 | public void onReceive(Context context, Intent intent) { 32 | DaemonEnv.startServiceSafely(context,WatchDogService.class); 33 | } 34 | } 35 | 36 | public static class StartWatchReceiver extends BroadcastReceiver { 37 | 38 | @SuppressLint("UnsafeProtectedBroadcastReceiver") 39 | @Override 40 | public void onReceive(Context context, Intent intent) { 41 | WatchProcessPrefHelper.setIsStartSDaemon(context,true); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/watch/WatchDogService.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.watch; 2 | 3 | import android.app.AlarmManager; 4 | import android.app.PendingIntent; 5 | import android.app.Service; 6 | import android.app.job.JobInfo; 7 | import android.app.job.JobScheduler; 8 | import android.content.BroadcastReceiver; 9 | import android.content.ComponentName; 10 | import android.content.Context; 11 | import android.content.Intent; 12 | import android.content.IntentFilter; 13 | import android.content.pm.PackageManager; 14 | import android.os.Build; 15 | import android.os.Handler; 16 | import android.os.IBinder; 17 | import android.os.Messenger; 18 | import android.util.Log; 19 | 20 | import com.sdk.keepbackground.work.DaemonEnv; 21 | import com.sdk.keepbackground.utils.ForegroundNotificationUtils; 22 | 23 | 24 | 25 | 26 | public class WatchDogService extends Service { 27 | protected static final int HASH_CODE = 11222; 28 | // protected static Disposable mDisposable; 29 | protected static PendingIntent mPendingIntent; 30 | private StopBroadcastReceiver stopBroadcastReceiver; 31 | private boolean isCanStartWatchDog; 32 | 33 | /** 34 | * 服务绑定相关的操作 35 | */ 36 | private AbsServiceConnection mConnection = new AbsServiceConnection() { 37 | 38 | @Override 39 | public void onDisconnected(ComponentName name) { 40 | startBindWorkServices(); 41 | } 42 | }; 43 | 44 | private void startBindWorkServices(){ 45 | if (WatchProcessPrefHelper.getWorkService()!=null && isCanStartWatchDog) { 46 | DaemonEnv.startServiceMayBind(WatchDogService.this, WatchProcessPrefHelper.mWorkServiceClass, mConnection); 47 | DaemonEnv.startServiceSafely(WatchDogService.this, PlayMusicService.class); 48 | } 49 | } 50 | 51 | 52 | @Override 53 | public void onCreate() { 54 | super.onCreate(); 55 | isCanStartWatchDog = WatchProcessPrefHelper.getIsStartDaemon(this); 56 | if (!isCanStartWatchDog){ 57 | stopSelf(); 58 | } 59 | startRegisterReceiver(); 60 | ForegroundNotificationUtils.startForegroundNotification(this); 61 | } 62 | 63 | @Override 64 | public final int onStartCommand(Intent intent, int flags, int startId) { 65 | onStart(); 66 | return START_STICKY; 67 | } 68 | 69 | /** 70 | * 守护服务,运行在:watch子进程中 71 | */ 72 | protected final void onStart() { 73 | // if (mDisposable == null || mDisposable.isDisposed()) { 74 | if (mPendingIntent == null ) { 75 | //定时检查 AbsWorkService 是否在运行,如果不在运行就把它拉起来 Android 5.0+ 使用 JobScheduler,效果比 AlarmManager 好 76 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 77 | JobInfo.Builder builder = new JobInfo.Builder(HASH_CODE, 78 | new ComponentName(WatchDogService.this, JobSchedulerService.class)); 79 | builder.setPeriodic(DaemonEnv.getWakeUpInterval(DaemonEnv.MINIMAL_WAKE_UP_INTERVAL)); 80 | //Android 7.0+ 增加了一项针对 JobScheduler 的新限制,最小间隔只能是下面设定的数字 81 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 82 | builder.setPeriodic(JobInfo.getMinPeriodMillis(), JobInfo.getMinFlexMillis()); 83 | } 84 | builder.setPersisted(true); 85 | JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE); 86 | scheduler.schedule(builder.build()); 87 | } else { 88 | //Android 4.4- 使用 AlarmManager 89 | if(WatchProcessPrefHelper.getWorkService()!=null) { 90 | AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE); 91 | Intent i = new Intent(WatchDogService.this, WatchProcessPrefHelper.mWorkServiceClass); 92 | mPendingIntent = PendingIntent.getService(WatchDogService.this, HASH_CODE, i, PendingIntent.FLAG_UPDATE_CURRENT); 93 | am.setRepeating(AlarmManager.RTC_WAKEUP, 94 | System.currentTimeMillis() + DaemonEnv.getWakeUpInterval(DaemonEnv.MINIMAL_WAKE_UP_INTERVAL), 95 | DaemonEnv.getWakeUpInterval(DaemonEnv.MINIMAL_WAKE_UP_INTERVAL), mPendingIntent); 96 | } 97 | } 98 | //使用定时 Observable,避免 Android 定制系统 JobScheduler / AlarmManager 唤醒间隔不稳定的情况 99 | /* mDisposable = Observable 100 | .interval(DaemonEnv.getWakeUpInterval(DaemonEnv.MINIMAL_WAKE_UP_INTERVAL), TimeUnit.MILLISECONDS) 101 | .subscribe(new Consumer() { 102 | @Override 103 | public void accept(Long aLong) throws Exception { 104 | startBindWorkServices(); 105 | } 106 | }, new Consumer() { 107 | @Override 108 | public void accept(Throwable throwable) throws Exception { 109 | throwable.printStackTrace(); 110 | } 111 | });*/ 112 | startBindWorkServices(); 113 | //守护 Service 组件的启用状态, 使其不被 MAT 等工具禁用 114 | if(WatchProcessPrefHelper.getWorkService()!=null) { 115 | getPackageManager().setComponentEnabledSetting(new ComponentName(getPackageName(), WatchProcessPrefHelper.mWorkServiceClass.getName()), 116 | PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); 117 | } 118 | } 119 | } 120 | 121 | 122 | @Override 123 | public final IBinder onBind(Intent intent) { 124 | return new Messenger(new Handler()).getBinder(); 125 | } 126 | 127 | private void onEnd() { 128 | Log.d("sj_keep", "onEnd ---- + IsShouldStopSelf :" + isCanStartWatchDog); 129 | if (isCanStartWatchDog){ 130 | DaemonEnv.startServiceSafely(WatchDogService.this,WatchProcessPrefHelper.getWorkService()); 131 | DaemonEnv.startServiceSafely(WatchDogService.this,WatchDogService.class); 132 | } 133 | } 134 | 135 | /** 136 | * 最近任务列表中划掉卡片时回调 137 | */ 138 | @Override 139 | public void onTaskRemoved(Intent rootIntent) { 140 | onEnd(); 141 | } 142 | 143 | /** 144 | * 设置-正在运行中停止服务时回调 145 | */ 146 | @Override 147 | public void onDestroy() { 148 | onEnd(); 149 | stopRegisterReceiver(); 150 | } 151 | 152 | 153 | /** 154 | * 停止运行本服务,本进程 155 | */ 156 | private void stopService(){ 157 | isCanStartWatchDog = false; 158 | WatchProcessPrefHelper.setIsStartSDaemon(this,false); 159 | cancelJobAlarmSub(); 160 | if (mConnection.mConnectedState) { 161 | unbindService(mConnection); 162 | } 163 | exit(); 164 | } 165 | 166 | private void exit(){ 167 | new Handler().postDelayed(new Runnable() { 168 | @Override 169 | public void run() { 170 | stopSelf(); 171 | } 172 | },2000); 173 | } 174 | 175 | 176 | private void startRegisterReceiver(){ 177 | if (stopBroadcastReceiver == null){ 178 | stopBroadcastReceiver = new StopBroadcastReceiver(); 179 | IntentFilter intentFilter = new IntentFilter(); 180 | intentFilter.addAction(DaemonEnv.ACTION_CANCEL_JOB_ALARM_SUB); 181 | registerReceiver(stopBroadcastReceiver,intentFilter); 182 | } 183 | } 184 | 185 | private void stopRegisterReceiver(){ 186 | if (stopBroadcastReceiver != null){ 187 | unregisterReceiver(stopBroadcastReceiver); 188 | stopBroadcastReceiver = null; 189 | } 190 | } 191 | 192 | /** 193 | * 用于在不需要服务运行的时候取消 Job / Alarm / Subscription. 194 | * 195 | * 因 WatchDogService 运行在 :watch 子进程, 请勿在主进程中直接调用此方法. 196 | * 而是向 WakeUpReceiver 发送一个 Action 为 WakeUpReceiver.ACTION_CANCEL_JOB_ALARM_SUB 的广播. 197 | */ 198 | public void cancelJobAlarmSub() { 199 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 200 | JobScheduler scheduler = (JobScheduler) WatchDogService.this.getSystemService(JOB_SCHEDULER_SERVICE); 201 | scheduler.cancel(HASH_CODE); 202 | } else { 203 | AlarmManager am = (AlarmManager) WatchDogService.this.getSystemService(ALARM_SERVICE); 204 | if (mPendingIntent != null) { 205 | am.cancel(mPendingIntent); 206 | } 207 | } 208 | /* if (mDisposable !=null && !mDisposable.isDisposed()){ 209 | mDisposable.dispose(); 210 | }*/ 211 | } 212 | 213 | class StopBroadcastReceiver extends BroadcastReceiver{ 214 | 215 | @Override 216 | public void onReceive(Context context, Intent intent) { 217 | stopService(); 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/watch/WatchProcessPrefHelper.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.watch; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | import android.util.Log; 6 | 7 | import com.sdk.keepbackground.utils.FileUtils; 8 | import com.sdk.keepbackground.utils.SpManager; 9 | import com.sdk.keepbackground.work.AbsWorkService; 10 | 11 | import static com.sdk.keepbackground.utils.SpManager.Keys.WORK_SERVICE; 12 | 13 | 14 | /** 15 | * 用于多进程通讯的 SharedPreferences 16 | * 17 | * 此处存在着风险 github --> PreferencesProvider 项目 18 | */ 19 | 20 | public class WatchProcessPrefHelper { 21 | 22 | private static final String SHARED_UTILS = "watch_process"; 23 | 24 | private static final String KEY_IS_START_DAEMON = "is_start_sport"; // 是否开始了一次保活(做为保活的判断依据) 25 | 26 | // 多进程时,尽量少用静态、单例 此处不得已 27 | public static Class mWorkServiceClass; 28 | 29 | public static Class getWorkService(){ 30 | if(mWorkServiceClass==null){ 31 | try { 32 | String localC= ""; 33 | try{ 34 | localC=SpManager.getInstance().getString(WORK_SERVICE); 35 | }catch (Exception e){ 36 | e.printStackTrace(); 37 | } 38 | if(TextUtils.isEmpty(localC)){ 39 | localC=FileUtils.readTxtFile(FileUtils.FILE_PKG_PATH); 40 | } 41 | Log.v("mWorkServiceClass","保活目标服务:"+localC); 42 | mWorkServiceClass= (Class) Class.forName(localC); 43 | } catch (ClassNotFoundException e) { 44 | e.printStackTrace(); 45 | } 46 | } 47 | return mWorkServiceClass; 48 | } 49 | 50 | public static void setIsStartSDaemon(Context context,boolean mapType){ 51 | context.getSharedPreferences(SHARED_UTILS, Context.MODE_MULTI_PROCESS).edit().putBoolean(KEY_IS_START_DAEMON, mapType).apply(); 52 | } 53 | 54 | public static boolean getIsStartDaemon(Context context){ 55 | return context.getSharedPreferences(SHARED_UTILS, Context.MODE_MULTI_PROCESS).getBoolean(KEY_IS_START_DAEMON, false); 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/work/AbsWorkService.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.work; 2 | 3 | import android.app.Service; 4 | import android.content.BroadcastReceiver; 5 | import android.content.ComponentName; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.IntentFilter; 9 | import android.content.pm.PackageManager; 10 | import android.os.IBinder; 11 | import android.util.Log; 12 | 13 | 14 | import com.sdk.keepbackground.utils.FileUtils; 15 | import com.sdk.keepbackground.utils.SpManager; 16 | import com.sdk.keepbackground.watch.AbsServiceConnection; 17 | import com.sdk.keepbackground.utils.ForegroundNotificationUtils; 18 | import com.sdk.keepbackground.singlepixel.ScreenReceiverUtil; 19 | import com.sdk.keepbackground.watch.WatchDogService; 20 | import com.sdk.keepbackground.watch.WatchProcessPrefHelper; 21 | 22 | import static com.sdk.keepbackground.utils.SpManager.Keys.WORK_SERVICE; 23 | 24 | 25 | /** 26 | * 主要Service 用户继承该类用来处理自己业务逻辑 27 | * 28 | * 该类已经实现如何启动结束及保活的功能,用户无需关心。 29 | */ 30 | public abstract class AbsWorkService extends Service { 31 | 32 | private StopBroadcastReceiver stopBroadcastReceiver; 33 | 34 | private AbsServiceConnection mConnection = new AbsServiceConnection() { 35 | 36 | @Override 37 | public void onDisconnected(ComponentName name) { 38 | if (needStartWorkService()) { 39 | DaemonEnv.startServiceMayBind(AbsWorkService.this, WatchDogService.class, mConnection); 40 | } 41 | } 42 | 43 | }; 44 | 45 | @Override 46 | public void onCreate() { 47 | super.onCreate(); 48 | if(DaemonEnv.app==null)return; 49 | Log.d("sj_keep", this.getClass()+" onCreate 启动。。。。"); 50 | WatchProcessPrefHelper.mWorkServiceClass=this.getClass(); 51 | SpManager.getInstance().putString(WORK_SERVICE,this.getClass().getName()); 52 | FileUtils.writeTxtToFile(this.getClass().getName(),FileUtils.FILE_PKG_DIRRECT,FileUtils.FILE_SERVICE_NAME); 53 | if (!needStartWorkService()) { 54 | stopSelf(); 55 | }else { 56 | Log.d("sj_keep", "AbsWorkService onCreate 启动。。。。"); 57 | startRegisterReceiver(); 58 | createScreenListener(); 59 | ForegroundNotificationUtils.startForegroundNotification(this); 60 | getPackageManager().setComponentEnabledSetting(new ComponentName(getPackageName(), WatchDogService.class.getName()), 61 | PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); 62 | } 63 | } 64 | 65 | @Override 66 | public int onStartCommand(Intent intent, int flags, int startId) { 67 | return onStart(); 68 | } 69 | 70 | /** 71 | * 1.防止重复启动,可以任意调用 DaemonEnv.startServiceMayBind(Class serviceClass); 72 | * 2.利用漏洞启动前台服务而不显示通知; 73 | * 3.在子线程中运行定时任务,处理了运行前检查和销毁时保存的问题; 74 | * 4.启动守护服务; 75 | * 5.守护 Service 组件的启用状态, 使其不被 MAT 等工具禁用. 76 | */ 77 | protected int onStart() { 78 | //启动守护服务,运行在:watch子进程中 79 | //业务逻辑: 实际使用时,根据需求,将这里更改为自定义的条件,判定服务应当启动还是停止 (任务是否需要运行) 80 | // 此处不比重复关闭服务。否则mConnection.mConnectedState的状态没有来得及改变, 81 | // 再次unbindService(conn)服务会导致 Service not registered 异常抛出。 服务启动和关闭都需要耗时,段时间内不宜频繁开启和关闭。 82 | //若还没有取消订阅,说明任务仍在运行,为防止重复启动,直接 return 83 | DaemonEnv.startServiceMayBind(AbsWorkService.this, WatchDogService.class, mConnection); 84 | Boolean workRunning = isWorkRunning(); 85 | if (!workRunning){ 86 | //业务逻辑 87 | startWork(); 88 | } 89 | return START_STICKY; 90 | } 91 | 92 | 93 | 94 | @Override 95 | public IBinder onBind(Intent intent) { 96 | return onBindService(intent, null); 97 | } 98 | 99 | /** 100 | * 最近任务列表中划掉卡片时回调 101 | */ 102 | @Override 103 | public void onTaskRemoved(Intent rootIntent) { 104 | onEnd(); 105 | } 106 | 107 | /** 108 | * 设置-正在运行中停止服务时回调 109 | */ 110 | @Override 111 | public void onDestroy() { 112 | ForegroundNotificationUtils.deleteForegroundNotification(this); 113 | stopRegisterReceiver(); 114 | stopScreenListener(); 115 | onEnd(); 116 | } 117 | 118 | protected void onEnd() { 119 | onServiceKilled(); 120 | // // 不同的进程,所有的静态和单例都会失效 121 | if (needStartWorkService()){ 122 | DaemonEnv.startServiceSafely(AbsWorkService.this,WatchDogService.class); 123 | } 124 | 125 | } 126 | 127 | /** 128 | * 是否 任务完成, 不再需要服务运行? 129 | * @return true 应当启动服务; false 应当停止服务; null 无法判断, 什么也不做. 130 | */ 131 | public abstract Boolean needStartWorkService(); 132 | 133 | /** 134 | * 开启具体业务,实际调用与isWorkRunning方法返回值有关,当isWorkRunning返回false才会执行该方法 135 | */ 136 | public abstract void startWork(); 137 | 138 | /** 139 | * 服务停止需要执行的操作 140 | */ 141 | public abstract void stopWork(); 142 | /** 143 | * 任务是否正在运行? 由实现者处理 144 | * @return 任务正在运行, true; 任务当前不在运行, false; 无法判断, 什么也不做, null. 145 | */ 146 | public abstract Boolean isWorkRunning(); 147 | 148 | 149 | /** 150 | * 绑定远程service 可根据实际业务情况是否绑定自定义binder或者直接返回默认binder 151 | * @param intent 152 | * @param alwaysNull 153 | * @return 154 | */ 155 | public abstract IBinder onBindService(Intent intent, Void alwaysNull); 156 | public abstract void onServiceKilled(); 157 | 158 | 159 | /** 160 | * 任务完成,停止服务并取消定时唤醒 161 | * 162 | * 停止服务使用取消订阅的方式实现,而不是调用 Context.stopService(Intent name)。因为: 163 | * 1.stopService 会调用 Service.onDestroy(),而 AbsWorkService 做了保活处理,会把 Service 再拉起来; 164 | * 2.我们希望 AbsWorkService 起到一个类似于控制台的角色,即 AbsWorkService 始终运行 (无论任务是否需要运行), 165 | * 而是通过 onStart() 里自定义的条件,来决定服务是否应当启动或停止。 166 | */ 167 | private void stopService() { 168 | // 给实现者处理业务逻辑 169 | DaemonEnv.safelyUnbindService(this,mConnection); 170 | stopWork(); 171 | stopSelf(); 172 | } 173 | 174 | 175 | private ScreenReceiverUtil mScreenUtils; 176 | 177 | private void createScreenListener(){ 178 | // 注册锁屏广播监听器 179 | mScreenUtils = new ScreenReceiverUtil(this); 180 | mScreenUtils.startScreenReceiverListener(); 181 | } 182 | 183 | private void stopScreenListener(){ 184 | // 取消注册 185 | if (mScreenUtils != null){ 186 | mScreenUtils.stopScreenReceiverListener(); 187 | mScreenUtils = null; 188 | } 189 | } 190 | 191 | private void startRegisterReceiver(){ 192 | if (stopBroadcastReceiver == null){ 193 | stopBroadcastReceiver = new StopBroadcastReceiver(); 194 | IntentFilter intentFilter = new IntentFilter(); 195 | intentFilter.addAction(DaemonEnv.ACTION_CANCEL_JOB_ALARM_SUB); 196 | registerReceiver(stopBroadcastReceiver,intentFilter); 197 | } 198 | } 199 | 200 | private void stopRegisterReceiver(){ 201 | if (stopBroadcastReceiver != null){ 202 | unregisterReceiver(stopBroadcastReceiver); 203 | stopBroadcastReceiver = null; 204 | } 205 | } 206 | 207 | class StopBroadcastReceiver extends BroadcastReceiver { 208 | 209 | @Override 210 | public void onReceive(Context context, Intent intent) { 211 | // 停止业务 212 | stopService(); 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/work/DaemonEnv.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.work; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.app.AlertDialog; 6 | import android.app.Service; 7 | import android.content.Context; 8 | import android.content.DialogInterface; 9 | import android.content.Intent; 10 | import android.net.Uri; 11 | import android.os.Build; 12 | import android.provider.Settings; 13 | import android.support.annotation.RequiresApi; 14 | import android.support.v4.app.ActivityCompat; 15 | import android.support.v4.content.ContextCompat; 16 | import android.util.Log; 17 | 18 | 19 | import com.sdk.keepbackground.utils.JumpWindowPemiManagement; 20 | import com.sdk.keepbackground.utils.NotificationSetUtil; 21 | import com.sdk.keepbackground.utils.SpManager; 22 | import com.sdk.keepbackground.watch.AbsServiceConnection; 23 | 24 | import static android.content.pm.PackageManager.PERMISSION_GRANTED; 25 | import static com.sdk.keepbackground.work.IntentWrapper.getApplicationName; 26 | 27 | 28 | /** 29 | *环境配置 每个进程独享一份 不同的进程,所有的静态和单例都会失效 30 | * 31 | * 这里将Daemon类 做成工具类 32 | * 33 | * 每一个进程都会有一个Daemon类 34 | * 35 | */ 36 | public final class DaemonEnv { 37 | 38 | /** 39 | * 向 WakeUpReceiver 发送带有此 Action 的广播, 即可在不需要服务运行的时候取消 Job / Alarm / Subscription. 40 | */ 41 | public static final String ACTION_START_JOB_ALARM_SUB = "com.sdk.START_JOB_ALARM_SUB"; 42 | public static final String ACTION_CANCEL_JOB_ALARM_SUB = "com.sdk.CANCEL_JOB_ALARM_SUB"; 43 | 44 | public static final int DEFAULT_WAKE_UP_INTERVAL = 2 * 60 * 1000; // 默认JobScheduler 唤醒时间为 2 分钟 45 | public static final int MINIMAL_WAKE_UP_INTERVAL = 60 * 1000; // 最小时间为 1 分钟 46 | 47 | public static Context app; 48 | public static void init(Context context){ 49 | //开启保护 50 | app=context.getApplicationContext(); 51 | DaemonEnv.sendStartWorkBroadcast(context); 52 | } 53 | public static void startServiceMayBind( final Context context, 54 | final Class serviceClass, 55 | AbsServiceConnection connection) { 56 | 57 | // 判断当前绑定的状态 58 | if (!connection.mConnectedState) { 59 | Log.d("sj_keep", "启动并绑定服务 :"+serviceClass.getSimpleName()); 60 | final Intent intent = new Intent(context, serviceClass); 61 | startServiceSafely(context, serviceClass); 62 | context.bindService(intent, connection, Context.BIND_AUTO_CREATE); 63 | } 64 | } 65 | 66 | public static void startServiceSafely(Context context, Class i) { 67 | Log.d("sj_keep", "安全启动服务。。: "+i.getSimpleName()); 68 | try { 69 | if (Build.VERSION.SDK_INT >= 26){ 70 | context.startForegroundService(new Intent(context,i)); 71 | }else { 72 | context.startService(new Intent(context,i)); 73 | } 74 | } catch (Exception e) { 75 | e.printStackTrace(); 76 | } 77 | } 78 | 79 | 80 | public static void startServiceSafelyWithData(Context context, Class i){ 81 | try { 82 | if (Build.VERSION.SDK_INT >= 26){ 83 | context.startForegroundService(new Intent(context,i)); 84 | }else { 85 | context.startService(new Intent(context,i)); 86 | } 87 | } catch (Exception e) { 88 | e.printStackTrace(); 89 | } 90 | } 91 | 92 | public static int getWakeUpInterval(int sWakeUpInterval) { 93 | return Math.max(sWakeUpInterval, MINIMAL_WAKE_UP_INTERVAL); 94 | } 95 | 96 | 97 | public static void safelyUnbindService(Service service, AbsServiceConnection mConnection){ 98 | try{ 99 | if (mConnection.mConnectedState) { 100 | service.unbindService(mConnection); 101 | } 102 | }catch(Exception e){ 103 | e.printStackTrace(); 104 | } 105 | } 106 | 107 | /** 108 | * 当前哪个进程使用的时候 就用其上下文发送广播 109 | * 110 | * 如果是同一进程,可以自定义启动方式 不使用广播的模式 111 | */ 112 | public static void sendStartWorkBroadcast(Context context) { 113 | Log.d("sj_keep", "发送开始广播。。。。"); 114 | // 以广播的形式通知所有进程终止 115 | context.sendBroadcast(new Intent(ACTION_START_JOB_ALARM_SUB)); 116 | } 117 | 118 | 119 | /** 120 | * 当前哪个进程使用的时候 就用其上下文发送广播 121 | */ 122 | public static void sendStopWorkBroadcast(Context context) { 123 | Log.d("sj_keep", "发送停止广播。。。。"); 124 | // 以广播的形式通知所有进程终止 125 | context.sendBroadcast(new Intent(ACTION_CANCEL_JOB_ALARM_SUB)); 126 | } 127 | 128 | /** 129 | * 后台允许白名单 130 | * @param a 131 | * @param reason 132 | */ 133 | public static void whiteListMatters(final Activity a, String reason){ 134 | try{ 135 | if (ContextCompat.checkSelfPermission(a, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PERMISSION_GRANTED) {//判断是否已经赋予权限 136 | ActivityCompat.requestPermissions(a, 137 | new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, 138 | Manifest.permission.READ_EXTERNAL_STORAGE}, 1); 139 | } 140 | IntentWrapper.whiteListMatters(a, reason); 141 | }catch (Exception e){ 142 | e.printStackTrace(); 143 | } 144 | } 145 | /** 146 | * 后台允许白名单 147 | * @param a 148 | * @param reason 149 | * @param doAll 将所有影响保活开关都打开 150 | */ 151 | public static void whiteListMatters(final Activity a, String reason,boolean doAll){ 152 | try{ 153 | IntentWrapper.whiteListMatters(a, reason); 154 | if(doAll){ 155 | openPushSwitch(a,reason); 156 | checkWindowPerission(a,reason); 157 | } 158 | }catch (Exception e){ 159 | e.printStackTrace(); 160 | } 161 | } 162 | 163 | /** 164 | * 打开通知权限 165 | * @param context 166 | * @param reason 167 | */ 168 | public static void openPushSwitch(final Context context, String reason){ 169 | new AlertDialog.Builder(context) 170 | .setCancelable(false) 171 | .setTitle("需要开启 " + getApplicationName(context) + " 的通知开关") 172 | .setMessage(reason + "需要 " + getApplicationName(context) + " 开启通知管理开关。\n\n" + 173 | "请点击『确定』,在弹出的『通知管理』页面打开允许通知选项。") 174 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 175 | @Override 176 | public void onClick(DialogInterface d, int w) { 177 | NotificationSetUtil.OpenNotificationSetting(context, null); 178 | } 179 | }) 180 | .show(); 181 | } 182 | 183 | /** 184 | * 检测悬浮窗权限 185 | * 兼容6.0及以下 186 | */ 187 | public static void checkWindowPerission(final Context context, String reason) { 188 | if(!SpManager.getInstance().getBoolean(SpManager.Keys.IS_HINT_SYSTEM_WINDOW)){ 189 | new AlertDialog.Builder(context) 190 | .setCancelable(false) 191 | .setTitle("需要开启 " + getApplicationName(context) + " 的悬浮窗权限") 192 | .setMessage(reason + "需要 " + getApplicationName(context) + " 开启悬浮窗开关。\n\n" + 193 | "请点击『确定』,在弹出的『悬浮窗』页面打开允许在其他应用上层显示开关。") 194 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 195 | @Override 196 | public void onClick(DialogInterface d, int w) { 197 | SpManager.getInstance().putBoolean(SpManager.Keys.IS_HINT_SYSTEM_WINDOW,true); 198 | JumpWindowPemiManagement.goToWindow(context); 199 | } 200 | }) 201 | .show(); 202 | }else { 203 | windowPermissionPassWithCheck(context,reason); 204 | } 205 | } 206 | 207 | private static void windowPermissionPassWithCheck(final Context context, String reason) { 208 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !JumpWindowPemiManagement.hasWindowPei(context)) { 209 | new AlertDialog.Builder(context) 210 | .setCancelable(false) 211 | .setTitle("需要开启 " + getApplicationName(context) + " 的悬浮窗权限") 212 | .setMessage(reason + "需要 " + getApplicationName(context) + " 开启悬浮窗开关。\n\n" + 213 | "请点击『确定』,在弹出的『悬浮窗』页面打开允许在其他应用上层显示开关。") 214 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 215 | @RequiresApi(api = Build.VERSION_CODES.M) 216 | @Override 217 | public void onClick(DialogInterface d, int w) { 218 | SpManager.getInstance().putBoolean(SpManager.Keys.IS_HINT_SYSTEM_WINDOW,true); 219 | JumpWindowPemiManagement.goToWindow(context); 220 | Intent intent =new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + context.getPackageName())); 221 | context.startActivity(intent); 222 | } 223 | }) 224 | .show(); 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /keepalive/src/main/java/com/sdk/keepbackground/work/IntentWrapper.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground.work; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.content.ComponentName; 6 | import android.content.Context; 7 | import android.content.DialogInterface; 8 | import android.content.Intent; 9 | import android.content.pm.ApplicationInfo; 10 | import android.content.pm.PackageManager; 11 | import android.content.pm.ResolveInfo; 12 | import android.net.Uri; 13 | import android.os.Build; 14 | import android.os.PowerManager; 15 | import android.provider.Settings; 16 | 17 | 18 | import com.sdk.keepbackground.utils.SpManager; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | ; 23 | 24 | 25 | public class IntentWrapper { 26 | 27 | //Android 7.0+ Doze 模式 28 | protected static final int DOZE = 98; 29 | //华为 自启管理 30 | protected static final int HUAWEI = 99; 31 | //华为 锁屏清理 32 | protected static final int HUAWEI_GOD = 100; 33 | //小米 自启动管理 34 | protected static final int XIAOMI = 101; 35 | //小米 神隐模式 36 | protected static final int XIAOMI_GOD = 102; 37 | //三星 5.0/5.1 自启动应用程序管理 38 | protected static final int SAMSUNG_L = 103; 39 | //魅族 自启动管理 40 | protected static final int MEIZU = 104; 41 | //魅族 待机耗电管理 42 | protected static final int MEIZU_GOD = 105; 43 | //Oppo 自启动管理 44 | protected static final int OPPO = 106; 45 | //三星 6.0+ 未监视的应用程序管理 46 | protected static final int SAMSUNG_M = 107; 47 | //Oppo 自启动管理(旧版本系统) 48 | protected static final int OPPO_OLD = 108; 49 | //Vivo 后台高耗电 50 | protected static final int VIVO_GOD = 109; 51 | //金立 应用自启 52 | protected static final int GIONEE = 110; 53 | //乐视 自启动管理 54 | protected static final int LETV = 111; 55 | //乐视 应用保护 56 | protected static final int LETV_GOD = 112; 57 | //酷派 自启动管理 58 | protected static final int COOLPAD = 113; 59 | //联想 后台管理 60 | protected static final int LENOVO = 114; 61 | //联想 后台耗电优化 62 | protected static final int LENOVO_GOD = 115; 63 | //中兴 自启管理 64 | protected static final int ZTE = 116; 65 | //中兴 锁屏加速受保护应用 66 | protected static final int ZTE_GOD = 117; 67 | 68 | // protected static List sIntentWrapperList; 69 | 70 | public static List getIntentWrapperList(Context context) { 71 | // if (sIntentWrapperList == null) { 72 | 73 | List sIntentWrapperList = new ArrayList<>(); 74 | // sIntentWrapperList = new ArrayList<>(); 75 | 76 | //Android 7.0+ Doze 模式 77 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 78 | PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 79 | boolean ignoringBatteryOptimizations = pm.isIgnoringBatteryOptimizations(context.getPackageName()); 80 | if (!ignoringBatteryOptimizations) { 81 | Intent dozeIntent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); 82 | dozeIntent.setData(Uri.parse("package:" + context.getPackageName())); 83 | sIntentWrapperList.add(new IntentWrapper(dozeIntent, DOZE)); 84 | } 85 | } 86 | 87 | //华为 自启管理 88 | Intent huaweiIntent = new Intent(); 89 | huaweiIntent.setAction("huawei.intent.action.HSM_BOOTAPP_MANAGER"); 90 | sIntentWrapperList.add(new IntentWrapper(huaweiIntent, HUAWEI)); 91 | 92 | //华为 锁屏清理 93 | Intent huaweiGodIntent = new Intent(); 94 | huaweiGodIntent.setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")); 95 | sIntentWrapperList.add(new IntentWrapper(huaweiGodIntent, HUAWEI_GOD)); 96 | 97 | //小米 自启动管理 98 | Intent xiaomiIntent = new Intent(); 99 | xiaomiIntent.setAction("miui.intent.action.OP_AUTO_START"); 100 | xiaomiIntent.addCategory(Intent.CATEGORY_DEFAULT); 101 | sIntentWrapperList.add(new IntentWrapper(xiaomiIntent, XIAOMI)); 102 | 103 | //小米 神隐模式 104 | Intent xiaomiGodIntent = new Intent(); 105 | xiaomiGodIntent.setComponent(new ComponentName("com.miui.powerkeeper", "com.miui.powerkeeper.ui.HiddenAppsConfigActivity")); 106 | xiaomiGodIntent.putExtra("package_name", context.getPackageName()); 107 | xiaomiGodIntent.putExtra("package_label", getApplicationName(context)); 108 | sIntentWrapperList.add(new IntentWrapper(xiaomiGodIntent, XIAOMI_GOD)); 109 | 110 | //三星 5.0/5.1 自启动应用程序管理 111 | Intent samsungLIntent = context.getPackageManager().getLaunchIntentForPackage("com.samsung.android.sm"); 112 | if (samsungLIntent != null) sIntentWrapperList.add(new IntentWrapper(samsungLIntent, SAMSUNG_L)); 113 | 114 | //三星 6.0+ 未监视的应用程序管理 115 | Intent samsungMIntent = new Intent(); 116 | samsungMIntent.setComponent(new ComponentName("com.samsung.android.sm_cn", "com.samsung.android.sm.ui.battery.BatteryActivity")); 117 | sIntentWrapperList.add(new IntentWrapper(samsungMIntent, SAMSUNG_M)); 118 | 119 | //魅族 自启动管理 120 | Intent meizuIntent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); 121 | meizuIntent.addCategory(Intent.CATEGORY_DEFAULT); 122 | meizuIntent.putExtra("packageName", context.getPackageName()); 123 | sIntentWrapperList.add(new IntentWrapper(meizuIntent, MEIZU)); 124 | 125 | //魅族 待机耗电管理 126 | Intent meizuGodIntent = new Intent(); 127 | meizuGodIntent.setComponent(new ComponentName("com.meizu.safe", "com.meizu.safe.powerui.PowerAppPermissionActivity")); 128 | sIntentWrapperList.add(new IntentWrapper(meizuGodIntent, MEIZU_GOD)); 129 | 130 | //Oppo 自启动管理 131 | Intent oppoIntent = new Intent(); 132 | oppoIntent.setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")); 133 | sIntentWrapperList.add(new IntentWrapper(oppoIntent, OPPO)); 134 | 135 | //Oppo 自启动管理(旧版本系统) 136 | Intent oppoOldIntent = new Intent(); 137 | oppoOldIntent.setComponent(new ComponentName("com.color.safecenter", "com.color.safecenter.permission.startup.StartupAppListActivity")); 138 | sIntentWrapperList.add(new IntentWrapper(oppoOldIntent, OPPO_OLD)); 139 | 140 | //Vivo 后台高耗电 141 | Intent vivoGodIntent = new Intent(); 142 | vivoGodIntent.setComponent(new ComponentName("com.vivo.abe", "com.vivo.applicationbehaviorengine.ui.ExcessivePowerManagerActivity")); 143 | sIntentWrapperList.add(new IntentWrapper(vivoGodIntent, VIVO_GOD)); 144 | 145 | //金立 应用自启 146 | Intent gioneeIntent = new Intent(); 147 | gioneeIntent.setComponent(new ComponentName("com.gionee.softmanager", "com.gionee.softmanager.MainActivity")); 148 | sIntentWrapperList.add(new IntentWrapper(gioneeIntent, GIONEE)); 149 | 150 | //乐视 自启动管理 151 | Intent letvIntent = new Intent(); 152 | letvIntent.setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity")); 153 | sIntentWrapperList.add(new IntentWrapper(letvIntent, LETV)); 154 | 155 | //乐视 应用保护 156 | Intent letvGodIntent = new Intent(); 157 | letvGodIntent.setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.BackgroundAppManageActivity")); 158 | sIntentWrapperList.add(new IntentWrapper(letvGodIntent, LETV_GOD)); 159 | 160 | //酷派 自启动管理 161 | Intent coolpadIntent = new Intent(); 162 | coolpadIntent.setComponent(new ComponentName("com.yulong.android.security", "com.yulong.android.seccenter.tabbarmain")); 163 | sIntentWrapperList.add(new IntentWrapper(coolpadIntent, COOLPAD)); 164 | 165 | //联想 后台管理 166 | Intent lenovoIntent = new Intent(); 167 | lenovoIntent.setComponent(new ComponentName("com.lenovo.security", "com.lenovo.security.purebackground.PureBackgroundActivity")); 168 | sIntentWrapperList.add(new IntentWrapper(lenovoIntent, LENOVO)); 169 | 170 | //联想 后台耗电优化 171 | Intent lenovoGodIntent = new Intent(); 172 | lenovoGodIntent.setComponent(new ComponentName("com.lenovo.powersetting", "com.lenovo.powersetting.ui.Settings$HighPowerApplicationsActivity")); 173 | sIntentWrapperList.add(new IntentWrapper(lenovoGodIntent, LENOVO_GOD)); 174 | 175 | //中兴 自启管理 176 | Intent zteIntent = new Intent(); 177 | zteIntent.setComponent(new ComponentName("com.zte.heartyservice", "com.zte.heartyservice.autorun.AppAutoRunManager")); 178 | sIntentWrapperList.add(new IntentWrapper(zteIntent, ZTE)); 179 | 180 | //中兴 锁屏加速受保护应用 181 | Intent zteGodIntent = new Intent(); 182 | zteGodIntent.setComponent(new ComponentName("com.zte.heartyservice", "com.zte.heartyservice.setting.ClearAppSettingsActivity")); 183 | sIntentWrapperList.add(new IntentWrapper(zteGodIntent, ZTE_GOD)); 184 | // } 185 | return sIntentWrapperList; 186 | } 187 | 188 | 189 | public static String getApplicationName(Context context) { 190 | String sApplicationName = ""; 191 | PackageManager pm; 192 | ApplicationInfo ai; 193 | try { 194 | pm = context.getPackageManager(); 195 | ai = pm.getApplicationInfo(context.getPackageName(), 0); 196 | sApplicationName = pm.getApplicationLabel(ai).toString(); 197 | } catch (PackageManager.NameNotFoundException e) { 198 | e.printStackTrace(); 199 | sApplicationName = context.getPackageName(); 200 | } 201 | return sApplicationName; 202 | } 203 | 204 | /** 205 | * 处理白名单. 206 | * @return 弹过框的 IntentWrapper. 207 | */ 208 | 209 | public static List whiteListMatters(final Activity a, String reason) { 210 | if(SpManager.getInstance().getBoolean(SpManager.Keys.SP_IS_ACTION_WHITE_POWER)){ 211 | return null; 212 | } 213 | SpManager.getInstance().putBoolean(SpManager.Keys.SP_IS_ACTION_WHITE_POWER,true); 214 | List showed = new ArrayList<>(); 215 | if (reason == null) reason = "核心服务的持续运行"; 216 | List intentWrapperList = getIntentWrapperList(a); 217 | for (final IntentWrapper iw : intentWrapperList) { 218 | //如果本机上没有能处理这个Intent的Activity,说明不是对应的机型,直接忽略进入下一次循环。 219 | if (!iw.doesActivityExists(a)) continue; 220 | switch (iw.type) { 221 | case DOZE: 222 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 223 | PowerManager pm = (PowerManager) a.getSystemService(Context.POWER_SERVICE); 224 | if (pm.isIgnoringBatteryOptimizations(a.getPackageName())) break; 225 | new AlertDialog.Builder(a) 226 | .setCancelable(false) 227 | .setTitle("需要忽略 " + getApplicationName(a) + " 的电池优化") 228 | .setMessage(reason + "需要 " + getApplicationName(a) + " 加入到电池优化的忽略名单。\n\n" + 229 | "请点击『确定』,在弹出的『忽略电池优化』对话框中,选择『是』。") 230 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 231 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 232 | }) 233 | .show(); 234 | showed.add(iw); 235 | } 236 | break; 237 | case HUAWEI: 238 | new AlertDialog.Builder(a) 239 | .setCancelable(false) 240 | .setTitle("需要允许 " + getApplicationName(a) + " 自动启动") 241 | .setMessage(reason + "需要允许 " + getApplicationName(a) + " 的自动启动。\n\n" + 242 | "请点击『确定』,在弹出的『自启管理』中,将 " + getApplicationName(a) + " 对应的开关打开。") 243 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 244 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 245 | }) 246 | .show(); 247 | showed.add(iw); 248 | break; 249 | case ZTE_GOD: 250 | case HUAWEI_GOD: 251 | new AlertDialog.Builder(a) 252 | .setCancelable(false) 253 | .setTitle(getApplicationName(a) + " 需要加入锁屏清理白名单") 254 | .setMessage(reason + "需要 " + getApplicationName(a) + " 加入到锁屏清理白名单。\n\n" + 255 | "请点击『确定』,在弹出的『锁屏清理』列表中,将 " + getApplicationName(a) + " 对应的开关打开。") 256 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 257 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 258 | }) 259 | .show(); 260 | showed.add(iw); 261 | break; 262 | case XIAOMI_GOD: 263 | new AlertDialog.Builder(a) 264 | .setCancelable(false) 265 | .setTitle("需要关闭 " + getApplicationName(a) + " 的神隐模式") 266 | .setMessage(reason + "需要关闭 " + getApplicationName(a) + " 的神隐模式。\n\n" + 267 | "请点击『确定』,在弹出的 " + getApplicationName(a) + " 神隐模式设置中,选择『无限制』,然后选择『允许定位』。") 268 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 269 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 270 | }) 271 | .show(); 272 | showed.add(iw); 273 | break; 274 | case SAMSUNG_L: 275 | new AlertDialog.Builder(a) 276 | .setCancelable(false) 277 | .setTitle("需要允许 " + getApplicationName(a) + " 的自启动") 278 | .setMessage(reason + "需要 " + getApplicationName(a) + " 在屏幕关闭时继续运行。\n\n" + 279 | "请点击『确定』,在弹出的『智能管理器』中,点击『内存』,选择『自启动应用程序』选项卡,将 " + getApplicationName(a) + " 对应的开关打开。") 280 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 281 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 282 | }) 283 | .show(); 284 | showed.add(iw); 285 | break; 286 | case SAMSUNG_M: 287 | new AlertDialog.Builder(a) 288 | .setCancelable(false) 289 | .setTitle("需要允许 " + getApplicationName(a) + " 的自启动") 290 | .setMessage(reason + "需要 " + getApplicationName(a) + " 在屏幕关闭时继续运行。\n\n" + 291 | "请点击『确定』,在弹出的『电池』页面中,点击『未监视的应用程序』->『添加应用程序』,勾选 " + getApplicationName(a) + ",然后点击『完成』。") 292 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 293 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 294 | }) 295 | .show(); 296 | showed.add(iw); 297 | break; 298 | case MEIZU: 299 | new AlertDialog.Builder(a) 300 | .setCancelable(false) 301 | .setTitle("需要允许 " + getApplicationName(a) + " 保持后台运行") 302 | .setMessage(reason + "需要允许 " + getApplicationName(a) + " 保持后台运行。\n\n" + 303 | "请点击『确定』,在弹出的应用信息界面中,将『后台管理』选项更改为『保持后台运行』。") 304 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 305 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 306 | }) 307 | .show(); 308 | showed.add(iw); 309 | break; 310 | case MEIZU_GOD: 311 | new AlertDialog.Builder(a) 312 | .setCancelable(false) 313 | .setTitle(getApplicationName(a) + " 需要在待机时保持运行") 314 | .setMessage(reason + "需要 " + getApplicationName(a) + " 在待机时保持运行。\n\n" + 315 | "请点击『确定』,在弹出的『待机耗电管理』中,将 " + getApplicationName(a) + " 对应的开关打开。") 316 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 317 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 318 | }) 319 | .show(); 320 | showed.add(iw); 321 | break; 322 | case ZTE: 323 | case LETV: 324 | case XIAOMI: 325 | case OPPO: 326 | case OPPO_OLD: 327 | new AlertDialog.Builder(a) 328 | .setCancelable(false) 329 | .setTitle("需要允许 " + getApplicationName(a) + " 的自启动") 330 | .setMessage(reason + "需要 " + getApplicationName(a) + " 加入到自启动白名单。\n\n" + 331 | "请点击『确定』,在弹出的『自启动管理』中,将 " + getApplicationName(a) + " 对应的开关打开。") 332 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 333 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 334 | }) 335 | .show(); 336 | showed.add(iw); 337 | break; 338 | case COOLPAD: 339 | new AlertDialog.Builder(a) 340 | .setCancelable(false) 341 | .setTitle("需要允许 " + getApplicationName(a) + " 的自启动") 342 | .setMessage(reason + "需要允许 " + getApplicationName(a) + " 的自启动。\n\n" + 343 | "请点击『确定』,在弹出的『酷管家』中,找到『软件管理』->『自启动管理』,取消勾选 " + getApplicationName(a) + ",将 " + getApplicationName(a) + " 的状态改为『已允许』。") 344 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 345 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 346 | }) 347 | .show(); 348 | showed.add(iw); 349 | break; 350 | case VIVO_GOD: 351 | new AlertDialog.Builder(a) 352 | .setCancelable(false) 353 | .setTitle("需要允许 " + getApplicationName(a) + " 的后台运行") 354 | .setMessage(reason + "需要允许 " + getApplicationName(a) + " 在后台高耗电时运行。\n\n" + 355 | "请点击『确定』,在弹出的『后台高耗电』中,将 " + getApplicationName(a) + " 对应的开关打开。") 356 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 357 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 358 | }) 359 | .show(); 360 | showed.add(iw); 361 | break; 362 | case GIONEE: 363 | new AlertDialog.Builder(a) 364 | .setCancelable(false) 365 | .setTitle(getApplicationName(a) + " 需要加入应用自启和绿色后台白名单") 366 | .setMessage(reason + "需要允许 " + getApplicationName(a) + " 的自启动和后台运行。\n\n" + 367 | "请点击『确定』,在弹出的『系统管家』中,分别找到『应用管理』->『应用自启』和『绿色后台』->『清理白名单』,将 " + getApplicationName(a) + " 添加到白名单。") 368 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 369 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 370 | }) 371 | .show(); 372 | showed.add(iw); 373 | break; 374 | case LETV_GOD: 375 | new AlertDialog.Builder(a) 376 | .setCancelable(false) 377 | .setTitle("需要禁止 " + getApplicationName(a) + " 被自动清理") 378 | .setMessage(reason + "需要禁止 " + getApplicationName(a) + " 被自动清理。\n\n" + 379 | "请点击『确定』,在弹出的『应用保护』中,将 " + getApplicationName(a) + " 对应的开关关闭。") 380 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 381 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 382 | }) 383 | .show(); 384 | showed.add(iw); 385 | break; 386 | case LENOVO: 387 | new AlertDialog.Builder(a) 388 | .setCancelable(false) 389 | .setTitle("需要允许 " + getApplicationName(a) + " 的后台运行") 390 | .setMessage(reason + "需要允许 " + getApplicationName(a) + " 的后台自启、后台 GPS 和后台运行。\n\n" + 391 | "请点击『确定』,在弹出的『后台管理』中,分别找到『后台自启』、『后台 GPS』和『后台运行』,将 " + getApplicationName(a) + " 对应的开关打开。") 392 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 393 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 394 | }) 395 | .show(); 396 | showed.add(iw); 397 | break; 398 | case LENOVO_GOD: 399 | new AlertDialog.Builder(a) 400 | .setCancelable(false) 401 | .setTitle("需要关闭 " + getApplicationName(a) + " 的后台耗电优化") 402 | .setMessage(reason + "需要关闭 " + getApplicationName(a) + " 的后台耗电优化。\n\n" + 403 | "请点击『确定』,在弹出的『后台耗电优化』中,将 " + getApplicationName(a) + " 对应的开关关闭。") 404 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 405 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} 406 | }) 407 | .show(); 408 | showed.add(iw); 409 | break; 410 | } 411 | } 412 | return showed; 413 | } 414 | 415 | /** 416 | * 防止华为机型未加入白名单时按返回键回到桌面再锁屏后几秒钟进程被杀 417 | */ 418 | public static void onBackPressed(Activity a) { 419 | Intent launcherIntent = new Intent(Intent.ACTION_MAIN); 420 | launcherIntent.addCategory(Intent.CATEGORY_HOME); 421 | a.startActivity(launcherIntent); 422 | } 423 | 424 | protected Intent intent; 425 | protected int type; 426 | 427 | protected IntentWrapper(Intent intent, int type) { 428 | this.intent = intent; 429 | this.type = type; 430 | } 431 | 432 | /** 433 | * 判断本机上是否有能处理当前Intent的Activity 434 | */ 435 | protected boolean doesActivityExists(Context context) { 436 | PackageManager pm = context.getPackageManager(); 437 | List list = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); 438 | return list != null && list.size() > 0; 439 | } 440 | 441 | /** 442 | * 安全地启动一个Activity 443 | */ 444 | protected void startActivitySafely(Activity activityContext) { 445 | try { activityContext.startActivity(intent); } catch (Exception e) { e.printStackTrace(); } 446 | } 447 | } 448 | -------------------------------------------------------------------------------- /keepalive/src/main/res/drawable/icon1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/keepalive/src/main/res/drawable/icon1.webp -------------------------------------------------------------------------------- /keepalive/src/main/res/raw/no_notice.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ray512512/KeepAlive/e849769485213b7181a25eefbb18678afb294a60/keepalive/src/main/res/raw/no_notice.mp3 -------------------------------------------------------------------------------- /keepalive/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | sss 3 | 4 | -------------------------------------------------------------------------------- /keepalive/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | -------------------------------------------------------------------------------- /keepalive/src/main/res/xml/net_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /keepalive/src/test/java/com/sdk/keepbackground/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.sdk.keepbackground; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ###### 关于原理可移步 https://www.jianshu.com/p/b5371df6d7cb 这篇博客总结的很到位。 2 | #### 1.复制keepalive.aar 到app/libs ,app下build android中添加(keepalive.aar 在keepalive根目录下) 3 | 4 | ~~~java 5 | repositories { 6 | flatDir { 7 | dirs 'libs' 8 | } 9 | } 10 | ~~~ 11 | 12 | dependencies中添加 13 | 14 | ~~~java 15 | compile(name:'keepalive-x.x.x', ext:'aar') 16 | ~~~ 17 | 18 | #### 2. 在Application中初始化 19 | 20 | ~~~xml 21 | DaemonEnv.init(this); 22 | ~~~ 23 | 24 | 25 | 26 | #### 3.activity中向用户申请开启后台保活开关(可选) 27 | 28 | ~~~java 29 | DaemonEnv.whiteListMatters(this, "保活服务的持续运行"); 30 | ~~~ 31 | 32 | 33 | 34 | #### 4.开启业务服务(XXXService为任意继承AbsWorkService服务的业务服务,可选) 35 | 36 | ~~~java 37 | 开启业务服务:DaemonEnv.startServiceSafelyWithData(MainActivity.this,XXXService.class); 38 | 停止业务服务:DaemonEnv.sendStopWorkBroadcast(Context context) 39 | 40 | 集成该类需要重写几个抽象方法: 41 | /** 42 | * 是否 任务完成, 不再需要服务运行? 43 | @return true 应当启动服务; false 应当停止服务; null 无法判断, 什么也不做. 44 | */ 45 | @Override 46 | public Boolean needStartWorkService() { 47 | return true; 48 | } 49 | 50 | 51 | /** 52 | * 开启具体业务,实际执行与isWorkRunning方法返回值有关, 53 | * 当isWorkRunning返回false才会执行该方法 54 | */ 55 | @Override 56 | public void startWork() { 57 | 58 | } 59 | 60 | /** 61 | * 业务执行完成需要进行的操作 62 | * 手动停止服务sendStopWorkBroadcast时回调 63 | */ 64 | @Override 65 | public void stopWork() { 66 | 67 | } 68 | 69 | /** 70 | * 任务是否正在运行? 由实现者处理 71 | * @return 任务正在运行, true; 任务当前不在运行, false; 无法判断, 什么也不做, null. 72 | */ 73 | @Override 74 | public Boolean isWorkRunning() { 75 | return mIsRunning; 76 | } 77 | 78 | /** 79 | * 绑定远程service 可根据实际业务情况是否绑定自定义binder或者直接返回默认binder 80 | */ 81 | @Override 82 | public IBinder onBindService(Intent intent, Void aVoid) { 83 | return new Messenger(new Handler()).getBinder(); 84 | } 85 | 86 | /** 87 | * 服务被kill 88 | */ 89 | @Override 90 | public void onServiceKilled() { 91 | 92 | } 93 | ~~~ 94 | 95 | ###### 注:若需要更改代码,重新生成keepalive.aar ,方式: Terminal下执行gradlew againMakeJar 即可生成到keepalive下 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':keepalive' 2 | --------------------------------------------------------------------------------