(R.id.web_view).loadUrl("http://www.baidu.com")
18 | }
19 | }
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/AllSample/Android9/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 |
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
19 |
20 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/layout/fragment_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chiclaim/AndroidVersionDiff/d06a8847ec7f0ac107644df4fcfc9be3c5796cce/AllSample/Android9/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chiclaim/AndroidVersionDiff/d06a8847ec7f0ac107644df4fcfc9be3c5796cce/AllSample/Android9/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chiclaim/AndroidVersionDiff/d06a8847ec7f0ac107644df4fcfc9be3c5796cce/AllSample/Android9/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chiclaim/AndroidVersionDiff/d06a8847ec7f0ac107644df4fcfc9be3c5796cce/AllSample/Android9/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chiclaim/AndroidVersionDiff/d06a8847ec7f0ac107644df4fcfc9be3c5796cce/AllSample/Android9/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chiclaim/AndroidVersionDiff/d06a8847ec7f0ac107644df4fcfc9be3c5796cce/AllSample/Android9/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chiclaim/AndroidVersionDiff/d06a8847ec7f0ac107644df4fcfc9be3c5796cce/AllSample/Android9/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chiclaim/AndroidVersionDiff/d06a8847ec7f0ac107644df4fcfc9be3c5796cce/AllSample/Android9/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chiclaim/AndroidVersionDiff/d06a8847ec7f0ac107644df4fcfc9be3c5796cce/AllSample/Android9/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chiclaim/AndroidVersionDiff/d06a8847ec7f0ac107644df4fcfc9be3c5796cce/AllSample/Android9/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Android 9
3 |
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/AllSample/Android9/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/AllSample/Android9/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | jcenter()
6 | mavenCentral()
7 | maven { url "https://jitpack.io" }
8 | }
9 | dependencies {
10 | classpath "com.android.tools.build:gradle:4.1.1"
11 | classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10'
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 | mavenCentral()
23 | maven { url "https://jitpack.io" }
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
31 | ext {
32 | compileSdk = 32
33 | targetSdk = 32
34 | minSdk = 19
35 | }
--------------------------------------------------------------------------------
/AllSample/Android9/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=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
--------------------------------------------------------------------------------
/AllSample/Android9/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chiclaim/AndroidVersionDiff/d06a8847ec7f0ac107644df4fcfc9be3c5796cce/AllSample/Android9/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/AllSample/Android9/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Nov 18 12:59:11 CST 2020
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-6.5-bin.zip
7 |
--------------------------------------------------------------------------------
/AllSample/Android9/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 |
--------------------------------------------------------------------------------
/AllSample/Android9/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 |
--------------------------------------------------------------------------------
/AllSample/Android9/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
--------------------------------------------------------------------------------
/Android-10.md:
--------------------------------------------------------------------------------
1 | > Android 10 (API Level 29)
2 |
3 |
4 |
5 | ## 1. PackageManager.getPackageInfo
6 |
7 | 从 Android 10 (API Level 29)开始,只能获取当前应用的 packageInfo,无法获取其他应用的 packageInfo。例如通过 PackageManager.getPackageInfo 来判断程序是否安装在 Android 10 及以上是失效的:
8 |
9 | ```java
10 | var available = true
11 | try {
12 | // check if available
13 | packageManager.getPackageInfo("zmsoft.rest.phone", GET_CONFIGURATIONS)
14 | } catch (e: PackageManager.NameNotFoundException) {
15 | e.printStackTrace()
16 | // if not available set available as false
17 | available = false
18 | }
19 | println(available)
20 | ```
21 |
22 |
23 |
24 | > 本限制会对 `targetSdkVersion>=29` 的应用生效。
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Android-11.md:
--------------------------------------------------------------------------------
1 | > Android 11 (API Level 30)
2 |
3 |
4 |
5 | ## 1. 包可见性(Package visibility)
6 |
7 | 从 Android 11 (API Level 30)开始,只能获取当前应用的 packageInfo,默认无法获取其他应用的 packageInfo。影响的 API:
8 |
9 | - PackageManager.getPackageInfo
10 | - PackageManager.queryIntentActivities()
11 | - PackageManager.getInstalledApplications()
12 | - PackageManager.getApplicationInfo
13 | - PackageManager.getLaunchIntentForPackage
14 | - ...
15 |
16 | 例如通过 PackageManager.getPackageInfo 来判断程序是否安装在 Android 11 及以上是失效的:
17 |
18 | ```java
19 | var available = true
20 | try {
21 | // check if available
22 | packageManager.getPackageInfo("com.tencent.mm", GET_CONFIGURATIONS)
23 | } catch (e: PackageManager.NameNotFoundException) {
24 | e.printStackTrace()
25 | // if not available set available as false
26 | available = false
27 | }
28 | println(available)
29 | ```
30 |
31 |
32 |
33 | 如果需要查询第三方 App 的信息,从 Android 11 开始需要在清单文件中,添加需要查询的包名:
34 |
35 | ```
36 |
37 |
38 |
39 | ```
40 |
41 |
42 |
43 | > 本限制会对 `targetSdkVersion>=30 的应用生效。
44 |
45 |
46 |
47 | ## 2. 权限变更
48 |
49 |
50 |
51 | ### 2.1 一次性权限(One-time permissions)
52 |
53 | 从 Android 11 开始,当请求位置、麦克风或相机权限时,系统弹出的 Dialog 会多一个选项:本次运行允许(Only this time)
54 |
55 |
56 |
57 | ### 2.2 自动重置权限
58 |
59 | 从 Android 11 开始,如果你的 APP 几个月没用被使用,那么系统会将一些敏感的权限自动设置为 “拒绝”
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/Android-8.0.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Android 8 避坑指南
4 |
5 |
6 |
7 | > 本文的 Android 包含 Android 8.0 和 8.1 。另外,官方关于 [Android 8 Code Sample](https://developer.android.com/about/versions/oreo/android-8.0-samples) 有兴趣可以运行看下效果。
8 |
9 |
10 |
11 | ### 1. 后台服务限制
12 |
13 | 从 Android 8 开始,要避免随意的调用 `startService` 方法。否则会有很大概率出现:IllegalStateException。
14 |
15 | 根据 Android 官网描述,如果 App 在后台调用 `startService` 方法,就会抛出如下异常:
16 |
17 | ```
18 | java.lang.IllegalStateException: Not allowed to start service Intent
19 | ```
20 |
21 | 既然在后台不能调用,那在前台能不能调用?经过测试在 `onWindowFocusChanged` 方法里调用也不行。
22 |
23 |
24 |
25 | **解决方案**:
26 |
27 | 启动服务使用 `Context.startForegroundService()`,然后在 `onStartCommand` 方法中调用 `startForeground()`,需要注意 `Context.startForegroundService()` 和 `startForeground` 要配套调用。如果调用 ` Context.startForegroundService()` 后,没有在规定的时间调用 `startForeground`,就会抛出异常:
28 |
29 | ```java
30 | android.app.ForegroundServiceDidNotStartInTimeException
31 | 或
32 | android.app.RemoteServiceException
33 | Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord
34 | ```
35 |
36 |
37 |
38 | 注意,不能在`Context.startForegroundService()` 后调用 `stopService` 或 `stopSelf`,否则也会立马报上述异常。所以我们一定要注意调用 `stopService` 的时机(一般我们启动服务后, 会在任务执行完了 stopService)。使用是需要注意 2 点:
39 |
40 | - ` Context.startForegroundService()` 要和 `startForeground()` 配套使用
41 | - 调用 ` Context.startForegroundService()` 后,不能调用 `stopService` 或 `stopSelf`
42 |
43 |
44 |
45 | 经过观察线上日志,还是会有小概率出现 `RemoteServiceException` 或 `ForegroundServiceDidNotStartInTimeException` 异常。估计还是在 ` Context.startForegroundService()` 调用了 `stopService`。(后续如果找到具体原因,会及时更新本文档)
46 |
47 |
48 |
49 | 如果不是长时间的后台任务 ,可以考虑直接使用线程;如果是长时间的任务,也可以考虑使用 `WorkManager`,需要考虑 `WorkManager` 的及时性和各个厂商对`WorkManager` 的修改。
50 |
51 |
52 |
53 | > 本限制会对所有 Android 8 系统生效,不管应用的 `targetSdkVersion` 是多少。
54 |
55 |
56 |
57 | ### 2. 透明主题的 Activity 不能设置朝向
58 |
59 | 如果你将 Activity 的主题设置为透明样式:
60 |
61 | ```xml
62 |
65 | ```
66 |
67 | 那么你将喜提如下异常:
68 |
69 | ```java
70 | java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
71 | ```
72 |
73 | 经过不同的版本及不同的 `targetSdkVersion` 测试,上述问题只会在 `Android 8 (API Level 26)` 机器中出现:
74 |
75 | - 当程序的 `targetSdkVersion<=26` 则不会抛出异常。
76 | - 当程序的 `targetSdkVersion>26` 则抛出异常。
77 |
78 |
79 |
80 | 最坑的是在 [Android 8.0 的变更](https://developer.android.com/about/versions/oreo) 中没有找到有关描述(悄无声息改的)。我们绝大部分的应用都会兼容 Android 8.0,但是开发测试的机器往往是比较新的,导致上线才知道问题所在。
81 |
82 |
83 |
84 | ### 3. Notification 通知栏变化
85 |
86 | 从 Android 8.0 (API level 26)开始,每个 Notification 必须分配一个 channel。
87 |
88 | ```kotlin
89 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
90 | val channel = NotificationChannel(
91 | CHANNEL_ID,
92 | context.getString(R.string.downloader_notifier_channel_name),
93 | NotificationManager.IMPORTANCE_LOW
94 | )
95 | notificationManager.createNotificationChannel(channel)
96 | }
97 |
98 | val builder = NotificationCompat.Builder(context, CHANNEL_ID)
99 | .setSmallIcon(notifierSmallIcon)
100 | .setContentTitle(title)
101 | .setAutoCancel(true/false)
102 | .setOngoing(percent != 100)
103 | }
104 | notificationManager.notify(id, builder.build())
105 | ```
106 |
107 |
108 |
109 | > 通知栏的变化是在 App 的 `targetSdkVersion` >=26 才会生效。更多关于通知栏的细节可以查看: [Android 8 notification](https://developer.android.com/develop/ui/views/notifications/channels)
110 |
111 |
112 |
113 |
114 |
115 | ### 4. Broadcasts 广播的调整
116 |
117 |
118 |
119 | #### Broadcasts 的一些简单科普
120 |
121 |
122 |
123 | 关于隐式广播和显示广播:
124 |
125 | - 隐式广播(implicit broadcast):可以简单的理解为当事件发生时,如果你和其他应用程序都注册了广播,那么你们都会接收到该信息。(一对多关系)
126 | - 显式广播(explicit broadcasts):当事件发生时,只有你的应用程序会接收到该消息。(一对一关系)
127 |
128 |
129 |
130 | 广播的接收有两种方式 [receiving-broadcasts](https://developer.android.com/guide/components/broadcasts#receiving-broadcasts):
131 |
132 | - 在 manifest 中注册(manifest-declared receivers),民间也有称之为的静态广播,但是在 Android 官网没有类似的叫法。
133 | - 程序运行时注册(context-registered receivers),民间也有称之为的动态广播,但是在 Android 官网没有类似的叫法。
134 |
135 |
136 |
137 | `manifest-declared receivers` 和 `context-registered receivers` 区别是什么?
138 |
139 | - 在 manifest 中注册的广播接收者,当事件发生时,系统会启动你的应用程序,哪怕你的应用并没有运行。
140 | - 在运行时通过 context 对象注册的广播接收者,当事件发生时,需要 context 是有效的。当 context 是 Activity,那么 Activity 没有被销毁才能接收到广播。当 context 是 Application 时,那么只有在程序仍处于运行状态才能接收到广播。
141 |
142 |
143 |
144 |
145 |
146 | #### Broadcasts 在 Android 8 中的变化:
147 |
148 |
149 |
150 | 每当广播被发送,应用的广播接收者都会消耗资源。如果很多应用都去注册接收系统事件的广播,那么可能会导致这些 App 接连消耗系统资源。例如 `ACTION_PACKAGE_REPLACED` 是一个隐式广播,当事件发生时,所有注册的应用都会接收到事件。`ACTION_MY_PACKAGE_REPLACED` 就是一个显示广播,虽然 App 可能都会去注册,但是当该事件发生时,只有被升级的 APP 才会接收到。
151 |
152 |
153 |
154 | 所以,为了提高用户体验,避免广播对系统资源消耗过大的问题,Android 8 对广播做的调整,可以总结如下几条:
155 |
156 | - 不再支持在 manifest 清单文件注册隐式广播(implicit broadcast),但仍然支持在 manifest 清单文件注册显式广播(explicit broadcasts)
157 | - 可以在运行时注册广播(同时支持隐式和显示广播)
158 | - 如果广播要求签名权限(signature permission),那么也可以在 manifest 中注册隐式广播。因为当事件发生时,只有和该广播发送者拥有相同的签名的应用才会收到事件通知,而不是所有注册的应用。
159 |
160 |
161 |
162 | 测试案例:例如你想监听设备的网络的变化,该广播是隐式广播,如果在 manifest 中注册,则收不到网络变化的事件,运行时注册可以接收的到。测试代码可以查看 [AllSample/Android8](https://github.com/chiclaim/AndroidVersionDiff/tree/main/AllSample/Android8)
163 |
164 |
165 |
166 | > 本限制会对所有 Android 8 系统生效,不管应用的 `targetSdkVersion` 是多少。
167 |
168 |
169 |
170 |
171 |
172 | ### 5. ANDROID_ID 的变化
173 |
174 | Android 8 在隐私方面也做了一些变动:
175 |
176 | - 如果你的应用所在的系统版本是 Android 8 之前,然后用户将系统升级到 Android 8(API level 26),ANDROID_ID 的值不会发生改变,除非用户后面卸载重装了你的应用。
177 | - 如果你的应用是在 Android 8 上安装的,那么 ANDROID_ID 的值,是 `应用的签名(app siging key)`、`用户(user)`和`设备(device)`三个信息的组合,只要其中一个信息改变了, ANDROID_ID 的值都会发生变化。设备(device)恢复出厂设置, ANDROID_ID 的值也会改变。
178 | - 如果你的应用是在 Android 8 上安装的,那么 ANDROID_ID 的值不会因为卸载重装而发生改变。
179 | - 如果是因为系统升级导致应用的签名 key 发生变化,ANDROID_ID 的值不会改变。
180 |
181 |
182 |
183 | > 本限制会对所有 Android 8 系统生效,不管应用的 `targetSdkVersion` 是多少。
184 |
185 |
186 |
187 | ### 6. 系统属性 net.hostname 变更
188 |
189 | 从 Android 8.0 (API level 26) 开始,无法查询系统属性 `net.hostname`
190 |
191 | ```kotlin
192 | fun showNetHostName(view: View) {
193 | val value: String? = try {
194 | val getString: Method =
195 | Build::class.java.getDeclaredMethod("getString", String::class.java)
196 | getString.isAccessible = true
197 | getString.invoke(null, "net.hostname").toString()
198 | } catch (e: Exception) {
199 | e.printStackTrace()
200 | null
201 | }
202 | println(value)
203 | Toast.makeText(this, "net.hostname=$value", Toast.LENGTH_SHORT).show()
204 | }
205 | ```
206 |
207 |
208 |
209 | 经测试:上述代码在 android 12 (xiaomi 11) 返回 `unknown`;在 android 6.0.1 (read mi 4A) 返回 `Redmi4A-hongmishouji`
210 |
211 |
212 |
213 | > 本限制会对所有 Android 8 系统生效,不管应用的 `targetSdkVersion` 是多少。
214 |
215 |
216 |
217 | ### 7. Build.SERIAL 变更
218 |
219 | 从 Android 8.0 (API level 26) 开始 Build.SERIAL 已经被废弃:
220 |
221 | ```kotlin
222 | fun showBuildSerial(){
223 | println(Build.SERIAL)
224 | }
225 | ```
226 |
227 | 经测试:上述代码在 android 12 (xiaomi 11) 返回 `unknown`;在 android 6.0.1 (read mi 4A) 返回 `298bd1947d04`
228 |
229 | 可以使用 `Build.getSerial()` 来获取硬件序列号,该方法需要 `READ_PHONE_STATE` 权限【Build.getSerial 在 Android 10 中由变更】。
230 |
231 | > 本变更在 App 的 `targetSdkVersion` >=26 才会生效。
232 |
233 |
234 |
235 | ### 8. Thread.UncaughtExceptionHandler
236 |
237 | 如果应用自定义了 `Thread.UncaughtExceptionHandler`,但是没有调用默认的 `defaultExceptionHandler.uncaughtException`,一旦出现未捕获的异常,应用并不会立马退出, 直到应用弹出 ANR。([点击查看测试代码](https://github.com/chiclaim/AndroidVersionDiff/tree/main/AllSample/Android8))
238 |
239 | 在 Android 8 之前,不调用默认的 `defaultExceptionHandler.uncaughtException`,系统不会记录堆栈信息。从 Android 8 开始,系统则会记录异常的堆栈信息。
240 |
241 |
242 |
243 | > 本限制会对所有 Android 8 系统生效,不管应用的 `targetSdkVersion` 是多少。
244 |
245 |
246 |
247 | ### 9. Permission 行为调整
248 |
249 | 在 Android 8.0 (API level 26) 之前,应用运行时请求的权限被同意时,那么系统也会将属于该分组下的其他权限一同赋予。例如用于请求了 `READ_EXTERNAL_STORAGE` 那么系统也会赋予 `WRITE_EXTERNAL_STORAGE` 权限。
250 |
251 |
252 |
253 | 如果程序的 targetSdkVersion 为 Android 8,那么系统仅仅会赋予你请求的权限。同意分组的权限也需要你显式的请求,只不过系统会自动通过,不会给用户提示。例如用户请求 `READ_EXTERNAL_STORAGE` 被赋予,当请求 `WRITE_EXTERNAL_STORAGE` 权限时,系统会自动同意。
254 |
255 | > 本变更在 App 的 `targetSdkVersion` >=26 才会生效。
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
--------------------------------------------------------------------------------
/Android-9.0.md:
--------------------------------------------------------------------------------
1 | > Android 9 (API Level 28)
2 |
3 |
4 |
5 | ## 1. Foreground services 变更
6 |
7 | 在前面介绍 Android 8 变更的时候提到,如果在后台 `startService`,系统会抛出异常。需要我们启动前台服务。从 Android 9 开始,使用前台服务需要在清单文件中声明 `android.permission.FOREGROUND_SERVICE`权限,否则会抛出 `SecurityException`。
8 |
9 |
10 |
11 | > 本变更是在 App 的 `targetSdkVersion` >=28 才会生效
12 |
13 |
14 |
15 | ## 2. FLAG_ACTIVITY_NEW_TASK 变更
16 |
17 | 从 Android 9 开始,当你使用非 activity context 去启动 activity,必须加上 `FLAG_ACTIVITY_NEW_TASK`
18 |
19 |
20 |
21 | 根据官网的描述本变更,会影响所有 Android 9 及以上系统,不管 `targetSdkVersion` 是多少。但是经过实测:
22 |
23 |
24 |
25 | - Android 6(小米 红米 4A)会闪退
26 |
27 | - Android 7(小米 红米 Note 4X)不会闪退
28 |
29 | - Android 8 (三星 Galaxy S9+)不会闪退
30 |
31 | - Android 9(小米 MI 8 SE)当 targetSdkVersion >= 28 会闪退,否则不会闪退
32 |
33 | - Android 12(小米 11)当 targetSdkVersion >= 28 会闪退,否则不会闪退
34 |
35 |
36 |
37 | > 经测试:当系统版本小于 Android 9 ,则不会闪退;Android 9 及以上会根据 `targetSdkVersion` 是否大于等于 28。
38 |
39 |
40 |
41 | ## 3. 默认开启网络 TLS
42 |
43 | 从 Android 9 开始,默认不在支持 HTTP 请求。也就是 `cleartextTrafficPermitted` 默认为 false(cleartext 明文的意思,明文传输不被允许)。如果你需要请求 http 资源,你需要显式的将其设置为 true,例如:
44 |
45 | ```xml
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | ```
62 |
63 | 例如, WebView.load("http://www.baidu.com"),会直接提示 `net::ERR_CLEARTEXT_TRAFFIC_PERMITTED`
64 |
65 |
66 |
67 | > 经测试:本变更是在 App 的 `targetSdkVersion` >=28 才会生效
68 |
69 |
70 |
71 | ## 4. Apache HTTP client 库被废弃
72 |
73 | 在 Android 6.0 里就移除了对 `Apache HTTP client` 的支持,如果在 Android 2.3(API Level 9)或者更高的系统版本上使用该库,系统会使用 HttpURLConnection 代替。如果不想被 HttpURLConnection 替换,需要进行如下配置:
74 |
75 | ```gradle
76 | android {
77 | useLibrary 'org.apache.http.legacy'
78 | }
79 | ```
80 |
81 |
82 |
83 | 从 Android 9.0 开始,`Apache HTTP client` 已经从 bootclasspath 中移除,意味代码无法引用到该库了。如果你一定要使用该库,需要 targetSdkVersion>=28,同时在清单文件配置:
84 |
85 | ```xml
86 |
87 | ```
88 |
89 |
90 |
91 | 但是经过测试,加上这些配置后,依然无法引用到 `Apache HTTP client` 里的 API,看来已经彻底移除了。那如果要使用的话,只能像第三方库一样,手动添加依赖了。
92 |
93 |
94 |
95 | > 官方已经不推荐使用 Apache HTTP client 了,因为它的性能比较低。官方已经将 HttpUrlConnection 的底层实现使用了 OkHttp,所以建议直接使用 OkHttp。
96 |
97 |
98 |
99 | ## 5. 限制非 SDK 接口的使用
100 |
101 | 非 SDK 接口(non-SDK interfaces):简单来说就是系统没有暴露的类、方法、字段。
102 |
103 | 你可能会使用反射或 JNI 调用了非 SDK 接口 ,那么你的程序在将来可能会有稳定性和兼容性的问题。
104 |
105 | 在 Android 9 ,虽然允许你使用 非 SDK 接口,但是系统可能会通过 toast 和 log 的方式给你提示。如果你收到这样的提示,应该寻找其他的解决方案来代替。
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AndroidVersionDiff
2 |
3 |
4 |
5 | > https://developer.android.com/about/versions
6 |
7 |
8 |
9 | 随着 Android 对 `隐私安全` 的重视及对 `用户体验` 的持续优化,不同的 Android 版本会有许多 API 发生变化, 有些 API 的改动,如果开发者不去兼容,可能会导致程序闪退。新 Android 版本对 API 的改动是否生效分为两种情况:
10 |
11 | - 只有将 targetSdkVersion 设置到对应的新版本编号,新的 API 才会生效,否则还是按照老版本的逻辑走。
12 | - 只要用户的 Android 系统升级到了新版本,新 API 就会生效,不管 targetSdkVersion 设置为多少。
13 |
14 |
15 |
16 | 每个 Android 开发者都会因为没有及时去了解 Android 新版本的变更,自己的某一行代码导致程序的 bug 率上升。
17 |
18 |
19 |
20 | 更糟糕的是,有的新版本的变更,并没有在官方的文档提及。如果每次发版没有对所支持的不同版本的设备测试,就只能等线上报错才能发现问题。
21 |
22 |
23 |
24 | 所以,创建了本仓库来分享 Android 不同版本添加的新特性及 API 行为的调整。同时也会附上我对 Android 新版的兼容适配的实践。
25 |
26 |
27 |
28 | 希望更多的开发者能够共同来维护它,帮助 Android 开发者更加方便的开发出稳定的、体验优异的应用程序。
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------