├── .github └── workflows │ ├── ci-test.yml │ └── version-check.yml ├── .gitignore ├── .idea ├── encodings.xml ├── gradle.xml ├── misc.xml └── scopes │ └── scope_settings.xml ├── CHANGELOG.md ├── Contributing.md ├── LICENSE ├── README.md ├── build.gradle ├── codecov.yml ├── doc └── Android_Changes_from_7.x_to_8.0.0.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── qiniu │ │ └── android │ │ ├── BaseTest.java │ │ ├── FastDatePrinterTest.java │ │ ├── TempFile.java │ │ ├── TestConfig.java │ │ ├── UploadFileInfoTest.java │ │ ├── WaitTest.java │ │ ├── bigdata │ │ ├── BigDataConfigurationTest.java │ │ ├── client │ │ │ └── ClientTest.java │ │ └── pipeline │ │ │ └── PipelineTest.java │ │ ├── collect │ │ ├── UploadInfoReporterTest.java │ │ ├── UploadReportItemTest.java │ │ └── UplogTest.java │ │ ├── common │ │ ├── AutoZoneTest.java │ │ ├── CollectConfigTest.java │ │ └── FixZoneTest.java │ │ ├── http │ │ ├── HttpHeaderTest.java │ │ ├── HttpTest.java │ │ ├── HttpsTest.java │ │ ├── ResponseInfoTest.java │ │ ├── connectCheck │ │ │ └── ConnectCheckTest.java │ │ ├── dns │ │ │ ├── DnsApiTest.java │ │ │ ├── DnsCacheFileTest.java │ │ │ ├── DnsTransactionTest.java │ │ │ ├── HappyDnsTest.java │ │ │ └── SystemDnsTest.java │ │ ├── request │ │ │ ├── RequestTransactionTest.java │ │ │ └── httpclient │ │ │ │ └── SystemHttpClientTest.java │ │ └── serverRegion │ │ │ ├── UploadDomainRegionTest.java │ │ │ └── UploadServerFreezeManagerTest.java │ │ ├── serverRegion │ │ └── HttpServerManagerTest.java │ │ ├── storage │ │ ├── CancelTest.java │ │ ├── ComplexUploadSceneTest.java │ │ ├── ConcurrentResumeUploadTest.java │ │ ├── FormUploadTest.java │ │ ├── GlobalConfigurationTest.java │ │ ├── ResumeUploadTest.java │ │ ├── RetryTest.java │ │ ├── SyncFormUploadTest.java │ │ ├── TestFileRecorder.java │ │ ├── TokenTest.java │ │ ├── UploadBaseTest.java │ │ ├── UploadFlowTest.java │ │ ├── UriTest.java │ │ └── serverConfig │ │ │ └── ServerConfigTest.java │ │ ├── transaction │ │ └── TransactionManagerTest.java │ │ └── utils │ │ ├── AndroidNetworkTest.java │ │ ├── AsynTest.java │ │ ├── Base64Test.java │ │ ├── CacheTest.java │ │ ├── ContextTest.java │ │ ├── CrcTest.java │ │ ├── EtagTest.java │ │ ├── GZipTest.java │ │ ├── JsonTest.java │ │ ├── ListVectorTest.java │ │ ├── LogUtilsTest.java │ │ ├── NetworkTest.java │ │ ├── SingleFlightTest.java │ │ ├── StringUtilsTest.java │ │ └── UtilsTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── qiniu │ │ └── android │ │ ├── bigdata │ │ ├── Configuration.java │ │ ├── client │ │ │ ├── Client.java │ │ │ ├── CompletionHandler.java │ │ │ └── PostArgs.java │ │ └── pipeline │ │ │ ├── Pipeline.java │ │ │ └── Points.java │ │ ├── collect │ │ ├── ReportConfig.java │ │ ├── ReportItem.java │ │ └── UploadInfoReporter.java │ │ ├── common │ │ ├── AutoZone.java │ │ ├── Config.java │ │ ├── Constants.java │ │ ├── FixedZone.java │ │ ├── Zone.java │ │ ├── ZoneInfo.java │ │ └── ZonesInfo.java │ │ ├── http │ │ ├── CancellationHandler.java │ │ ├── Headers.java │ │ ├── HttpDate.java │ │ ├── ProgressHandler.java │ │ ├── ProxyConfiguration.java │ │ ├── ResponseInfo.java │ │ ├── UrlConverter.java │ │ ├── UserAgent.java │ │ ├── connectCheck │ │ │ └── ConnectChecker.java │ │ ├── dns │ │ │ ├── BaseDns.java │ │ │ ├── Dns.java │ │ │ ├── DnsCacheFile.java │ │ │ ├── DnsCacheInfo.java │ │ │ ├── DnsNetworkAddress.java │ │ │ ├── DnsPrefetchTransaction.java │ │ │ ├── DnsPrefetcher.java │ │ │ ├── DnsSource.java │ │ │ ├── HappyDns.java │ │ │ ├── HttpDns.java │ │ │ ├── IDnsNetworkAddress.java │ │ │ ├── SystemDns.java │ │ │ └── UdpDns.java │ │ ├── metrics │ │ │ ├── UploadMetrics.java │ │ │ ├── UploadRegionRequestMetrics.java │ │ │ ├── UploadSingleRequestMetrics.java │ │ │ └── UploadTaskMetrics.java │ │ ├── networkStatus │ │ │ ├── NetworkStatusManager.java │ │ │ └── UploadServerNetworkStatus.java │ │ ├── request │ │ │ ├── HttpRegionRequest.java │ │ │ ├── HttpSingleRequest.java │ │ │ ├── IRequestClient.java │ │ │ ├── IUploadRegion.java │ │ │ ├── IUploadServer.java │ │ │ ├── Request.java │ │ │ ├── RequestTransaction.java │ │ │ ├── UploadRequestInfo.java │ │ │ ├── UploadRequestState.java │ │ │ ├── handler │ │ │ │ ├── CheckCancelHandler.java │ │ │ │ ├── RequestProgressHandler.java │ │ │ │ └── RequestShouldRetryHandler.java │ │ │ └── httpclient │ │ │ │ ├── ByteBody.java │ │ │ │ ├── CountingRequestBody.java │ │ │ │ ├── MultipartBody.java │ │ │ │ └── SystemHttpClient.java │ │ └── serverRegion │ │ │ ├── HttpServerManager.java │ │ │ ├── UploadDomainRegion.java │ │ │ ├── UploadServer.java │ │ │ ├── UploadServerFreezeManager.java │ │ │ └── UploadServerFreezeUtil.java │ │ ├── storage │ │ ├── BaseUpload.java │ │ ├── ConcurrentResumeUpload.java │ │ ├── Configuration.java │ │ ├── FileRecorder.java │ │ ├── FormUpload.java │ │ ├── GlobalConfiguration.java │ │ ├── KeyGenerator.java │ │ ├── NetReadyHandler.java │ │ ├── PartsUpload.java │ │ ├── PartsUploadPerformer.java │ │ ├── PartsUploadPerformerV1.java │ │ ├── PartsUploadPerformerV2.java │ │ ├── Recorder.java │ │ ├── UpCancellationSignal.java │ │ ├── UpCompletionHandler.java │ │ ├── UpProgress.java │ │ ├── UpProgressBytesHandler.java │ │ ├── UpProgressHandler.java │ │ ├── UpToken.java │ │ ├── UploadBlock.java │ │ ├── UploadData.java │ │ ├── UploadInfo.java │ │ ├── UploadInfoV1.java │ │ ├── UploadInfoV2.java │ │ ├── UploadManager.java │ │ ├── UploadOptions.java │ │ ├── UploadSource.java │ │ ├── UploadSourceFile.java │ │ ├── UploadSourceStream.java │ │ ├── UploadSourceUri.java │ │ └── serverConfig │ │ │ ├── ServerConfig.java │ │ │ ├── ServerConfigCache.java │ │ │ ├── ServerConfigMonitor.java │ │ │ ├── ServerConfigSynchronizer.java │ │ │ └── ServerUserConfig.java │ │ ├── transaction │ │ └── TransactionManager.java │ │ └── utils │ │ ├── AndroidNetwork.java │ │ ├── AsyncRun.java │ │ ├── BytesUtils.java │ │ ├── Cache.java │ │ ├── Constants.java │ │ ├── ContextGetter.java │ │ ├── Crc32.java │ │ ├── Etag.java │ │ ├── FastDatePrinter.java │ │ ├── GZipUtil.java │ │ ├── GroupTaskThread.java │ │ ├── IPAddressUtil.java │ │ ├── Json.java │ │ ├── ListUtils.java │ │ ├── ListVector.java │ │ ├── LogUtil.java │ │ ├── MD5.java │ │ ├── MapUtils.java │ │ ├── SingleFlight.java │ │ ├── StringMap.java │ │ ├── StringUtils.java │ │ ├── UrlSafeBase64.java │ │ ├── UrlUtils.java │ │ ├── Utils.java │ │ └── Wait.java │ └── res │ └── values │ └── strings.xml ├── mvn_push.gradle └── settings.gradle /.github/workflows/ci-test.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | paths-ignore: 4 | - '**.md' 5 | pull_request: 6 | paths-ignore: 7 | - '**.md' 8 | name: Run Test Cases 9 | jobs: 10 | test-on-mac: 11 | runs-on: macos-12 12 | steps: 13 | - name: Checkout repo 14 | uses: actions/checkout@v2 15 | with: 16 | ref: ${{ github.ref }} 17 | - name: Setup Java Version 18 | uses: actions/setup-java@v2 19 | with: 20 | java-version: 11 21 | distribution: temurin 22 | cache: gradle 23 | - name: Run Cases 24 | uses: reactivecircus/android-emulator-runner@v2 25 | with: 26 | api-level: 22 27 | profile: Nexus 6 28 | arch: x86_64 29 | script: | 30 | ./gradlew connectedCheck 31 | ./gradlew build 32 | ./gradlew :library:createDebugAndroidTestCoverageReport --info --stacktrace 33 | ./gradlew connectedAndroidTest --info --stacktrace 34 | - name: Upload coverage.txt 35 | run: | 36 | bash <(curl -s https://codecov.io/bash) 37 | -------------------------------------------------------------------------------- /.github/workflows/version-check.yml: -------------------------------------------------------------------------------- 1 | name: Android SDK Version Check 2 | on: 3 | push: 4 | tags: 5 | - "v[0-9]+.[0-9]+.[0-9]+" 6 | jobs: 7 | linux: 8 | name: Version Check 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v2 13 | - name: Set env 14 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV 15 | - name: Check 16 | run: | 17 | set -e 18 | grep -qF "## ${RELEASE_VERSION}" CHANGELOG.md 19 | grep -qF "${RELEASE_VERSION}" README.md 20 | grep -qF "public static final String VERSION = \"${RELEASE_VERSION}\";" library/src/main/java/com/qiniu/android/common/Constants.java 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | curl/.cxx 5 | curl/.idea 6 | curl/gradle/* 7 | curl/gradlew* 8 | 9 | # files for the dex VM 10 | *.dex 11 | 12 | # Java class files 13 | *.class 14 | 15 | # generated files 16 | bin/ 17 | gen/ 18 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Eclipse project files 23 | .classpath 24 | .project 25 | .settings 26 | 27 | # Gradle 28 | .gradle 29 | build/ 30 | 31 | # System 32 | .DS_Store 33 | java_pid* 34 | 35 | # Android Studio 36 | .idea/workspace.xml 37 | .idea/libraries 38 | .idea/kotlinc.xml 39 | .idea/caches/ 40 | library/library.iml 41 | .idea/* 42 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # 贡献代码指南 2 | 3 | 我们非常欢迎大家来贡献代码,我们会向贡献者致以最诚挚的敬意。 4 | 5 | 一般可以通过在Github上提交[Pull Request](https://github.com/qiniu/android-sdk)来贡献代码。 6 | 7 | ## Pull Request要求 8 | 9 | - **代码规范** 参考 。 10 | 11 | - **代码格式** 提交前请使用Android Studio 默认风格进行格式化。 12 | 13 | - **必须添加测试!** - 如果没有测试(单元测试、集成测试都可以),那么提交的补丁是不会通过的。 14 | 15 | - **记得更新文档** - 保证`README.md`以及其他相关文档及时更新,和代码的变更保持一致性。 16 | 17 | - **考虑我们的发布周期** - 我们的版本号会服从[SemVer v2.0.0](http://semver.org/),我们绝对不会随意变更对外的API。 18 | 19 | - **创建feature分支** - 最好不要从你的master分支提交 pull request。 20 | 21 | - **一个feature提交一个pull请求** - 如果你的代码变更了多个操作,那就提交多个pull请求吧。 22 | 23 | - **清晰的commit历史** - 保证你的pull请求的每次commit操作都是有意义的。如果你开发中需要执行多次的即时commit操作,那么请把它们放到一起再提交pull请求。 24 | 25 | ## 运行测试 26 | 27 | ``` bash 28 | $ ./gradlew connectedAndroidTest 29 | ``` 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Qiniu, Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | //task wrapper(type: Wrapper) { 3 | // gradleVersion = '2.13' 4 | //} 5 | 6 | buildscript { 7 | repositories { 8 | mavenCentral() 9 | google() 10 | } 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:7.3.1' 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 | jcenter() 21 | google() 22 | mavenCentral() 23 | } 24 | // gradle.projectsEvaluated { 25 | // tasks.withType(JavaCompile) { 26 | // options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" 27 | // } 28 | // } 29 | } 30 | subprojects { 31 | afterEvaluate { 32 | project -> 33 | if (project.hasProperty("android-sdk")) { 34 | android { 35 | compileSdkVersion = rootProject.compileSdkVersion 36 | buildToolsVersion = rootProject.buildToolsVersion 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | ci: 3 | - prow.qiniu.io # prow 里面运行需添加,其他 CI 不要 4 | require_ci_to_pass: no # 改为 no,否则 codecov 会等待其他 GitHub 上所有 CI 通过才会留言。 5 | 6 | github_checks: #关闭github checks 7 | annotations: false 8 | 9 | comment: 10 | layout: "reach, diff, flags, files" 11 | behavior: new # 默认是更新旧留言,改为 new,删除旧的,增加新的。 12 | require_changes: false # if true: only post the comment if coverage changes 13 | require_base: no # [yes :: must have a base report to post] 14 | require_head: yes # [yes :: must have a head report to post] 15 | branches: # branch names that can post comment 16 | - "master" 17 | 18 | coverage: 19 | status: # 评判 pr 通过的标准 20 | patch: off 21 | project: # project 统计所有代码x 22 | default: 23 | # basic 24 | target: 70% # 总体通过标准 25 | threshold: 3% # 允许单次下降的幅度 26 | base: auto 27 | if_not_found: success 28 | if_ci_failed: error 29 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | 3 | VERSION_NAME= 4 | VERSION_CODE= 5 | GROUP=com.qiniu 6 | POM_DESCRIPTION=Qiniu Cloud Storage SDK for Android 7 | POM_URL=https://github.com/qiniu/android-sdk 8 | POM_SCM_URL=https://github.com/qiniu/android-sdk 9 | POM_SCM_CONNECTION=scm:git@github.com:qiniu/android-sdk.git 10 | POM_SCM_DEV_CONNECTION=scm:git@github.com:qiniu/android-sdk.git 11 | POM_LICENCE_NAME=MIT License 12 | POM_LICENCE_URL=http://opensource.org/licenses/MIT 13 | POM_LICENCE_DIST=repo 14 | POM_DEVELOPER_ID=qiniu 15 | POM_DEVELOPER_NAME=Qiniu 16 | POM_INCEPTION_YEAR=2012 17 | POM_EMAIL=sdk@qiniu.com -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiniu/android-sdk/1986efca1a120a68c5fa09c1ca61662fa4539701/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jul 06 18:09:15 CST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 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 Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | import java.util.regex.Matcher 2 | 3 | apply plugin: 'com.android.library' 4 | 5 | def versionName() { 6 | String config = getProjectDir().getPath() + '/src/main/java/com/qiniu/android/common/Constants.java' 7 | String fileContents = new File(config).text 8 | Matcher myMatcher = fileContents =~ /VERSION = "(.+)";/ 9 | String version = myMatcher[0][1] 10 | println(version) 11 | return version 12 | } 13 | 14 | def versionNameToCode(String version) { 15 | String v = version.replaceAll(/\./, '') 16 | return v.toLong() 17 | } 18 | 19 | String version = versionName() 20 | int code = versionNameToCode(version) 21 | 22 | android { 23 | compileSdkVersion 33 24 | // buildToolsVersion '29.0.3' 25 | defaultConfig { 26 | //applicationId "com.qiniu.android" 27 | minSdkVersion 14 28 | targetSdkVersion 33 29 | versionCode code 30 | versionName version 31 | 32 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 33 | } 34 | 35 | buildTypes { 36 | release { 37 | minifyEnabled false 38 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 39 | } 40 | debug { 41 | testCoverageEnabled = true 42 | } 43 | } 44 | 45 | lintOptions { 46 | warning 'InvalidPackage' 47 | } 48 | compileOptions { 49 | targetCompatibility JavaVersion.VERSION_1_8 50 | sourceCompatibility JavaVersion.VERSION_1_8 51 | } 52 | ndkVersion '20.0.5594570' 53 | 54 | // useLibrary 'android.test.runner' 55 | // useLibrary 'android.test.base' 56 | // useLibrary 'android.test.mock' 57 | } 58 | 59 | 60 | dependencies { 61 | implementation 'com.squareup.okhttp3:okhttp:4.9.1' 62 | implementation 'com.qiniu:happy-dns:2.0.1' 63 | 64 | // for javax.annotation.Nullable use in custom MultipartBody and Headers implements. 65 | // implementation 'com.google.code.findbugs:jsr305:3.0.2' 66 | implementation 'org.conscrypt:conscrypt-android:2.2.1' 67 | implementation fileTree(include: ['*.jar'], dir: 'libs') 68 | 69 | androidTestImplementation 'junit:junit:4.13.2' 70 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 71 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 72 | 73 | // androidTestImplementation "androidx.test:runner:1.4.0" 74 | // androidTestImplementation "androidx.test:core:1.4.0" 75 | // androidTestImplementation "androidx.test:rules:1.4.0" 76 | 77 | // androidTestImplementation 'org.testng:testng:6.9.6' 78 | androidTestCompileOnly project(path: ':library') 79 | // androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 80 | } 81 | 82 | task releaseJar(type: Jar, dependsOn: 'build') { 83 | //指定生成的jar名 84 | baseName('qiniu-android-sdk-' + version) 85 | //从哪里打包class文件 86 | // from('build/intermediates/classes/release/com/qiniu/android/dns/') 87 | //打包到jar后的目录结构 88 | // into('com/qiniu/android/dns/') 89 | //去掉不需要打包的目录和文件 90 | exclude('test/', 'BuildConfig.class', 'R.class') 91 | //去掉R$开头的文件 92 | exclude { it.name.startsWith('R$') } 93 | } 94 | 95 | android.libraryVariants.all { variant -> 96 | def name = variant.buildType.name 97 | def task = project.tasks.create "jar${name.capitalize()}", Jar 98 | task.dependsOn variant.javaCompileProvider 99 | task.from variant.javaCompileProvider.get().destinationDir 100 | task.exclude '**/R.*', '**/R$*.*', '**/BuildConfig.class' 101 | artifacts.add('archives', task) 102 | } 103 | 104 | setProperty('VERSION_NAME', version) 105 | setProperty('VERSION_CODE', code) 106 | 107 | apply from: '../mvn_push.gradle' 108 | -------------------------------------------------------------------------------- /library/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=qiniu-android-sdk-library 2 | POM_ARTIFACT_ID=qiniu-android-sdk 3 | POM_PACKAGING=aar 4 | android.debug.obsoleteApi=true 5 | android.enableAapt2=false 6 | org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Applications/Android Studio.app/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/TempFile.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | 7 | 8 | /** 9 | * Created by bailong on 14/10/11. 10 | */ 11 | public final class TempFile { 12 | public static void remove(File f) { 13 | if (f == null){ 14 | return; 15 | } 16 | f.delete(); 17 | } 18 | 19 | public static File createFile(int kiloSize) throws IOException { 20 | return createFile(kiloSize, "qiniu_" + (1024 * kiloSize) + "k"); 21 | } 22 | 23 | public static File createFile(int kiloSize, String fileName) throws IOException { 24 | FileOutputStream fos = null; 25 | try { 26 | long size = (long) (1024 * kiloSize); 27 | File f = File.createTempFile(fileName, ".tmp"); 28 | f.createNewFile(); 29 | fos = new FileOutputStream(f); 30 | byte[] b = getByte(1023 * 4); 31 | long s = 0; 32 | while (s < size) { 33 | int l = (int) Math.min(b.length, size - s); 34 | fos.write(b, 0, l); 35 | s += l; 36 | } 37 | fos.flush(); 38 | return f; 39 | } finally { 40 | if (fos != null) { 41 | try { 42 | fos.close(); 43 | } catch (IOException e) { 44 | e.printStackTrace(); 45 | } 46 | } 47 | } 48 | } 49 | 50 | // public static byte[] createData(int kiloSize){ 51 | // 52 | // long size = (long) (1024 * kiloSize); 53 | // byte[] data = new byte[0]; 54 | // 55 | // byte[] b = getByte(1024); 56 | // long s = 0; 57 | // while (s < size) { 58 | // int l = (int) Math.min(b.length, size - s); 59 | // s += l; 60 | // } 61 | // return data; 62 | // } 63 | 64 | public static byte[] getByte(int len) { 65 | return getByte(len, 0); 66 | } 67 | public static byte[] getByte(int len, int index) { 68 | byte[] b = new byte[len]; 69 | b[0] = (byte)(index & 0xFF); 70 | for (int i = 1; i < len; i++) { 71 | b[i] = 'b'; 72 | } 73 | b[len - 2] = '\r'; 74 | b[len - 1] = '\n'; 75 | return b; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/UploadFileInfoTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | 8 | @RunWith(AndroidJUnit4.class) 9 | public class UploadFileInfoTest extends BaseTest { 10 | 11 | @Test 12 | public void testCreateFromJsonError(){ 13 | 14 | 15 | // UploadFileInfo fileInfo = UploadFileInfo.fileFromJson(null); 16 | // 17 | // assertTrue(fileInfo == null); 18 | // 19 | // fileInfo = new UploadFileInfo(0, 1 , 1); 20 | // assertTrue(fileInfo.progress() == 0); 21 | // 22 | // assertTrue(fileInfo.nextUploadData() == null); 23 | // 24 | // assertTrue(fileInfo.isAllUploaded() == true); 25 | // 26 | // fileInfo.clearUploadState(); 27 | // 28 | // 29 | // UploadFileInfo.UploadData data = UploadFileInfo.UploadData.dataFromJson(null); 30 | // 31 | // assertTrue(data == null); 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/WaitTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.utils.Wait; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | @RunWith(AndroidJUnit4.class) 11 | public class WaitTest extends BaseTest { 12 | 13 | public int count = 0; 14 | 15 | @Test 16 | public void testWait() { 17 | 18 | final Wait wait = new Wait(); 19 | 20 | new Thread(new Runnable() { 21 | @Override 22 | public void run() { 23 | 24 | for (int i = 0; i < 1000; i++) { 25 | count += 1; 26 | } 27 | 28 | wait.stopWait(); 29 | } 30 | }).start(); 31 | 32 | wait.startWait(); 33 | 34 | assertEquals(count, 1000); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/bigdata/BigDataConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.bigdata; 2 | 3 | 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | @RunWith(AndroidJUnit4.class) 12 | public class BigDataConfigurationTest { 13 | 14 | @Test 15 | public void testCopy() { 16 | Configuration configuration = new Configuration(); 17 | configuration.connectTimeout = 15; 18 | 19 | Configuration configurationCopy = Configuration.copy(configuration); 20 | 21 | assertTrue(configurationCopy.connectTimeout == configuration.connectTimeout); 22 | } 23 | 24 | @Test 25 | public void testCopyNoValue() { 26 | Configuration configuration = null; 27 | 28 | Configuration configurationCopy = Configuration.copy(configuration); 29 | 30 | assertTrue(configurationCopy != null); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/bigdata/client/ClientTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.bigdata.client; 2 | 3 | 4 | import androidx.test.ext.junit.runners.AndroidJUnit4; 5 | 6 | import com.qiniu.android.BaseTest; 7 | import com.qiniu.android.TestConfig; 8 | import com.qiniu.android.http.ResponseInfo; 9 | import com.qiniu.android.storage.UpToken; 10 | import com.qiniu.android.utils.StringMap; 11 | 12 | import org.json.JSONObject; 13 | import org.junit.Test; 14 | import org.junit.runner.RunWith; 15 | 16 | @RunWith(AndroidJUnit4.class) 17 | public class ClientTest extends BaseTest { 18 | 19 | @Test 20 | public void testSyncGet() { 21 | 22 | Client client = new Client(null, 90, 90, null, null); 23 | ResponseInfo responseInfo = client.syncGet("https://up.qiniup.com/crossdomain.xml", null); 24 | assertTrue(responseInfo != null); 25 | assertTrue(responseInfo.statusCode == 200); 26 | } 27 | 28 | @Test 29 | public void testAsyncGet() { 30 | 31 | final WaitCondition waitCondition = new WaitCondition(); 32 | Client client = new Client(); 33 | client.asyncGet("https://up.qiniup.com/crossdomain.xml", null, null, new CompletionHandler() { 34 | @Override 35 | public void complete(ResponseInfo info, JSONObject response) { 36 | 37 | assertTrue(info != null); 38 | assertTrue(info.statusCode == 200); 39 | waitCondition.shouldWait = false; 40 | } 41 | }); 42 | 43 | wait(waitCondition, 10 * 60); 44 | } 45 | 46 | @Test 47 | public void testMultipartSyncPost() { 48 | 49 | PostArgs postArgs = new PostArgs(); 50 | postArgs.data = "123".getBytes(); 51 | postArgs.mimeType = "text/plain"; 52 | postArgs.params = new StringMap(); 53 | postArgs.params.put("x:foo", "foo"); 54 | 55 | UpToken token = UpToken.parse(TestConfig.commonToken); 56 | 57 | Client client = new Client(null, 90, 90, null, null); 58 | ResponseInfo responseInfo = client.syncMultipartPost("https://up.qiniup.com", postArgs, token); 59 | 60 | assertTrue(responseInfo != null); 61 | } 62 | 63 | @Test 64 | public void testMultipartAsyncPost() { 65 | 66 | final WaitCondition waitCondition = new WaitCondition(); 67 | 68 | PostArgs postArgs = new PostArgs(); 69 | postArgs.data = "123".getBytes(); 70 | postArgs.mimeType = "text/plain"; 71 | postArgs.params = new StringMap(); 72 | 73 | UpToken token = UpToken.parse(TestConfig.commonToken); 74 | 75 | Client client = new Client(null, 90, 90, null, null); 76 | client.asyncMultipartPost("https://up.qiniu.com", postArgs, token, null, new CompletionHandler() { 77 | @Override 78 | public void complete(ResponseInfo info, JSONObject response) { 79 | 80 | assertTrue(info != null); 81 | waitCondition.shouldWait = false; 82 | } 83 | }, null); 84 | 85 | wait(waitCondition, 10 * 60); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/collect/UplogTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.collect; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.TestConfig; 6 | import com.qiniu.android.BaseTest; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | @RunWith(AndroidJUnit4.class) 12 | public class UplogTest extends BaseTest { 13 | 14 | @Test 15 | public void test() { 16 | } 17 | 18 | private void no_testUplog() { 19 | ReportItem item = new ReportItem(); 20 | item.setReport(ReportItem.LogTypeRequest, ReportItem.RequestKeyLogType); 21 | item.setReport(1634567890, ReportItem.RequestKeyUpTime); 22 | item.setReport(200, ReportItem.RequestKeyStatusCode); 23 | item.setReport("reqid", ReportItem.RequestKeyRequestId); 24 | item.setReport("host", ReportItem.RequestKeyHost); 25 | item.setReport("remoteAddress", ReportItem.RequestKeyRemoteIp); 26 | item.setReport(80, ReportItem.RequestKeyPort); 27 | item.setReport("bucket", ReportItem.RequestKeyTargetBucket); 28 | item.setReport("key", ReportItem.RequestKeyTargetKey); 29 | item.setReport(123, ReportItem.RequestKeyTotalElapsedTime); 30 | item.setReport(123, ReportItem.RequestKeyDnsElapsedTime); 31 | item.setReport(123, ReportItem.RequestKeyConnectElapsedTime); 32 | item.setReport(123, ReportItem.RequestKeyTLSConnectElapsedTime); 33 | item.setReport(123, ReportItem.RequestKeyRequestElapsedTime); 34 | item.setReport(123, ReportItem.RequestKeyWaitElapsedTime); 35 | item.setReport(123, ReportItem.RequestKeyResponseElapsedTime); 36 | item.setReport(123, ReportItem.RequestKeyFileOffset); 37 | item.setReport(123, ReportItem.RequestKeyBytesSent); 38 | item.setReport(123, ReportItem.RequestKeyBytesTotal); 39 | item.setReport("123", ReportItem.RequestKeyPid); 40 | item.setReport(123, ReportItem.RequestKeyTid); 41 | item.setReport("regionid", ReportItem.RequestKeyTargetRegionId); 42 | item.setReport("regionid", ReportItem.RequestKeyCurrentRegionId); 43 | item.setReport("errorType", ReportItem.RequestKeyErrorType); 44 | 45 | item.setReport("errorDesc", ReportItem.RequestKeyErrorDescription); 46 | item.setReport("form", ReportItem.RequestKeyUpType); 47 | item.setReport("android", ReportItem.RequestKeyOsName); 48 | item.setReport("10", ReportItem.RequestKeyOsVersion); 49 | item.setReport("Android", ReportItem.RequestKeySDKName); 50 | item.setReport("8.3.3", ReportItem.RequestKeySDKVersion); 51 | item.setReport(1623456789, ReportItem.RequestKeyClientTime); 52 | item.setReport("wifi", ReportItem.RequestKeyNetworkType); 53 | item.setReport(-1, ReportItem.RequestKeySignalStrength); 54 | 55 | item.setReport("server", ReportItem.RequestKeyPrefetchedDnsSource); 56 | item.setReport(10, ReportItem.RequestKeyPrefetchedBefore); 57 | item.setReport("lastPrefetchErrorMessage", ReportItem.RequestKeyPrefetchedErrorMessage); 58 | 59 | item.setReport("okhttp", ReportItem.RequestKeyHttpClient); 60 | item.setReport("4.2.2", ReportItem.RequestKeyHttpClientVersion); 61 | item.setReport("disable", ReportItem.RequestKeyNetworkMeasuring); 62 | 63 | // 劫持标记 64 | item.setReport("hijacked", ReportItem.RequestKeyHijacking); 65 | item.setReport("syncDnsSource", ReportItem.RequestKeyDnsSource); 66 | item.setReport("syncDnsError", ReportItem.RequestKeyDnsErrorMessage); 67 | 68 | // 统计当前请求上传速度 / 总耗时 69 | item.setReport(123, ReportItem.RequestKeyPerceptiveSpeed); 70 | 71 | item.setReport("http1.1", ReportItem.RequestKeyHttpVersion); 72 | 73 | UploadInfoReporter.getInstance().report(item, TestConfig.commonToken); 74 | 75 | // wait(new WaitConditional() { 76 | // @Override 77 | // public boolean shouldWait() { 78 | // return true; 79 | // } 80 | // }, 10 * 60); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/common/CollectConfigTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.common; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import androidx.test.ext.junit.runners.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | @RunWith(AndroidJUnit4.class) 11 | public class CollectConfigTest { 12 | 13 | @Test 14 | public void testQuick(){ 15 | Config.quick(); 16 | assertTrue(Config.uploadThreshold == 1024); 17 | assertTrue(Config.interval == 2); 18 | } 19 | 20 | @Test 21 | public void testNormal(){ 22 | Config.normal(); 23 | assertTrue(Config.uploadThreshold == 16*1024); 24 | assertTrue(Config.interval == 10); 25 | } 26 | 27 | @Test 28 | public void testSlow(){ 29 | Config.slow(); 30 | assertTrue(Config.uploadThreshold == 150*1024); 31 | assertTrue(Config.interval == 300); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/common/FixZoneTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.common; 2 | 3 | 4 | import androidx.test.ext.junit.runners.AndroidJUnit4; 5 | 6 | import com.qiniu.android.BaseTest; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | @RunWith(AndroidJUnit4.class) 12 | public class FixZoneTest extends BaseTest { 13 | 14 | @Test 15 | public void testCreateByRegionId() { 16 | FixedZone zone = FixedZone.createWithRegionId("na0"); 17 | ZoneInfo zoneInfo = zone.getZonesInfo(null).zonesInfo.get(0); 18 | 19 | assertTrue(zoneInfo.regionId.equals("na0")); 20 | assertTrue(zoneInfo.domains.get(0).equals("upload-na0.qiniup.com")); 21 | assertTrue(zoneInfo.domains.get(1).equals("up-na0.qiniup.com")); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/http/HttpHeaderTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import com.qiniu.android.utils.LogUtil; 6 | 7 | import androidx.test.ext.junit.runners.AndroidJUnit4; 8 | 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | 13 | import java.util.Date; 14 | import java.util.HashMap; 15 | 16 | @RunWith(AndroidJUnit4.class) 17 | public class HttpHeaderTest { 18 | 19 | private Headers headers; 20 | 21 | @Before 22 | public void setUp() throws Exception { 23 | 24 | HashMap keyVaules = new HashMap<>(); 25 | keyVaules.put("date", "2020-07-15 07:40:01"); 26 | keyVaules.put("host", "127.0.0.1"); 27 | headers = Headers.of(keyVaules); 28 | LogUtil.i(headers.names().toString()); 29 | } 30 | 31 | @Test 32 | public void testValue() { 33 | 34 | assertTrue(headers.values("date") != null); 35 | assertTrue(headers.values("host") != null); 36 | } 37 | 38 | @Test 39 | public void testCount() { 40 | 41 | assertTrue(headers.byteCount() > 0); 42 | } 43 | 44 | @Test 45 | public void testBuilder() { 46 | String dateKey = "date"; 47 | String dateValue = HttpDate.format(new Date()); 48 | String hostKey = "host"; 49 | String hostValue = "127.0.0.1"; 50 | 51 | Headers.Builder builder0 = new Headers.Builder(); 52 | builder0.add(dateKey, dateValue); 53 | builder0.add(hostKey + ":" + hostValue); 54 | Headers headers0 = builder0.build(); 55 | 56 | assertTrue(headers0.get(dateKey).equals(dateValue)); 57 | assertTrue(headers0.getDate(dateKey) != null); 58 | assertTrue(headers0.get(hostKey).equals(hostValue)); 59 | 60 | 61 | Headers.Builder builder1 = new Headers.Builder(); 62 | builder1.addAll(headers0); 63 | assertTrue(builder1.get(dateKey).equals(dateValue)); 64 | 65 | 66 | Headers headers1 = headers0.newBuilder().build(); 67 | assertTrue(headers1.equals(headers0)); 68 | assertTrue(headers1.toString().equals(headers0.toString())); 69 | assertTrue(headers1.hashCode() == headers0.hashCode()); 70 | 71 | 72 | Headers headers = Headers.of(hostKey, hostValue, dateKey, dateValue); 73 | assertTrue(headers.get(dateKey).equals(dateValue)); 74 | 75 | 76 | headers1.toMultimap(); 77 | 78 | 79 | builder1.set("time", "2020-07-10 10:20:14"); 80 | headers1 = builder1.build(); 81 | assertTrue(headers1.get("time").equals("2020-07-10 10:20:14")); 82 | 83 | builder1.removeAll("time"); 84 | headers1 = builder1.build(); 85 | assertTrue(headers1.get("time") == null); 86 | } 87 | 88 | @Test 89 | public void testBuilderError() { 90 | 91 | Headers headers = null; 92 | 93 | String headersString = null; 94 | try { 95 | headers = Headers.of(headersString); 96 | } catch (Exception e) { 97 | assertTrue(true); 98 | } 99 | 100 | try { 101 | headers = Headers.of("key"); 102 | } catch (Exception e) { 103 | assertTrue(true); 104 | } 105 | 106 | try { 107 | headers = Headers.of("key", null); 108 | } catch (Exception e) { 109 | assertTrue(true); 110 | } 111 | 112 | try { 113 | headers = Headers.of("key", ""); 114 | } catch (Exception e) { 115 | assertTrue(true); 116 | } 117 | 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/http/ResponseInfoTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | @RunWith(AndroidJUnit4.class) 11 | public class ResponseInfoTest extends BaseTest { 12 | 13 | @Test 14 | public void testCreate(){ 15 | 16 | ResponseInfo responseInfo = null; 17 | 18 | responseInfo = ResponseInfo.zeroSize(""); 19 | assertTrue(responseInfo.statusCode == ResponseInfo.ZeroSizeFile); 20 | 21 | responseInfo = ResponseInfo.cancelled(); 22 | assertTrue(responseInfo.statusCode == ResponseInfo.Cancelled); 23 | 24 | responseInfo = ResponseInfo.invalidArgument(""); 25 | assertTrue(responseInfo.statusCode == ResponseInfo.InvalidArgument); 26 | 27 | responseInfo = ResponseInfo.invalidToken(""); 28 | assertTrue(responseInfo.statusCode == ResponseInfo.InvalidToken); 29 | 30 | responseInfo = ResponseInfo.fileError(null); 31 | assertTrue(responseInfo.statusCode == ResponseInfo.InvalidFile); 32 | 33 | responseInfo = ResponseInfo.networkError(""); 34 | assertTrue(responseInfo.statusCode == ResponseInfo.NetworkError); 35 | assertTrue(responseInfo.isNetworkBroken()); 36 | assertTrue(responseInfo.needRetry()); 37 | 38 | responseInfo = ResponseInfo.localIOError(""); 39 | assertTrue(responseInfo.statusCode == ResponseInfo.LocalIOError); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/http/connectCheck/ConnectCheckTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.connectCheck; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | import com.qiniu.android.storage.GlobalConfiguration; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | @RunWith(AndroidJUnit4.class) 12 | public class ConnectCheckTest extends BaseTest { 13 | 14 | @Test 15 | public void testCheck() { 16 | 17 | int maxCount = 100; 18 | int successCount = 0; 19 | for (int i = 0; i < maxCount; i++) { 20 | if (ConnectChecker.isConnected(ConnectChecker.check())) { 21 | successCount += 1; 22 | } 23 | } 24 | 25 | assertEquals("maxCount:" + maxCount + " successCount:" + successCount, maxCount, successCount); 26 | } 27 | 28 | @Test 29 | public void testCustomCheckHosts() { 30 | GlobalConfiguration.getInstance().connectCheckURLStrings = new String[]{"https://www.qiniu.com", "https://www.google.com"}; 31 | int maxCount = 20; 32 | int successCount = 0; 33 | for (int i = 0; i < maxCount; i++) { 34 | if (ConnectChecker.isConnected(ConnectChecker.check())) { 35 | successCount += 1; 36 | } 37 | } 38 | 39 | assertTrue("maxCount:" + maxCount + " successCount:" + successCount, successCount > 12); 40 | } 41 | 42 | @Test 43 | public void testNotConnected() { 44 | GlobalConfiguration.getInstance().connectCheckURLStrings = new String[]{"https://connect.a.com", "https://connect.a.com"}; 45 | int maxCount = 100; 46 | int successCount = 0; 47 | for (int i = 0; i < maxCount; i++) { 48 | if (ConnectChecker.isConnected(ConnectChecker.check())) { 49 | successCount += 1; 50 | } 51 | } 52 | 53 | assertEquals("maxCount:" + maxCount + " successCount:" + successCount, 0, successCount); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/http/dns/DnsApiTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.TestConfig; 6 | import com.qiniu.android.common.AutoZone; 7 | import com.qiniu.android.common.FixedZone; 8 | import com.qiniu.android.BaseTest; 9 | import com.qiniu.android.storage.UpToken; 10 | 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * Created by jemy on 2019/8/20. 19 | */ 20 | 21 | @RunWith(AndroidJUnit4.class) 22 | public class DnsApiTest extends BaseTest { 23 | 24 | @Override @Before 25 | public void setUp() throws Exception { 26 | } 27 | 28 | @Test 29 | public void testLocalLoad() { 30 | 31 | final String host = "uplog.qbox.me"; 32 | final DnsPrefetcher dnsPrefetcher = DnsPrefetcher.getInstance(); 33 | dnsPrefetcher.localFetch(); 34 | 35 | wait(new WaitConditional() { 36 | @Override 37 | public boolean shouldWait() { 38 | List list = dnsPrefetcher.getInetAddressByHost(host); 39 | if (list == null || list.size() == 0){ 40 | return true; 41 | } else { 42 | return false; 43 | } 44 | } 45 | }, 60); 46 | 47 | List addressList = dnsPrefetcher.getInetAddressByHost(host); 48 | assertTrue(addressList.size() > 0); 49 | } 50 | 51 | private void testRecover(){ 52 | final String host = "uplog.qbox.me"; 53 | 54 | final DnsPrefetcher dnsPrefetcher = DnsPrefetcher.getInstance(); 55 | dnsPrefetcher.recoverCache(); 56 | 57 | List addressList = dnsPrefetcher.getInetAddressByHost(host); 58 | assertTrue(addressList.size() > 0); 59 | 60 | dnsPrefetcher.invalidNetworkAddress(addressList.get(0)); 61 | } 62 | 63 | @Test 64 | public void testPreFetch() { 65 | 66 | final String host = "upload.qiniup.com"; 67 | FixedZone fixedZone = new FixedZone(new String[]{host}); 68 | 69 | final DnsPrefetcher dnsPrefetcher = DnsPrefetcher.getInstance(); 70 | dnsPrefetcher.checkAndPrefetchDnsIfNeed(null, fixedZone, UpToken.parse(TestConfig.token_z0)); 71 | 72 | wait(new WaitConditional() { 73 | @Override 74 | public boolean shouldWait() { 75 | List list = dnsPrefetcher.getInetAddressByHost(host); 76 | if (list == null || list.size() == 0){ 77 | return true; 78 | } else { 79 | return false; 80 | } 81 | } 82 | }, 60); 83 | 84 | List addressList = dnsPrefetcher.getInetAddressByHost(host); 85 | assertTrue(addressList.size() > 0); 86 | } 87 | 88 | @Test 89 | public void testMutiThreadPrefetch(){ 90 | 91 | final AutoZone zone = new AutoZone(); 92 | final TestParam param = new TestParam(); 93 | 94 | for (int i = 0; i < param.count; i++) { 95 | new Thread(new Runnable() { 96 | @Override 97 | public void run() { 98 | boolean isSuccess = DnsPrefetchTransaction.addDnsCheckAndPrefetchTransaction(null, zone, UpToken.parse(TestConfig.token_z0)); 99 | synchronized (this){ 100 | if (isSuccess){ 101 | param.successCount += 1; 102 | } 103 | param.completeCount += 1; 104 | } 105 | } 106 | }).start(); 107 | } 108 | 109 | wait(new WaitConditional() { 110 | @Override 111 | public boolean shouldWait() { 112 | if (param.completeCount != param.count){ 113 | return true; 114 | } else { 115 | return false; 116 | } 117 | } 118 | }, 60); 119 | 120 | assertTrue((param.successCount <= 1)); 121 | } 122 | 123 | private static class TestParam{ 124 | int count = 100; 125 | int successCount = 0; 126 | int completeCount = 0; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/http/dns/DnsCacheFileTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | import com.qiniu.android.storage.GlobalConfiguration; 7 | import com.qiniu.android.utils.LogUtil; 8 | 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | 12 | import java.io.IOException; 13 | 14 | @RunWith(AndroidJUnit4.class) 15 | public class DnsCacheFileTest extends BaseTest { 16 | 17 | @Test 18 | public void testCreate(){ 19 | try { 20 | DnsCacheFile file = new DnsCacheFile(null); 21 | if (file != null){ 22 | assertTrue(false); 23 | } 24 | } catch (IOException e) { 25 | assertTrue(true); 26 | } 27 | 28 | 29 | DnsCacheFile file = null; 30 | try { 31 | file = new DnsCacheFile(GlobalConfiguration.getInstance().dnsCacheDir); 32 | } catch (IOException e) { 33 | e.printStackTrace(); 34 | } 35 | LogUtil.i("DnsCacheFile name:" + file.getFileName()); 36 | assertTrue(file != null); 37 | 38 | } 39 | 40 | @Test 41 | public void testValue(){ 42 | 43 | String dataStringBefore = "123"; 44 | byte[] dataBefore = dataStringBefore.getBytes(); 45 | 46 | DnsCacheFile file = null; 47 | try { 48 | file = new DnsCacheFile(GlobalConfiguration.getInstance().dnsCacheDir); 49 | } catch (IOException e) { 50 | e.printStackTrace(); 51 | } 52 | 53 | file.set("key", dataBefore); 54 | 55 | byte[] dataAfter = file.get("key"); 56 | String dataStringAfter = new String(dataAfter); 57 | 58 | assertTrue(dataStringBefore.equals(dataStringAfter)); 59 | 60 | 61 | file.del("key"); 62 | dataAfter = file.get("key"); 63 | assertTrue(dataAfter == null); 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/http/dns/DnsTransactionTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.TestConfig; 6 | import com.qiniu.android.common.AutoZone; 7 | import com.qiniu.android.common.Zone; 8 | import com.qiniu.android.BaseTest; 9 | import com.qiniu.android.storage.UpToken; 10 | 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | 14 | 15 | /** 16 | * Created by yangsen on 2020/6/9 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class DnsTransactionTest extends BaseTest { 20 | 21 | private final int maxTestCount = 100; 22 | private int completeCount = 0; 23 | private int successCount = 0; 24 | 25 | @Test 26 | public void testLocalLoad(){ 27 | 28 | completeCount = 0; 29 | successCount = 0; 30 | 31 | for (int i = 0; i < maxTestCount; i++) { 32 | new Thread(new Runnable() { 33 | @Override 34 | public void run() { 35 | 36 | boolean isSuccess = DnsPrefetchTransaction.addDnsLocalLoadTransaction(); 37 | if (isSuccess){ 38 | successCount += 1; 39 | } 40 | completeCount += 1; 41 | } 42 | }).start(); 43 | } 44 | 45 | wait(new WaitConditional() { 46 | @Override 47 | public boolean shouldWait() { 48 | if (completeCount < maxTestCount) { 49 | return true; 50 | } else { 51 | return false; 52 | } 53 | } 54 | }, 60); 55 | 56 | assertTrue(successCount < 2); 57 | } 58 | 59 | @Test 60 | public void test_CheckAndPrefetch(){ 61 | 62 | completeCount = 0; 63 | successCount = 0; 64 | 65 | final Zone zone = new AutoZone(); 66 | for (int i = 0; i < maxTestCount; i++) { 67 | new Thread(new Runnable() { 68 | @Override 69 | public void run() { 70 | 71 | boolean isSuccess = DnsPrefetchTransaction.addDnsCheckAndPrefetchTransaction(null, zone, UpToken.parse(TestConfig.token_z0)); 72 | synchronized (this) { 73 | if (isSuccess) { 74 | successCount += 1; 75 | } 76 | completeCount += 1; 77 | } 78 | } 79 | }).start(); 80 | } 81 | 82 | wait(new WaitConditional() { 83 | @Override 84 | public boolean shouldWait() { 85 | if (completeCount < maxTestCount) { 86 | return true; 87 | } else { 88 | return false; 89 | } 90 | } 91 | }, 60); 92 | 93 | assertTrue("successCount:" + successCount, successCount < 3); 94 | } 95 | 96 | @Test 97 | public void testCheckWhetherCachedValid(){ 98 | 99 | completeCount = 0; 100 | successCount = 0; 101 | 102 | final Zone zone = new AutoZone(); 103 | for (int i = 0; i < maxTestCount; i++) { 104 | new Thread(new Runnable() { 105 | @Override 106 | public void run() { 107 | 108 | boolean isSuccess = DnsPrefetchTransaction.setDnsCheckWhetherCachedValidTransactionAction(); 109 | if (isSuccess){ 110 | successCount += 1; 111 | } 112 | completeCount += 1; 113 | } 114 | }).start(); 115 | } 116 | 117 | wait(new WaitConditional() { 118 | @Override 119 | public boolean shouldWait() { 120 | if (completeCount < maxTestCount) { 121 | return true; 122 | } else { 123 | return false; 124 | } 125 | } 126 | }, 60); 127 | 128 | assertTrue(successCount < 2); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/http/dns/HappyDnsTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import java.net.UnknownHostException; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by yangsen on 2020/6/8 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class HappyDnsTest extends BaseTest { 19 | 20 | @Test 21 | public void testDns(){ 22 | 23 | HappyDns happyDns = new HappyDns(); 24 | List inetAddressList = new ArrayList<>(); 25 | 26 | try { 27 | inetAddressList = happyDns.lookup("qiniu.com"); 28 | } catch (UnknownHostException e) { 29 | e.printStackTrace(); 30 | } 31 | 32 | assertTrue(inetAddressList.size() > 0); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/http/dns/SystemDnsTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | import com.qiniu.android.utils.Utils; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import java.net.UnknownHostException; 12 | import java.util.List; 13 | 14 | @RunWith(AndroidJUnit4.class) 15 | public class SystemDnsTest extends BaseTest { 16 | 17 | @Test 18 | public void testDnsLookup() { 19 | SystemDns dns = new SystemDns(10); 20 | try { 21 | List result = dns.lookup("uplog.qbox.me"); 22 | assertTrue("testDnsLookup fail:", result != null && result.size() > 0); 23 | } catch (UnknownHostException e) { 24 | e.printStackTrace(); 25 | fail("testDnsLookup fail because:" + e.getMessage()); 26 | } 27 | } 28 | 29 | @Test 30 | public void testDnsTimeout() { 31 | long start = Utils.currentSecondTimestamp(); 32 | int timeout = 5; 33 | SystemDns dns = new SystemDns(timeout); 34 | try { 35 | List result = dns.lookup("abc.mock.cn"); 36 | assertTrue("testDnsTimeout fail:", result == null || result.size() == 0); 37 | } catch (UnknownHostException e) { 38 | e.printStackTrace(); 39 | } 40 | long end = Utils.currentSecondTimestamp(); 41 | assertTrue("testDnsTimeout fail because timeout to long", end <= (start + timeout + 1)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/http/serverRegion/UploadDomainRegionTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.serverRegion; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.common.FixedZone; 6 | import com.qiniu.android.http.request.IUploadServer; 7 | import com.qiniu.android.http.request.UploadRequestState; 8 | import com.qiniu.android.BaseTest; 9 | import com.qiniu.android.utils.Utils; 10 | 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | 14 | @RunWith(AndroidJUnit4.class) 15 | public class UploadDomainRegionTest extends BaseTest { 16 | 17 | @Test 18 | public void testGetOneServer(){ 19 | 20 | String host = "baidu.com"; 21 | String type = Utils.getIpType(null, host); 22 | FixedZone zone = new FixedZone(new String[]{host}); 23 | 24 | UploadDomainRegion region = new UploadDomainRegion(null); 25 | region.setupRegionData(zone.getZonesInfo(null).zonesInfo.get(0)); 26 | 27 | UploadServerFreezeManager.getInstance().freezeType(type, 100); 28 | 29 | UploadRequestState state = new UploadRequestState(); 30 | state.setUseOldServer(false); 31 | IUploadServer server = region.getNextServer(state, null, null); 32 | 33 | assertNotNull(server); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/http/serverRegion/UploadServerFreezeManagerTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.serverRegion; 2 | 3 | 4 | import androidx.test.ext.junit.runners.AndroidJUnit4; 5 | 6 | import com.qiniu.android.BaseTest; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | @RunWith(AndroidJUnit4.class) 12 | public class UploadServerFreezeManagerTest extends BaseTest { 13 | 14 | @Test 15 | public void testFreeze() { 16 | 17 | String host = "baidu.com"; 18 | String type = host; 19 | UploadServerFreezeManager.getInstance().freezeType(type, 10); 20 | 21 | boolean isFrozen = UploadServerFreezeManager.getInstance().isTypeFrozen(type); 22 | assertTrue(isFrozen); 23 | } 24 | 25 | @Test 26 | public void testUnfreeze() { 27 | 28 | String host = "baidu.com"; 29 | String type = host; 30 | UploadServerFreezeManager.getInstance().freezeType(type, 10); 31 | 32 | boolean isFrozen = UploadServerFreezeManager.getInstance().isTypeFrozen(type); 33 | assertTrue(isFrozen); 34 | 35 | UploadServerFreezeManager.getInstance().unfreezeType(type); 36 | isFrozen = UploadServerFreezeManager.getInstance().isTypeFrozen(type); 37 | assertTrue(isFrozen == false); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/serverRegion/HttpServerManagerTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.serverRegion; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | import com.qiniu.android.http.serverRegion.HttpServerManager; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | @RunWith(AndroidJUnit4.class) 12 | public class HttpServerManagerTest extends BaseTest { 13 | 14 | @Test 15 | public void testServer() { 16 | String host = "up.qiniu.com"; 17 | String ip = "192.168.1.1"; 18 | int duration = 10; 19 | HttpServerManager manager = HttpServerManager.getInstance(); 20 | assertFalse("null host and ip add success", manager.addHttp3Server(null, null, duration)); 21 | assertFalse("null host add success", manager.addHttp3Server(null, ip, duration)); 22 | assertFalse("null ip add success", manager.addHttp3Server(host, null, duration)); 23 | assertFalse("empty host and ip add success", manager.addHttp3Server("", "", duration)); 24 | assertFalse("empty host add success", manager.addHttp3Server("", ip, duration)); 25 | assertFalse("empty ip add success", manager.addHttp3Server(host, "", duration)); 26 | assertFalse("liveDuration < 0 add success", manager.addHttp3Server(host, ip, -1)); 27 | 28 | manager.addHttp3Server(host, ip, duration); 29 | assertTrue("host ip should support", manager.isServerSupportHttp3(host, ip)); 30 | 31 | assertFalse("null host and ip should not support", manager.isServerSupportHttp3(null, null)); 32 | assertFalse("null host should not support", manager.isServerSupportHttp3(null, ip)); 33 | assertFalse("null ip should not support", manager.isServerSupportHttp3(host, null)); 34 | 35 | assertFalse("empty host and ip should not support", manager.isServerSupportHttp3("", "")); 36 | assertFalse("empty host should not support", manager.isServerSupportHttp3("", ip)); 37 | assertFalse("empty ip should not support", manager.isServerSupportHttp3(host, "ip")); 38 | 39 | assertFalse("no exist host and ip should not support", manager.isServerSupportHttp3("host", "ip")); 40 | assertFalse("no exist hos should not support", manager.isServerSupportHttp3("host", ip)); 41 | assertFalse("no exist ip should not support", manager.isServerSupportHttp3(host, "ip")); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/storage/GlobalConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | 4 | import androidx.test.ext.junit.runners.AndroidJUnit4; 5 | 6 | import com.qiniu.android.BaseTest; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | 15 | /** 16 | * Created by yangsen on 2020/6/3 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class GlobalConfigurationTest extends BaseTest { 20 | 21 | @Test 22 | public void testUpload() { 23 | GlobalConfiguration configuration = GlobalConfiguration.getInstance(); 24 | configuration.dohIpv4Servers = null; 25 | configuration.udpDnsIpv4Servers = null; 26 | configuration.connectCheckURLStrings = null; 27 | List dohIpv4Servers = Arrays.asList(configuration.getDohIpv4Servers()); 28 | List udpDnsIpv4Servers = Arrays.asList(configuration.getUdpDnsIpv4Servers()); 29 | List connectCheckURLStrings = Arrays.asList(configuration.getConnectCheckUrls()); 30 | assertTrue("dohIpv4Servers cfg error", dohIpv4Servers.contains("https://8.8.8.8/dns-query")); 31 | assertTrue("udpDnsIpv4Servers cfg error", udpDnsIpv4Servers.contains("8.8.8.8")); 32 | assertTrue("connectCheckURLStrings cfg error", connectCheckURLStrings.contains("https://www.qiniu.com")); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/storage/TokenTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | import static org.junit.Assert.assertNotSame; 4 | import static org.junit.Assert.assertNull; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import androidx.test.ext.junit.runners.AndroidJUnit4; 8 | 9 | import com.qiniu.android.TestConfig; 10 | 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | 14 | import java.util.Date; 15 | 16 | /** 17 | * Created by bailong on 15/6/1. 18 | */ 19 | @RunWith(AndroidJUnit4.class) 20 | public class TokenTest { 21 | 22 | @Test 23 | public void testRight() { 24 | 25 | UpToken t = UpToken.parse(TestConfig.commonToken); 26 | 27 | assertTrue("uptoken is invalid", !UpToken.isInvalid(t)); 28 | assertTrue("token isValidForDuration error", t.isValidForDuration(5*60)); 29 | assertTrue("token isValidBeforeDate error", t.isValidBeforeDate(new Date())); 30 | assertTrue(t.toString() != null); 31 | assertNotSame(t, null); 32 | } 33 | 34 | public void testEmpty() { 35 | UpToken t = UpToken.parse(null); 36 | assertNull("== 1 ==", t); 37 | 38 | t = UpToken.parse(""); 39 | assertNull("== 2 ==", t); 40 | 41 | t = UpToken.parse("1:2:3"); 42 | assertNull("== 3 ==", t); 43 | } 44 | 45 | @Test 46 | public void testReturnUrl() { 47 | UpToken t = UpToken.parse("QWYn5TFQsLLU1pL5MFEmX3s5DmHdUThav9WyOWOm:1jLiztn4plVyeB8Hie1ryO5z9uo=:eyJzY29wZSI6InB5c2RrIiwiZGVhZGxpbmUiOjE0MzM0ODM5MzYsInJldHVyblVybCI6Imh0dHA6Ly8xMjcuMC4wLjEvIn0="); 48 | assertTrue(t.hasReturnUrl()); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/utils/AndroidNetworkTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | @RunWith(AndroidJUnit4.class) 11 | public class AndroidNetworkTest extends BaseTest { 12 | 13 | @Test 14 | public void testHostIP(){ 15 | String ip = AndroidNetwork.getHostIP(); 16 | assertTrue(ip != null); 17 | } 18 | 19 | @Test 20 | public void testNetworkType(){ 21 | String type = AndroidNetwork.networkType(ContextGetter.applicationContext()); 22 | assertTrue(type != null); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/utils/Base64Test.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import com.qiniu.android.BaseTest; 4 | 5 | import org.junit.Test; 6 | 7 | import java.io.UnsupportedEncodingException; 8 | 9 | 10 | public class Base64Test extends BaseTest { 11 | 12 | @Test 13 | public void testEncode() throws UnsupportedEncodingException { 14 | String data = "你好/+="; 15 | String result = UrlSafeBase64.encodeToString(data); 16 | assertEquals("5L2g5aW9Lys9", result); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/utils/CacheTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | 7 | import org.json.JSONException; 8 | import org.json.JSONObject; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | 12 | @RunWith(AndroidJUnit4.class) 13 | public class CacheTest extends BaseTest { 14 | 15 | @Test 16 | public void testCache() { 17 | Info info = new Info(); 18 | info.foo = "foo"; 19 | info.bar = 1; 20 | 21 | String key = "info_key"; 22 | Cache cache = new Cache.Builder(Info.class) 23 | .setVersion("v1") 24 | .setFlushCount(1) 25 | .builder(); 26 | 27 | cache.cache(key, info, true); 28 | 29 | 30 | // 1. 测试内存缓存 31 | Info memInfo = (Info) cache.cacheForKey(key); 32 | assertEquals("foo", memInfo.foo); 33 | 34 | // 2. 测试删除内存缓存 35 | cache.clearMemoryCache(); 36 | memInfo = (Info) cache.cacheForKey(key); 37 | assertEquals(null, memInfo); 38 | 39 | // 3. 测试 load 40 | cache = new Cache.Builder(Info.class) 41 | .setVersion("v1") 42 | .setFlushCount(1) 43 | .builder(); 44 | memInfo = (Info) cache.cacheForKey(key); 45 | assertEquals("foo", memInfo.foo); 46 | 47 | // 4. 测试清除磁盘缓存测试 48 | cache.clearDiskCache(); 49 | cache = new Cache.Builder(Info.class) 50 | .setVersion("v1") 51 | .setFlushCount(1) 52 | .builder(); 53 | memInfo = (Info) cache.cacheForKey(key); 54 | assertEquals(null, memInfo); 55 | 56 | // 5. 测试异步 flush 57 | cache.cache(key, info, false); 58 | 59 | try { 60 | Thread.sleep(3 * 1000); 61 | } catch (Exception e) { 62 | } 63 | 64 | cache = new Cache.Builder(Info.class) 65 | .setVersion("v1") 66 | .setFlushCount(1) 67 | .builder(); 68 | memInfo = (Info) cache.cacheForKey(key); 69 | assertEquals("foo", memInfo.foo); 70 | assertEquals(1, memInfo.bar); 71 | } 72 | 73 | static class Info implements Cache.Object { 74 | String foo; 75 | int bar; 76 | 77 | Info(){ 78 | } 79 | 80 | public Info(JSONObject jsonObject) { 81 | if (jsonObject == null) { 82 | return; 83 | } 84 | 85 | this.foo = jsonObject.optString("foo"); 86 | this.bar = jsonObject.optInt("bar"); 87 | } 88 | 89 | @Override 90 | public JSONObject toJson() { 91 | JSONObject jsonObject = new JSONObject(); 92 | try { 93 | jsonObject.put("foo", this.foo); 94 | jsonObject.put("bar", this.bar); 95 | } catch (JSONException e) { 96 | e.printStackTrace(); 97 | } 98 | return jsonObject; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/utils/ContextTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | 5 | import android.content.Context; 6 | 7 | import androidx.test.ext.junit.runners.AndroidJUnit4; 8 | 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | 12 | @RunWith(AndroidJUnit4.class) 13 | public class ContextTest { 14 | 15 | @Test 16 | public void testEncode() { 17 | Context c = ContextGetter.applicationContext(); 18 | assertNotNull(c); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/utils/CrcTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import androidx.test.ext.junit.runners.AndroidJUnit4; 6 | 7 | import com.qiniu.android.utils.Crc32; 8 | 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | 12 | @RunWith(AndroidJUnit4.class) 13 | public class CrcTest { 14 | 15 | @Test 16 | public void testCrc() { 17 | byte[] data = "Hello, World!".getBytes(); 18 | long result = Crc32.bytes(data); 19 | assertEquals(3964322768L, result); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/utils/EtagTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | import com.qiniu.android.TempFile; 7 | import com.qiniu.android.common.Constants; 8 | import com.qiniu.android.storage.Configuration; 9 | 10 | import junit.framework.Assert; 11 | 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | import java.io.File; 16 | import java.io.IOException; 17 | import java.io.UnsupportedEncodingException; 18 | 19 | @RunWith(AndroidJUnit4.class) 20 | public class EtagTest extends BaseTest { 21 | 22 | @Test 23 | public void testData() { 24 | String m = Etag.data(new byte[0]); 25 | assertEquals("Fto5o-5ea0sNMlW_75VgGJCv2AcJ", m); 26 | 27 | try { 28 | String etag = Etag.data("etag".getBytes(Constants.UTF_8)); 29 | assertEquals("FpLiADEaVoALPkdb8tJEJyRTXoe_", etag); 30 | } catch (UnsupportedEncodingException e) { 31 | e.printStackTrace(); 32 | } 33 | } 34 | 35 | @Test 36 | public void testFile() throws IOException { 37 | File f = TempFile.createFile(1024); 38 | assertEquals("FhHnGzB75K2JC4YOzKDMLEiaeSKm", Etag.file(f)); 39 | TempFile.remove(f); 40 | f = TempFile.createFile(4 * 1024); 41 | assertEquals("FuPHVcYFMpfuoCTDGF5PCjMY9xxu", Etag.file(f)); 42 | TempFile.remove(f); 43 | f = TempFile.createFile(5 * 1024); 44 | assertEquals("lkr1cErNyp23IdWan82rufDn3dzT", Etag.file(f)); 45 | TempFile.remove(f); 46 | f = TempFile.createFile(8 * 1024); 47 | assertEquals("lkRgUHWNADyQ0TRirdqoS7UWFql4", Etag.file(f)); 48 | TempFile.remove(f); 49 | f = TempFile.createFile(9 * 1024); 50 | assertEquals("lvlmp343GVuq367WF4XTMetchhid", Etag.file(f)); 51 | TempFile.remove(f); 52 | } 53 | 54 | @Test 55 | public void testLongToInt() { 56 | long len = 2323435710l; 57 | int b = (int) ((len + Configuration.BLOCK_SIZE - 1) / Configuration.BLOCK_SIZE); 58 | assertEquals("不应该溢出", 554, b); 59 | int a = (int) (len + Configuration.BLOCK_SIZE - 1) / Configuration.BLOCK_SIZE; 60 | assertTrue("预计会溢出", 554 != a); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/utils/GZipTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import java.util.Arrays; 11 | 12 | @RunWith(AndroidJUnit4.class) 13 | public class GZipTest extends BaseTest { 14 | 15 | @Test 16 | public void testGZipString(){ 17 | 18 | String string = null; 19 | 20 | byte[] gzip = GZipUtil.gZip(string); 21 | assertTrue(gzip == null); 22 | 23 | string = ""; 24 | gzip = GZipUtil.gZip(string); 25 | assertTrue(Arrays.equals(gzip, string.getBytes())); 26 | 27 | 28 | string = "ABCDEFG"; 29 | gzip = GZipUtil.gZip(string); 30 | 31 | byte[] gUnzip = GZipUtil.gUnzip(gzip); 32 | String stringGUnzip = new String(gUnzip); 33 | 34 | assertTrue(string.equals(stringGUnzip)); 35 | } 36 | 37 | @Test 38 | public void testGZipByte(){ 39 | 40 | byte[] bytes = null; 41 | 42 | byte[] gzip = GZipUtil.gZip(bytes); 43 | assertTrue(gzip == null); 44 | 45 | bytes = new byte[0]; 46 | gzip = GZipUtil.gZip(bytes); 47 | assertTrue(Arrays.equals(bytes, gzip)); 48 | 49 | 50 | String string = "ABCDEFG"; 51 | bytes = string.getBytes(); 52 | gzip = GZipUtil.gZip(bytes); 53 | 54 | byte[] gUnzip = GZipUtil.gUnzip(gzip); 55 | String stringGUnzip = new String(gUnzip); 56 | 57 | assertTrue(string.equals(stringGUnzip)); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/utils/JsonTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | 7 | import junit.framework.Assert; 8 | 9 | import org.json.JSONException; 10 | import org.json.JSONObject; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | 14 | import java.util.ArrayList; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | /** 20 | * Created by Simon on 3/3/16. 21 | */ 22 | @RunWith(AndroidJUnit4.class) 23 | public class JsonTest extends BaseTest { 24 | 25 | private boolean showContent = false; 26 | 27 | @Test 28 | public void testEmpty() { 29 | JSONObject json = new JSONObject(); 30 | assertNotNull(json); 31 | assertEquals("{}", json.toString()); 32 | } 33 | 34 | // e: org.json.JSONException: End of input at character 0 of 35 | @Test 36 | public void testEmpty1() throws JSONException { 37 | String str = ""; 38 | Exception ex = null; 39 | try { 40 | JSONObject json = new JSONObject(str); 41 | } catch (JSONException e) { 42 | ex = e; 43 | } 44 | assertNotNull(ex); 45 | if (showContent) { 46 | assertEquals(str, ex.getMessage()); 47 | } 48 | } 49 | 50 | //e: org.json.JSONException: End of input at character 2 of 51 | @Test 52 | public void testEmpty2() throws JSONException { 53 | String str = " "; 54 | Exception ex = null; 55 | try { 56 | JSONObject json = new JSONObject(str); 57 | } catch (JSONException e) { 58 | ex = e; 59 | } 60 | assertNotNull(ex); 61 | if (showContent) { 62 | assertEquals(str, ex.getMessage()); 63 | } 64 | } 65 | 66 | @Test 67 | public void testB() throws JSONException { 68 | String str = "{}"; 69 | JSONObject json = new JSONObject(str); 70 | assertNotNull(json); 71 | if (showContent) { 72 | assertEquals(str, json.toString()); 73 | } 74 | } 75 | 76 | // e: org.json.JSONException: Value [] of type org.json.JSONArray cannot be converted to JSONObject 77 | @Test 78 | public void testArray() throws JSONException { 79 | String str = "[]"; 80 | Exception ex = null; 81 | try { 82 | JSONObject json = new JSONObject(str);// should JSONArray 83 | } catch (JSONException e) { 84 | ex = e; 85 | } 86 | assertNotNull(ex); 87 | if (showContent) { 88 | assertEquals(str, ex.getMessage()); 89 | } 90 | } 91 | 92 | //e: org.json.JSONException: Value null of type org.json.JSONObject$1 cannot be converted to JSONObject 93 | @Test 94 | public void testNull() throws JSONException { 95 | String str = "null"; 96 | Exception ex = null; 97 | try { 98 | JSONObject json = new JSONObject(str); 99 | } catch (JSONException e) { 100 | ex = e; 101 | } 102 | assertNotNull(ex); 103 | if (showContent) { 104 | assertEquals(str, ex.getMessage()); 105 | } 106 | } 107 | 108 | @Test 109 | public void testEncodeMap() { 110 | Map m = new HashMap<>(); 111 | m.put("a", 1); 112 | String s = Json.encodeMap(m); 113 | assertEquals("{\"a\":1}", s); 114 | } 115 | 116 | @Test 117 | public void testEncodeList() { 118 | List l = new ArrayList<>(); 119 | l.add("a"); 120 | String s = Json.encodeList(l); 121 | assertEquals("[\"a\"]", s); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/utils/ListVectorTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | 7 | import junit.framework.Assert; 8 | 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | 12 | import java.util.List; 13 | 14 | @RunWith(AndroidJUnit4.class) 15 | public class ListVectorTest extends BaseTest { 16 | 17 | @Test 18 | public void testVectorList() { 19 | 20 | final int count = 1000; 21 | final ListVector v = new ListVector<>(); 22 | 23 | new Thread(new Runnable() { 24 | @Override 25 | public void run() { 26 | while (v.size() < count) { 27 | vectorAdd(v); 28 | } 29 | } 30 | }).start(); 31 | 32 | while (v.size() < count) { 33 | vectorList(v); 34 | } 35 | 36 | assertTrue("v:" + v, v.size() == count); 37 | } 38 | 39 | @Test 40 | public void testVectorSubList() { 41 | 42 | final int count = 1000; 43 | final ListVector listVector = new ListVector<>(); 44 | 45 | while (listVector.size() < count) { 46 | vectorAdd(listVector); 47 | } 48 | 49 | final ListVector v = listVector.subList(0, count/2); 50 | 51 | new Thread(new Runnable() { 52 | @Override 53 | public void run() { 54 | while (v.size() < count) { 55 | vectorAdd(v); 56 | } 57 | } 58 | }).start(); 59 | 60 | 61 | while (v.size() < count) { 62 | vectorList(v); 63 | } 64 | 65 | assertTrue("v:" + v, v.size() == count); 66 | } 67 | 68 | private void vectorAdd(List v) { 69 | String e = v.size() + ""; 70 | v.add(e); 71 | System.out.println("add e:" + e); 72 | } 73 | 74 | private void vectorList(ListVector v) { 75 | final int size = v.size(); 76 | v.enumerateObjects(new ListVector.EnumeratorHandler() { 77 | @Override 78 | public boolean enumerate(String s) { 79 | System.out.println("size:" + size + " value:" + s); 80 | return false; 81 | } 82 | }); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/utils/LogUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import android.util.Log; 4 | 5 | import androidx.test.ext.junit.runners.AndroidJUnit4; 6 | 7 | import com.qiniu.android.BaseTest; 8 | 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | 12 | @RunWith(AndroidJUnit4.class) 13 | public class LogUtilsTest extends BaseTest { 14 | 15 | @Test 16 | public void testLog() { 17 | 18 | LogUtil.enableLog(true); 19 | LogUtil.setLogLevel(Log.VERBOSE); 20 | 21 | Throwable throwable = new Throwable(); 22 | 23 | assertTrue(validLogCode(LogUtil.v("log"))); 24 | assertTrue(validLogCode(LogUtil.v("v", "log"))); 25 | assertTrue(validLogCode(LogUtil.v("v", "log", null))); 26 | assertTrue(validLogCode(LogUtil.v("v", "log", throwable))); 27 | 28 | assertTrue(validLogCode(LogUtil.d("log"))); 29 | assertTrue(validLogCode(LogUtil.d("v", "log"))); 30 | assertTrue(validLogCode(LogUtil.d("v", "log", null))); 31 | assertTrue(validLogCode(LogUtil.d("v", "log", throwable))); 32 | 33 | assertTrue(validLogCode(LogUtil.i("log"))); 34 | assertTrue(validLogCode(LogUtil.i("v", "log"))); 35 | assertTrue(validLogCode(LogUtil.i("v", "log", null))); 36 | assertTrue(validLogCode(LogUtil.i("v", "log", throwable))); 37 | 38 | assertTrue(validLogCode(LogUtil.w("log"))); 39 | assertTrue(validLogCode(LogUtil.w("v", "log"))); 40 | assertTrue(validLogCode(LogUtil.w("v", "log", null))); 41 | assertTrue(validLogCode(LogUtil.w("v", "log", throwable))); 42 | 43 | assertTrue(validLogCode(LogUtil.e("log"))); 44 | assertTrue(validLogCode(LogUtil.e("v", "log"))); 45 | assertTrue(validLogCode(LogUtil.e("v", "log", null))); 46 | assertTrue(validLogCode(LogUtil.e("v", "log", throwable))); 47 | 48 | LogUtil.enableLog(false); 49 | } 50 | 51 | private boolean validLogCode(int code) { 52 | return !(code == -10000 || code == -10001); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/utils/NetworkTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import androidx.test.ext.junit.runners.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | /** 11 | * Created by bailong on 16/9/7. 12 | */ 13 | @RunWith(AndroidJUnit4.class) 14 | public class NetworkTest { 15 | 16 | @Test 17 | public void testConnected() { 18 | boolean stat = AndroidNetwork.isNetWorkReady(); 19 | assertTrue(stat); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/utils/StringUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | @RunWith(AndroidJUnit4.class) 11 | public class StringUtilsTest extends BaseTest { 12 | 13 | @Test 14 | public void testJoin(){ 15 | 16 | String string = ""; 17 | 18 | string = StringUtils.join(new String[]{"1", "2"}, "-"); 19 | assertTrue(string.equals("1-2")); 20 | 21 | string = StringUtils.jsonJoin(new String[]{"1", "2"}); 22 | assertTrue(string.equals("\"1\",\"2\"")); 23 | 24 | } 25 | 26 | @Test 27 | public void testTransform(){ 28 | 29 | String string = ""; 30 | 31 | string = StringUtils.jsonJoin(new Long[]{1L, 2L}); 32 | assertTrue(string.equals("\"1\",\"2\"")); 33 | 34 | String[] stringArray = StringUtils.longToString(new Long[]{1L, 2L}); 35 | assertTrue(stringArray.length == 2); 36 | 37 | string = "1234"; 38 | byte[] data = StringUtils.toByteArray(string); 39 | byte[] dataR = new byte[]{-84, -19, 0, 5, 116, 0, 4, 49, 50, 51, 52}; 40 | assertTrue(new String(dataR).equals(new String(data))); 41 | 42 | Object stringR = StringUtils.toObject(dataR); 43 | assertTrue(stringR.equals(string)); 44 | 45 | 46 | string = "tom"; 47 | string = StringUtils.upperCase(string); 48 | assertTrue(string.equals("Tom")); 49 | } 50 | 51 | @Test 52 | public void testToken(){ 53 | String token = "jH983zIUFIP1OVumiBVGeAfiLYJvwrF45S-t22eu:mWEhowqz4sG301DXU6CB3IO7Zss=:eyJzY29wZSI6InpvbmUxLXNwYWNlIiwiZGVhZGxpbmUiOjE1OTczOTMzOTYsICJyZXR1cm5Cb2R5Ijoie1wiZm9vXCI6JCh4OmZvbyksIFwiYmFyXCI6JCh4OmJhciksIFwibWltZVR5cGVcIjokKG1pbWVUeXBlKSwgXCJoYXNoXCI6JChldGFnKSwgXCJrZXlcIjokKGtleSksIFwiZm5hbWVcIjokKGZuYW1lKX0ifQ=="; 54 | String ak = StringUtils.getAkAndScope(token); 55 | assertTrue(ak.equals("jH983zIUFIP1OVumiBVGeAfiLYJvwrF45S-t22euzone1-space")); 56 | 57 | String bucket = StringUtils.getBucket(token); 58 | assertTrue(bucket.equals("zone1-space")); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/qiniu/android/utils/UtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import com.qiniu.android.BaseTest; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | @RunWith(AndroidJUnit4.class) 11 | public class UtilsTest extends BaseTest { 12 | 13 | @Test 14 | public void testIPType(){ 15 | 16 | String testHost = "host"; 17 | String type = ""; 18 | 19 | type = Utils.getIpType("10.10.120.3", testHost); 20 | assertTrue(type.equals(testHost + "-10-10")); 21 | 22 | type = Utils.getIpType("130.101.120.3", testHost); 23 | assertTrue(type.equals(testHost + "-130-101")); 24 | 25 | type = Utils.getIpType("2000:0000:0000:0000:0001:2345:6789:abcd", testHost); 26 | assertTrue(type.equals(testHost + "-ipv6-2000-0000-0000-0000")); 27 | 28 | type = Utils.getIpType("2000:0:0:0:0001:2345:6789:abcd", testHost); 29 | assertTrue(type.equals(testHost + "-ipv6-2000-0000-0000-0000")); 30 | 31 | type = Utils.getIpType("2000::0001:2345:6789:abcd", testHost); 32 | assertTrue(type.equals(testHost + "-ipv6-2000-0000-0000-0000")); 33 | } 34 | 35 | @Test 36 | public void testIsIPType(){ 37 | 38 | String ip = null; 39 | boolean isIpv6 = false; 40 | 41 | ip = "10.10.120.3"; 42 | isIpv6 = Utils.isIpv6(ip); 43 | assertFalse(ip, isIpv6); 44 | 45 | ip = "130.101.120.3"; 46 | isIpv6 = Utils.isIpv6(ip); 47 | assertFalse(ip, isIpv6); 48 | 49 | ip = "2000:0000:0000:0000:0001:2345:6789:abcd"; 50 | isIpv6 = Utils.isIpv6(ip); 51 | assertTrue(ip, isIpv6); 52 | 53 | ip = "2000:0:0:0:0001:2345:6789:abcd"; 54 | isIpv6 = Utils.isIpv6(ip); 55 | assertTrue(ip, isIpv6); 56 | 57 | ip = "2000::0001:2345:6789:abcd"; 58 | isIpv6 = Utils.isIpv6(ip); 59 | assertTrue(ip, isIpv6); 60 | 61 | ip = "0::0"; 62 | isIpv6 = Utils.isIpv6(ip); 63 | assertTrue(ip, isIpv6); 64 | 65 | ip = "ffff::ffff:2345:6789:abcd"; 66 | isIpv6 = Utils.isIpv6(ip); 67 | assertTrue(ip, isIpv6); 68 | 69 | ip = "ff1::ffff:2345:6789:abcd"; 70 | isIpv6 = Utils.isIpv6(ip); 71 | assertTrue(ip, isIpv6); 72 | 73 | ip = "ffff1::ffff:2345:6789:abcd"; 74 | isIpv6 = Utils.isIpv6(ip); 75 | assertFalse(ip, isIpv6); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/bigdata/Configuration.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.bigdata; 2 | 3 | import com.qiniu.android.http.ProxyConfiguration; 4 | 5 | /** 6 | * Created by long on 2017/7/25. 7 | * 8 | * @hidden 9 | */ 10 | 11 | public final class Configuration implements Cloneable { 12 | 13 | /** 14 | * pipelineHost 15 | */ 16 | public String pipelineHost = "https://pipeline.qiniu.com"; 17 | 18 | /** 19 | * 请求 proxy 20 | */ 21 | public ProxyConfiguration proxy; 22 | 23 | 24 | /** 25 | * 连接超时时间,单位 秒 26 | */ 27 | public int connectTimeout = 3; 28 | 29 | /** 30 | * 服务器响应超时时间 单位 秒 31 | */ 32 | public int responseTimeout = 10; 33 | 34 | /** 35 | * 构造函数 36 | */ 37 | public Configuration() { 38 | } 39 | 40 | /** 41 | * Configuration copy 42 | * 43 | * @param config 待 copy 对象 44 | * @return Configuration 45 | */ 46 | public static Configuration copy(Configuration config) { 47 | if (config == null) { 48 | return new Configuration(); 49 | } 50 | try { 51 | return config.clone(); 52 | } catch (CloneNotSupportedException e) { 53 | return new Configuration(); 54 | } 55 | } 56 | 57 | /** 58 | * Configuration clone 59 | * 60 | * @return Configuration 61 | */ 62 | public Configuration clone() throws CloneNotSupportedException { 63 | return (Configuration) super.clone(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/bigdata/client/CompletionHandler.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.bigdata.client; 2 | 3 | import com.qiniu.android.http.ResponseInfo; 4 | 5 | import org.json.JSONObject; 6 | 7 | /** 8 | * 定义请求完成后续动作的处理接口 9 | * 10 | * @hidden 11 | */ 12 | public interface CompletionHandler { 13 | /** 14 | * 用户自定义的处理对象必须实现的接口方法 15 | * 16 | * @param info 响应的调试信息 17 | * @param response 响应的数据 18 | */ 19 | void complete(ResponseInfo info, JSONObject response); 20 | } 21 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/bigdata/client/PostArgs.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.bigdata.client; 2 | 3 | import com.qiniu.android.utils.StringMap; 4 | 5 | import java.io.File; 6 | 7 | /** 8 | * 定义请求参数列表 9 | * 10 | * @hidden 11 | */ 12 | public final class PostArgs { 13 | /** 14 | * 上传的数据 15 | */ 16 | public byte[] data; 17 | /** 18 | * 上传的文件 19 | */ 20 | public File file; 21 | /** 22 | * 请求参数 23 | */ 24 | public StringMap params; 25 | /** 26 | * 上传文件名 27 | */ 28 | public String fileName; 29 | /** 30 | * 上传文件或数据的MimeType 31 | */ 32 | public String mimeType; 33 | 34 | /** 35 | * 构造函数 36 | */ 37 | public PostArgs() { 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/collect/ReportConfig.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.collect; 2 | 3 | import com.qiniu.android.common.Config; 4 | import com.qiniu.android.utils.Utils; 5 | 6 | /** 7 | * 记录配置 8 | */ 9 | public class ReportConfig { 10 | 11 | /** 12 | * 是否开启sdk上传信息搜集 默认为YES 13 | */ 14 | public boolean isReportEnable; 15 | 16 | /** 17 | * 每次上传时间间隔 单位:分钟 默认为0.5分钟 18 | */ 19 | public double interval; 20 | 21 | /** 22 | * 记录文件大于 uploadThreshold 会触发上传,单位:字节 默认为16 * 1024 23 | */ 24 | public long uploadThreshold; 25 | 26 | /** 27 | * 记录文件最大值 要大于 uploadThreshold 单位:字节 默认为4 * 1024 * 1024 28 | */ 29 | public long maxRecordFileSize; 30 | 31 | /** 32 | * 记录文件所在文件夹目录 33 | */ 34 | public final String recordDirectory; 35 | 36 | /** 37 | * 信息上报服务器地址 38 | */ 39 | public final String serverURL; 40 | 41 | /** 42 | * 信息上报请求超时时间 单位:秒 默认为10秒 43 | */ 44 | public int timeoutInterval; 45 | 46 | private static ReportConfig instance = new ReportConfig(); 47 | 48 | private ReportConfig() { 49 | this.isReportEnable = Config.isRecord; 50 | this.interval = Config.interval; 51 | this.serverURL = Config.upLogURL; 52 | if (Config.recordDir != null) { 53 | this.recordDirectory = Config.recordDir; 54 | } else { 55 | this.recordDirectory = Utils.sdkDirectory() + "/report"; 56 | } 57 | this.maxRecordFileSize = Config.maxRecordFileSize; 58 | this.uploadThreshold = Config.uploadThreshold; 59 | this.timeoutInterval = 10; 60 | } 61 | 62 | /** 63 | * 获取配置单例 64 | * 65 | * @return 配置单例 66 | */ 67 | public static ReportConfig getInstance() { 68 | return instance; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/common/Config.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.common; 2 | 3 | import com.qiniu.android.utils.ContextGetter; 4 | 5 | /** 6 | * Created by Simon on 11/22/16. 7 | */ 8 | public final class Config { 9 | 10 | /** 11 | * 上传信息收集文件的地址 只保留域名部分 eg:uplog.qbox.me 12 | */ 13 | public final static String upLogURL = "uplog.qbox.me"; 14 | 15 | /** 16 | * 是否记录上传状态信息。 true 表示记录,false 表示不记录。 17 | *

18 | * 记录上传信息条件: 19 | * isRecord 为 true, 20 | * 记录文件大小 小于 maxRecordFileSize . 21 | *

22 | * 记录文件大小 大于 maxRecordFileSize 时, 则暂停记录信息。 23 | */ 24 | public static boolean isRecord = true; 25 | 26 | /** 27 | * 是否上传记录的上传状态信息。true 表示上传,false 表示不上传。 28 | *

29 | * 上传条件: 30 | * 增加一条上传记录时触发, 31 | * isRecord 为 true, isUpload 为 true, 32 | * 且 记录文件大小 大于 uploadThreshold, 33 | * 且 距上次上传时间大于 minInteval . 34 | *

35 | * 上传成功后,清空记录文件文件 36 | */ 37 | public static boolean isUpload = true; 38 | 39 | /** 40 | * 上传信息记录文件保存的目录, 绝对路径。 41 | * 默认使用当前应用的缓存目录: getCacheDir() 42 | */ 43 | public static String recordDir = null; 44 | 45 | 46 | static { 47 | try { 48 | recordDir = ContextGetter.applicationContext().getCacheDir().getAbsolutePath(); 49 | } catch (Throwable e) { 50 | e.fillInStackTrace(); 51 | } 52 | } 53 | 54 | /** 55 | * 记录上传信息文件最大值,单位:字节。 56 | *

57 | * 记录文件大于此值后暂停记录上传信息。 58 | */ 59 | public static int maxRecordFileSize = 4 * 1024 * 1024; 60 | 61 | /** 62 | * 记录文件大于 uploadThreshold 后才可能触发上传,单位:字节。 63 | * 可依据客户上传频率、文件大小做调整 64 | */ 65 | public static int uploadThreshold = 16 * 1024; 66 | 67 | /** 68 | * 每次上传最小时间间隔.单位:分钟 69 | */ 70 | public static double interval = 0.5; 71 | 72 | /** 73 | * preQuery host 74 | */ 75 | public static String preQueryHost00 = "uc.qiniuapi.com"; 76 | 77 | /** 78 | * preQuery host 79 | */ 80 | public static String preQueryHost01 = "kodo-config.qiniuapi.com"; 81 | 82 | /** 83 | * preQuery host 84 | */ 85 | public static String preQueryHost02 = "uc.qbox.me"; 86 | 87 | /** 88 | * preQuery host 89 | */ 90 | @Deprecated 91 | public static String preQueryHost03 = "api.qiniu.com"; 92 | 93 | /** 94 | * 获取 preQuery hosts 95 | * 96 | * @return preQuery hosts 97 | */ 98 | public static String[] preQueryHosts() { 99 | return new String[]{preQueryHost00, preQueryHost01, preQueryHost02}; 100 | } 101 | 102 | /** 103 | * 当网络切换到 wifi 下,切换到此设置 104 | */ 105 | public static void quick() { 106 | uploadThreshold = 1 * 1024; 107 | interval = 2; 108 | } 109 | 110 | /** 111 | * 标准设置 112 | */ 113 | public static void normal() { 114 | uploadThreshold = 16 * 1024; 115 | interval = 10; 116 | } 117 | 118 | /** 119 | * 网络走流量时,可切换到此设置 120 | */ 121 | public static void slow() { 122 | uploadThreshold = 150 * 1024; 123 | interval = 300; 124 | } 125 | 126 | private Config() { 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/common/Constants.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.common; 2 | 3 | 4 | /** 5 | * 常量定义 6 | */ 7 | public final class Constants { 8 | 9 | /** 10 | * SDK 版本号 11 | */ 12 | public static final String VERSION = "8.8.0"; 13 | 14 | /** 15 | * UTF-8 编码 16 | */ 17 | public static final String UTF_8 = "utf-8"; 18 | 19 | private Constants() { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/common/Zone.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.common; 2 | 3 | import com.qiniu.android.http.ResponseInfo; 4 | import com.qiniu.android.http.metrics.UploadRegionRequestMetrics; 5 | import com.qiniu.android.storage.Configuration; 6 | import com.qiniu.android.storage.UpToken; 7 | 8 | /** 9 | * Created by bailong on 15/10/10. 10 | */ 11 | public abstract class Zone { 12 | 13 | /** 14 | * 构造函数 15 | */ 16 | protected Zone() { 17 | } 18 | 19 | /** 20 | * 根据上传 token 获取 zone 21 | * 22 | * @param token 上传 token 23 | * @return 区域信息 24 | */ 25 | public abstract ZonesInfo getZonesInfo(UpToken token); 26 | 27 | /** 28 | * 根据上传 token 对区域进行预查询 29 | * 30 | * @param token 上传 token 31 | * @param completeHandler 预查询结束回调 32 | */ 33 | @Deprecated 34 | public abstract void preQuery(UpToken token, QueryHandler completeHandler); 35 | 36 | /** 37 | * 根据上传 token 对区域进行预查询 38 | * 39 | * @param configuration 配置信息 40 | * @param token 上传 token 41 | * @param completeHandler 预查询结束回调 42 | */ 43 | public abstract void query(Configuration configuration, UpToken token, final QueryHandlerV2 completeHandler); 44 | 45 | /** 46 | * 预查询结束回调 47 | */ 48 | public interface QueryHandler { 49 | /** 50 | * 预查询结束回调 51 | * 52 | * @param code 状态码 53 | * @param responseInfo 查询响应 54 | * @param metrics 查询指标 55 | */ 56 | void complete(int code, ResponseInfo responseInfo, UploadRegionRequestMetrics metrics); 57 | } 58 | 59 | /** 60 | * 预查询结束回调 61 | */ 62 | public interface QueryHandlerV2 { 63 | /** 64 | * 预查询结束回调 65 | * 66 | * @param responseInfo 查询响应 67 | * @param metrics 查询指标 68 | * @param zonesInfo 区域信息 69 | */ 70 | void complete(ResponseInfo responseInfo, UploadRegionRequestMetrics metrics, ZonesInfo zonesInfo); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/CancellationHandler.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * 取消对调定义 7 | */ 8 | public interface CancellationHandler { 9 | 10 | /** 11 | * 定义用户取消数据或文件上传的信号 12 | * 13 | * @return 是否已取消 14 | */ 15 | boolean isCancelled(); 16 | 17 | /** 18 | * 取消异常 19 | */ 20 | class CancellationException extends IOException { 21 | 22 | /** 23 | * 构造函数 24 | */ 25 | public CancellationException() { 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/ProgressHandler.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http; 2 | 3 | /** 4 | * 定义进度处理接口 5 | */ 6 | public interface ProgressHandler { 7 | /** 8 | * 用户自定义进度处理对象必须实现的接口方法 9 | * 10 | * @param bytesWritten 已经写入字节 11 | * @param totalSize 总字节数 12 | */ 13 | void onProgress(long bytesWritten, long totalSize); 14 | } 15 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/ProxyConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.net.Proxy; 6 | 7 | import okhttp3.Authenticator; 8 | import okhttp3.Credentials; 9 | import okhttp3.Route; 10 | 11 | /** 12 | * http 代理 13 | */ 14 | public final class ProxyConfiguration { 15 | 16 | /** 17 | * hostAddress 18 | */ 19 | public final String hostAddress; 20 | 21 | /** 22 | * port 23 | */ 24 | public final int port; 25 | 26 | /** 27 | * user 28 | */ 29 | public final String user; 30 | 31 | /** 32 | * password 33 | */ 34 | public final String password; 35 | 36 | /** 37 | * type 38 | */ 39 | public final Proxy.Type type; 40 | 41 | /** 42 | * 构造函数 43 | * 44 | * @param hostAddress 服务器域名或IP,比如proxy.com, 192.168.1.1 45 | * @param port 端口 46 | * @param user 用户名,无则填null 47 | * @param password 用户密码,无则填null 48 | * @param type proxy type 49 | */ 50 | public ProxyConfiguration(String hostAddress, int port, String user, String password, java.net.Proxy.Type type) { 51 | this.hostAddress = hostAddress; 52 | this.port = port; 53 | this.user = user; 54 | this.password = password; 55 | this.type = type; 56 | } 57 | 58 | /** 59 | * 构造函数 60 | * 61 | * @param hostAddress 服务器域名或IP,比如proxy.com, 192.168.1.1 62 | * @param port 端口 63 | */ 64 | public ProxyConfiguration(String hostAddress, int port) { 65 | this(hostAddress, port, null, null, Proxy.Type.HTTP); 66 | } 67 | 68 | /** 69 | * 获取 proxy 70 | * 71 | * @return proxy 72 | */ 73 | public Proxy proxy() { 74 | return new Proxy(type, new InetSocketAddress(hostAddress, port)); 75 | } 76 | 77 | /** 78 | * 获取 authenticator 79 | * 80 | * @return Authenticator 81 | */ 82 | public Authenticator authenticator() { 83 | return new Authenticator() { 84 | @Override 85 | public okhttp3.Request authenticate(Route route, okhttp3.Response response) throws IOException { 86 | String credential = Credentials.basic(user, password); 87 | return response.request().newBuilder(). 88 | header("Proxy-Authorization", credential). 89 | header("Proxy-Connection", "Keep-Alive").build(); 90 | } 91 | }; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/UrlConverter.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http; 2 | 3 | /** 4 | * url 拦截器 5 | */ 6 | public interface UrlConverter { 7 | 8 | /** 9 | * url 拦截器,可以转换 url 10 | * 11 | * @param url url 12 | * @return 转换后的 url 13 | */ 14 | String convert(String url); 15 | } 16 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/UserAgent.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http; 2 | 3 | import android.os.Build; 4 | import android.text.TextUtils; 5 | 6 | import com.qiniu.android.common.Constants; 7 | import com.qiniu.android.utils.StringUtils; 8 | import com.qiniu.android.utils.Utils; 9 | 10 | import java.nio.charset.Charset; 11 | import java.util.Locale; 12 | import java.util.Random; 13 | 14 | import static java.lang.String.format; 15 | 16 | /** 17 | * Created by bailong on 15/6/23. 18 | * 19 | * @hidden 20 | */ 21 | public final class UserAgent { 22 | private static UserAgent _instance = new UserAgent(); 23 | 24 | /** 25 | * id 26 | */ 27 | public final String id; 28 | 29 | /** 30 | * UserAgent 31 | */ 32 | public final String ua; 33 | 34 | private UserAgent() { 35 | id = genId(); 36 | ua = getUserAgent(id); 37 | } 38 | 39 | /** 40 | * 获取 UserAgent 单例 41 | * 42 | * @return UserAgent 单例 43 | */ 44 | public static UserAgent instance() { 45 | return _instance; 46 | } 47 | 48 | private static String genId() { 49 | Random r = new Random(); 50 | return System.currentTimeMillis() + "" + r.nextInt(999); 51 | } 52 | 53 | static String getUserAgent(String id) { 54 | String addition = Utils.isDebug() ? "_Debug" : ""; 55 | return format("QiniuAndroid%s/%s (%s; %s; %s", addition, Constants.VERSION, 56 | Utils.systemVersion(), Utils.systemName(), id); 57 | } 58 | 59 | /** 60 | * 获取 UserAgent 字符串 61 | * 62 | * @param part part 63 | * @return UserAgent 字符串 64 | */ 65 | public String getUa(String part) { 66 | String _part = ("" + part).trim(); 67 | if (_part.length() > 15) { 68 | _part = _part.substring(0, Math.min(16, _part.length())); 69 | } 70 | return new String((ua + "; " + _part + ")").getBytes(Charset.forName("ISO-8859-1"))); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/dns/BaseDns.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.LinkedBlockingQueue; 5 | import java.util.concurrent.ThreadPoolExecutor; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | class BaseDns { 9 | int timeout = 10; 10 | static final ExecutorService executor = new ThreadPoolExecutor(0, 4, 11 | 60L, TimeUnit.SECONDS, new LinkedBlockingQueue()); 12 | } 13 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/dns/Dns.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | import java.net.UnknownHostException; 4 | import java.util.List; 5 | 6 | /** 7 | * Dns 解析器 8 | * 9 | * Created by sxci on 03/04/2018. 10 | */ 11 | public interface Dns { 12 | 13 | /** 14 | * Dns 解析 host 域名 15 | * 16 | * @param hostname host 域名 17 | * @return 解析结果 18 | * @throws UnknownHostException 异常信息 19 | */ 20 | List lookup(String hostname) throws UnknownHostException; 21 | } 22 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/dns/DnsCacheFile.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | import com.qiniu.android.storage.Recorder; 4 | 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.FileOutputStream; 8 | import java.io.IOException; 9 | 10 | /** 11 | * Created by jemy on 2019/9/17. 12 | * 13 | * @hidden 14 | */ 15 | 16 | public class DnsCacheFile implements Recorder { 17 | 18 | /** 19 | * dns 缓存路径 20 | */ 21 | public String directory; 22 | 23 | /** 24 | * dns 缓存文件句柄 25 | */ 26 | public File f; 27 | 28 | /** 29 | * DnsCacheFile 构造函数 30 | * 31 | * @param directory dns 缓存路径 32 | * @throws IOException 异常信息 33 | */ 34 | public DnsCacheFile(String directory) throws IOException { 35 | if (directory == null) { 36 | throw new IOException("directory invalid"); 37 | } 38 | 39 | this.directory = directory; 40 | f = new File(directory); 41 | 42 | if (!f.exists()) { 43 | boolean r = f.mkdirs(); 44 | if (!r) { 45 | throw new IOException("mkdir failed"); 46 | } 47 | } 48 | 49 | if (!f.isDirectory()) { 50 | throw new IOException("does not mkdir"); 51 | } 52 | } 53 | 54 | /** 55 | * 设置DNS缓存 56 | * 57 | * @param key 缓存文件明 58 | * @param data 缓存数据 59 | */ 60 | @Override 61 | public synchronized void set(String key, byte[] data) { 62 | File f = new File(directory, key); 63 | if (f.exists()) { 64 | f.delete(); 65 | } 66 | 67 | FileOutputStream fo = null; 68 | try { 69 | fo = new FileOutputStream(f); 70 | fo.write(data); 71 | } catch (IOException e) { 72 | e.printStackTrace(); 73 | } 74 | if (fo != null) { 75 | try { 76 | fo.close(); 77 | } catch (IOException e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | } 82 | 83 | /** 84 | * 获取缓存 85 | * 86 | * @param key 缓存文件名 87 | */ 88 | @Override 89 | public synchronized byte[] get(String key) { 90 | File f = new File(directory, key); 91 | if (!f.exists()) { 92 | return null; 93 | } 94 | 95 | FileInputStream fi = null; 96 | byte[] data = null; 97 | int read = 0; 98 | try { 99 | data = new byte[(int) f.length()]; 100 | fi = new FileInputStream(f); 101 | read = fi.read(data); 102 | } catch (IOException e) { 103 | e.printStackTrace(); 104 | } 105 | if (fi != null) { 106 | try { 107 | fi.close(); 108 | } catch (IOException e) { 109 | e.printStackTrace(); 110 | } 111 | } 112 | if (read == 0) { 113 | return null; 114 | } 115 | return data; 116 | } 117 | 118 | //f.delete()=false时才会有fs.length>1的情况 119 | public String getFileName() { 120 | return "dnsCache"; 121 | } 122 | 123 | @Override 124 | public synchronized void del(String key) { 125 | if (key != null) { 126 | File f = new File(directory, key); 127 | f.delete(); 128 | } 129 | } 130 | 131 | synchronized void clearCache() throws IOException { 132 | if (f == null) { 133 | throw new IOException("directory invalid"); 134 | } 135 | 136 | File[] subFiles = f.listFiles(); 137 | if (subFiles != null && subFiles.length > 0) { 138 | for (File f : subFiles) { 139 | f.delete(); 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/dns/DnsNetworkAddress.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | import com.qiniu.android.storage.GlobalConfiguration; 4 | import com.qiniu.android.utils.Utils; 5 | 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | class DnsNetworkAddress implements IDnsNetworkAddress, java.io.Serializable { 10 | 11 | private final String hostValue; 12 | private final String ipValue; 13 | private final Long ttlValue; 14 | private final String sourceValue; 15 | private final Long timestampValue; 16 | 17 | static DnsNetworkAddress address(JSONObject jsonObject){ 18 | String hostValue = null; 19 | String ipValue = null; 20 | Long ttlValue = null; 21 | String sourceValue = null; 22 | Long timestampValue = null; 23 | try { 24 | hostValue = jsonObject.getString("hostValue"); 25 | } catch (JSONException e) {} 26 | try { 27 | ipValue = jsonObject.getString("ipValue"); 28 | } catch (JSONException e) {} 29 | try { 30 | ttlValue = jsonObject.getLong("ttlValue"); 31 | } catch (JSONException e) {} 32 | try { 33 | timestampValue = jsonObject.getLong("timestampValue"); 34 | } catch (JSONException e) {} 35 | try { 36 | sourceValue = jsonObject.getString("sourceValue"); 37 | } catch (JSONException e) {} 38 | 39 | DnsNetworkAddress networkAddress = new DnsNetworkAddress(hostValue, ipValue, ttlValue, sourceValue, timestampValue); 40 | return networkAddress; 41 | } 42 | 43 | DnsNetworkAddress(String hostValue, 44 | String ipValue, 45 | Long ttlValue, 46 | String sourceValue, 47 | Long timestampValue) { 48 | this.hostValue = hostValue; 49 | this.ipValue = ipValue; 50 | this.ttlValue = ttlValue; 51 | this.sourceValue = sourceValue; 52 | this.timestampValue = timestampValue; 53 | } 54 | 55 | JSONObject toJson(){ 56 | JSONObject jsonObject = new JSONObject(); 57 | try { 58 | jsonObject.put("hostValue", this.hostValue); 59 | } catch (JSONException e) {} 60 | try { 61 | jsonObject.put("ipValue", this.ipValue); 62 | } catch (JSONException e) {} 63 | try { 64 | jsonObject.put("ttlValue", this.ttlValue); 65 | } catch (JSONException e) {} 66 | try { 67 | jsonObject.put("timestampValue", this.timestampValue); 68 | } catch (JSONException e) {} 69 | try { 70 | jsonObject.put("sourceValue", this.sourceValue); 71 | } catch (JSONException e) {} 72 | return jsonObject; 73 | } 74 | 75 | @Override 76 | public String getHostValue() { 77 | return hostValue; 78 | } 79 | 80 | @Override 81 | public String getIpValue() { 82 | return ipValue; 83 | } 84 | 85 | @Override 86 | public Long getTtlValue() { 87 | return ttlValue; 88 | } 89 | 90 | @Override 91 | public String getSourceValue() { 92 | return sourceValue; 93 | } 94 | 95 | @Override 96 | public Long getTimestampValue() { 97 | return timestampValue; 98 | } 99 | 100 | boolean isValid() { 101 | if (timestampValue == null || ipValue == null || ipValue.length() == 0) { 102 | return false; 103 | } 104 | int maxTTL = GlobalConfiguration.getInstance().dnsCacheMaxTTL; 105 | return (Utils.currentTimestamp() / 1000) < timestampValue + maxTTL; 106 | } 107 | 108 | boolean needRefresh() { 109 | if (timestampValue == null || ttlValue == null || ipValue == null || ipValue.length() == 0) { 110 | return false; 111 | } 112 | int ttl = ttlValue.intValue(); 113 | return (Utils.currentTimestamp() / 1000) > timestampValue + ttl; 114 | } 115 | } -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/dns/DnsSource.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | /** 4 | * Dns 解析源 5 | */ 6 | public class DnsSource { 7 | 8 | /** 9 | * Doh 解析源 10 | */ 11 | public static final String Doh = "doh"; 12 | 13 | /** 14 | * udp 方式的解析源 15 | */ 16 | public static final String Udp = "dns"; 17 | 18 | /** 19 | * DnsPod 解析源 20 | */ 21 | public static final String Dnspod = "dnspod"; 22 | 23 | /** 24 | * System 解析源 25 | */ 26 | public static final String System = "system"; 27 | 28 | /** 29 | * 自定义解析源 30 | */ 31 | public static final String Custom = "customized"; 32 | 33 | /** 34 | * 未知解析源 35 | */ 36 | public static final String None = "none"; 37 | 38 | private DnsSource() { 39 | } 40 | 41 | /** 42 | * 判断解析源是否为 Doh 43 | * 44 | * @param source 解析源 45 | * @return 解析源是否为 Doh 46 | */ 47 | public static boolean isDoh(String source) { 48 | return source != null && source.contains(Doh); 49 | } 50 | 51 | /** 52 | * 判断解析源是否为 Udp 53 | * 54 | * @param source 解析源 55 | * @return 解析源是否为 Udp 56 | */ 57 | public static boolean isUdp(String source) { 58 | return source != null && source.contains(Udp); 59 | } 60 | 61 | /** 62 | * 判断解析源是否为 DnsPod 63 | * 64 | * @param source 解析源 65 | * @return 解析源是否为 DnsPod 66 | */ 67 | public static boolean isDnspod(String source) { 68 | return source != null && source.contains(Dnspod); 69 | } 70 | 71 | /** 72 | * 判断解析源是否为系统的 73 | * 74 | * @param source 解析源 75 | * @return 解析源是否为系统的 76 | */ 77 | public static boolean isSystem(String source) { 78 | return source != null && source.contains(System); 79 | } 80 | 81 | /** 82 | * 判断解析源是否为自定义的 83 | * 84 | * @param source 解析源 85 | * @return 解析源是否为自定义的 86 | */ 87 | public static boolean isCustom(String source) { 88 | return source != null && source.contains(Custom); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/dns/HappyDns.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | import com.qiniu.android.dns.DnsManager; 4 | import com.qiniu.android.storage.GlobalConfiguration; 5 | 6 | import java.io.IOException; 7 | import java.net.UnknownHostException; 8 | import java.util.List; 9 | 10 | /** 11 | * Created by yangsen on 2020/6/8 12 | * 13 | * @hidden 14 | */ 15 | @Deprecated 16 | public class HappyDns implements Dns { 17 | 18 | private Dns customDns; 19 | private SystemDns systemDns; 20 | 21 | private DnsQueryErrorHandler errorHandler; 22 | 23 | /** 24 | * 构造函数 25 | */ 26 | public HappyDns() { 27 | int dnsTimeout = GlobalConfiguration.getInstance().dnsResolveTimeout; 28 | systemDns = new SystemDns(dnsTimeout); 29 | customDns = GlobalConfiguration.getInstance().dns; 30 | } 31 | 32 | void setQueryErrorHandler(DnsQueryErrorHandler handler) { 33 | errorHandler = handler; 34 | } 35 | 36 | @Override 37 | public List lookup(String hostname) throws UnknownHostException { 38 | List addressList = null; 39 | int dnsTimeout = GlobalConfiguration.getInstance().dnsResolveTimeout; 40 | // 自定义 dns 41 | if (customDns != null) { 42 | try { 43 | addressList = customDns.lookup(hostname); 44 | } catch (IOException e) { 45 | handleDnsError(e, hostname); 46 | } 47 | if (addressList != null && addressList.size() > 0) { 48 | return addressList; 49 | } 50 | } 51 | 52 | // 系统 dns 53 | try { 54 | addressList = systemDns.lookup(hostname); 55 | } catch (IOException e) { 56 | handleDnsError(e, hostname); 57 | } 58 | if (addressList != null && addressList.size() > 0) { 59 | return addressList; 60 | } 61 | 62 | // http dns 63 | try { 64 | HttpDns httpDns = new HttpDns(dnsTimeout); 65 | addressList = httpDns.lookup(hostname); 66 | } catch (IOException e) { 67 | handleDnsError(e, hostname); 68 | } 69 | if (addressList != null && addressList.size() > 0) { 70 | return addressList; 71 | } 72 | 73 | // udp dns 74 | try { 75 | UdpDns udpDns = new UdpDns(dnsTimeout); 76 | addressList = udpDns.lookup(hostname); 77 | } catch (IOException e) { 78 | handleDnsError(e, hostname); 79 | } 80 | 81 | return addressList; 82 | } 83 | 84 | private void handleDnsError(IOException e, String host) { 85 | if (errorHandler != null) { 86 | errorHandler.queryError(e, host); 87 | } 88 | } 89 | 90 | interface DnsQueryErrorHandler extends DnsManager.QueryErrorHandler { 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/dns/HttpDns.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | import com.qiniu.android.dns.Domain; 4 | import com.qiniu.android.dns.IResolver; 5 | import com.qiniu.android.dns.Record; 6 | import com.qiniu.android.dns.dns.DohResolver; 7 | import com.qiniu.android.storage.GlobalConfiguration; 8 | 9 | import java.io.IOException; 10 | import java.net.UnknownHostException; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.LinkedBlockingQueue; 15 | import java.util.concurrent.ThreadPoolExecutor; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | /** 19 | * Http Dns 20 | * 21 | * @hidden 22 | */ 23 | public class HttpDns extends BaseDns implements Dns { 24 | 25 | private IResolver httpIpv4Resolver; 26 | private IResolver httpIpv6Resolver; 27 | 28 | /** 29 | * 构造函数 30 | * 31 | * @param timeout 解析超时时间 32 | */ 33 | public HttpDns(int timeout) { 34 | String[] dohIpv4Servers = GlobalConfiguration.getInstance().getDohIpv4Servers(); 35 | if (dohIpv4Servers != null && dohIpv4Servers.length > 0) { 36 | httpIpv4Resolver = new DohResolver(dohIpv4Servers, Record.TYPE_A, timeout, executor); 37 | } 38 | 39 | String[] dohIpv6Servers = GlobalConfiguration.getInstance().getDohIpv6Servers(); 40 | if (dohIpv6Servers != null && dohIpv6Servers.length > 0) { 41 | httpIpv6Resolver = new DohResolver(dohIpv6Servers, Record.TYPE_A, timeout, executor); 42 | } 43 | } 44 | 45 | /** 46 | * Dns 解析函数 47 | * 48 | * @param hostname host 域名 49 | * @return 解析结果 50 | * @throws UnknownHostException 异常 51 | */ 52 | @Override 53 | public List lookup(String hostname) throws UnknownHostException { 54 | if (!GlobalConfiguration.getInstance().dohEnable) { 55 | return null; 56 | } 57 | 58 | if (httpIpv4Resolver == null && httpIpv6Resolver == null) { 59 | throw new UnknownHostException("resolver server is invalid"); 60 | } 61 | 62 | Record[] records = null; 63 | if (httpIpv4Resolver != null) { 64 | try { 65 | records = httpIpv4Resolver.resolve(new Domain(hostname), null); 66 | } catch (IOException ignore) { 67 | } 68 | } 69 | 70 | if ((records == null || records.length == 0) && httpIpv6Resolver != null) { 71 | try { 72 | records = httpIpv6Resolver.resolve(new Domain(hostname), null); 73 | } catch (IOException ignore) { 74 | } 75 | } 76 | 77 | if (records == null || records.length == 0) { 78 | return null; 79 | } 80 | 81 | ArrayList addressList = new ArrayList<>(); 82 | for (Record record : records) { 83 | if (record.isA() || record.isAAAA()) { 84 | String source = DnsSource.Doh + ":<" + record.server + ">"; 85 | DnsNetworkAddress address = new DnsNetworkAddress(hostname, record.value, record.timeStamp, source, record.timeStamp); 86 | addressList.add(address); 87 | } 88 | } 89 | 90 | return addressList; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/dns/IDnsNetworkAddress.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | 4 | /** 5 | * Dns 预解析信息 6 | * 7 | * @hidden 8 | */ 9 | public interface IDnsNetworkAddress { 10 | 11 | /** 12 | * 预解析的域名 13 | * 14 | * @return 预解析的域名 15 | */ 16 | String getHostValue(); 17 | 18 | /** 19 | * 预解析域名的 IP 信息 20 | * 21 | * @return 预解析域名的 IP 信息 22 | */ 23 | String getIpValue(); 24 | 25 | /** 26 | * 预解析域名的 IP 有效时间 单位:秒 27 | * 28 | * @return 预解析域名的 IP 有效时间 29 | */ 30 | Long getTtlValue(); 31 | 32 | /** 33 | * 预解析的源,自定义dns返回 "customized" 34 | * 35 | * @return 预解析的源 36 | */ 37 | String getSourceValue(); 38 | 39 | /** 40 | * 解析到 host 时的时间戳,单位:秒 41 | * 42 | * @return 解析到 host 时的时间戳 43 | */ 44 | Long getTimestampValue(); 45 | } 46 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/dns/SystemDns.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | import com.qiniu.android.storage.GlobalConfiguration; 4 | 5 | import java.net.InetAddress; 6 | import java.net.UnknownHostException; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.Date; 10 | import java.util.List; 11 | import java.util.concurrent.Callable; 12 | import java.util.concurrent.ExecutorService; 13 | import java.util.concurrent.Future; 14 | import java.util.concurrent.FutureTask; 15 | import java.util.concurrent.LinkedBlockingQueue; 16 | import java.util.concurrent.ThreadPoolExecutor; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | /** 20 | * Created by yangsen on 2020/5/28 21 | * 22 | * @hidden 23 | */ 24 | public class SystemDns extends BaseDns implements Dns { 25 | 26 | /** 27 | * 构造函数 28 | */ 29 | public SystemDns() { 30 | } 31 | 32 | /** 33 | * 构造函数 34 | * 35 | * @param timeout DNS 解析超时时间 36 | */ 37 | public SystemDns(int timeout) { 38 | this.timeout = timeout; 39 | } 40 | 41 | /** 42 | * DNS 解析域名 43 | * 44 | * @param hostname 域名 45 | * @return 解析结果 46 | * @throws UnknownHostException 异常 47 | */ 48 | public List lookupInetAddress(final String hostname) throws UnknownHostException { 49 | if (hostname == null) { 50 | throw new UnknownHostException("hostname is null"); 51 | } else { 52 | try { 53 | Future> task = executor.submit(new Callable>() { 54 | @Override 55 | public List call() throws Exception { 56 | return Arrays.asList(InetAddress.getAllByName(hostname)); 57 | } 58 | }); 59 | return task.get(timeout, TimeUnit.SECONDS); 60 | } catch (Exception var4) { 61 | UnknownHostException unknownHostException = 62 | new UnknownHostException("dns broken when lookup of " + hostname); 63 | unknownHostException.initCause(var4); 64 | throw unknownHostException; 65 | } 66 | } 67 | } 68 | 69 | /** 70 | * DNS 解析域名 71 | * 72 | * @param hostname 域名 73 | * @return 解析结果 74 | * @throws UnknownHostException 异常 75 | */ 76 | @Override 77 | public List lookup(String hostname) throws UnknownHostException { 78 | long timestamp = new Date().getTime() / 1000; 79 | long defaultTTL = GlobalConfiguration.getInstance().dnsCacheTime; 80 | ArrayList addressList = new ArrayList<>(); 81 | List inetAddressList = lookupInetAddress(hostname); 82 | for (InetAddress inetAddress : inetAddressList) { 83 | DnsNetworkAddress address = new DnsNetworkAddress(inetAddress.getHostName(), inetAddress.getHostAddress(), defaultTTL, DnsSource.System, timestamp); 84 | addressList.add(address); 85 | } 86 | return addressList; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/dns/UdpDns.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.dns; 2 | 3 | import com.qiniu.android.dns.Domain; 4 | import com.qiniu.android.dns.IResolver; 5 | import com.qiniu.android.dns.Record; 6 | import com.qiniu.android.dns.dns.DnsUdpResolver; 7 | import com.qiniu.android.storage.GlobalConfiguration; 8 | 9 | import java.io.IOException; 10 | import java.net.UnknownHostException; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.LinkedBlockingQueue; 15 | import java.util.concurrent.ThreadPoolExecutor; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | /** 19 | * udp dns 20 | * 21 | * @hidden 22 | */ 23 | public class UdpDns extends BaseDns implements Dns { 24 | private IResolver udpIpv4Resolver; 25 | private IResolver udpIpv6Resolver; 26 | 27 | /** 28 | * 构造函数 29 | * 30 | * @param timeout 超时时间,单位:秒 31 | */ 32 | public UdpDns(int timeout) { 33 | String[] udpIpv4Servers = GlobalConfiguration.getInstance().getUdpDnsIpv4Servers(); 34 | if (udpIpv4Servers != null && udpIpv4Servers.length > 0) { 35 | udpIpv4Resolver = new DnsUdpResolver(udpIpv4Servers, Record.TYPE_A, timeout, executor); 36 | } 37 | 38 | String[] udpIpv6Servers = GlobalConfiguration.getInstance().getUdpDnsIpv6Servers(); 39 | if (udpIpv6Servers != null && udpIpv6Servers.length > 0) { 40 | udpIpv6Resolver = new DnsUdpResolver(udpIpv6Servers, Record.TYPE_A, timeout, executor); 41 | } 42 | } 43 | 44 | /** 45 | * 解析域名 46 | * 47 | * @param hostname host 域名 48 | * @return 解析结果 49 | * @throws UnknownHostException 异常 50 | */ 51 | @Override 52 | public List lookup(String hostname) throws UnknownHostException { 53 | if (!GlobalConfiguration.getInstance().udpDnsEnable) { 54 | return null; 55 | } 56 | 57 | if (udpIpv4Resolver == null && udpIpv6Resolver == null) { 58 | throw new UnknownHostException("resolver server is invalid"); 59 | } 60 | 61 | Record[] records = null; 62 | if (udpIpv4Resolver != null) { 63 | try { 64 | records = udpIpv4Resolver.resolve(new Domain(hostname), null); 65 | } catch (IOException ignore) { 66 | } 67 | } 68 | 69 | if ((records == null || records.length == 0) && udpIpv6Resolver != null) { 70 | try { 71 | records = udpIpv6Resolver.resolve(new Domain(hostname), null); 72 | } catch (IOException ignore) { 73 | } 74 | } 75 | 76 | if (records == null || records.length == 0) { 77 | return null; 78 | } 79 | 80 | ArrayList addressList = new ArrayList<>(); 81 | for (Record record : records) { 82 | if (record.isA() || record.isAAAA()) { 83 | String source = DnsSource.Udp + ":<" + record.server + ">"; 84 | DnsNetworkAddress address = new DnsNetworkAddress(hostname, record.value, record.timeStamp, source, record.timeStamp); 85 | addressList.add(address); 86 | } 87 | } 88 | 89 | return addressList; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/metrics/UploadMetrics.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.metrics; 2 | 3 | import com.qiniu.android.utils.Utils; 4 | 5 | import java.util.Date; 6 | 7 | /** 8 | * 上传指标 9 | * 10 | * @hidden 11 | */ 12 | public class UploadMetrics { 13 | 14 | /** 15 | * 开始时间 16 | */ 17 | protected Date startDate = null; 18 | 19 | /** 20 | * 结束时间 21 | */ 22 | protected Date endDate = null; 23 | 24 | /** 25 | * 构造函数 26 | */ 27 | protected UploadMetrics() { 28 | } 29 | 30 | /** 31 | * 开始 32 | */ 33 | public void start() { 34 | startDate = new Date(); 35 | } 36 | 37 | /** 38 | * 结束 39 | */ 40 | public void end() { 41 | endDate = new Date(); 42 | } 43 | 44 | /** 45 | * 获取开始时间 46 | * 47 | * @return 开始时间 48 | */ 49 | public Date getStartDate() { 50 | return startDate; 51 | } 52 | 53 | /** 54 | * 获取总耗时 55 | * 56 | * @return 总耗时 57 | */ 58 | public long totalElapsedTime() { 59 | if (startDate == null || endDate == null) { 60 | return 0; 61 | } 62 | return Utils.dateDuration(startDate, endDate); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/metrics/UploadRegionRequestMetrics.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.metrics; 2 | 3 | import com.qiniu.android.http.request.IUploadRegion; 4 | import com.qiniu.android.utils.Utils; 5 | 6 | import java.util.Date; 7 | import java.util.List; 8 | import java.util.concurrent.CopyOnWriteArrayList; 9 | 10 | /** 11 | * 区域上传事务 12 | * 13 | * @hidden 14 | */ 15 | public class UploadRegionRequestMetrics extends UploadMetrics { 16 | 17 | /** 18 | * 上传区域 19 | */ 20 | public final IUploadRegion region; 21 | 22 | private List metricsList = new CopyOnWriteArrayList<>(); 23 | 24 | /** 25 | * 构造函数 26 | * 27 | * @param region 上传区域 28 | */ 29 | public UploadRegionRequestMetrics(IUploadRegion region) { 30 | this.region = region; 31 | } 32 | 33 | /** 34 | * 请求的次数 35 | * 36 | * @return 请求的次数 37 | */ 38 | public Integer requestCount() { 39 | return metricsList.size(); 40 | } 41 | 42 | /** 43 | * 发送数据的大小 44 | * 45 | * @return 发送数据的大小 46 | */ 47 | public Long bytesSend() { 48 | if (metricsList.isEmpty()) { 49 | return 0L; 50 | } 51 | long bytes = 0; 52 | for (UploadSingleRequestMetrics metrics : metricsList) { 53 | if (metrics != null) { 54 | bytes += metrics.bytesSend(); 55 | } 56 | } 57 | return bytes; 58 | } 59 | 60 | /** 61 | * 获取最后一个请求指标 62 | * 63 | * @return 最后一个请求指标 64 | */ 65 | public UploadSingleRequestMetrics lastMetrics() { 66 | int size = metricsList.size(); 67 | return size < 1 ? null : metricsList.get(size - 1); 68 | } 69 | 70 | /** 71 | * 添加新的上传请求指标 72 | * 73 | * @param metricsList 新的上传请求指标 74 | */ 75 | public void addMetricsList(List metricsList) { 76 | if (metricsList == null || metricsList.size() == 0) { 77 | return; 78 | } 79 | for (UploadSingleRequestMetrics metrics : metricsList) { 80 | if (metrics != null) { 81 | this.metricsList.add(metrics); 82 | } 83 | } 84 | } 85 | 86 | /** 87 | * 添加新的上传请求指标 88 | * 89 | * @param metrics 新的上传请求指标 90 | */ 91 | public void addMetrics(UploadRegionRequestMetrics metrics) { 92 | if (metrics == null || metrics.region == null || metrics.region.getZoneInfo() == null 93 | || metrics.region.getZoneInfo().regionId == null 94 | || region == null || region.getZoneInfo() == null || region.getZoneInfo().regionId == null 95 | || metrics.metricsList == null || metrics.metricsList.size() == 0) { 96 | 97 | return; 98 | } 99 | String thisRegionId = metrics.region.getZoneInfo().getRegionId(); 100 | String metricsRegionId = metrics.region.getZoneInfo().getRegionId(); 101 | if (thisRegionId.equals(metricsRegionId)) { 102 | // 拼接开始和结束时间,开始时间要最老的,结束时间要最新的 103 | if (startDate != null && metrics.startDate != null && startDate.getTime() > metrics.startDate.getTime()) { 104 | startDate = metrics.startDate; 105 | } 106 | if (endDate != null && metrics.endDate != null && endDate.getTime() < metrics.endDate.getTime()) { 107 | endDate = metrics.endDate; 108 | } 109 | 110 | addMetricsList(metrics.metricsList); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/networkStatus/NetworkStatusManager.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.networkStatus; 2 | 3 | import com.qiniu.android.utils.Cache; 4 | import com.qiniu.android.utils.Utils; 5 | 6 | import org.json.JSONObject; 7 | 8 | 9 | /** 10 | * 网络状态管理器 11 | * 12 | * @hidden 13 | */ 14 | public class NetworkStatusManager { 15 | 16 | private static final NetworkStatusManager networkStatusManager = new NetworkStatusManager(); 17 | 18 | private final Cache cache = new Cache.Builder(NetworkStatus.class) 19 | .setVersion("v2") 20 | .setFlushCount(10) 21 | .builder(); 22 | 23 | /** 24 | * 获取单例 25 | * 26 | * @return NetworkStatusManager 单例 27 | */ 28 | public static NetworkStatusManager getInstance() { 29 | return networkStatusManager; 30 | } 31 | 32 | private NetworkStatusManager() { 33 | } 34 | 35 | /** 36 | * 获取网络状态唯一标识 37 | * 38 | * @param host host 39 | * @param ip ip 40 | * @return 唯一标识 41 | */ 42 | @Deprecated 43 | public static String getNetworkStatusType(String host, String ip) { 44 | return Utils.getIpType(ip, host); 45 | } 46 | 47 | /** 48 | * 获取网络状态唯一标识 49 | * 50 | * @param httpVersion httpVersion 51 | * @param host host 52 | * @param ip ip 53 | * @return 唯一标识 54 | */ 55 | public static String getNetworkStatusType(String httpVersion, String host, String ip) { 56 | return Utils.getIpType(httpVersion, ip, host); 57 | } 58 | 59 | /** 60 | * 获取网络状态 61 | * 62 | * @param type 网络状态唯一标识 63 | * @return 网络状态 64 | */ 65 | public NetworkStatus getNetworkStatus(String type) { 66 | if (type == null || type.isEmpty()) { 67 | return null; 68 | } 69 | 70 | Cache.Object object = cache.cacheForKey(type); 71 | if (object instanceof NetworkStatus) { 72 | return (NetworkStatus) object; 73 | } else { 74 | return new NetworkStatus(); 75 | } 76 | } 77 | 78 | /** 79 | * 更新网络状态 80 | * 81 | * @param type 网络状态唯一标识 82 | * @param speed 网速 83 | */ 84 | public void updateNetworkStatus(String type, int speed) { 85 | if (type == null || type.isEmpty()) { 86 | return; 87 | } 88 | 89 | NetworkStatus status = new NetworkStatus(); 90 | status.speed = speed; 91 | this.cache.cache(type, status, false); 92 | } 93 | 94 | /** 95 | * default speed 96 | */ 97 | protected static final int DefaultSpeed = 600; 98 | 99 | /** 100 | * 网络状态 101 | * 102 | * @hidden 103 | */ 104 | public static class NetworkStatus implements Cache.Object { 105 | 106 | private int speed = DefaultSpeed; 107 | 108 | /** 109 | * 获取速度 110 | * 111 | * @return 速度 112 | */ 113 | public int getSpeed() { 114 | return speed; 115 | } 116 | 117 | /** 118 | * 设置速度 119 | * 120 | * @param speed speed 121 | */ 122 | public void setSpeed(int speed) { 123 | this.speed = speed; 124 | } 125 | 126 | private NetworkStatus() { 127 | } 128 | 129 | /** 130 | * 构造函数 131 | * 132 | * @param jsonObject 网络状态 json 数据 133 | */ 134 | public NetworkStatus(JSONObject jsonObject) { 135 | if (jsonObject == null) { 136 | return; 137 | } 138 | 139 | try { 140 | this.speed = jsonObject.getInt("speed"); 141 | } catch (Exception ignored) { 142 | } 143 | } 144 | 145 | /** 146 | * 获取 json 数据 147 | * 148 | * @return json 数据 149 | */ 150 | @Override 151 | public JSONObject toJson() { 152 | JSONObject jsonObject = new JSONObject(); 153 | try { 154 | jsonObject.put("speed", speed); 155 | } catch (Exception ignored) { 156 | } 157 | return jsonObject; 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/networkStatus/UploadServerNetworkStatus.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.networkStatus; 2 | 3 | import com.qiniu.android.http.request.IUploadServer; 4 | 5 | /** 6 | * UploadServerNetworkStatus 7 | * 8 | * @hidden 9 | */ 10 | public class UploadServerNetworkStatus { 11 | 12 | private UploadServerNetworkStatus() { 13 | } 14 | 15 | /** 16 | * 获取网络状态较好的 server 17 | * 18 | * @param serverA serverA 19 | * @param serverB serverB 20 | * @return 网络状态较好的 server 21 | */ 22 | public static IUploadServer getBetterNetworkServer(IUploadServer serverA, IUploadServer serverB) { 23 | return isServerNetworkBetter(serverA, serverB) ? serverA : serverB; 24 | } 25 | 26 | /** 27 | * serverA 网络状态是否较 serverB 好 28 | * 如果两个 Server 网速相同且类别相同优先使用 serverA,类别不同 HTTP/3 较好 29 | * 30 | * @param serverA serverA 31 | * @param serverB serverB 32 | * @return 是否较好 33 | */ 34 | public static boolean isServerNetworkBetter(IUploadServer serverA, IUploadServer serverB) { 35 | if (serverA == null) { 36 | return false; 37 | } else if (serverB == null) { 38 | return true; 39 | } 40 | 41 | String serverTypeA = NetworkStatusManager.getNetworkStatusType(serverA.getHttpVersion(), serverA.getHost(), serverA.getIp()); 42 | String serverTypeB = NetworkStatusManager.getNetworkStatusType(serverB.getHttpVersion(), serverB.getHost(), serverB.getIp()); 43 | if (serverTypeA == null || serverTypeA.length() == 0) { 44 | return false; 45 | } else if (serverTypeB == null || serverTypeB.length() == 0) { 46 | return true; 47 | } 48 | 49 | NetworkStatusManager.NetworkStatus serverStatusA = NetworkStatusManager.getInstance().getNetworkStatus(serverTypeA); 50 | NetworkStatusManager.NetworkStatus serverStatusB = NetworkStatusManager.getInstance().getNetworkStatus(serverTypeB); 51 | 52 | int serverASpeed = serverStatusA.getSpeed(); 53 | int serverBSpeed = serverStatusB.getSpeed(); 54 | String serverAHttpVersion = serverA.getHttpVersion(); 55 | String serverBHttpVersion = serverB.getHttpVersion(); 56 | if (serverAHttpVersion == null) { 57 | serverAHttpVersion = ""; 58 | } 59 | if (serverBHttpVersion == null) { 60 | serverBHttpVersion = ""; 61 | } 62 | if (serverAHttpVersion.equals(IUploadServer.HttpVersion3) && !serverAHttpVersion.equals(serverBHttpVersion)) { 63 | if (serverASpeed < 200 && serverBSpeed == NetworkStatusManager.DefaultSpeed) { 64 | return true; 65 | } else if (serverASpeed > NetworkStatusManager.DefaultSpeed && serverBSpeed > 400) { 66 | return false; 67 | } 68 | } else if (serverBHttpVersion.equals(IUploadServer.HttpVersion3) && !serverAHttpVersion.equals(serverBHttpVersion)) { 69 | if (serverBSpeed < 200 && serverASpeed == NetworkStatusManager.DefaultSpeed) { 70 | return false; 71 | } else if (serverBSpeed > NetworkStatusManager.DefaultSpeed && serverASpeed > 400) { 72 | return true; 73 | } 74 | } 75 | return serverBSpeed <= serverASpeed; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/request/IRequestClient.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.request; 2 | 3 | import com.qiniu.android.http.ProxyConfiguration; 4 | import com.qiniu.android.http.ResponseInfo; 5 | import com.qiniu.android.http.metrics.UploadSingleRequestMetrics; 6 | 7 | import org.json.JSONObject; 8 | 9 | /** 10 | * 请求 Client 抽象 11 | */ 12 | public abstract class IRequestClient { 13 | 14 | /** 15 | * 请求进度协议 16 | */ 17 | public interface Progress { 18 | 19 | /** 20 | * 请求进度回调 21 | * 22 | * @param totalBytesWritten totalBytesWritten 23 | * @param totalBytesExpectedToWrite totalBytesExpectedToWrite 24 | */ 25 | void progress(long totalBytesWritten, long totalBytesExpectedToWrite); 26 | } 27 | 28 | /** 29 | * 请求完成回调 30 | */ 31 | public interface CompleteHandler { 32 | 33 | /** 34 | * 请求完成回调 35 | * 36 | * @param responseInfo 请求响应信息 37 | * @param metrics 请求指标 38 | * @param response 请求响应信息 39 | */ 40 | void complete(ResponseInfo responseInfo, UploadSingleRequestMetrics metrics, JSONObject response); 41 | } 42 | 43 | /** 44 | * 构造函数 45 | */ 46 | protected IRequestClient() { 47 | } 48 | 49 | /** 50 | * 触发请求 51 | * 52 | * @param request 请求信息 53 | * @param options 可选信息 54 | * @param progress 进度回调 55 | * @param complete 完成回调 56 | */ 57 | public abstract void request(Request request, 58 | Options options, 59 | Progress progress, 60 | CompleteHandler complete); 61 | 62 | /** 63 | * 取消 64 | */ 65 | public abstract void cancel(); 66 | 67 | /** 68 | * 获取 ClientId 69 | * 70 | * @return ClientId 71 | */ 72 | public String getClientId() { 73 | return "customized"; 74 | } 75 | 76 | /** 77 | * 可选信息 78 | */ 79 | public static class Options { 80 | 81 | /** 82 | * 上传请求的 Server 83 | */ 84 | public final IUploadServer server; 85 | 86 | /** 87 | * 是否使用异步 88 | */ 89 | public final boolean isAsync; 90 | 91 | /** 92 | * 请求的代理 93 | */ 94 | public final ProxyConfiguration connectionProxy; 95 | 96 | /** 97 | * 构造函数 98 | * 99 | * @param server 上传请求的 Server 100 | * @param isAsync 是否使用异步 101 | * @param connectionProxy 请求的代理 102 | */ 103 | public Options(IUploadServer server, boolean isAsync, ProxyConfiguration connectionProxy) { 104 | this.server = server; 105 | this.isAsync = isAsync; 106 | this.connectionProxy = connectionProxy; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/request/IUploadRegion.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.request; 2 | 3 | import com.qiniu.android.common.ZoneInfo; 4 | import com.qiniu.android.http.ResponseInfo; 5 | 6 | /** 7 | * 上传区域 8 | * 9 | * @hidden 10 | */ 11 | public interface IUploadRegion { 12 | 13 | /** 14 | * 是否有效 15 | * 16 | * @return 是否有效 17 | */ 18 | boolean isValid(); 19 | 20 | /** 21 | * 是否和另一个 region 相等 22 | * 23 | * @param region 另一个 region 24 | * @return 是否和另一个 region 相等 25 | */ 26 | boolean isEqual(IUploadRegion region); 27 | 28 | /** 29 | * 获取 ZoneInfo 30 | * 31 | * @return ZoneInfo 32 | */ 33 | ZoneInfo getZoneInfo(); 34 | 35 | /** 36 | * 配置 ZoneInfo 37 | * 38 | * @param zoneInfo ZoneInfo 39 | */ 40 | void setupRegionData(ZoneInfo zoneInfo); 41 | 42 | /** 43 | * 获取下一个上传的 Server 信息 44 | * 45 | * @param requestState 请求状态 46 | * @param responseInfo 请求响应信息 47 | * @param freezeServer 冻结的 Server 信息 48 | * @return 下一个上传的 Server 信息 49 | */ 50 | IUploadServer getNextServer(UploadRequestState requestState, ResponseInfo responseInfo, IUploadServer freezeServer); 51 | 52 | /** 53 | * 更新 host 的 IP 缓存信息 54 | * 55 | * @param host host 56 | */ 57 | void updateIpListFormHost(String host); 58 | } 59 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/request/IUploadServer.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.request; 2 | 3 | 4 | import java.net.InetAddress; 5 | 6 | /** 7 | * upload server 信息 8 | * 9 | * @hidden 10 | */ 11 | public abstract class IUploadServer { 12 | 13 | /** 14 | * HTTP/1.1 15 | */ 16 | public static String HttpVersion1 = "http_version_1"; 17 | 18 | /** 19 | * HTTP/2 20 | */ 21 | public static String HttpVersion2 = "http_version_2"; 22 | 23 | /** 24 | * HTTP/3 25 | */ 26 | public static String HttpVersion3 = "http_version_3"; 27 | 28 | /** 29 | * 构造函数 30 | */ 31 | protected IUploadServer() { 32 | } 33 | 34 | /** 35 | * 是否使用 HTTP/3 36 | * 37 | * @return 是否使用 HTTP/3 38 | */ 39 | public boolean isHttp3() { 40 | String httpVersion = getHttpVersion(); 41 | if (httpVersion == null) { 42 | return false; 43 | } 44 | return httpVersion.equals(IUploadServer.HttpVersion3); 45 | } 46 | 47 | /** 48 | * 是否使用 HTTP/2 49 | * 50 | * @return 是否使用 HTTP/2 51 | */ 52 | public boolean isHttp2() { 53 | String httpVersion = getHttpVersion(); 54 | if (httpVersion == null) { 55 | return false; 56 | } 57 | return httpVersion.equals(IUploadServer.HttpVersion2); 58 | } 59 | 60 | /** 61 | * 获取 ServerId 62 | * 63 | * @return ServerId 64 | */ 65 | public abstract String getServerId(); 66 | 67 | /** 68 | * 获取 HttpVersion 69 | * 70 | * @return HttpVersion 71 | */ 72 | public abstract String getHttpVersion(); 73 | 74 | /** 75 | * 获取 Host 76 | * 77 | * @return Host 78 | */ 79 | public abstract String getHost(); 80 | 81 | /** 82 | * 获取 IP 83 | * 84 | * @return IP 85 | */ 86 | public abstract String getIp(); 87 | 88 | /** 89 | * 获取 DNS 解析 Source 90 | * 91 | * @return DNS 解析 Source 92 | */ 93 | public abstract String getSource(); 94 | 95 | /** 96 | * 获取 DNS 解析时间戳 97 | * 98 | * @return DNS 解析时间戳 99 | */ 100 | public abstract Long getIpPrefetchedTime(); 101 | 102 | /** 103 | * 获取 DNS 解析信息 104 | * 105 | * @return DNS 解析信息 106 | */ 107 | public InetAddress getInetAddress() { 108 | String ip = getIp(); 109 | String host = getHost(); 110 | if (getHost() == null || ip == null || ip.length() == 0) { 111 | return null; 112 | } 113 | 114 | try { 115 | InetAddress ipAddress = InetAddress.getByName(ip); 116 | return InetAddress.getByAddress(host, ipAddress.getAddress()); 117 | } catch (Exception e) { 118 | return null; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/request/UploadRequestInfo.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.request; 2 | 3 | class UploadRequestInfo { 4 | 5 | static final String RequestTypeUCQuery = "uc_query"; 6 | static final String RequestTypeForm = "form"; 7 | static final String RequestTypeMkblk = "mkblk"; 8 | static final String RequestTypeBput = "bput"; 9 | static final String RequestTypeMkfile = "mkfile"; 10 | static final String RequestTypeInitParts = "init_parts"; 11 | static final String RequestTypeUploadPart = "upload_part"; 12 | static final String RequestTypeCompletePart = "complete_part"; 13 | static final String RequestTypeUpLog = "uplog"; 14 | static final String RequestTypeServerConfig = "server_config"; 15 | static final String RequestTypeServerUserConfig = "server_user_config"; 16 | 17 | String requestType; 18 | String bucket; 19 | String key; 20 | Long fileOffset; 21 | String targetRegionId; 22 | String currentRegionId; 23 | 24 | boolean shouldReportRequestLog(){ 25 | return !requestType.equals(RequestTypeUpLog); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/request/UploadRequestState.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.request; 2 | 3 | /** 4 | * 上传状态 5 | * 6 | * @hidden 7 | */ 8 | public class UploadRequestState { 9 | 10 | private boolean couldUseHttp3; 11 | private boolean isUseOldServer; 12 | private boolean isUserCancel; 13 | 14 | /** 15 | * 构造函数 16 | */ 17 | public UploadRequestState() { 18 | } 19 | 20 | /** 21 | * 是否可以使用 HTTP/3 22 | * 23 | * @return 是否可以使用 HTTP/3 24 | */ 25 | public boolean couldUseHttp3() { 26 | return couldUseHttp3; 27 | } 28 | 29 | void setCouldUseHttp3(boolean couldUseHttp3) { 30 | this.couldUseHttp3 = couldUseHttp3; 31 | } 32 | 33 | boolean isUserCancel() { 34 | return isUserCancel; 35 | } 36 | 37 | void setUserCancel(boolean isUserCancel) { 38 | this.isUserCancel = isUserCancel; 39 | } 40 | 41 | /** 42 | * 是否使用支持 sni 的域名 43 | * 44 | * @return 是否使用支持 sni 的域名 45 | */ 46 | public boolean isUseOldServer() { 47 | return isUseOldServer; 48 | } 49 | 50 | /** 51 | * 设置是否使用支持 sni 的域名 52 | * 53 | * @param useOldServer 是否使用支持 sni 的域名 54 | */ 55 | public void setUseOldServer(boolean useOldServer) { 56 | isUseOldServer = useOldServer; 57 | } 58 | 59 | /** 60 | * clone 61 | * 62 | * @return clone 后的对象 63 | */ 64 | protected UploadRequestState clone() { 65 | UploadRequestState state = new UploadRequestState(); 66 | state.isUseOldServer = isUseOldServer; 67 | state.isUserCancel = isUserCancel; 68 | return state; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/request/handler/CheckCancelHandler.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.request.handler; 2 | 3 | /** 4 | * CheckCancelHandler 5 | * 6 | * @hidden 7 | */ 8 | public interface CheckCancelHandler { 9 | 10 | /** 11 | * 查看是否取消 12 | * 13 | * @return 是否取消 14 | */ 15 | boolean checkCancel(); 16 | } 17 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/request/handler/RequestProgressHandler.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.request.handler; 2 | 3 | /** 4 | * 请求进度回调 5 | * 6 | * @hidden 7 | */ 8 | public interface RequestProgressHandler { 9 | 10 | /** 11 | * 请求进度回调 12 | * 13 | * @param totalBytesWritten 已发送数据大小 14 | * @param totalBytesExpectedToWrite 总数据大小 15 | */ 16 | void progress(long totalBytesWritten, long totalBytesExpectedToWrite); 17 | } 18 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/request/handler/RequestShouldRetryHandler.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.request.handler; 2 | 3 | import com.qiniu.android.http.ResponseInfo; 4 | 5 | import org.json.JSONObject; 6 | 7 | /** 8 | * 请求重试回调 9 | * 10 | * @hidden 11 | */ 12 | public interface RequestShouldRetryHandler { 13 | 14 | /** 15 | * 请求重试回调 16 | * 17 | * @param responseInfo 上次请求的响应信息 18 | * @param response 上次请求的响应信息 19 | * @return 是否可以重试 20 | */ 21 | boolean shouldRetry(ResponseInfo responseInfo, JSONObject response); 22 | } 23 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/request/httpclient/ByteBody.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.request.httpclient; 2 | 3 | import java.io.IOException; 4 | import java.util.Arrays; 5 | 6 | import okhttp3.MediaType; 7 | import okhttp3.RequestBody; 8 | import okio.BufferedSink; 9 | 10 | /** 11 | * Created by yangsen on 2020/6/10 12 | * 13 | * @hidden 14 | */ 15 | public class ByteBody extends RequestBody { 16 | 17 | private static final int SEGMENT_SIZE = 1024 * 16; // okio.Segment.SIZE 18 | 19 | private final MediaType mediaType; 20 | private final byte[] body; 21 | 22 | /** 23 | * 请求体 24 | * 25 | * @param mediaType mediaType 26 | * @param body 请求体 byte 数组 27 | */ 28 | public ByteBody(MediaType mediaType, byte[] body) { 29 | 30 | this.mediaType = mediaType; 31 | this.body = body; 32 | } 33 | 34 | @Override 35 | public MediaType contentType() { 36 | return mediaType; 37 | } 38 | 39 | @Override 40 | public long contentLength() throws IOException { 41 | return body.length; 42 | } 43 | 44 | @Override 45 | public void writeTo(BufferedSink bufferedSink) throws IOException { 46 | 47 | int byteOffset = 0; 48 | int byteSize = SEGMENT_SIZE; 49 | while (byteOffset < body.length) { 50 | byteSize = Math.min(byteSize, body.length - byteOffset); 51 | RequestBody requestBody = getRequestBodyWithRange(byteOffset, byteSize); 52 | requestBody.writeTo(bufferedSink); 53 | bufferedSink.flush(); 54 | 55 | byteOffset += byteSize; 56 | } 57 | } 58 | 59 | 60 | private RequestBody getRequestBodyWithRange(int location, int size) { 61 | byte[] data = Arrays.copyOfRange(body, location, location + size); 62 | return RequestBody.create(contentType(), data); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/request/httpclient/CountingRequestBody.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.request.httpclient; 2 | 3 | import com.qiniu.android.http.CancellationHandler; 4 | import com.qiniu.android.http.ProgressHandler; 5 | import com.qiniu.android.utils.AsyncRun; 6 | 7 | import java.io.IOException; 8 | 9 | import okhttp3.MediaType; 10 | import okhttp3.RequestBody; 11 | import okio.Buffer; 12 | import okio.BufferedSink; 13 | import okio.ForwardingSink; 14 | import okio.Okio; 15 | import okio.Sink; 16 | 17 | /** 18 | * Created by bailong on 16/1/8. 19 | * 20 | * @hidden 21 | */ 22 | public final class CountingRequestBody extends RequestBody { 23 | private static final int SEGMENT_SIZE = 2048; // okio.Segment.SIZE 24 | 25 | 26 | private final RequestBody body; 27 | private final ProgressHandler progress; 28 | private final long totalSize; 29 | private final CancellationHandler cancellationHandler; 30 | 31 | /** 32 | * CountingRequestBody 构造函数 33 | * 34 | * @param body 请求体 35 | * @param progress 请求进度回调 36 | * @param totalSize 请求体总大小 37 | * @param cancellationHandler 取消函数 38 | */ 39 | public CountingRequestBody(RequestBody body, ProgressHandler progress, long totalSize, 40 | CancellationHandler cancellationHandler) { 41 | this.body = body; 42 | this.progress = progress; 43 | this.totalSize = totalSize; 44 | this.cancellationHandler = cancellationHandler; 45 | } 46 | 47 | /** 48 | * 获取请求体大小 49 | * 50 | * @return 请求体大小 51 | * @throws IOException 异常 52 | */ 53 | @Override 54 | public long contentLength() throws IOException { 55 | return body.contentLength(); 56 | } 57 | 58 | /** 59 | * 获取请求 ContentType 60 | * 61 | * @return 请求 ContentType 62 | */ 63 | @Override 64 | public MediaType contentType() { 65 | return body.contentType(); 66 | } 67 | 68 | /** 69 | * 写入数据 70 | * 71 | * @param sink BufferedSink 72 | * @throws IOException 异常 73 | */ 74 | @Override 75 | public void writeTo(BufferedSink sink) throws IOException { 76 | BufferedSink bufferedSink; 77 | 78 | CountingSink countingSink = new CountingSink(sink); 79 | bufferedSink = Okio.buffer(countingSink); 80 | 81 | body.writeTo(bufferedSink); 82 | 83 | bufferedSink.flush(); 84 | } 85 | 86 | /** 87 | * 请求进度 Sink 88 | * 89 | * @hidden 90 | */ 91 | protected final class CountingSink extends ForwardingSink { 92 | 93 | private int bytesWritten = 0; 94 | 95 | /** 96 | * 构造方法 97 | * 98 | * @param delegate Sink 99 | */ 100 | public CountingSink(Sink delegate) { 101 | super(delegate); 102 | } 103 | 104 | /** 105 | * 写入数据 106 | * 107 | * @param source Buffer 108 | * @param byteCount byteCount 109 | * @throws IOException 异常 110 | */ 111 | @Override 112 | public void write(Buffer source, long byteCount) throws IOException { 113 | if (cancellationHandler == null && progress == null) { 114 | super.write(source, byteCount); 115 | return; 116 | } 117 | if (cancellationHandler != null && cancellationHandler.isCancelled()) { 118 | throw new CancellationHandler.CancellationException(); 119 | } 120 | super.write(source, byteCount); 121 | bytesWritten += byteCount; 122 | if (progress != null) { 123 | AsyncRun.runInMain(new Runnable() { 124 | @Override 125 | public void run() { 126 | progress.onProgress(bytesWritten, totalSize); 127 | } 128 | }); 129 | } 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/serverRegion/HttpServerManager.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.serverRegion; 2 | 3 | import com.qiniu.android.utils.Utils; 4 | 5 | import java.util.Locale; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | /** 9 | * HttpServerManager 10 | * 11 | * @hidden 12 | */ 13 | public class HttpServerManager { 14 | private ConcurrentHashMap serversInfo = new ConcurrentHashMap<>(); 15 | private final static HttpServerManager manager = new HttpServerManager(); 16 | 17 | private HttpServerManager() { 18 | } 19 | 20 | /** 21 | * 单例对象 22 | * 23 | * @return 单例对象 24 | */ 25 | public static HttpServerManager getInstance() { 26 | return manager; 27 | } 28 | 29 | /** 30 | * 添加支持 http3 的 Host/ip 组合 31 | * 32 | * @param host 支持 http3 的 Host 33 | * @param ip 支持 http3 Host 对应的 ip 34 | * @param liveDuration 有效时间 单位: 秒 35 | * @return 是否添加 36 | */ 37 | public boolean addHttp3Server(String host, String ip, int liveDuration) { 38 | if (host == null || host.length() == 0 || ip == null || ip.length() == 0 || liveDuration < 0) { 39 | return false; 40 | } 41 | 42 | String type = getServerType(host, ip); 43 | long deadline = Utils.currentSecondTimestamp() + liveDuration; 44 | serversInfo.put(type, deadline); 45 | return true; 46 | } 47 | 48 | /** 49 | * Host/ip 组合是否支持 http3 50 | * 51 | * @param host host 52 | * @param ip ip 53 | * @return 是否支持 http3 54 | */ 55 | public boolean isServerSupportHttp3(String host, String ip) { 56 | if (host == null || host.length() == 0 || ip == null || ip.length() == 0) { 57 | return false; 58 | } 59 | 60 | boolean support = false; 61 | String type = getServerType(host, ip); 62 | Long deadline = serversInfo.get(type); 63 | if (deadline != null && deadline > Utils.currentSecondTimestamp()) { 64 | support = true; 65 | } 66 | return support; 67 | } 68 | 69 | private String getServerType(String host, String ip) { 70 | return String.format(Locale.ENGLISH, "%s:%s", host, ip); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/serverRegion/UploadServer.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.serverRegion; 2 | 3 | import com.qiniu.android.http.request.IUploadServer; 4 | 5 | /** 6 | * 上传服务 7 | * 8 | * @hidden 9 | */ 10 | public class UploadServer extends IUploadServer { 11 | 12 | private final String serverId; 13 | private final String host; 14 | private final String ip; 15 | private final String source; 16 | private final Long ipPrefetchedTime; 17 | private String httpVersion; 18 | 19 | /** 20 | * 构造函数 21 | * 22 | * @param serverId server id 23 | * @param host host 24 | * @param ip ip 25 | * @param source dns 解析源 26 | * @param ipPrefetchedTime dns 解析时间 27 | */ 28 | public UploadServer(String serverId, 29 | String host, 30 | String ip, 31 | String source, 32 | Long ipPrefetchedTime) { 33 | this.serverId = serverId; 34 | this.host = host; 35 | this.ip = ip; 36 | this.source = source; 37 | this.ipPrefetchedTime = ipPrefetchedTime; 38 | } 39 | 40 | /** 41 | * 获取 server id 42 | * 43 | * @return server id 44 | */ 45 | @Override 46 | public String getServerId() { 47 | return this.serverId; 48 | } 49 | 50 | /** 51 | * 获取 HTTP version 52 | * 53 | * @return HTTP version 54 | */ 55 | @Override 56 | public String getHttpVersion() { 57 | return httpVersion; 58 | } 59 | 60 | /** 61 | * 配置 HTTP version 62 | * 63 | * @param httpVersion HTTP version 64 | */ 65 | public void setHttpVersion(String httpVersion) { 66 | this.httpVersion = httpVersion; 67 | } 68 | 69 | /** 70 | * 获取 IP 71 | * 72 | * @return IP 73 | */ 74 | @Override 75 | public String getIp() { 76 | return ip; 77 | } 78 | 79 | /** 80 | * 获取 dns 解析源 81 | * 82 | * @return dns 解析源 83 | */ 84 | @Override 85 | public String getSource() { 86 | return source; 87 | } 88 | 89 | /** 90 | * 获取 dns 解析时间 91 | * 92 | * @return dns 解析时间 93 | */ 94 | @Override 95 | public Long getIpPrefetchedTime() { 96 | return ipPrefetchedTime; 97 | } 98 | 99 | /** 100 | * 获取服务域名 101 | * 102 | * @return 服务域名 103 | */ 104 | @Override 105 | public String getHost() { 106 | return host; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/serverRegion/UploadServerFreezeManager.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.serverRegion; 2 | 3 | import com.qiniu.android.utils.Utils; 4 | 5 | import java.util.Date; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | /** 9 | * Created by yangsen on 2020/6/3 10 | * 11 | * @hidden 12 | */ 13 | public class UploadServerFreezeManager { 14 | 15 | private ConcurrentHashMap frozenInfo = new ConcurrentHashMap<>(); 16 | private final static UploadServerFreezeManager manager = new UploadServerFreezeManager(); 17 | 18 | /** 19 | * 构造函数 20 | */ 21 | public UploadServerFreezeManager() { 22 | } 23 | 24 | /** 25 | * 获取单例 26 | * 27 | * @return 单例 28 | */ 29 | public static UploadServerFreezeManager getInstance() { 30 | return manager; 31 | } 32 | 33 | /** 34 | * 查看 type 是否冻结 35 | * 36 | * @param type type 37 | * @return 是否冻结 38 | */ 39 | public boolean isTypeFrozen(String type) { 40 | if (type == null || type.length() == 0) { 41 | return true; 42 | } 43 | boolean isFrozen = true; 44 | UploadServerFreezeItem item = frozenInfo.get(type); 45 | if (item == null || !item.isFrozenByDate(new Date())) { 46 | isFrozen = false; 47 | } 48 | return isFrozen; 49 | } 50 | 51 | /** 52 | * 冻结 type 53 | * 54 | * @param type type 55 | * @param frozenTime 冻结时间 56 | */ 57 | public void freezeType(String type, int frozenTime) { 58 | if (type == null || type.length() == 0) { 59 | return; 60 | } 61 | UploadServerFreezeItem item = frozenInfo.get(type); 62 | if (item == null) { 63 | item = new UploadServerFreezeItem(type); 64 | frozenInfo.put(type, item); 65 | } 66 | item.freeze(frozenTime); 67 | } 68 | 69 | /** 70 | * 解冻 type 71 | * 72 | * @param type type 73 | */ 74 | public void unfreezeType(String type) { 75 | if (type == null || type.length() == 0) { 76 | return; 77 | } 78 | frozenInfo.remove(type); 79 | } 80 | 81 | private static class UploadServerFreezeItem { 82 | protected final String type; 83 | private Date freezeDate; 84 | 85 | private UploadServerFreezeItem(String type) { 86 | this.type = type; 87 | } 88 | 89 | private synchronized boolean isFrozenByDate(Date date) { 90 | boolean isFrozen = true; 91 | if (freezeDate == null || freezeDate.getTime() < date.getTime()) { 92 | isFrozen = false; 93 | } 94 | return isFrozen; 95 | } 96 | 97 | private synchronized void freeze(int frozenTime) { 98 | freezeDate = new Date(Utils.currentTimestamp() + frozenTime * 1000); 99 | } 100 | 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/http/serverRegion/UploadServerFreezeUtil.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.http.serverRegion; 2 | 3 | import com.qiniu.android.utils.Utils; 4 | 5 | /** 6 | * UploadServerFreezeUtil 7 | * 8 | * @hidden 9 | */ 10 | public class UploadServerFreezeUtil { 11 | private final static UploadServerFreezeManager globalHttp3Freezer = new UploadServerFreezeManager(); 12 | 13 | private UploadServerFreezeUtil() { 14 | } 15 | 16 | /** 17 | * 获取 HTTP/3 冻结管理单例 18 | * 19 | * @return 单例 20 | */ 21 | public static UploadServerFreezeManager globalHttp3Freezer() { 22 | return globalHttp3Freezer; 23 | } 24 | 25 | private final static UploadServerFreezeManager globalHttp2Freezer = new UploadServerFreezeManager(); 26 | 27 | /** 28 | * 获取 HTTP/2 冻结管理单例 29 | * 30 | * @return 单例 31 | */ 32 | public static UploadServerFreezeManager globalHttp2Freezer() { 33 | return globalHttp2Freezer; 34 | } 35 | 36 | /** 37 | * 查看 type 是否被冻结管理者冻结 38 | * 39 | * @param type type 40 | * @param freezeManagerList 结管理者 41 | * @return 是否冻结 42 | */ 43 | public static boolean isTypeFrozenByFreezeManagers(String type, UploadServerFreezeManager[] freezeManagerList) { 44 | if (type == null || type.length() == 0) { 45 | return true; 46 | } 47 | if (freezeManagerList == null || freezeManagerList.length == 0) { 48 | return false; 49 | } 50 | 51 | boolean isFrozen = false; 52 | for (UploadServerFreezeManager freezeManager : freezeManagerList) { 53 | isFrozen = freezeManager.isTypeFrozen(type); 54 | if (isFrozen) { 55 | break; 56 | } 57 | } 58 | return isFrozen; 59 | } 60 | 61 | /** 62 | * 获取 type 63 | * 64 | * @param host host 65 | * @param ip ip 66 | * @return type 67 | */ 68 | public static String getFrozenType(String host, String ip) { 69 | String ipType = Utils.getIpType(ip, host); 70 | return String.format("%s-%s", host, ipType); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/ConcurrentResumeUpload.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | import com.qiniu.android.utils.GroupTaskThread; 4 | import com.qiniu.android.utils.LogUtil; 5 | import com.qiniu.android.utils.StringUtils; 6 | 7 | class ConcurrentResumeUpload extends PartsUpload { 8 | 9 | private GroupTaskThread groupTaskThread; 10 | 11 | protected ConcurrentResumeUpload(UploadSource source, 12 | String key, 13 | UpToken token, 14 | UploadOptions option, 15 | Configuration config, 16 | Recorder recorder, 17 | String recorderKey, 18 | UpTaskCompletionHandler completionHandler) { 19 | super(source, key, token, option, config, recorder, recorderKey, completionHandler); 20 | } 21 | 22 | @Override 23 | protected int prepareToUpload() { 24 | return super.prepareToUpload(); 25 | } 26 | 27 | @Override 28 | protected void uploadRestData(final UploadFileRestDataCompleteHandler completeHandler) { 29 | LogUtil.i("key:" + StringUtils.toNonnullString(key)); 30 | 31 | GroupTaskThread.GroupTaskCompleteHandler taskCompleteHandler = new GroupTaskThread.GroupTaskCompleteHandler() { 32 | @Override 33 | public void complete() { 34 | completeHandler.complete(); 35 | } 36 | }; 37 | 38 | groupTaskThread = new GroupTaskThread(taskCompleteHandler); 39 | for (int i = 0; i < config.concurrentTaskCount; i++) { 40 | groupTaskThread.addTask(new GroupTaskThread.GroupTask() { 41 | @Override 42 | public void run(final GroupTaskThread.GroupTask task) { 43 | performUploadRestData(new UploadFileRestDataCompleteHandler() { 44 | @Override 45 | public void complete() { 46 | task.taskComplete(); 47 | } 48 | }); 49 | } 50 | }); 51 | } 52 | 53 | groupTaskThread.start(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/FormUpload.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | import com.qiniu.android.http.ResponseInfo; 4 | import com.qiniu.android.http.request.RequestTransaction; 5 | import com.qiniu.android.http.request.handler.RequestProgressHandler; 6 | import com.qiniu.android.http.metrics.UploadRegionRequestMetrics; 7 | import com.qiniu.android.utils.LogUtil; 8 | import com.qiniu.android.utils.StringUtils; 9 | 10 | import org.json.JSONObject; 11 | 12 | class FormUpload extends BaseUpload { 13 | 14 | private boolean isAsync = true; 15 | private final UpProgress upProgress; 16 | private RequestTransaction uploadTransaction; 17 | 18 | protected FormUpload(byte[] data, 19 | String key, 20 | String fileName, 21 | UpToken token, 22 | UploadOptions option, 23 | Configuration config, 24 | UpTaskCompletionHandler completionHandler) { 25 | super(data, key, fileName, token, option, config, completionHandler); 26 | this.upProgress = new UpProgress(this.option.progressHandler); 27 | } 28 | 29 | @Override 30 | protected void startToUpload() { 31 | super.startToUpload(); 32 | 33 | LogUtil.i("key:" + StringUtils.toNonnullString(key) + " form上传"); 34 | 35 | uploadTransaction = new RequestTransaction(config, option, getTargetRegion(), getCurrentRegion(), key, token); 36 | 37 | RequestProgressHandler progressHandler = new RequestProgressHandler() { 38 | @Override 39 | public void progress(long totalBytesWritten, long totalBytesExpectedToWrite) { 40 | upProgress.progress(key, totalBytesWritten, totalBytesExpectedToWrite); 41 | } 42 | }; 43 | uploadTransaction.uploadFormData(data, fileName, isAsync, progressHandler, new RequestTransaction.RequestCompleteHandler() { 44 | @Override 45 | public void complete(ResponseInfo responseInfo, UploadRegionRequestMetrics requestMetrics, JSONObject response) { 46 | addRegionRequestMetricsOfOneFlow(requestMetrics); 47 | 48 | if (!responseInfo.isOK()) { 49 | if (!switchRegionAndUploadIfNeededWithErrorResponse(responseInfo)) { 50 | completeAction(responseInfo, response); 51 | } 52 | return; 53 | } 54 | 55 | upProgress.notifyDone(key, data.length); 56 | completeAction(responseInfo, response); 57 | } 58 | }); 59 | } 60 | 61 | @Override 62 | String getUpType() { 63 | return UploadUpTypeForm; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/KeyGenerator.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * 本地持久化上传纪录key生成工具 7 | */ 8 | public interface KeyGenerator { 9 | /** 10 | * 根据服务器的 key 和本地文件名生成持久化纪录的 key 11 | * 12 | * @param key 服务器的 key 13 | * @param file 本地文件名 14 | * @return 持久化上传纪录的 key 15 | */ 16 | @Deprecated 17 | String gen(String key, File file); 18 | 19 | /** 20 | * 根据服务器的key和本地文件唯一 ID 生成持久化纪录的key 21 | * 如果开启断点续传功能,请确保持久化纪录的 key 相同的文件一定是同一个 22 | * SDK 断点续传流程: 23 | * 1. 用户调用上传接口上传资源 A 24 | * 2. 根据资源 A 信息调用 {@link KeyGenerator#gen(String, String)} 生成持久化纪录的 key 25 | * 3. 根据生成持久化纪录的 key 获取本地缓存记录,无缓存则直接走新资源上传流程 26 | * 4. 解析缓存记录中的 sourceId 对比当前资源 A 的 sourceId,如果不同则走新资源上传流程 27 | * 5. 对比缓存资源的 size 和待上传资源 A 的 size,如果两个 size 均不为 -1 且不相等, 28 | * 则走新资源上传流程;size 等于 -1 时,资源 A 为 InputStream 且不知道文件流大小,不验证 size 29 | * 6. 断点续传生效,进入断点续传流程 30 | * 31 | * @param key 服务器的key 32 | * @param sourceId 本地文件 ID 33 | * File: fileName + modifyTime 34 | * Uri: fileName + modifyTime 35 | * InputStream: fileName 36 | * @return 持久化上传纪录的key 37 | */ 38 | String gen(String key, String sourceId); 39 | } 40 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/NetReadyHandler.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | /** 4 | * Created by bailong on 16/9/7. 5 | * 6 | * @hidden 7 | */ 8 | public interface NetReadyHandler { 9 | 10 | /** 11 | * 等待网络正常连接 12 | */ 13 | void waitReady(); 14 | } 15 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/Recorder.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | /** 4 | * 定义分片上传时纪录上传进度的接口 5 | */ 6 | public interface Recorder { 7 | 8 | /** 9 | * 新建或更新文件分片上传的进度 10 | * 11 | * @param key 持久化的键 12 | * @param data 持久化的内容 13 | */ 14 | void set(String key, byte[] data); 15 | 16 | /** 17 | * 获取文件分片上传的进度信息 18 | * 19 | * @param key 持久化的键 20 | * @return 对应的信息 21 | */ 22 | byte[] get(String key); 23 | 24 | /** 25 | * 删除文件分片上传的进度文件 26 | * 27 | * @param key 持久化的键 28 | */ 29 | void del(String key); 30 | 31 | /** 32 | * 获取记录的文件名 33 | * 34 | * @return 记录的文件名 35 | */ 36 | String getFileName(); 37 | } 38 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/UpCancellationSignal.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | import com.qiniu.android.http.CancellationHandler; 4 | 5 | /** 6 | * 定义用户取消数据或文件上传的信号 7 | * 用户取消上传时,必须实现的接口 8 | */ 9 | public interface UpCancellationSignal extends CancellationHandler { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/UpCompletionHandler.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | import com.qiniu.android.http.ResponseInfo; 4 | 5 | import org.json.JSONObject; 6 | 7 | /** 8 | * 定义数据或文件上传结束后的处理动作 9 | */ 10 | public interface UpCompletionHandler { 11 | 12 | /** 13 | * 用户自定义的内容上传完成后处理动作必须实现的方法 14 | * 建议用户自己处理异常。若未处理,抛出的异常被直接丢弃。 15 | * 16 | * @param key 文件上传保存名称 17 | * @param info 上传完成返回日志信息 18 | * @param response 上传完成的回复内容 19 | */ 20 | void complete(String key, ResponseInfo info, JSONObject response); 21 | } 22 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/UpProgress.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | import com.qiniu.android.utils.AsyncRun; 4 | import com.qiniu.android.utils.LogUtil; 5 | 6 | class UpProgress { 7 | 8 | private long maxProgressUploadBytes = -1; 9 | private long previousUploadBytes = 0; 10 | private final UpProgressHandler handler; 11 | 12 | UpProgress(UpProgressHandler handler) { 13 | this.handler = handler; 14 | } 15 | 16 | public void progress(final String key, long uploadBytes, final long totalBytes) { 17 | if (handler == null || uploadBytes < 0 || (totalBytes > 0 && uploadBytes > totalBytes)) { 18 | return; 19 | } 20 | 21 | if (totalBytes > 0) { 22 | synchronized (this) { 23 | if (this.maxProgressUploadBytes < 0) { 24 | this.maxProgressUploadBytes = (long) (totalBytes * 0.95); 25 | } 26 | } 27 | 28 | if (uploadBytes > this.maxProgressUploadBytes) { 29 | return; 30 | } 31 | } 32 | 33 | synchronized (this) { 34 | if (uploadBytes > this.previousUploadBytes) { 35 | this.previousUploadBytes = uploadBytes; 36 | } else { 37 | // 不大于之前回调百分比,不再回调 38 | return; 39 | } 40 | } 41 | 42 | notify(key, uploadBytes, totalBytes); 43 | } 44 | 45 | public void notifyDone(final String key, final long totalBytes) { 46 | notify(key, totalBytes, totalBytes); 47 | } 48 | 49 | private void notify(final String key, long uploadBytes, final long totalBytes) { 50 | if (handler == null) { 51 | return; 52 | } 53 | 54 | // 不管什么类型 source, 在资源 EOF 时间,均可以获取到文件的大小 55 | if (handler instanceof UpProgressBytesHandler) { 56 | AsyncRun.runInMain(new Runnable() { 57 | @Override 58 | public void run() { 59 | LogUtil.i("key:" + key + " progress uploadBytes:" + uploadBytes + " totalBytes:" + totalBytes); 60 | ((UpProgressBytesHandler) handler).progress(key, uploadBytes, totalBytes); 61 | } 62 | }); 63 | return; 64 | } 65 | 66 | if (totalBytes <= 0) { 67 | return; 68 | } 69 | 70 | final double notifyPercent = (double) uploadBytes / (double) totalBytes; 71 | AsyncRun.runInMain(new Runnable() { 72 | @Override 73 | public void run() { 74 | LogUtil.i("key:" + key + " progress:" + notifyPercent); 75 | handler.progress(key, notifyPercent); 76 | } 77 | }); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/UpProgressBytesHandler.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | /** 4 | * UpProgressBytesHandler 5 | */ 6 | public interface UpProgressBytesHandler extends UpProgressHandler { 7 | 8 | /** 9 | * 用户自定义进度处理类必须实现的方法 10 | * 注: 11 | * 使用此接口,{@link UpProgressHandler#progress(String, double)} 会无效 12 | * 13 | * @param key 上传文件的保存文件名 14 | * @param uploadBytes 已上传大小 15 | * @param totalBytes 总大小;无法获取大小时为 -1 16 | */ 17 | void progress(String key, long uploadBytes, long totalBytes); 18 | } 19 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/UpProgressHandler.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | /** 4 | * 定义数据或文件上传的进度处理方法 5 | */ 6 | public interface UpProgressHandler { 7 | 8 | /** 9 | * 用户自定义进度处理类必须实现的方法 10 | * 11 | * @param key 上传文件的保存文件名 12 | * @param percent 上传进度,取值范围[0, 1.0] 13 | */ 14 | void progress(String key, double percent); 15 | } 16 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/UploadData.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | import org.json.JSONObject; 4 | 5 | class UploadData { 6 | 7 | final long offset; 8 | final int size; 9 | final int index; 10 | 11 | String md5; 12 | String etag; 13 | 14 | private State state; 15 | private long uploadSize = 0; 16 | 17 | byte[] data; 18 | 19 | UploadData(long offset, int size, int index) { 20 | this.offset = offset; 21 | this.size = size; 22 | this.index = index; 23 | this.state = State.NeedToCheck; 24 | this.uploadSize = 0; 25 | } 26 | 27 | static UploadData dataFromJson(JSONObject jsonObject) throws Exception { 28 | if (jsonObject == null) { 29 | return null; 30 | } 31 | 32 | long offset = jsonObject.getLong("offset"); 33 | int size = jsonObject.getInt("size"); 34 | int index = jsonObject.getInt("index"); 35 | String etag = jsonObject.optString("etag"); 36 | State state = State.state(jsonObject.getInt("state")); 37 | String md5 = jsonObject.optString("md5"); 38 | 39 | UploadData uploadData = new UploadData(offset, size, index); 40 | uploadData.etag = etag; 41 | uploadData.md5 = md5; 42 | uploadData.state = state; 43 | uploadData.uploadSize = 0; 44 | return uploadData; 45 | } 46 | 47 | // 需要上传,但需要检测块信息是否有效 48 | boolean needToUpload() { 49 | switch (state) { 50 | case NeedToCheck: 51 | case WaitToUpload: 52 | return true; 53 | default: 54 | return false; 55 | } 56 | } 57 | 58 | // 是否已经上传 59 | boolean isUploaded() { 60 | return state == State.Complete; 61 | } 62 | 63 | State getState() { 64 | return state; 65 | } 66 | 67 | void updateState(State state) { 68 | switch (state) { 69 | case NeedToCheck: 70 | case WaitToUpload: 71 | case Uploading: 72 | uploadSize = 0; 73 | etag = null; 74 | break; 75 | case Complete: 76 | data = null; 77 | } 78 | this.state = state; 79 | } 80 | 81 | void setUploadSize(long uploadSize) { 82 | this.uploadSize = uploadSize; 83 | } 84 | 85 | long uploadSize() { 86 | return state == State.Complete ? size : uploadSize; 87 | } 88 | 89 | void clearUploadState() { 90 | etag = null; 91 | md5 = null; 92 | state = State.NeedToCheck; 93 | } 94 | 95 | void checkStateAndUpdate() { 96 | if ((state == State.WaitToUpload || state == State.Uploading) && data == null) { 97 | state = State.NeedToCheck; 98 | } 99 | } 100 | 101 | JSONObject toJsonObject() throws Exception { 102 | JSONObject jsonObject = new JSONObject(); 103 | jsonObject.putOpt("offset", offset); 104 | jsonObject.putOpt("size", size); 105 | jsonObject.putOpt("index", index); 106 | jsonObject.putOpt("etag", etag); 107 | jsonObject.putOpt("md5", md5); 108 | jsonObject.putOpt("state", state.intValue()); 109 | return jsonObject; 110 | } 111 | 112 | enum State { 113 | NeedToCheck, // 需要检测数据 114 | WaitToUpload, // 等待上传 115 | Uploading, // 正在上传 116 | Complete; // 上传结束 117 | 118 | private int intValue() { 119 | return this.ordinal(); 120 | } 121 | 122 | private static State state(int value) { 123 | State[] states = State.values(); 124 | if (value < 0 || value >= states.length) { 125 | return NeedToCheck; 126 | } else { 127 | return states[value]; 128 | } 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/UploadInfo.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | import java.io.IOException; 7 | 8 | abstract class UploadInfo { 9 | 10 | private String sourceId; 11 | private long sourceSize = UploadSource.UnknownSourceSize; 12 | protected String fileName = null; 13 | 14 | private UploadSource source; 15 | 16 | UploadInfo(UploadSource source) { 17 | this.source = source; 18 | this.sourceSize = source.getSize(); 19 | this.sourceId = source.getId() != null ? source.getId() : ""; 20 | } 21 | 22 | void setInfoFromJson(JSONObject jsonObject) { 23 | try { 24 | sourceSize = jsonObject.getLong("sourceSize"); 25 | sourceId = jsonObject.optString("sourceId"); 26 | } catch (JSONException ignored) { 27 | } 28 | } 29 | 30 | /** 31 | * 是否可以重新加载文件信息,也即是否可以重新读取信息 32 | * 33 | * @return return 34 | */ 35 | boolean couldReloadSource() { 36 | return source.couldReloadSource(); 37 | } 38 | 39 | /** 40 | * 重新加载文件信息,以便于重新读取 41 | * 42 | * @return 重新加载是否成功 43 | */ 44 | boolean reloadSource() { 45 | return source.reloadSource(); 46 | } 47 | 48 | /** 49 | * 是否为同一个 UploadInfo 50 | * 同一个:source 相同,上传方式相同 51 | */ 52 | boolean isSameUploadInfo(UploadInfo info) { 53 | if (info == null || !sourceId.equals(info.sourceId)) { 54 | return false; 55 | } 56 | 57 | // 检测文件大小,如果能获取到文件大小的话,就进行检测 58 | if (info.sourceSize > UploadSource.UnknownSourceSize && 59 | sourceSize > UploadSource.UnknownSourceSize && 60 | info.sourceSize != sourceSize) { 61 | return false; 62 | } 63 | 64 | return true; 65 | } 66 | 67 | /** 68 | * 获取资源 id 69 | * 70 | * @return 资源 id 71 | */ 72 | String getSourceId() { 73 | return sourceId; 74 | } 75 | 76 | /** 77 | * 获取资源大小 78 | * 79 | * @return 80 | */ 81 | long getSourceSize() { 82 | return source.getSize(); 83 | } 84 | 85 | /** 86 | * 数据源是否有效,为空则无效 87 | * 88 | * @return 是否有效 89 | */ 90 | boolean hasValidResource() { 91 | return source != null; 92 | } 93 | 94 | /** 95 | * 是否有效 96 | * 97 | * @return 是否有效 98 | */ 99 | boolean isValid() { 100 | return hasValidResource(); 101 | } 102 | 103 | /** 104 | * 获取已上传数据的大小 105 | * 106 | * @return 已上传数据的大小 107 | */ 108 | abstract long uploadSize(); 109 | 110 | /** 111 | * 文件内容是否完全上传完毕 112 | * 113 | * @return 是否完全上传完毕 114 | */ 115 | abstract boolean isAllUploaded(); 116 | 117 | /** 118 | * 清楚上传状态 119 | */ 120 | abstract void clearUploadState(); 121 | 122 | /** 123 | * 检查文件状态, 主要处理没有 data 但处于上传状态 124 | */ 125 | abstract void checkInfoStateAndUpdate(); 126 | 127 | /** 128 | * 转 json 129 | * 130 | * @return json 131 | */ 132 | JSONObject toJsonObject() { 133 | JSONObject jsonObject = new JSONObject(); 134 | try { 135 | jsonObject.put("sourceId", sourceId); 136 | jsonObject.put("sourceSize", getSourceSize()); 137 | } catch (JSONException ignore) { 138 | } 139 | return jsonObject; 140 | } 141 | 142 | void close() { 143 | source.close(); 144 | } 145 | 146 | byte[] readData(int dataSize, long dataOffset) throws IOException { 147 | if (source == null) { 148 | throw new IOException("file is not exist"); 149 | } 150 | 151 | byte[] data = null; 152 | synchronized (source) { 153 | data = source.readData(dataSize, dataOffset); 154 | } 155 | if (data != null && (data.length != dataSize || data.length == 0)) { 156 | sourceSize = dataOffset + data.length; 157 | } 158 | return data; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/UploadSource.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | import java.io.IOException; 4 | 5 | abstract class UploadSource { 6 | /** 7 | * 未知大小 8 | */ 9 | static final long UnknownSourceSize = -1; 10 | 11 | /** 12 | * 获取资源唯一标识 13 | * 作为断点续传时判断是否为同一资源的依据之一; 14 | * 如果两个资源的 record key 和 资源唯一标识均相同则认为资源为同一资源,断点续传才会生效 15 | * 注: 16 | * 同一资源的数据必须完全相同,否则上传可能会出现异常 17 | * 18 | * @return 资源修改时间 19 | */ 20 | abstract String getId(); 21 | 22 | /** 23 | * 是否可以重新加载文件信息,也即是否可以重新读取信息 24 | * @return return 25 | */ 26 | abstract boolean couldReloadSource(); 27 | 28 | /** 29 | * 重新加载文件信息,以便于重新读取 30 | * 31 | * @return 重新加载是否成功 32 | */ 33 | abstract boolean reloadSource(); 34 | 35 | /** 36 | * 获取资源文件名 37 | * @return 资源文件名 38 | */ 39 | abstract String getFileName(); 40 | 41 | /** 42 | * 获取资源大小 43 | * 无法获取大小时返回 -1 44 | * 作用: 45 | * 1. 验证资源是否为同一资源 46 | * 2. 计算上传进度 47 | * 48 | * @return 资源大小 49 | */ 50 | abstract long getSize(); 51 | 52 | /** 53 | * 读取数据 54 | * 1. 返回 byte[] 可能为空,但不会为 null; 55 | * 2. 当 byte[] 大小和 dataSize 不同时,则源数据已经读取结束 56 | * 3. 读取异常时抛出 IOException 57 | * 4. 仅支持串行调用,且 dataOffset 依次递增 58 | * 59 | * @param dataSize 数据大小 60 | * @param dataOffset 数据偏移量 61 | * @return 数据 62 | * @throws IOException 异常 63 | */ 64 | abstract byte[] readData(int dataSize, long dataOffset) throws IOException; 65 | 66 | /** 67 | * 关闭流 68 | */ 69 | abstract void close(); 70 | 71 | /** 72 | * 资源类型 73 | */ 74 | abstract String getSourceType(); 75 | } 76 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/UploadSourceFile.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.RandomAccessFile; 6 | 7 | class UploadSourceFile extends UploadSource { 8 | 9 | private Exception readException = null; 10 | private final File file; 11 | private final RandomAccessFile randomAccessFile; 12 | 13 | UploadSourceFile(File file) { 14 | this.file = file; 15 | RandomAccessFile randomAccessFile = null; 16 | try { 17 | randomAccessFile = new RandomAccessFile(file, "r"); 18 | } catch (Exception e) { 19 | readException = e; 20 | } 21 | this.randomAccessFile = randomAccessFile; 22 | } 23 | 24 | @Override 25 | public String getId() { 26 | return getFileName() + "_" + file.lastModified(); 27 | } 28 | 29 | @Override 30 | public boolean couldReloadSource() { 31 | return randomAccessFile != null; 32 | } 33 | 34 | @Override 35 | public boolean reloadSource() { 36 | return true; 37 | } 38 | 39 | @Override 40 | public String getFileName() { 41 | return file.getName(); 42 | } 43 | 44 | @Override 45 | public long getSize() { 46 | return file.length(); 47 | } 48 | 49 | @Override 50 | public byte[] readData(int dataSize, long dataOffset) throws IOException { 51 | if (randomAccessFile == null) { 52 | if (readException != null) { 53 | throw new IOException(readException); 54 | } else { 55 | throw new IOException("file is invalid"); 56 | } 57 | } 58 | 59 | int readSize = 0; 60 | byte[] buffer = new byte[dataSize]; 61 | try { 62 | randomAccessFile.seek(dataOffset); 63 | while (readSize < dataSize) { 64 | int ret = randomAccessFile.read(buffer, readSize, (dataSize - readSize)); 65 | if (ret < 0) { 66 | break; 67 | } 68 | readSize += ret; 69 | } 70 | 71 | if (readSize < dataSize) { 72 | byte[] newBuffer = new byte[readSize]; 73 | System.arraycopy(buffer, 0, newBuffer, 0, readSize); 74 | buffer = newBuffer; 75 | } 76 | } catch (IOException e) { 77 | throw new IOException(e.getMessage()); 78 | } 79 | return buffer; 80 | } 81 | 82 | @Override 83 | public void close() { 84 | if (randomAccessFile != null) { 85 | try { 86 | randomAccessFile.close(); 87 | } catch (IOException e) { 88 | try { 89 | randomAccessFile.close(); 90 | } catch (IOException ignored) { 91 | } 92 | } 93 | } 94 | } 95 | 96 | @Override 97 | String getSourceType() { 98 | return "File"; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/UploadSourceStream.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage; 2 | 3 | import com.qiniu.android.utils.StringUtils; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | class UploadSourceStream extends UploadSource { 8 | 9 | private long readOffset = 0; 10 | 11 | private InputStream inputStream; 12 | private String id; 13 | private boolean hasSize = false; 14 | private long size = UploadSource.UnknownSourceSize; 15 | private String fileName; 16 | 17 | UploadSourceStream(InputStream inputStream) { 18 | this.inputStream = inputStream; 19 | } 20 | 21 | protected InputStream getInputStream() { 22 | return inputStream; 23 | } 24 | 25 | protected void setInputStream(InputStream inputStream) { 26 | this.inputStream = inputStream; 27 | } 28 | 29 | 30 | void setId(String id) { 31 | this.id = id; 32 | } 33 | 34 | @Override 35 | public String getId() { 36 | return !StringUtils.isNullOrEmpty(id) ? id : fileName; 37 | } 38 | 39 | public void setFileName(String fileName) { 40 | this.fileName = fileName; 41 | } 42 | 43 | @Override 44 | public String getFileName() { 45 | return fileName; 46 | } 47 | 48 | @Override 49 | public long getSize() { 50 | if (size > UnknownSourceSize) { 51 | return size; 52 | } else { 53 | return UnknownSourceSize; 54 | } 55 | } 56 | 57 | void setSize(long size) { 58 | this.hasSize = size > 0; 59 | this.size = size; 60 | } 61 | 62 | @Override 63 | public boolean couldReloadSource() { 64 | return false; 65 | } 66 | 67 | @Override 68 | public boolean reloadSource() { 69 | readOffset = 0; 70 | return false; 71 | } 72 | 73 | @Override 74 | public byte[] readData(int dataSize, long dataOffset) throws IOException { 75 | if (inputStream == null) { 76 | throw new IOException("inputStream is empty"); 77 | } 78 | 79 | byte[] buffer = null; 80 | synchronized (this) { 81 | boolean isEOF = false; 82 | while (true) { 83 | if (readOffset == dataOffset) { 84 | int readSize = 0; 85 | buffer = new byte[dataSize]; 86 | while (readSize < dataSize) { 87 | int ret = inputStream.read(buffer, readSize, dataSize - readSize); 88 | if (ret < 0) { 89 | isEOF = true; 90 | break; 91 | } 92 | readSize += ret; 93 | } 94 | 95 | if (readSize < dataSize) { 96 | byte[] newBuffer = new byte[readSize]; 97 | System.arraycopy(buffer, 0, newBuffer, 0, readSize); 98 | buffer = newBuffer; 99 | } 100 | 101 | readOffset += readSize; 102 | if (isEOF) { 103 | size = readOffset; 104 | } 105 | break; 106 | } else if (readOffset < dataOffset) { 107 | readOffset += inputStream.skip(dataOffset - readOffset); 108 | } else { 109 | throw new IOException("read stream data error"); 110 | } 111 | } 112 | } 113 | return buffer; 114 | } 115 | 116 | @Override 117 | public void close() { 118 | } 119 | 120 | @Override 121 | String getSourceType() { 122 | return hasSize ? "Stream:HasSize" : "Stream:NoSize"; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/serverConfig/ServerConfigCache.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage.serverConfig; 2 | 3 | import com.qiniu.android.storage.Recorder; 4 | import com.qiniu.android.utils.Cache; 5 | 6 | class ServerConfigCache { 7 | 8 | private static final String kServerConfigDiskKey = "ServerConfig"; 9 | private static final String kServerUserConfigDiskKey = "ServerUserConfig"; 10 | 11 | private final Cache configCache = new Cache.Builder(ServerConfig.class) 12 | .setVersion("v1") 13 | .builder(); 14 | private final Cache userConfigCache = new Cache.Builder(ServerUserConfig.class) 15 | .setVersion("v1") 16 | .builder(); 17 | ; 18 | 19 | ServerConfigCache() { 20 | } 21 | 22 | ServerConfig getConfig() { 23 | Cache.Object object = this.configCache.cacheForKey(kServerConfigDiskKey); 24 | if (object instanceof ServerConfig) { 25 | return (ServerConfig) object; 26 | } 27 | return null; 28 | } 29 | 30 | void setConfig(ServerConfig config) { 31 | this.configCache.cache(kServerConfigDiskKey, config, true); 32 | } 33 | 34 | ServerUserConfig getUserConfig() { 35 | Cache.Object object = this.userConfigCache.cacheForKey(kServerUserConfigDiskKey); 36 | if (object instanceof ServerUserConfig) { 37 | return (ServerUserConfig) object; 38 | } 39 | return null; 40 | } 41 | 42 | void setUserConfig(ServerUserConfig userConfig) { 43 | this.userConfigCache.cache(kServerUserConfigDiskKey, userConfig, true); 44 | } 45 | 46 | public synchronized void removeConfigCache() { 47 | this.configCache.clearDiskCache(); 48 | this.configCache.clearMemoryCache(); 49 | this.userConfigCache.clearDiskCache(); 50 | this.userConfigCache.clearMemoryCache(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/storage/serverConfig/ServerUserConfig.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.storage.serverConfig; 2 | 3 | import com.qiniu.android.utils.Cache; 4 | import com.qiniu.android.utils.Utils; 5 | 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | /** 10 | * server user config 11 | * 12 | * @hidden 13 | */ 14 | public class ServerUserConfig implements Cache.Object { 15 | 16 | private long timestamp; 17 | private long ttl = 10; 18 | private Boolean http3Enable; 19 | private Boolean networkCheckEnable; 20 | 21 | private JSONObject info; 22 | 23 | /** 24 | * 构造函数 25 | * 26 | * @param info json 数据 27 | */ 28 | public ServerUserConfig(JSONObject info) { 29 | if (info == null) { 30 | return; 31 | } 32 | 33 | this.info = info; 34 | 35 | this.ttl = info.optLong("ttl", 5 * 60); 36 | 37 | if (info.opt("timestamp") != null) { 38 | this.timestamp = info.optLong("timestamp"); 39 | } 40 | if (this.timestamp == 0) { 41 | this.timestamp = Utils.currentSecondTimestamp(); 42 | try { 43 | info.putOpt("timestamp", this.timestamp); 44 | } catch (JSONException ignored) { 45 | } 46 | } 47 | 48 | JSONObject http3 = info.optJSONObject("http3"); 49 | if (http3 != null && http3.opt("enabled") != null) { 50 | this.http3Enable = http3.optBoolean("enabled"); 51 | } 52 | 53 | JSONObject networkCheck = info.optJSONObject("network_check"); 54 | if (networkCheck != null && networkCheck.opt("enabled") != null) { 55 | this.networkCheckEnable = networkCheck.optBoolean("enabled"); 56 | } 57 | } 58 | 59 | /** 60 | * 获取 json 数据 61 | * 62 | * @return json 数据 63 | */ 64 | @Override 65 | public JSONObject toJson() { 66 | return info; 67 | } 68 | 69 | /** 70 | * HTTP/3 是否开启 71 | * 72 | * @return HTTP/3 是否开启 73 | */ 74 | public Boolean getHttp3Enable() { 75 | return http3Enable; 76 | } 77 | 78 | /** 79 | * 网络检测是否开启 80 | * 81 | * @return 网络检测是否开启 82 | */ 83 | public Boolean getNetworkCheckEnable() { 84 | return networkCheckEnable; 85 | } 86 | 87 | /** 88 | * 获取 json 信息 89 | * 90 | * @return json 信息 91 | */ 92 | public JSONObject getInfo() { 93 | return toJson(); 94 | } 95 | 96 | /** 97 | * 配置是否有效 98 | * 99 | * @return 配置是否有效 100 | */ 101 | public boolean isValid() { 102 | return Utils.currentSecondTimestamp() < (this.timestamp + this.ttl); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/AsyncRun.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | import java.util.Timer; 7 | import java.util.TimerTask; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Executors; 10 | import java.util.concurrent.LinkedBlockingQueue; 11 | import java.util.concurrent.ThreadPoolExecutor; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | 15 | /** 16 | * Created by bailong on 14/10/22. 17 | * 18 | * @hidden 19 | */ 20 | public final class AsyncRun { 21 | 22 | private static final Handler mainThreadHandler = new Handler(Looper.getMainLooper()); 23 | 24 | private static int threadPoolSize = 1; 25 | private static int maxThreadPoolSize = 6; 26 | private static ExecutorService executorService = new ThreadPoolExecutor(threadPoolSize, maxThreadPoolSize, 27 | 1000L, TimeUnit.MILLISECONDS, 28 | new LinkedBlockingQueue()); 29 | 30 | private AsyncRun() { 31 | } 32 | 33 | /** 34 | * 主线程执行任务 35 | * 36 | * @param r 执行体 37 | */ 38 | public static void runInMain(Runnable r) { 39 | if (Looper.getMainLooper() == Looper.myLooper()) { 40 | r.run(); 41 | } else { 42 | mainThreadHandler.post(r); 43 | } 44 | } 45 | 46 | /** 47 | * 延迟执行任务 48 | * 49 | * @param delay 延迟执行时间,单位:ms 50 | * @param r 执行体 51 | */ 52 | public static void runInMain(int delay, final Runnable r) { 53 | 54 | delayTimerTask(delay, new TimerTask() { 55 | @Override 56 | public void run() { 57 | mainThreadHandler.post(r); 58 | this.cancel(); 59 | } 60 | }); 61 | } 62 | 63 | /** 64 | * 后台执行任务 65 | * 66 | * @param r 执行体 67 | */ 68 | public static void runInBack(Runnable r) { 69 | executorService.submit(r); 70 | } 71 | 72 | /** 73 | * 延迟执行任务 74 | * 75 | * @param delay 延迟执行时间,单位:ms 76 | * @param r 执行体 77 | */ 78 | public static void runInBack(int delay, final Runnable r) { 79 | 80 | delayTimerTask(delay, new TimerTask() { 81 | @Override 82 | public void run() { 83 | executorService.submit(r); 84 | this.cancel(); 85 | } 86 | }); 87 | } 88 | 89 | private static void delayTimerTask(int delay, TimerTask timerTask) { 90 | Timer timer = new Timer(); 91 | timer.schedule(timerTask, delay); 92 | } 93 | 94 | } 95 | 96 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/BytesUtils.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Bytes 工具 7 | * 8 | * @hidden 9 | */ 10 | public class BytesUtils { 11 | 12 | private BytesUtils() { 13 | } 14 | 15 | /** 16 | * 获取 byte 数组的子数组 17 | * 18 | * @param source 源 byte 数组 19 | * @param from 子数组开始位置 20 | * @param length 子数组长度 21 | * @return 子数组 22 | * @throws IOException 异常 23 | */ 24 | public static byte[] subBytes(byte[] source, int from, int length) throws IOException { 25 | if (length + from > source.length) { 26 | throw new IOException("copy bytes out of range"); 27 | } 28 | 29 | byte[] buffer = new byte[length]; 30 | System.arraycopy(source, from, buffer, 0, length); 31 | return buffer; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/Constants.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | /** 4 | * 常量定义 5 | */ 6 | public class Constants { 7 | 8 | /** 9 | * Unknown network class 10 | */ 11 | public static final String NETWORK_CLASS_UNKNOWN = "none"; 12 | 13 | /** 14 | * wifi net work 15 | */ 16 | public static final String NETWORK_WIFI = "wifi"; 17 | 18 | /** 19 | * "2G" networks 20 | */ 21 | public static final String NETWORK_CLASS_2_G = "2g"; 22 | 23 | /** 24 | * "3G" networks 25 | */ 26 | public static final String NETWORK_CLASS_3_G = "3g"; 27 | 28 | /** 29 | * "4G" networks 30 | */ 31 | public static final String NETWORK_CLASS_4_G = "4g"; 32 | 33 | /** 34 | * "5G" networks 35 | */ 36 | public static final String NETWORK_CLASS_5_G = "5g"; 37 | 38 | private Constants() { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/ContextGetter.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.qiniu.android.storage.GlobalConfiguration; 7 | 8 | /** 9 | * Created by bailong on 16/9/7. 10 | * 11 | * @hidden 12 | */ 13 | public final class ContextGetter { 14 | private static Context context = applicationContext(); 15 | 16 | private ContextGetter() { 17 | } 18 | 19 | /** 20 | * application Context 21 | * 22 | * @return Context 23 | */ 24 | public static Context applicationContext() { 25 | if (context != null) { 26 | return context; 27 | } 28 | 29 | if (GlobalConfiguration.appContext != null) { 30 | return GlobalConfiguration.appContext; 31 | } 32 | 33 | Application app = getApplicationUsingReflection(); 34 | if (app != null) { 35 | context = app.getApplicationContext(); 36 | } 37 | return context; 38 | } 39 | 40 | private static Application getApplicationUsingReflection() { 41 | Application app = null; 42 | try { 43 | Class activity = Class.forName("android.app.ActivityThread"); 44 | app = (Application) activity.getMethod("currentApplication").invoke(null, (Object[]) null); 45 | 46 | if (app == null) { 47 | Object localObject = activity.getMethod("currentActivityThread", new Class[0]).invoke(null, (Object[]) null); 48 | app = (Application) activity.getMethod("getApplication").invoke(localObject, (Object[]) null); 49 | } 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | 54 | return app; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/Crc32.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.IOException; 6 | import java.util.zip.CRC32; 7 | 8 | /** 9 | * 计算文件或二进制数据的crc32校验码 10 | * 11 | * @hidden 12 | */ 13 | public final class Crc32 { 14 | 15 | private Crc32() { 16 | } 17 | 18 | /** 19 | * 计算二进制字节校验码 20 | * 21 | * @param data 二进制数据 22 | * @param offset 起始字节索引 23 | * @param length 校验字节长度 24 | * @return 校验码 25 | */ 26 | public static long bytes(byte[] data, int offset, int length) { 27 | CRC32 crc32 = new CRC32(); 28 | crc32.update(data, offset, length); 29 | return crc32.getValue(); 30 | } 31 | 32 | /** 33 | * 计算二进制字节校验码 34 | * 35 | * @param data 二进制数据 36 | * @return 校验码 37 | */ 38 | public static long bytes(byte[] data) { 39 | return bytes(data, 0, data.length); 40 | } 41 | 42 | /** 43 | * 对文件内容计算crc32校验码 44 | * 45 | * @param f 需要计算crc32校验码的文件 46 | * @return crc校验码 47 | * @throws IOException 读取文件异常 48 | */ 49 | public static long file(File f) throws IOException { 50 | FileInputStream fi = new FileInputStream(f); 51 | byte[] buff = new byte[64 * 1024]; 52 | int len; 53 | CRC32 crc32 = new CRC32(); 54 | try { 55 | while ((len = fi.read(buff)) != -1) { 56 | crc32.update(buff, 0, len); 57 | } 58 | } catch (Exception e) { 59 | e.printStackTrace(); 60 | } finally { 61 | fi.close(); 62 | } 63 | 64 | return crc32.getValue(); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/GZipUtil.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.util.zip.GZIPInputStream; 7 | import java.util.zip.GZIPOutputStream; 8 | 9 | /** 10 | * GZip 工具 11 | * 12 | * @hidden 13 | */ 14 | public class GZipUtil { 15 | 16 | private GZipUtil() { 17 | } 18 | 19 | /** 20 | * 压缩字符串 21 | * 22 | * @param string 带压缩数据 23 | * @return 压缩后的数据 24 | */ 25 | public static byte[] gZip(String string) { 26 | if (string == null) { 27 | return null; 28 | } 29 | return gZip(string.getBytes()); 30 | } 31 | 32 | /** 33 | * 压缩 byte 数组 34 | * 35 | * @param bytes 带压缩数据 36 | * @return 压缩后的数据 37 | */ 38 | public static byte[] gZip(byte[] bytes) { 39 | if (bytes == null) { 40 | return null; 41 | } 42 | if (bytes.length == 0) { 43 | return bytes; 44 | } 45 | 46 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 47 | GZIPOutputStream gzip = null; 48 | try { 49 | gzip = new GZIPOutputStream(out); 50 | gzip.write(bytes); 51 | } catch (IOException e) { 52 | } finally { 53 | if (gzip != null) { 54 | try { 55 | gzip.close(); 56 | } catch (IOException e) { 57 | } 58 | } 59 | } 60 | 61 | return out.toByteArray(); 62 | } 63 | 64 | /** 65 | * 解压缩 byte 数组 66 | * 67 | * @param bytes 待解压缩数据 68 | * @return 解压缩后的数据 69 | */ 70 | public static byte[] gUnzip(byte[] bytes) { 71 | if (bytes == null) { 72 | return null; 73 | } 74 | if (bytes.length == 0) { 75 | return bytes; 76 | } 77 | 78 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 79 | ByteArrayInputStream in = new ByteArrayInputStream(bytes); 80 | 81 | GZIPInputStream gunzip = null; 82 | try { 83 | gunzip = new GZIPInputStream(in); 84 | byte[] buffer = new byte[256]; 85 | int n; 86 | while ((n = gunzip.read(buffer)) >= 0) { 87 | out.write(buffer, 0, n); 88 | } 89 | } catch (IOException e) { 90 | } finally { 91 | if (gunzip != null) { 92 | try { 93 | gunzip.close(); 94 | } catch (IOException e) { 95 | } 96 | } 97 | } 98 | 99 | return out.toByteArray(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/Json.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import org.json.JSONArray; 4 | import org.json.JSONObject; 5 | 6 | import java.util.Collection; 7 | import java.util.Map; 8 | 9 | /** 10 | * Created by long on 2017/7/25. 11 | * 12 | * @hidden 13 | */ 14 | public final class Json { 15 | 16 | private Json() { 17 | } 18 | 19 | /** 20 | * map 转 json 21 | * 22 | * @param map map 23 | * @return json string 24 | */ 25 | public static String encodeMap(Map map) { 26 | JSONObject obj = new JSONObject(map); 27 | return obj.toString(); 28 | } 29 | 30 | /** 31 | * Collection 转 json 32 | * 33 | * @param collection Collection 34 | * @return json string 35 | */ 36 | public static String encodeList(Collection collection) { 37 | JSONArray array = new JSONArray(collection); 38 | return array.toString(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/ListUtils.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * List 工具类 7 | */ 8 | public class ListUtils { 9 | 10 | /** 11 | * 判断 List 是否为空 12 | * 13 | * @param objects List 14 | * @return 是否为空 15 | * @param List 中的元素类型 16 | */ 17 | public static boolean isEmpty(List objects) { 18 | return objects == null || objects.isEmpty(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/ListVector.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collection; 5 | import java.util.List; 6 | import java.util.Vector; 7 | import java.util.concurrent.CopyOnWriteArrayList; 8 | 9 | /** 10 | * ListVector 11 | * 12 | * @param 元素类型 13 | * @hidden 14 | */ 15 | public class ListVector extends Vector { 16 | 17 | /** 18 | * 构造函数 19 | */ 20 | public ListVector() { 21 | super(); 22 | } 23 | 24 | /** 25 | * 构造函数 26 | * 27 | * @param initialCapacity initialCapacity 28 | * @param capacityIncrement capacityIncrement 29 | */ 30 | public ListVector(int initialCapacity, int capacityIncrement) { 31 | super(initialCapacity, capacityIncrement); 32 | } 33 | 34 | /** 35 | * 对象遍历 36 | * 37 | * @param handler handler 38 | */ 39 | public synchronized void enumerateObjects(EnumeratorHandler handler) { 40 | if (handler == null) { 41 | return; 42 | } 43 | 44 | final E[] elementData = (E[]) this.elementData; 45 | final int elementCount = this.elementCount; 46 | for (int i = 0; i < elementCount; i++) { 47 | if (handler.enumerate(elementData[i])) { 48 | break; 49 | } 50 | } 51 | } 52 | 53 | /** 54 | * create subList 55 | * 56 | * @param fromIndex low endpoint (inclusive) of the subList 57 | * @param toIndex high endpoint (exclusive) of the subList 58 | * @return subList 59 | */ 60 | @Override 61 | public synchronized ListVector subList(int fromIndex, int toIndex) { 62 | ListVector listVector = new ListVector(); 63 | // c.toArray might (incorrectly) not return Object[] (see 6260652) 64 | if (elementData.getClass() != Object[].class) { 65 | listVector.elementData = Arrays.copyOf(elementData, elementCount, Object[].class); 66 | listVector.elementCount = listVector.elementData.length; 67 | } else { 68 | listVector.elementData = Arrays.copyOf(elementData, elementCount); 69 | listVector.elementCount = elementCount; 70 | } 71 | return listVector; 72 | } 73 | 74 | /** 75 | * EnumeratorHandler 76 | * 77 | * @param enumerate 对象的类型 78 | * @hidden 79 | */ 80 | public interface EnumeratorHandler { 81 | 82 | /** 83 | * enumerate 84 | * 85 | * @param t T 86 | * @return stop 87 | */ 88 | boolean enumerate(T t); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/MD5.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import java.security.MessageDigest; 4 | 5 | import com.qiniu.android.dns.util.Hex; 6 | 7 | /** 8 | * MD5 util 9 | * 10 | * @hidden 11 | */ 12 | public class MD5 { 13 | 14 | private MD5() { 15 | } 16 | 17 | /** 18 | * MD5 加密 19 | * 20 | * @param data 带加密数据 21 | * @return 已加密数据 22 | */ 23 | public static String encrypt(byte[] data) { 24 | try { 25 | MessageDigest digest = MessageDigest.getInstance("MD5"); 26 | digest.update(data); 27 | return Hex.encodeHexString(digest.digest()); 28 | } catch (Exception e) { 29 | e.printStackTrace(); 30 | } 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/MapUtils.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * Map 工具类 7 | */ 8 | public class MapUtils { 9 | 10 | /** 11 | * 判断 Map 是否为空 12 | * 13 | * @param objects Map 14 | * @return 是否为空 15 | * @param key 类型 16 | * @param value 类型 17 | */ 18 | public static boolean isEmpty(Map objects) { 19 | return objects == null || objects.isEmpty(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/UrlSafeBase64.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import android.util.Base64; 4 | 5 | import com.qiniu.android.common.Constants; 6 | 7 | import java.io.UnsupportedEncodingException; 8 | 9 | /** 10 | * URL安全的Base64编码和解码 11 | * 12 | * @hidden 13 | */ 14 | public final class UrlSafeBase64 { 15 | 16 | private UrlSafeBase64() { 17 | } 18 | 19 | /** 20 | * 编码字符串 21 | * 22 | * @param data 待编码字符串 23 | * @return 结果字符串 24 | */ 25 | public static String encodeToString(String data) { 26 | try { 27 | return encodeToString(data.getBytes(Constants.UTF_8)); 28 | } catch (UnsupportedEncodingException e) { 29 | //never in 30 | e.printStackTrace(); 31 | } 32 | return null; 33 | } 34 | 35 | /** 36 | * 编码数据 37 | * 38 | * @param data 字节数组 39 | * @return 结果字符串 40 | */ 41 | public static String encodeToString(byte[] data) { 42 | return Base64.encodeToString(data, Base64.URL_SAFE | Base64.NO_WRAP); 43 | } 44 | 45 | /** 46 | * 解码数据 47 | * 48 | * @param data 编码过的字符串 49 | * @return 原始数据 50 | */ 51 | public static byte[] decode(String data) { 52 | try { 53 | return Base64.decode(data, Base64.URL_SAFE | Base64.NO_WRAP); 54 | } catch (RuntimeException e) { 55 | return null; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/UrlUtils.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | /** 4 | * UrlUtils 5 | * 6 | * @hidden 7 | */ 8 | public class UrlUtils { 9 | 10 | private UrlUtils() { 11 | } 12 | 13 | /** 14 | * 移除 host 中的 scheme 15 | * 16 | * @param host host 17 | * @return host 18 | */ 19 | public static String removeHostScheme(String host) { 20 | if (host == null || StringUtils.isNullOrEmpty(host)) { 21 | return null; 22 | } 23 | 24 | host = host.replace("http://", ""); 25 | host = host.replace("https://", ""); 26 | return host; 27 | } 28 | 29 | 30 | /** 31 | * 如果 host 包含 scheme 则优先使用 host 中包含的 scheme 32 | * 如果 host 不包含 scheme 则按照 useHttps 增加 scheme 33 | * 34 | * @param host host 35 | * @param useHttps 是否使用 https 请求 36 | * @return url 37 | */ 38 | public static String setHostScheme(String host, boolean useHttps) { 39 | if (StringUtils.isNullOrEmpty(host)) { 40 | return null; 41 | } 42 | 43 | if (host.startsWith("http://") || host.startsWith("https://")) { 44 | return host; 45 | } 46 | 47 | return (useHttps ? "https://" : "http://") + host; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /library/src/main/java/com/qiniu/android/utils/Wait.java: -------------------------------------------------------------------------------- 1 | package com.qiniu.android.utils; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | 5 | /** 6 | * wait 7 | * 8 | * @hidden 9 | */ 10 | public class Wait { 11 | 12 | final CountDownLatch completeSingle = new CountDownLatch(1); 13 | 14 | /** 15 | * 构造函数 16 | */ 17 | public Wait() { 18 | } 19 | 20 | /** 21 | * 开始等待 22 | */ 23 | public void startWait() { 24 | while (completeSingle.getCount() > 0) { 25 | try { 26 | completeSingle.await(); 27 | break; 28 | } catch (InterruptedException e) { 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * 停止等待 35 | */ 36 | public void stopWait() { 37 | completeSingle.countDown(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | qiniu-sdk 3 | 4 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "android-sdk" 2 | include ':library' 3 | --------------------------------------------------------------------------------