transformInputs = transformInvocation.inputs
38 | TransformOutputProvider outputProvider = transformInvocation.outputProvider
39 | if (outputProvider != null) {
40 | outputProvider.deleteAll()
41 | }
42 |
43 | transformInputs.each { TransformInput transformInput ->
44 | // 遍历directoryInputs(文件夹中的class文件) directoryInputs代表着以源码方式参与项目编译的所有目录结构及其目录下的源码文件
45 | // 比如我们手写的类以及R.class、BuildConfig.class以及MainActivity.class等
46 | transformInput.directoryInputs.each { DirectoryInput directoryInput ->
47 | File dir = directoryInput.file
48 | if (dir) {
49 | dir.traverse(type: FileType.FILES, nameFilter: ~/.*\.class/) { File file ->
50 | System.out.println("find class: " + file.name)
51 | //对class文件进行读取与解析
52 | ClassReader classReader = new ClassReader(file.bytes)
53 | //对class文件的写入
54 | ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
55 | //访问class文件相应的内容,解析到某一个结构就会通知到ClassVisitor的相应方法
56 | ClassVisitor classVisitor = new LifecycleClassVisitor(classWriter)
57 | //依次调用 ClassVisitor接口的各个方法
58 | classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES)
59 | //toByteArray方法会将最终修改的字节码以 byte 数组形式返回。
60 | byte[] bytes = classWriter.toByteArray()
61 |
62 | //通过文件流写入方式覆盖掉原先的内容,实现class文件的改写。
63 | //FileOutputStream outputStream = new FileOutputStream( file.parentFile.absolutePath + File.separator + fileName)
64 | FileOutputStream outputStream = new FileOutputStream(file.path)
65 | outputStream.write(bytes)
66 | outputStream.close()
67 | }
68 | }
69 |
70 | //处理完输入文件后把输出传给下一个文件
71 | def dest = outputProvider.getContentLocation(directoryInput.name, directoryInput.contentTypes,
72 | directoryInput.scopes, Format.DIRECTORY)
73 | FileUtils.copyDirectory(directoryInput.file, dest)
74 | }
75 | }
76 | }
77 |
78 | }
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/asm_lifecycle_plugin/src/main/java/danny/jiang/asm/LifecycleClassVisitor.java:
--------------------------------------------------------------------------------
1 | package danny.jiang.asm;
2 |
3 | import org.objectweb.asm.ClassVisitor;
4 | import org.objectweb.asm.MethodVisitor;
5 | import org.objectweb.asm.Opcodes;
6 |
7 | public class LifecycleClassVisitor extends ClassVisitor {
8 | private String className;
9 | private String superName;
10 |
11 | public LifecycleClassVisitor(ClassVisitor cv) {
12 | super(Opcodes.ASM5, cv);
13 | }
14 |
15 | @Override
16 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
17 | super.visit(version, access, name, signature, superName, interfaces);
18 | this.className = name;
19 | this.superName = superName;
20 | }
21 |
22 | @Override
23 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
24 | System.out.println("ClassVisitor visitMethod name-------" + name + ", superName is " + superName);
25 | MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
26 |
27 | if (superName.equals("android/support/v7/app/AppCompatActivity")) {
28 | if (name.startsWith("onCreate")) {
29 | //处理onCreate()方法
30 | return new LifecycleMethodVisitor(mv, className, name);
31 | }
32 | }
33 | return mv;
34 | }
35 |
36 | @Override
37 | public void visitEnd() {
38 | super.visitEnd();
39 | }
40 | }
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/asm_lifecycle_plugin/src/main/java/danny/jiang/asm/LifecycleMethodVisitor.java:
--------------------------------------------------------------------------------
1 | package danny.jiang.asm;
2 |
3 | import org.objectweb.asm.MethodVisitor;
4 | import org.objectweb.asm.Opcodes;
5 |
6 | public class LifecycleMethodVisitor extends MethodVisitor {
7 | private String className;
8 | private String methodName;
9 |
10 | public LifecycleMethodVisitor(MethodVisitor methodVisitor, String className, String methodName) {
11 | super(Opcodes.ASM5, methodVisitor);
12 | this.className = className;
13 | this.methodName = methodName;
14 | }
15 |
16 | //方法执行前插入
17 | @Override
18 | public void visitCode() {
19 | super.visitCode();
20 | System.out.println("MethodVisitor visitCode------");
21 |
22 | mv.visitLdcInsn("TAG");
23 | mv.visitLdcInsn(className + "---->" + methodName);
24 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, "android/util/Log", "i", "(Ljava/lang/String;Ljava/lang/String;)I", false);
25 | mv.visitInsn(Opcodes.POP);
26 | }
27 | }
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/asm_lifecycle_plugin/src/main/resources/META-INF/gradle-plugins/danny.asm.lifecycle.properties:
--------------------------------------------------------------------------------
1 | implementation-class=danny.jiang.plugin.LifeCyclePlugin
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/asm_lifecycle_repo/danny/lifecycle/plugin/asm_lifecycle_plugin/1.0.0/asm_lifecycle_plugin-1.0.0.jar.md5:
--------------------------------------------------------------------------------
1 | a35e2669740084ca05d7fd716e2a5f89
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/asm_lifecycle_repo/danny/lifecycle/plugin/asm_lifecycle_plugin/1.0.0/asm_lifecycle_plugin-1.0.0.jar.sha1:
--------------------------------------------------------------------------------
1 | dba612759c9c4a8084d3986bb5b9e007f33eeeb7
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/asm_lifecycle_repo/danny/lifecycle/plugin/asm_lifecycle_plugin/1.0.0/asm_lifecycle_plugin-1.0.0.pom:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | danny.lifecycle.plugin
6 | asm_lifecycle_plugin
7 | 1.0.0
8 |
9 |
10 | com.android.tools.build
11 | gradle
12 | 3.4.2
13 | compile
14 |
15 |
16 | org.ow2.asm
17 | asm
18 | 7.1
19 | runtime
20 |
21 |
22 | org.ow2.asm
23 | asm-commons
24 | 7.1
25 | runtime
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/asm_lifecycle_repo/danny/lifecycle/plugin/asm_lifecycle_plugin/1.0.0/asm_lifecycle_plugin-1.0.0.pom.md5:
--------------------------------------------------------------------------------
1 | ccddd4064f8341fb23a1020af498a629
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/asm_lifecycle_repo/danny/lifecycle/plugin/asm_lifecycle_plugin/1.0.0/asm_lifecycle_plugin-1.0.0.pom.sha1:
--------------------------------------------------------------------------------
1 | 097dba0174615dac341b9f26b96d1d432ed98bac
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/asm_lifecycle_repo/danny/lifecycle/plugin/asm_lifecycle_plugin/maven-metadata.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | danny.lifecycle.plugin
4 | asm_lifecycle_plugin
5 |
6 | 1.0.0
7 |
8 | 1.0.0
9 |
10 | 20200315081504
11 |
12 |
13 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/asm_lifecycle_repo/danny/lifecycle/plugin/asm_lifecycle_plugin/maven-metadata.xml.md5:
--------------------------------------------------------------------------------
1 | 8c054a2ef3ed6c76696ffe1732bb470e
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/asm_lifecycle_repo/danny/lifecycle/plugin/asm_lifecycle_plugin/maven-metadata.xml.sha1:
--------------------------------------------------------------------------------
1 | aadbc51537d1dbbd59b3d8f5bfbbb23840a3e837
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/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.4.1'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 |
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/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 | # 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 |
15 |
16 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Mar 14 22:34:28 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-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/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 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/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 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/ASMLifeCycleDemo/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':asm_lifecycle_plugin'
2 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_four/SourceCode/README.md:
--------------------------------------------------------------------------------
1 | 通过Demo演示Android中如何使用ASM实现编译插桩
2 |
3 | 知识点:
4 | 1. 如何自定义Gradle插件
5 | 2. Transform API的使用
6 | 3. 使用ASM实现插入字节码到.class文件中
7 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/.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 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "material.danny_jiang.com.lagoutouchexplanation"
7 | minSdkVersion 22
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | implementation 'com.android.support:appcompat-v7:28.0.0'
24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
25 | testImplementation 'junit:junit:4.12'
26 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
28 | }
29 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/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 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/androidTest/java/material/danny_jiang/com/lagoutouchexplanation/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package material.danny_jiang.com.lagoutouchexplanation;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("material.danny_jiang.com.lagoutouchexplanation", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/java/material/danny_jiang/com/lagoutouchexplanation/CancelEventActivity.java:
--------------------------------------------------------------------------------
1 | package material.danny_jiang.com.lagoutouchexplanation;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.app.AppCompatActivity;
6 |
7 | /**
8 | * Created by Danny 姜.
9 | */
10 | public class CancelEventActivity extends AppCompatActivity {
11 |
12 | @Override
13 | protected void onCreate(@Nullable Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.activity_cancel);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/java/material/danny_jiang/com/lagoutouchexplanation/InterceptActivity.java:
--------------------------------------------------------------------------------
1 | package material.danny_jiang.com.lagoutouchexplanation;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.app.AppCompatActivity;
6 |
7 | /**
8 | * Created by Danny 姜.
9 | */
10 | public class InterceptActivity extends AppCompatActivity {
11 |
12 | @Override
13 | protected void onCreate(@Nullable Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 |
16 | setContentView(R.layout.activity_intercept);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/java/material/danny_jiang/com/lagoutouchexplanation/MainActivity.java:
--------------------------------------------------------------------------------
1 | package material.danny_jiang.com.lagoutouchexplanation;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 | import android.os.Bundle;
5 |
6 | public class MainActivity extends AppCompatActivity {
7 |
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | setContentView(R.layout.activity_main);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/java/material/danny_jiang/com/lagoutouchexplanation/PrintUtils.java:
--------------------------------------------------------------------------------
1 | package material.danny_jiang.com.lagoutouchexplanation;
2 |
3 | import android.util.Log;
4 | import android.view.MotionEvent;
5 |
6 | /**
7 | * Created by Danny 姜.
8 | */
9 | public class PrintUtils {
10 |
11 | public static void printEvent(String tag, String methodName, MotionEvent event) {
12 | switch (event.getAction()) {
13 | case MotionEvent.ACTION_DOWN:
14 | Log.i(tag, methodName + ":" + "DOWN");
15 | break;
16 | case MotionEvent.ACTION_MOVE:
17 | Log.i(tag, methodName + ":" + "MOVE");
18 | break;
19 | case MotionEvent.ACTION_UP:
20 | Log.i(tag, methodName + ":" + "UP");
21 | break;
22 | case MotionEvent.ACTION_CANCEL:
23 | Log.i(tag, methodName + ":" + "CANCEL");
24 | break;
25 | }
26 | }
27 |
28 | public static void printParam(String tag, String reg) {
29 | Log.i(tag, reg);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/java/material/danny_jiang/com/lagoutouchexplanation/views/CaptureTouchView.java:
--------------------------------------------------------------------------------
1 | package material.danny_jiang.com.lagoutouchexplanation.views;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.MotionEvent;
6 | import android.view.View;
7 |
8 | import static material.danny_jiang.com.lagoutouchexplanation.PrintUtils.printEvent;
9 | import static material.danny_jiang.com.lagoutouchexplanation.PrintUtils.printParam;
10 |
11 | /**
12 | * Created by Danny 姜.
13 | */
14 | public class CaptureTouchView extends View {
15 | private static final String TAG = CaptureTouchView.class.getSimpleName();
16 |
17 | public CaptureTouchView(Context context, AttributeSet attrs) {
18 | super(context, attrs);
19 | setOnTouchListener(new OnTouchListener() {
20 | @Override
21 | public boolean onTouch(View v, MotionEvent event) {
22 | return true;
23 | }
24 | });
25 | }
26 |
27 | @Override
28 | public boolean dispatchTouchEvent(MotionEvent event) {
29 | printEvent(TAG, "dispatchTouchEvent", event);
30 | boolean result = super.dispatchTouchEvent(event);
31 | printParam(TAG, "dispatchTouchEvent result is " + result);
32 | return result;
33 | }
34 |
35 | @Override
36 | public boolean onTouchEvent(MotionEvent event) {
37 | printEvent(TAG,"onTouchEvent", event);
38 | return true;
39 | }
40 |
41 | @Override
42 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
43 | setMeasuredDimension(500, 300);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/java/material/danny_jiang/com/lagoutouchexplanation/views/DownInterceptGroup.java:
--------------------------------------------------------------------------------
1 | package material.danny_jiang.com.lagoutouchexplanation.views;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.util.Log;
6 | import android.view.Gravity;
7 | import android.view.MotionEvent;
8 | import android.view.View;
9 | import android.widget.FrameLayout;
10 |
11 | import static material.danny_jiang.com.lagoutouchexplanation.PrintUtils.printEvent;
12 |
13 | /**
14 | * Created by Danny 姜.
15 | */
16 | public class DownInterceptGroup extends FrameLayout {
17 |
18 | private static final String TAG = DownInterceptGroup.class.getSimpleName();
19 |
20 | public DownInterceptGroup(Context context, AttributeSet attrs) {
21 | super(context, attrs);
22 | }
23 |
24 | @Override
25 | public boolean dispatchTouchEvent(MotionEvent event) {
26 | printEvent(TAG, "dispatchTouchEvent", event);
27 | return super.dispatchTouchEvent(event);
28 | }
29 |
30 | @Override
31 | public boolean onInterceptTouchEvent(MotionEvent ev) {
32 | printEvent(TAG, "onInterceptTouchEvent", ev);
33 | return super.onInterceptTouchEvent(ev);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/java/material/danny_jiang/com/lagoutouchexplanation/views/MyScrollView.java:
--------------------------------------------------------------------------------
1 | package material.danny_jiang.com.lagoutouchexplanation.views;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.MotionEvent;
6 | import android.widget.ScrollView;
7 |
8 | import static material.danny_jiang.com.lagoutouchexplanation.PrintUtils.printEvent;
9 | import static material.danny_jiang.com.lagoutouchexplanation.PrintUtils.printParam;
10 |
11 | /**
12 | * Created by Danny 姜.
13 | */
14 | public class MyScrollView extends ScrollView {
15 |
16 | private static final String TAG = MyScrollView.class.getSimpleName();
17 |
18 | public MyScrollView(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | }
21 |
22 | @Override
23 | public boolean onInterceptTouchEvent(MotionEvent event) {
24 | printEvent(TAG,"onTouchEvent", event);
25 | boolean intercepted = super.onInterceptTouchEvent(event);
26 | printParam(TAG, "MyScrollView intercepted is " + intercepted);
27 | return intercepted;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/layout/activity_cancel.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
18 |
19 |
25 |
26 |
32 |
33 |
39 |
40 |
46 |
47 |
53 |
54 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/layout/activity_intercept.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingking888/AndroidEngineerAdvanced/2ffe994b165851cc004b857913d5c40a93684d97/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingking888/AndroidEngineerAdvanced/2ffe994b165851cc004b857913d5c40a93684d97/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingking888/AndroidEngineerAdvanced/2ffe994b165851cc004b857913d5c40a93684d97/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingking888/AndroidEngineerAdvanced/2ffe994b165851cc004b857913d5c40a93684d97/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingking888/AndroidEngineerAdvanced/2ffe994b165851cc004b857913d5c40a93684d97/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingking888/AndroidEngineerAdvanced/2ffe994b165851cc004b857913d5c40a93684d97/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingking888/AndroidEngineerAdvanced/2ffe994b165851cc004b857913d5c40a93684d97/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingking888/AndroidEngineerAdvanced/2ffe994b165851cc004b857913d5c40a93684d97/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingking888/AndroidEngineerAdvanced/2ffe994b165851cc004b857913d5c40a93684d97/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingking888/AndroidEngineerAdvanced/2ffe994b165851cc004b857913d5c40a93684d97/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | LagouTouchExplanation
3 |
4 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/app/src/test/java/material/danny_jiang/com/lagoutouchexplanation/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package material.danny_jiang.com.lagoutouchexplanation;
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 | }
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/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.4.1'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 |
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/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 | # 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 |
15 |
16 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 29 22:52:40 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-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/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 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/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 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_fourteen/SourceCode/LagouTouchExplanation/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_seventeen/SourceCode/SquareUtils.java:
--------------------------------------------------------------------------------
1 | package material.danny_jiang.com.picassosenior.utils;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.IntRange;
5 | import android.support.annotation.WorkerThread;
6 |
7 | import com.squareup.picasso.OkHttp3Downloader;
8 | import com.squareup.picasso.Picasso;
9 |
10 | import java.io.File;
11 | import java.io.IOException;
12 |
13 | import material.danny_jiang.com.picassosenior.MyApplication;
14 | import okhttp3.Cache;
15 | import okhttp3.Interceptor;
16 | import okhttp3.MediaType;
17 | import okhttp3.OkHttpClient;
18 | import okhttp3.Response;
19 | import okhttp3.ResponseBody;
20 | import okio.Buffer;
21 | import okio.BufferedSource;
22 | import okio.ForwardingSource;
23 | import okio.Okio;
24 | import okio.Source;
25 |
26 | /**
27 | * Created by Danny 姜
28 | */
29 | public class SquareUtils {
30 |
31 | static private OkHttpClient client;
32 |
33 | static public synchronized OkHttpClient getClient() {
34 | if (client == null) {
35 | final File cacheDir = MyApplication.getInstance().getExternalCacheDir();
36 | client = new OkHttpClient.Builder()
37 | //Interceptor -> cache -> NetworkInterceptor
38 | //.addNetworkInterceptor(getLogger())
39 | .cache(new Cache(new File(cacheDir, "okhttp"), 60 * 1024 * 1024))
40 | //.dispatcher(getDispatcher())
41 | //.dns(HTTP_DNS)
42 | .build();
43 | }
44 | return client;
45 | }
46 |
47 | /**
48 | * Not singleton
49 | */
50 | private static OkHttpClient getProgressBarClient(final ProgressListener listener) {
51 | return getClient().newBuilder().addNetworkInterceptor(new Interceptor() {
52 | @Override
53 | public Response intercept(Chain chain) throws IOException {
54 | Response originalResponse = chain.proceed(chain.request());
55 | return originalResponse.newBuilder()
56 | .body(new ProgressResponseBody(originalResponse.body(), listener))
57 | .build();
58 | }
59 | }).build();
60 | }
61 |
62 | /**
63 | * Download Big Image only, Not singleton but shared cache
64 | */
65 | static public Picasso getPicasso(Context context, ProgressListener listener) {
66 | OkHttpClient client = getProgressBarClient(listener);
67 | OkHttp3Downloader downloader = new OkHttp3Downloader(client);
68 | return new Picasso.Builder(context).downloader(downloader)
69 | .memoryCache(com.squareup.picasso.Cache.NONE)
70 | .build();
71 | }
72 |
73 | public interface ProgressListener {
74 | @WorkerThread
75 | void update(@IntRange(from = 0, to = 100) int percent);
76 | }
77 |
78 | private static class ProgressResponseBody extends ResponseBody {
79 |
80 | private final ResponseBody responseBody;
81 | private final ProgressListener progressListener;
82 | private BufferedSource bufferedSource;
83 |
84 | public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
85 | this.responseBody = responseBody;
86 | this.progressListener = progressListener;
87 | }
88 |
89 | @Override
90 | public MediaType contentType() {
91 | return responseBody.contentType();
92 | }
93 |
94 | @Override
95 | public long contentLength() {
96 | return responseBody.contentLength();
97 | }
98 |
99 | @Override
100 | public BufferedSource source() {
101 | if (bufferedSource == null) {
102 | bufferedSource = Okio.buffer(source(responseBody.source()));
103 | }
104 | return bufferedSource;
105 | }
106 |
107 | private Source source(Source source) {
108 |
109 | return new ForwardingSource(source) {
110 | long totalBytesRead = 0L;
111 |
112 | @Override
113 | public long read(Buffer sink, long byteCount) throws IOException {
114 | long bytesRead = super.read(sink, byteCount);
115 | // read() returns the number of bytes read, or -1 if this source is exhausted.
116 | totalBytesRead += bytesRead != -1 ? bytesRead : 0;
117 | if (progressListener != null) {
118 | progressListener.update(
119 | ((int) ((100 * totalBytesRead) / responseBody.contentLength())));
120 | }
121 | return bytesRead;
122 | }
123 | };
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_six/SourceCode/ClassInit.java:
--------------------------------------------------------------------------------
1 | public class ClassInit{
2 | public static int value = 1;
3 |
4 | // 静态语句块在初始化阶段执行
5 | static{
6 | System.out.println("ClassInit static block!");
7 | }
8 |
9 | // 非静态语句块只在创建对象实例时被执行
10 | {
11 | System.out.println("ClassInit non-static block!");
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_six/SourceCode/ClassInitTest.java:
--------------------------------------------------------------------------------
1 | public class ClassInitTest{
2 | public static void main(String[] ags){
3 |
4 | ClassInit.value = 2;
5 | //System.out.println("~~~~~~~~~~~~~~~~~~~~");
6 |
7 | ClassInit ci = new ClassInit();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_six/SourceCode/InitOrder.java:
--------------------------------------------------------------------------------
1 | public class InitOrder {
2 | public static void main(String[] args){
3 | Child child = new Child();
4 | System.out.println("~~~~~~~~~~~~~~~~~~~~~~~");
5 | child = new Child();
6 | }
7 |
8 | static class Child extends Parent{
9 | static {
10 | System.out.println("Child static block!");
11 | }
12 |
13 | {
14 | System.out.println("Child non-static block!");
15 | }
16 |
17 | public Child(){
18 | System.out.println("Child constructor!");
19 | }
20 | }
21 |
22 | static class Parent{
23 | static {
24 | System.out.println("Parent static block!");
25 | }
26 |
27 | {
28 | System.out.println("Parent non-static block!");
29 | }
30 |
31 | public Parent(){
32 | System.out.println("Parent constructor!");
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_six/SourceCode/README.md:
--------------------------------------------------------------------------------
1 | >class 在初始化阶段,只会初始化与类相关的静态赋值语句和静态语句,也就是有 static 关键字修饰的信息,而没有 static 修饰的语句块在实例化对象的时候才会执行。
2 |
3 | #### ClassInit.java
4 | ```
5 | public class ClassInit{
6 | public static int value = 1;
7 |
8 | // 静态语句块在初始化阶段执行
9 | static{
10 | System.out.println("ClassInit static block!");
11 | }
12 |
13 | // 非静态语句块只在创建对象实例时被执行
14 | {
15 | System.out.println("ClassInit non-static block!");
16 | }
17 | }
18 | ```
19 |
20 | #### ClassInitTest.java
21 | ```
22 | public class ClassInitTest{
23 | public static void main(String[] ags){
24 |
25 | ClassInit.value = 2;
26 | }
27 | }
28 | ```
29 | 执行结果如下:
30 | ```
31 | ClassInit static block!
32 | ```
33 | 只有静态语句被执行,但是如果在main方法中使用new创建ClassInit对象,如下:
34 | ```
35 | public class ClassInitTest{
36 | public static void main(String[] ags){
37 |
38 | ClassInit.value = 2;
39 |
40 | ClassInit ci = new ClassInit();
41 | }
42 | }
43 | ```
44 | 则非静态语句块也会被执行,运行结果如下:
45 | ```
46 | ClassInit static block!
47 | ClassInit non-static block!
48 | ```
49 |
50 | #### InitOrder.java
51 | 对象的初始化顺序如下:
52 | 静态变量/静态代码块 -> 普通代码块 -> 构造函数
53 | ```
54 | public class InitOrder {
55 | public static void main(String[] args){
56 | Child child = new Child();
57 | System.out.println("~~~~~~~~~~~~~~~~~~~~~~~");
58 | child = new Child();
59 | }
60 |
61 | static class Child extends Parent{
62 | static {
63 | System.out.println("Child static block!");
64 | }
65 |
66 | {
67 | System.out.println("Child non-static block!");
68 | }
69 |
70 | public Child(){
71 | System.out.println("Child constructor!");
72 | }
73 | }
74 |
75 | static class Parent{
76 | static {
77 | System.out.println("Parent static block!");
78 | }
79 |
80 | {
81 | System.out.println("Parent non-static block!");
82 | }
83 |
84 | public Parent(){
85 | System.out.println("Parent constructor!");
86 | }
87 | }
88 | }
89 | ```
90 | 执行结果如下:
91 | ```
92 | Parent static block!
93 | Child static block!
94 | Parent non-static block!
95 | Parent constructor!
96 | Child non-static block!
97 | Child constructor!
98 | ~~~~~~~~~~~~~~~~~~~~~~~
99 | Parent non-static block!
100 | Parent constructor!
101 | Child non-static block!
102 | Child constructor!
103 | ```
104 | 说明:
105 | 1. 父类静态变量和静态代码块;
106 | 2. 子类静态变量和静态代码块;
107 | 3. 父类普通成员变量和普通代码块;
108 | 4. 父类的构造函数;
109 | 5. 子类普通成员变量和普通代码块;
110 | 6. 子类的构造函数。
111 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_thirty/HtmlLessons/第30讲:如何设计一个比较合理的 LogUtil 类?.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 第30讲:如何设计一个比较合理的 LogUtil 类?
6 |
7 |
8 | 我们在项目中经常会打印各种 Log 信息,用来查看程序运行中的详细情况。因此打印日志俨然已经成了程序开发工作中的一部分,而设计一个合理的 LogUtils 也成了一个好的程序员的必选条件之一。
9 |
10 |
11 |
12 | 设置 Debug 开关
13 | 有时候为了调试方便,我们甚至会将用户的账号、密码、余额等信息也打印到控制台,但是如果这部分 Log 信息也出现在线上版本中,那用户的私密信息,或者程序相关核心实现都会被暴露;除此之外,打印日志的代码并不属于业务需求的必要代码,复杂的 Log 信息还会造成一定的性能损耗,所以这部分代码都不应该出现在线上版本的 App 中。因此我们需要设置一个开关来控制是否打印 Log 日志,只有在 Debug 版本才会打开此开关,如图所示:
14 | 
15 |
16 |
17 | 通常情况下我们会使用 BuildConfig.DEBUG 来作为是否要打印日志的开关。但是使用这个变量具有一定的局限性。比如现场突然发现一个异常现象,而我们需要现场抓取异常的日志信息加以分析。因为是 release 版本,所有不会有任何 log 信息被打印。因此这个开关的设置最好具有一定的灵活性,比如可以再加一层 System Property 的设置,如图所示:
18 | 
19 |
20 |
21 | 上述代码打印结果如下所示:
22 | 
23 |
24 |
25 | 使用 System Property 的好处是一旦设置之后,即使重启 App,System Property 中的变量依旧是设置之后的值,与 Android 中的 SharedPreference 非常相似。开发者只要定义好通过何种方式将这种属性打开即可,建议仿照 Android 系统设置中的“开发者选项”来实现,当用户快速连续点击某 item 时,才将此属性打开。
26 | 另外,我们还可以通过 ProGuard 在打包阶段清除某些 Log 日志、打印代码,具体规则如下:
27 | 
28 |
29 |
30 | 设置 log 日志本地保存
31 | 有时候我们需要将部分 log 日志以文件的形式保存在手机磁盘中,因此我们还需要设置开关,控制日志是打印在控制台还是保存到文件中。如下所示:
32 | 
33 |
34 |
35 | 因为涉及文件的写操作,所以最好是在子线程中完成日志的保存。因此在 LogUtils 中可以使用线程池控制子线程完成日志保存,如下所示:
36 | 
37 |
38 |
39 | Config 文件统一配置
40 | 如果 LogUtils 中的开关较多,再加上还有其他配置项,比如日志保存为文件的路径等。这种情况可以使用一个全局的 Config 来配置 LogUtils 中所有的配置项,如下所示:
41 | 
42 |
43 |
44 | 特殊格式转换
45 | 我们经常会处理一些特殊格式的数据,比如 JSON、XML。为了打印这部分数据,还需要在 LogUtils 类中做一些格式转换的操作:
46 | 
47 |
48 | 借助于三方库打印 log
49 |
50 |
51 |
52 | 如果感觉自己封装一个 LogUtils 类比较麻烦,或者没有好的实现思路,那就不妨尝试使用行内内已经成熟的 log 日志库。
53 | XLog
54 | XLog 是比较常用的打印日志开源库,GitHub 地址参考 XLog github。XLog 基本囊括了我们上文介绍的所有功能:
55 |
84 | XLog 使用比较简单,先调用 init 方法进行初始化,最好是在 Application 中。
85 | 
86 |
87 |
88 | 然后就可以直接调用 Xlog 的静态方法打印相应日志即可:
89 | 
90 |
91 |
92 | 也可以在打印日志时,添加局部的配置信息:
93 | 
94 |
95 |
96 | 打印结果类似下图所示:
97 | 
98 |
99 |
100 | 可以看出,除了打印日志的类和方法,XLog 还能打印线程信息以及调用栈信息。
101 | 总结
102 | 这节课主要介绍了项目开发中对 LogUtils 类的配置,因为项目中会在很多地方打印各种日志信息,所以为了方便统一管理,我们应该将所有日志打印的工作都集中到一个 Utils 类中。LogUtils 应该对外部提供相应的开关,用来设置是否需要打印日志,以及打印日志的通道,如果是将日志保存在文件中等耗时操作,还应该考虑在子线程中完成。
103 |
104 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_twenty_nine/HtmlLessons/第29讲:MVP 中 presenter 生命周期的管理.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 第29讲:MVP 中 presenter 生命周期的管理
6 |
7 |
8 | 我们经常在 Android MVP 架构中的 Presenter 层做一些耗时操作,比如请求网络数据等。然后根据请求后的结果刷新 View。但是如果按返回结束 Activity,而 Presenter 依然在执行耗时操作,那么就有可能造成内存泄漏,严重时甚至会造成程序崩溃,因为 Presenter 中的 View 已经变为 null。为了解决这个问题,我们需要将 Activity 的某些生命周期方法与 Presenter 保持一致。
9 | Lifecycle 绑定 Presenter 生命周期
10 | LifeCycle 的使用很简单,Activity 通过继承 AppCompatActivity 会自动继承来自父类 ComponentActivity 的方法 getLifeCycle。具体使用如图所示:
11 | 
12 | onStateChanged 方法会在 Activity 的生命周期发生变化时被触发,比如打开 LoginActivity 时就会显示如下日志:
13 | 
14 | 在 LoginActivity 中按下返回键,则打印如下日志:
15 | 
16 | LifeCycle 还提供了注解的方式供使用,因此我们可以很容易地创建一个接口 IPresenter 类,在这个接口中声明对各种 Activity 生命周期的回调,如图所示:
17 | 
18 | 上图中 IPresenter 接口通过注解的方式,将 Activity 的生命周期绑定到相应的方法上,我们只要在 BasePresenter 中实现上述方法,并在方法中做数据绑定与取消的操作即可,具体如图所示:
19 | 
20 | 注意:上图中的代码存在一点问题,使用了 Android 中的 Log 来打印日志信息。严格来说在 Presenter 层应该禁止出现任何 Android 中的类,这里为了快速演示效果,所以直接使用 Log 打印日志。
21 | 接下来只要再修改 LoginActivity,将 BasePresenter 注册到 LifeCycle 中即可,如图所示:
22 | 
23 | 重新打开 LoginActivity,显示日志结果如图所示:
24 | 
25 | 关闭 LoginActivity,显示日志如图所示:
26 | 
27 | 可以看出当 Activity 执行 onDestroy 时,BasePresenter 的 onDestroy 方法也会被执行。
28 | 在 LoginActivity 方法中有 login 方法,此方法会执行 BasePresenter 中的 login 方法,如图所示:
29 | 
30 | 在 BasePresenter 的 login 方法中,模拟执行了一段耗时操作。如果在 Activity onDestroy 时,BasePresenter 还没有处理完耗时操作,则会造成内存泄漏。解决办法就是在 BasePresenter 的 onDestroy 方法中停止正在执行的耗时操作,如图所示:
31 | 
32 | 合理使用 Presenter 生命周期
33 | 并不是所有的 Activity 的生命周期都需要通知 Presenter。举一个例子,新增的需求是根据 GPS 定位,展示用户的位置。但是为了节省电量,有可能会在灭屏之后,解绑定 GPS 定位的接收事件。
34 | 如果使用 MVP 架构,需要有一个 TrackingActivity 实现 MVP 的接口 TrackingView,并在生命周期方法中调用 presenter 的相应方法,如图所示:
35 | 
36 | TrackingPresenter 是 presenter 层的实现,内部实现了 GPS 定位的监听事件,并分别在 resume 和 stop 方法中绑定和解绑定 GPS。如图所示:
37 |
38 | 
39 | 上述写法是常规的 MVP 写法,但是存在 2 个问题:
40 |
41 | -
42 |
GpsTracker 实际的控制周期是跟 Activity 有关的,因为亮屏和灭屏事件是在 Activity 中接收的,中间多了一层 Presenter 层其实是多余的。
43 |
44 | -
45 |
从重构的角度看,TrackingPresenter 其实违反了职责单一原则(Single Responsibility),因为 Presenter 层的主要作用是用来刷新 View,但是上述代码中 TrackingPresenter 还负责对 GpsTracker 进行管理。
46 |
47 |
48 | 这种情况下,我们可以将 GpsTracker 初始化在 Activity 中,将 GpsTracker 的绑定与解绑定都在 Activity 中管理。最后将 GpsTracker 传给 TrackingPresenter 执行业务上的逻辑,具体实现如下:
49 | TrackingActivity
50 | 
51 | TrackingActivity 中对 tracker 进行管理,并且根据 Presenter 层的逻辑处理,回调 showCurrentPosition 方法。
52 | TrackingPresenter
53 | 
54 | TrackingPresenter 只负责对 GPS 事件的监听,并根据结果刷新 View。
55 |
56 | 这样 View 层和 Presenter 层的职责单一原则就完成了,在完成实际需求的前提下,也丝毫不影响 Presenter 层的单元测试。当然,并没有绝对正确或者错误的架构,说到底代码具体要怎样写,功能具体应该怎样实现,最终还是要看实际业务场景。
57 |
58 | 总结
59 | 这节课我主要对 MVP 架构中 Presenter 层的使用做了 2 点优化介绍:
60 |
61 | -
62 |
如何支持 Presenter 的生命周期,使其在 Activity 被销毁时也能取消相应的耗时请求。
63 |
64 | -
65 |
合理使用 Presenter 的生命周期,Activity 中所有的方法都委托给 Presenter 来处理是不合理的,这样会造成 Presenter 层极其庞大,也难以维护,有时也会违反职责单一原则。
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_two/SourceCode/GCRootClassVariable.java:
--------------------------------------------------------------------------------
1 | public class GCRootClassVariable{
2 | private static int _10MB = 10 * 1024 * 1024;
3 | private byte[] memory;
4 | private GCRootClassVariable classVariable;
5 |
6 | public GCRootClassVariable(int size){
7 | memory = new byte[size];
8 | }
9 |
10 | public static void main(String[] args){
11 | System.out.println("程序开始:");
12 | printMemory();
13 | GCRootClassVariable g = new GCRootClassVariable(4 * _10MB);
14 | g.classVariable = new GCRootClassVariable(8 * _10MB);
15 | g = null;
16 | System.gc();
17 | System.out.println("GC完成");
18 | printMemory();
19 | }
20 |
21 | /**
22 | * 打印出当前JVM剩余空间和总的空间大小
23 | */
24 | public static void printMemory() {
25 | System.out.print("free is " + Runtime.getRuntime().freeMemory()/1024/1024 + " M, ");
26 | System.out.println("total is " + Runtime.getRuntime().totalMemory()/1024/1024 + " M, ");
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_two/SourceCode/GCRootLocalVariable.java:
--------------------------------------------------------------------------------
1 | public class GCRootLocalVariable {
2 | private int _10MB = 10 * 1024 * 1024;
3 | private byte[] memory = new byte[8 * _10MB];
4 |
5 | public static void main(String[] args){
6 | System.out.println("开始时:");
7 | printMemory();
8 | method();
9 | System.gc();
10 | System.out.println("第二次GC完成");
11 | printMemory();
12 | }
13 |
14 | public static void method() {
15 | GCRootLocalVariable g = new GCRootLocalVariable();
16 | System.gc();
17 | System.out.println("第一次GC完成");
18 | printMemory();
19 | }
20 |
21 | /**
22 | * 打印出当前JVM剩余空间和总的空间大小
23 | */
24 | public static void printMemory() {
25 | System.out.print("free is " + Runtime.getRuntime().freeMemory()/1024/1024 + " M, ");
26 | System.out.println("total is " + Runtime.getRuntime().totalMemory()/1024/1024 + " M, ");
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_two/SourceCode/GCRootStaticVariable.java:
--------------------------------------------------------------------------------
1 | public class GCRootStaticVariable{
2 | private static int _10MB = 10 * 1024 * 1024;
3 | private byte[] memory;
4 | private static GCRootStaticVariable staticVariable;
5 |
6 | public GCRootStaticVariable(int size) {
7 | memory = new byte[size];
8 | }
9 |
10 | public static void main(String[] args){
11 | System.out.println("程序开始:");
12 | printMemory();
13 | GCRootStaticVariable g = new GCRootStaticVariable(4 * _10MB);
14 | g.staticVariable = new GCRootStaticVariable(8 * _10MB);
15 | // 将g置为null, 调用GC时可以回收此对象内存
16 | g = null;
17 | System.gc();
18 | System.out.println("GC完成");
19 | printMemory();
20 | }
21 |
22 | /**
23 | * 打印出当前JVM剩余空间和总的空间大小
24 | */
25 | public static void printMemory() {
26 | System.out.print("free is " + Runtime.getRuntime().freeMemory()/1024/1024 + " M, ");
27 | System.out.println("total is " + Runtime.getRuntime().totalMemory()/1024/1024 + " M, ");
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_two/SourceCode/GCRootThread.java:
--------------------------------------------------------------------------------
1 | public class GCRootThread{
2 | private int _10MB = 10 * 1024 * 1024;
3 | private byte[] memory = new byte[8 * _10MB];
4 |
5 | public static void main(String[] args) throws Exception {
6 | System.out.println("开始前内存情况:");
7 | printMemory();
8 | AsyncTask at = new AsyncTask(new GCRootThread());
9 | Thread thread = new Thread(at);
10 | thread.start();
11 | System.gc();
12 | System.out.println("main方法执行完毕,完成GC");
13 | printMemory();
14 |
15 | thread.join();
16 | at = null;
17 | System.gc();
18 | System.out.println("线程代码执行完毕,完成GC");
19 | printMemory();
20 | }
21 |
22 | /**
23 | * 打印出当前JVM剩余空间和总的空间大小
24 | */
25 | public static void printMemory() {
26 | System.out.print("free is " + Runtime.getRuntime().freeMemory()/1024/1024 + " M, ");
27 | System.out.println("total is " + Runtime.getRuntime().totalMemory()/1024/1024 + " M, ");
28 | }
29 |
30 | private static class AsyncTask implements Runnable {
31 | private GCRootThread gcRootThread;
32 | public AsyncTask(GCRootThread gcRootThread){
33 | this.gcRootThread = gcRootThread;
34 | }
35 | @Override
36 | public void run() {
37 | try{
38 | Thread.sleep(500);
39 | } catch(Exception e){}
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/AndroidLessons/lesson_two/SourceCode/MinorGCTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * VM agrs: -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails
3 | * -XX:SurvivorRatio=8 -XX:+UseSerialGC
4 | */
5 |
6 | public class MinorGCTest {
7 | private static final int _1MB = 1024 * 1024;
8 |
9 | public static void testAllocation() {
10 | byte[] allocation1, allocation2, allocation3, allocation4;
11 | allocation1 = new byte[2 * _1MB];
12 | allocation2 = new byte[2 * _1MB];
13 | allocation3 = new byte[2 * _1MB];
14 | allocation4 = new byte[4 * _1MB];
15 | }
16 |
17 | public static void main(String[] agrs) {
18 | testAllocation();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AndroidEngineerAdvanced
2 |
3 | 安卓工程师进阶之路
4 |
5 | 无论是你短期内想提升 Android 内功实力,突破自己工作中的能力瓶颈,还是准备参加 Android 面试,都会在这个课程中有所收获。
6 |
7 | 根据面试时经常被问到的几个方向,划分了 4 个模块来展开:
8 |
9 | 1.JVM 必知必会:通过介绍 JVM 和 DVM ,使你对 Java 字节码与 Dalvik 字节码的执行机制有一定的理解。
10 |
11 | 2.Android 核心技术:介绍 Android 开发中常用的核心技术,比如自定义 View、Handler,以及一些开源框架的原理实现。
12 |
13 | 3.源码分析:通过剖析部分 Android Framework 源码,使你对 Activity 启动、APK 安装过程等流程了然于胸。
14 |
15 | 4.常见问题剖析:介绍一些项目中常见的疑难问题,使你能够对现有项目做出合理并迅速的重构优化。
16 |
17 | 为便于你理解,采用“知识点 + 项目实践”的讲解方式,侧重总结工作上的实践经验,并和你分享一些疑难问题的解决思路,让你在以后的工作中,能够有方法论的指导。
18 |
--------------------------------------------------------------------------------