├── .github
├── ISSUE_TEMPLATE
│ └── ----.md
└── workflows
│ └── main.yml
├── .gitignore
├── LICENSE
├── README.md
├── README_EN.md
├── build.gradle
├── docs
├── api
│ ├── -net
│ │ ├── androidx.lifecycle
│ │ │ ├── index.html
│ │ │ ├── life.html
│ │ │ ├── scope-life.html
│ │ │ └── scope-net-life.html
│ │ ├── com.drake.net.body
│ │ │ ├── -net-request-body
│ │ │ │ ├── -net-request-body.html
│ │ │ │ ├── content-length.html
│ │ │ │ ├── content-type.html
│ │ │ │ ├── index.html
│ │ │ │ └── write-to.html
│ │ │ ├── -net-response-body
│ │ │ │ ├── -net-response-body.html
│ │ │ │ ├── content-length.html
│ │ │ │ ├── content-type.html
│ │ │ │ ├── index.html
│ │ │ │ └── source.html
│ │ │ ├── file-name.html
│ │ │ ├── index.html
│ │ │ ├── name.html
│ │ │ ├── peek-bytes.html
│ │ │ ├── to-net-request-body.html
│ │ │ ├── to-net-response-body.html
│ │ │ └── value.html
│ │ ├── com.drake.net.cache
│ │ │ ├── -cache-mode
│ │ │ │ ├── -r-e-a-d
│ │ │ │ │ └── index.html
│ │ │ │ ├── -r-e-a-d_-t-h-e-n_-r-e-q-u-e-s-t
│ │ │ │ │ └── index.html
│ │ │ │ ├── -r-e-q-u-e-s-t_-t-h-e-n_-r-e-a-d
│ │ │ │ │ └── index.html
│ │ │ │ ├── -w-r-i-t-e
│ │ │ │ │ └── index.html
│ │ │ │ └── index.html
│ │ │ ├── -force-cache
│ │ │ │ ├── -companion
│ │ │ │ │ ├── has-vary-all.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── key.html
│ │ │ │ │ ├── vary-headers.html
│ │ │ │ │ └── vary-matches.html
│ │ │ │ ├── cache.html
│ │ │ │ ├── close.html
│ │ │ │ ├── delete.html
│ │ │ │ ├── directory.html
│ │ │ │ ├── evict-all.html
│ │ │ │ ├── flush.html
│ │ │ │ ├── index.html
│ │ │ │ ├── initialize.html
│ │ │ │ ├── is-closed.html
│ │ │ │ ├── max-size.html
│ │ │ │ ├── size.html
│ │ │ │ ├── urls.html
│ │ │ │ ├── write-abort-count.html
│ │ │ │ └── write-success-count.html
│ │ │ └── index.html
│ │ ├── com.drake.net.component
│ │ │ ├── -progress
│ │ │ │ ├── -progress.html
│ │ │ │ ├── current-byte-count.html
│ │ │ │ ├── current-size.html
│ │ │ │ ├── finish.html
│ │ │ │ ├── index.html
│ │ │ │ ├── interval-byte-count.html
│ │ │ │ ├── interval-time.html
│ │ │ │ ├── progress.html
│ │ │ │ ├── remain-size.html
│ │ │ │ ├── remain-time-seconds.html
│ │ │ │ ├── remain-time.html
│ │ │ │ ├── speed-bytes.html
│ │ │ │ ├── speed-size.html
│ │ │ │ ├── start-elapsed-realtime.html
│ │ │ │ ├── to-string.html
│ │ │ │ ├── total-byte-count.html
│ │ │ │ ├── total-size.html
│ │ │ │ ├── use-time-seconds.html
│ │ │ │ └── use-time.html
│ │ │ └── index.html
│ │ ├── com.drake.net.convert
│ │ │ ├── -j-s-o-n-convert
│ │ │ │ ├── -j-s-o-n-convert.html
│ │ │ │ ├── code.html
│ │ │ │ ├── index.html
│ │ │ │ ├── message.html
│ │ │ │ ├── on-convert.html
│ │ │ │ ├── parse-body.html
│ │ │ │ └── success.html
│ │ │ ├── -net-converter
│ │ │ │ ├── -d-e-f-a-u-l-t
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── on-convert.html
│ │ │ │ ├── index.html
│ │ │ │ └── on-convert.html
│ │ │ └── index.html
│ │ ├── com.drake.net.cookie
│ │ │ ├── -persistent-cookie-jar
│ │ │ │ ├── -persistent-cookie-jar.html
│ │ │ │ ├── add-all.html
│ │ │ │ ├── clear.html
│ │ │ │ ├── context.html
│ │ │ │ ├── db-name.html
│ │ │ │ ├── get-all.html
│ │ │ │ ├── index.html
│ │ │ │ ├── load-for-request.html
│ │ │ │ ├── remove.html
│ │ │ │ └── save-from-response.html
│ │ │ └── index.html
│ │ ├── com.drake.net.exception
│ │ │ ├── -convert-exception
│ │ │ │ ├── -convert-exception.html
│ │ │ │ ├── index.html
│ │ │ │ └── tag.html
│ │ │ ├── -download-file-exception
│ │ │ │ ├── -download-file-exception.html
│ │ │ │ ├── index.html
│ │ │ │ └── tag.html
│ │ │ ├── -http-failure-exception
│ │ │ │ ├── -http-failure-exception.html
│ │ │ │ └── index.html
│ │ │ ├── -http-response-exception
│ │ │ │ ├── -http-response-exception.html
│ │ │ │ ├── index.html
│ │ │ │ └── response.html
│ │ │ ├── -net-cancellation-exception.html
│ │ │ ├── -net-cancellation-exception
│ │ │ │ ├── -net-cancellation-exception.html
│ │ │ │ └── index.html
│ │ │ ├── -net-connect-exception
│ │ │ │ ├── -net-connect-exception.html
│ │ │ │ └── index.html
│ │ │ ├── -net-exception
│ │ │ │ ├── -net-exception.html
│ │ │ │ ├── get-localized-message.html
│ │ │ │ ├── index.html
│ │ │ │ ├── occurred.html
│ │ │ │ └── request.html
│ │ │ ├── -net-socket-timeout-exception
│ │ │ │ ├── -net-socket-timeout-exception.html
│ │ │ │ └── index.html
│ │ │ ├── -net-unknown-host-exception
│ │ │ │ ├── -net-unknown-host-exception.html
│ │ │ │ └── index.html
│ │ │ ├── -networking-exception
│ │ │ │ ├── -networking-exception.html
│ │ │ │ └── index.html
│ │ │ ├── -no-cache-exception
│ │ │ │ ├── -no-cache-exception.html
│ │ │ │ ├── get-localized-message.html
│ │ │ │ └── index.html
│ │ │ ├── -request-params-exception
│ │ │ │ ├── -request-params-exception.html
│ │ │ │ ├── index.html
│ │ │ │ └── tag.html
│ │ │ ├── -response-exception
│ │ │ │ ├── -response-exception.html
│ │ │ │ ├── index.html
│ │ │ │ └── tag.html
│ │ │ ├── -server-response-exception
│ │ │ │ ├── -server-response-exception.html
│ │ │ │ ├── index.html
│ │ │ │ └── tag.html
│ │ │ ├── -u-r-l-parse-exception
│ │ │ │ ├── -u-r-l-parse-exception.html
│ │ │ │ ├── get-localized-message.html
│ │ │ │ ├── index.html
│ │ │ │ └── occurred.html
│ │ │ └── index.html
│ │ ├── com.drake.net.interceptor
│ │ │ ├── -log-record-interceptor
│ │ │ │ ├── -log-record-interceptor.html
│ │ │ │ ├── enabled.html
│ │ │ │ ├── index.html
│ │ │ │ ├── intercept.html
│ │ │ │ ├── request-byte-count.html
│ │ │ │ └── response-byte-count.html
│ │ │ ├── -net-ok-http-interceptor
│ │ │ │ ├── index.html
│ │ │ │ └── intercept.html
│ │ │ ├── -request-interceptor
│ │ │ │ ├── index.html
│ │ │ │ └── interceptor.html
│ │ │ ├── -retry-interceptor
│ │ │ │ ├── -retry-interceptor.html
│ │ │ │ ├── index.html
│ │ │ │ ├── intercept.html
│ │ │ │ └── retry-count.html
│ │ │ └── index.html
│ │ ├── com.drake.net.interfaces
│ │ │ ├── -net-dialog-factory
│ │ │ │ ├── -d-e-f-a-u-l-t
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── on-create.html
│ │ │ │ ├── index.html
│ │ │ │ └── on-create.html
│ │ │ ├── -net-error-handler
│ │ │ │ ├── -d-e-f-a-u-l-t
│ │ │ │ │ └── index.html
│ │ │ │ ├── index.html
│ │ │ │ ├── on-error.html
│ │ │ │ └── on-state-error.html
│ │ │ ├── -progress-listener
│ │ │ │ ├── -progress-listener.html
│ │ │ │ ├── elapsed-time.html
│ │ │ │ ├── index.html
│ │ │ │ ├── interval-byte-count.html
│ │ │ │ ├── interval.html
│ │ │ │ └── on-progress.html
│ │ │ └── index.html
│ │ ├── com.drake.net.log
│ │ │ ├── -log-recorder
│ │ │ │ ├── enabled.html
│ │ │ │ ├── generate-id.html
│ │ │ │ ├── index.html
│ │ │ │ ├── record-exception.html
│ │ │ │ ├── record-request.html
│ │ │ │ └── record-response.html
│ │ │ ├── -message-type
│ │ │ │ ├── -r-e-q-u-e-s-t_-b-o-d-y
│ │ │ │ │ └── index.html
│ │ │ │ ├── -r-e-q-u-e-s-t_-e-n-d
│ │ │ │ │ └── index.html
│ │ │ │ ├── -r-e-q-u-e-s-t_-h-e-a-d-e-r
│ │ │ │ │ └── index.html
│ │ │ │ ├── -r-e-q-u-e-s-t_-m-e-t-h-o-d
│ │ │ │ │ └── index.html
│ │ │ │ ├── -r-e-q-u-e-s-t_-t-i-m-e
│ │ │ │ │ └── index.html
│ │ │ │ ├── -r-e-q-u-e-s-t_-u-r-l
│ │ │ │ │ └── index.html
│ │ │ │ ├── -r-e-s-p-o-n-s-e_-b-o-d-y
│ │ │ │ │ └── index.html
│ │ │ │ ├── -r-e-s-p-o-n-s-e_-e-n-d
│ │ │ │ │ └── index.html
│ │ │ │ ├── -r-e-s-p-o-n-s-e_-e-r-r-o-r
│ │ │ │ │ └── index.html
│ │ │ │ ├── -r-e-s-p-o-n-s-e_-h-e-a-d-e-r
│ │ │ │ │ └── index.html
│ │ │ │ ├── -r-e-s-p-o-n-s-e_-s-t-a-t-u-s
│ │ │ │ │ └── index.html
│ │ │ │ ├── -r-e-s-p-o-n-s-e_-t-i-m-e
│ │ │ │ │ └── index.html
│ │ │ │ ├── -u-n-k-n-o-w-n
│ │ │ │ │ └── index.html
│ │ │ │ ├── index.html
│ │ │ │ └── type.html
│ │ │ └── index.html
│ │ ├── com.drake.net.okhttp
│ │ │ ├── index.html
│ │ │ ├── set-converter.html
│ │ │ ├── set-debug.html
│ │ │ ├── set-dialog-factory.html
│ │ │ ├── set-error-handler.html
│ │ │ ├── set-request-interceptor.html
│ │ │ ├── set-s-s-l-certificate.html
│ │ │ ├── to-net-okhttp.html
│ │ │ └── trust-s-s-l-certificate.html
│ │ ├── com.drake.net.reflect
│ │ │ ├── $-gson$-preconditions
│ │ │ │ ├── check-argument.html
│ │ │ │ ├── check-not-null.html
│ │ │ │ └── index.html
│ │ │ ├── $-gson$-types
│ │ │ │ ├── array-of.html
│ │ │ │ ├── canonicalize.html
│ │ │ │ ├── equals.html
│ │ │ │ ├── get-array-component-type.html
│ │ │ │ ├── get-collection-element-type.html
│ │ │ │ ├── get-map-key-and-value-types.html
│ │ │ │ ├── get-raw-type.html
│ │ │ │ ├── index.html
│ │ │ │ ├── new-parameterized-type-with-owner.html
│ │ │ │ ├── resolve.html
│ │ │ │ ├── subtype-of.html
│ │ │ │ ├── supertype-of.html
│ │ │ │ └── type-to-string.html
│ │ │ ├── -type-token
│ │ │ │ ├── equals.html
│ │ │ │ ├── get-array.html
│ │ │ │ ├── get-parameterized.html
│ │ │ │ ├── get.html
│ │ │ │ ├── hash-code.html
│ │ │ │ ├── index.html
│ │ │ │ ├── raw-type.html
│ │ │ │ ├── to-string.html
│ │ │ │ └── type.html
│ │ │ ├── index.html
│ │ │ └── type-token-of.html
│ │ ├── com.drake.net.request
│ │ │ ├── -base-request
│ │ │ │ ├── -base-request.html
│ │ │ │ ├── add-download-listener.html
│ │ │ │ ├── add-header.html
│ │ │ │ ├── add-query.html
│ │ │ │ ├── build-request.html
│ │ │ │ ├── converter.html
│ │ │ │ ├── enqueue.html
│ │ │ │ ├── execute.html
│ │ │ │ ├── headers.html
│ │ │ │ ├── http-url.html
│ │ │ │ ├── index.html
│ │ │ │ ├── method.html
│ │ │ │ ├── ok-http-client.html
│ │ │ │ ├── ok-http-request.html
│ │ │ │ ├── param.html
│ │ │ │ ├── remove-header.html
│ │ │ │ ├── set-cache-control.html
│ │ │ │ ├── set-cache-key.html
│ │ │ │ ├── set-cache-mode.html
│ │ │ │ ├── set-cache-valid-time.html
│ │ │ │ ├── set-client.html
│ │ │ │ ├── set-download-dir.html
│ │ │ │ ├── set-download-file-name-conflict.html
│ │ │ │ ├── set-download-file-name-decode.html
│ │ │ │ ├── set-download-file-name.html
│ │ │ │ ├── set-download-md5-verify.html
│ │ │ │ ├── set-download-temp-file.html
│ │ │ │ ├── set-extra.html
│ │ │ │ ├── set-group.html
│ │ │ │ ├── set-header.html
│ │ │ │ ├── set-headers.html
│ │ │ │ ├── set-id.html
│ │ │ │ ├── set-k-type.html
│ │ │ │ ├── set-path.html
│ │ │ │ ├── set-query.html
│ │ │ │ ├── set-url.html
│ │ │ │ ├── tag-of.html
│ │ │ │ ├── tag.html
│ │ │ │ └── to-result.html
│ │ │ ├── -body-request
│ │ │ │ ├── -body-request.html
│ │ │ │ ├── add-upload-listener.html
│ │ │ │ ├── body.html
│ │ │ │ ├── build-request.html
│ │ │ │ ├── form-body.html
│ │ │ │ ├── index.html
│ │ │ │ ├── json.html
│ │ │ │ ├── media-type.html
│ │ │ │ ├── method.html
│ │ │ │ ├── param.html
│ │ │ │ └── part-body.html
│ │ │ ├── -media-const
│ │ │ │ ├── -f-o-r-m.html
│ │ │ │ ├── -g-i-f.html
│ │ │ │ ├── -h-t-m-l.html
│ │ │ │ ├── -i-m-g.html
│ │ │ │ ├── -j-p-e-g.html
│ │ │ │ ├── -j-s-o-n.html
│ │ │ │ ├── -m-p4.html
│ │ │ │ ├── -o-c-t-e-t_-s-t-r-e-a-m.html
│ │ │ │ ├── -p-n-g.html
│ │ │ │ ├── -t-x-t.html
│ │ │ │ ├── -u-r-l-e-n-c-o-d-e-d.html
│ │ │ │ ├── -x-m-l.html
│ │ │ │ └── index.html
│ │ │ ├── -method
│ │ │ │ ├── -d-e-l-e-t-e
│ │ │ │ │ └── index.html
│ │ │ │ ├── -g-e-t
│ │ │ │ │ └── index.html
│ │ │ │ ├── -h-e-a-d
│ │ │ │ │ └── index.html
│ │ │ │ ├── -o-p-t-i-o-n-s
│ │ │ │ │ └── index.html
│ │ │ │ ├── -p-a-t-c-h
│ │ │ │ │ └── index.html
│ │ │ │ ├── -p-o-s-t
│ │ │ │ │ └── index.html
│ │ │ │ ├── -p-u-t
│ │ │ │ │ └── index.html
│ │ │ │ ├── -t-r-a-c-e
│ │ │ │ │ └── index.html
│ │ │ │ └── index.html
│ │ │ ├── -url-request
│ │ │ │ ├── -url-request.html
│ │ │ │ ├── index.html
│ │ │ │ └── param.html
│ │ │ ├── converter.html
│ │ │ ├── download-conflict-rename.html
│ │ │ ├── download-file-dir.html
│ │ │ ├── download-file-name-decode.html
│ │ │ ├── download-file-name.html
│ │ │ ├── download-listeners.html
│ │ │ ├── download-md5-verify.html
│ │ │ ├── download-temp-file.html
│ │ │ ├── extra.html
│ │ │ ├── extras.html
│ │ │ ├── group.html
│ │ │ ├── headers.html
│ │ │ ├── id.html
│ │ │ ├── index.html
│ │ │ ├── k-type.html
│ │ │ ├── set-converter.html
│ │ │ ├── set-extra.html
│ │ │ ├── tag-of.html
│ │ │ ├── tags.html
│ │ │ └── upload-listeners.html
│ │ ├── com.drake.net.response
│ │ │ ├── convert.html
│ │ │ ├── file-name.html
│ │ │ ├── file.html
│ │ │ └── index.html
│ │ ├── com.drake.net.scope
│ │ │ ├── -android-scope
│ │ │ │ ├── -android-scope.html
│ │ │ │ ├── cancel.html
│ │ │ │ ├── catch.html
│ │ │ │ ├── close.html
│ │ │ │ ├── coroutine-context.html
│ │ │ │ ├── dispatcher.html
│ │ │ │ ├── finally.html
│ │ │ │ ├── handle-error.html
│ │ │ │ ├── index.html
│ │ │ │ ├── launch.html
│ │ │ │ └── scope-group.html
│ │ │ ├── -dialog-coroutine-scope
│ │ │ │ ├── -dialog-coroutine-scope.html
│ │ │ │ ├── activity.html
│ │ │ │ ├── cancelable.html
│ │ │ │ ├── dialog.html
│ │ │ │ └── index.html
│ │ │ ├── -net-coroutine-scope
│ │ │ │ ├── -net-coroutine-scope.html
│ │ │ │ ├── cancel.html
│ │ │ │ ├── handle-error.html
│ │ │ │ ├── index.html
│ │ │ │ ├── launch.html
│ │ │ │ └── preview.html
│ │ │ ├── -page-coroutine-scope
│ │ │ │ ├── --index--.html
│ │ │ │ ├── -page-coroutine-scope.html
│ │ │ │ ├── handle-error.html
│ │ │ │ ├── index.html
│ │ │ │ └── page.html
│ │ │ ├── -state-coroutine-scope
│ │ │ │ ├── -state-coroutine-scope.html
│ │ │ │ ├── handle-error.html
│ │ │ │ ├── index.html
│ │ │ │ └── state.html
│ │ │ ├── -view-coroutine-scope
│ │ │ │ ├── -view-coroutine-scope.html
│ │ │ │ ├── index.html
│ │ │ │ └── view.html
│ │ │ └── index.html
│ │ ├── com.drake.net.tag
│ │ │ ├── -net-tag
│ │ │ │ ├── -cache-key
│ │ │ │ │ ├── -cache-key.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── value.html
│ │ │ │ ├── -cache-valid-time
│ │ │ │ │ ├── -cache-valid-time.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── value.html
│ │ │ │ ├── -download-file-conflict-rename
│ │ │ │ │ ├── -download-file-conflict-rename.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── value.html
│ │ │ │ ├── -download-file-dir
│ │ │ │ │ ├── -download-file-dir.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── value.html
│ │ │ │ ├── -download-file-m-d5-verify
│ │ │ │ │ ├── -download-file-m-d5-verify.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── value.html
│ │ │ │ ├── -download-file-name-decode
│ │ │ │ │ ├── -download-file-name-decode.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── value.html
│ │ │ │ ├── -download-file-name
│ │ │ │ │ ├── -download-file-name.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── value.html
│ │ │ │ ├── -download-listeners
│ │ │ │ │ ├── -download-listeners.html
│ │ │ │ │ └── index.html
│ │ │ │ ├── -download-temp-file
│ │ │ │ │ ├── -download-temp-file.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── value.html
│ │ │ │ ├── -extras
│ │ │ │ │ ├── -extras.html
│ │ │ │ │ └── index.html
│ │ │ │ ├── -request-group
│ │ │ │ │ ├── -request-group.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── value.html
│ │ │ │ ├── -request-id
│ │ │ │ │ ├── -request-id.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── value.html
│ │ │ │ ├── -request-k-type
│ │ │ │ │ ├── -request-k-type.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── value.html
│ │ │ │ ├── -upload-listeners
│ │ │ │ │ ├── -upload-listeners.html
│ │ │ │ │ └── index.html
│ │ │ │ └── index.html
│ │ │ └── index.html
│ │ ├── com.drake.net.time
│ │ │ ├── -interval-status
│ │ │ │ ├── -s-t-a-t-e_-a-c-t-i-v-e
│ │ │ │ │ └── index.html
│ │ │ │ ├── -s-t-a-t-e_-i-d-l-e
│ │ │ │ │ └── index.html
│ │ │ │ ├── -s-t-a-t-e_-p-a-u-s-e
│ │ │ │ │ └── index.html
│ │ │ │ └── index.html
│ │ │ ├── -interval
│ │ │ │ ├── -interval.html
│ │ │ │ ├── cancel.html
│ │ │ │ ├── close.html
│ │ │ │ ├── count.html
│ │ │ │ ├── end.html
│ │ │ │ ├── finish.html
│ │ │ │ ├── index.html
│ │ │ │ ├── life.html
│ │ │ │ ├── only-resumed.html
│ │ │ │ ├── pause.html
│ │ │ │ ├── reset.html
│ │ │ │ ├── resume.html
│ │ │ │ ├── start.html
│ │ │ │ ├── state.html
│ │ │ │ ├── stop.html
│ │ │ │ ├── subscribe.html
│ │ │ │ └── switch.html
│ │ │ └── index.html
│ │ ├── com.drake.net.transform
│ │ │ ├── -deferred-transform
│ │ │ │ ├── -deferred-transform.html
│ │ │ │ ├── block.html
│ │ │ │ ├── deferred.html
│ │ │ │ └── index.html
│ │ │ ├── index.html
│ │ │ └── transform.html
│ │ ├── com.drake.net.utils
│ │ │ ├── -https
│ │ │ │ ├── -un-safe-hostname-verifier.html
│ │ │ │ ├── -un-safe-trust-manager.html
│ │ │ │ └── index.html
│ │ │ ├── -tip-utils
│ │ │ │ ├── index.html
│ │ │ │ └── toast.html
│ │ │ ├── debounce.html
│ │ │ ├── fastest.html
│ │ │ ├── file-name.html
│ │ │ ├── index.html
│ │ │ ├── is-networking.html
│ │ │ ├── launch-in.html
│ │ │ ├── md5.html
│ │ │ ├── media-type.html
│ │ │ ├── run-main.html
│ │ │ ├── scope-dialog.html
│ │ │ ├── scope-life.html
│ │ │ ├── scope-net-life.html
│ │ │ ├── scope-net.html
│ │ │ ├── scope.html
│ │ │ ├── to-request-body.html
│ │ │ ├── with-default.html
│ │ │ ├── with-i-o.html
│ │ │ ├── with-main.html
│ │ │ └── with-unconfined.html
│ │ ├── com.drake.net
│ │ │ ├── -delete.html
│ │ │ ├── -get.html
│ │ │ ├── -head.html
│ │ │ ├── -net-config
│ │ │ │ ├── -t-a-g.html
│ │ │ │ ├── app.html
│ │ │ │ ├── converter.html
│ │ │ │ ├── debug.html
│ │ │ │ ├── dialog-factory.html
│ │ │ │ ├── error-handler.html
│ │ │ │ ├── host.html
│ │ │ │ ├── index.html
│ │ │ │ ├── initialize.html
│ │ │ │ ├── ok-http-client.html
│ │ │ │ ├── request-interceptor.html
│ │ │ │ └── running-calls.html
│ │ │ ├── -net
│ │ │ │ ├── add-download-listener.html
│ │ │ │ ├── add-upload-listener.html
│ │ │ │ ├── cancel-all.html
│ │ │ │ ├── cancel-group.html
│ │ │ │ ├── cancel-id.html
│ │ │ │ ├── debug.html
│ │ │ │ ├── delete.html
│ │ │ │ ├── get-request-by-group.html
│ │ │ │ ├── get-request-by-id.html
│ │ │ │ ├── get.html
│ │ │ │ ├── head.html
│ │ │ │ ├── index.html
│ │ │ │ ├── options.html
│ │ │ │ ├── patch.html
│ │ │ │ ├── post.html
│ │ │ │ ├── put.html
│ │ │ │ ├── remove-download-listener.html
│ │ │ │ ├── remove-upload-listener.html
│ │ │ │ └── trace.html
│ │ │ ├── -options.html
│ │ │ ├── -patch.html
│ │ │ ├── -post.html
│ │ │ ├── -put.html
│ │ │ ├── -trace.html
│ │ │ └── index.html
│ │ ├── okhttp3
│ │ │ ├── -ok-http-utils
│ │ │ │ ├── add-lenient.html
│ │ │ │ ├── disk-lru-cache.html
│ │ │ │ ├── headers.html
│ │ │ │ ├── index.html
│ │ │ │ └── tags.html
│ │ │ └── index.html
│ │ └── package-list
│ ├── images
│ │ ├── anchor-copy-button.svg
│ │ ├── arrow_down.svg
│ │ ├── copy-icon.svg
│ │ ├── copy-successful-icon.svg
│ │ ├── docs_logo.svg
│ │ ├── footer-go-to-link.svg
│ │ ├── go-to-top-icon.svg
│ │ └── logo-icon.svg
│ ├── index.html
│ ├── navigation.html
│ ├── scripts
│ │ ├── clipboard.js
│ │ ├── main.js
│ │ ├── navigation-loader.js
│ │ ├── navigation-pane.json
│ │ ├── pages.json
│ │ ├── platform-content-handler.js
│ │ └── sourceset_dependencies.js
│ └── styles
│ │ ├── jetbrains-mono.css
│ │ ├── logo-styles.css
│ │ ├── main.css
│ │ └── style.css
├── auto-dialog.md
├── auto-pull.md
├── auto-refresh.md
├── auto-state.md
├── cache.md
├── callback.md
├── cancel.md
├── config.md
├── converter-customize.md
├── converter-struct.md
├── converter.md
├── cookie.md
├── coroutine-request.md
├── css
│ └── extra.css
├── debounce.md
├── download-file.md
├── error-global.md
├── error-single.md
├── error-throws.md
├── error-tip.md
├── error.md
├── fastest.md
├── https.md
├── img
│ ├── book-open.svg
│ ├── code-preview.png
│ ├── discussesions.svg
│ ├── issues.svg
│ ├── logo.gif
│ └── preview.png
├── index.md
├── interceptor.md
├── interval.md
├── issues.md
├── kotlin-serialization.md
├── log-notice.md
├── log-recorder.md
├── material
│ └── partials
│ │ └── footer.html
├── model-generate.md
├── okhttp-client.md
├── progress.md
├── repeat-request.md
├── request.md
├── scope.md
├── sync-request.md
├── tag.md
├── thread.md
├── timing.md
├── track.md
├── updates.md
├── upload-file.md
└── view-model.md
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── mkdocs.yml
├── net
├── .gitignore
├── build.gradle
├── consumer-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ ├── androidx
│ │ └── lifecycle
│ │ │ └── Scope.kt
│ ├── com
│ │ └── drake
│ │ │ └── net
│ │ │ ├── Net.kt
│ │ │ ├── NetConfig.kt
│ │ │ ├── NetCoroutine.kt
│ │ │ ├── body
│ │ │ ├── BodyExtension.kt
│ │ │ ├── NetRequestBody.kt
│ │ │ └── NetResponseBody.kt
│ │ │ ├── cache
│ │ │ ├── CacheMode.kt
│ │ │ └── ForceCache.kt
│ │ │ ├── component
│ │ │ └── Progress.kt
│ │ │ ├── convert
│ │ │ ├── JSONConvert.kt
│ │ │ └── NetConverter.kt
│ │ │ ├── cookie
│ │ │ └── PersistentCookieJar.kt
│ │ │ ├── exception
│ │ │ ├── ConvertException.kt
│ │ │ ├── DownloadFileException.kt
│ │ │ ├── HttpFailureException.kt
│ │ │ ├── HttpResponseException.kt
│ │ │ ├── NetCancellationException.kt
│ │ │ ├── NetConnectException.kt
│ │ │ ├── NetException.kt
│ │ │ ├── NetSocketTimeoutException.kt
│ │ │ ├── NetUnknownHostException.kt
│ │ │ ├── NetworkingException.kt
│ │ │ ├── NoCacheException.kt
│ │ │ ├── RequestParamsException.kt
│ │ │ ├── ResponseException.kt
│ │ │ ├── ServerResponseException.kt
│ │ │ └── URLParseException.kt
│ │ │ ├── interceptor
│ │ │ ├── LogRecordInterceptor.kt
│ │ │ ├── NetOkHttpInterceptor.kt
│ │ │ ├── RequestInterceptor.kt
│ │ │ └── RetryInterceptor.kt
│ │ │ ├── interfaces
│ │ │ ├── NetDialogFactory.kt
│ │ │ ├── NetErrorHandler.kt
│ │ │ └── ProgressListener.kt
│ │ │ ├── internal
│ │ │ ├── NetDeferred.kt
│ │ │ └── NetInitializer.kt
│ │ │ ├── log
│ │ │ ├── LogRecorder.kt
│ │ │ └── MessageType.kt
│ │ │ ├── okhttp
│ │ │ ├── OkHttpBuilder.kt
│ │ │ └── OkHttpExtension.kt
│ │ │ ├── reflect
│ │ │ └── TypeUtils.kt
│ │ │ ├── request
│ │ │ ├── BaseRequest.kt
│ │ │ ├── BodyRequest.kt
│ │ │ ├── MediaConst.kt
│ │ │ ├── Method.kt
│ │ │ ├── RequestBuilder.kt
│ │ │ ├── RequestExtension.kt
│ │ │ └── UrlRequest.kt
│ │ │ ├── response
│ │ │ └── ResponseExtension.kt
│ │ │ ├── scope
│ │ │ ├── AndroidScope.kt
│ │ │ ├── DialogCoroutineScope.kt
│ │ │ ├── NetCoroutineScope.kt
│ │ │ ├── PageCoroutineScope.kt
│ │ │ ├── StateCoroutineScope.kt
│ │ │ └── ViewCoroutineScope.kt
│ │ │ ├── tag
│ │ │ └── NetTag.kt
│ │ │ ├── time
│ │ │ ├── Interval.kt
│ │ │ └── IntervalStatus.kt
│ │ │ ├── transform
│ │ │ └── DeferredTransform.kt
│ │ │ └── utils
│ │ │ ├── Fastest.kt
│ │ │ ├── FileUtils.kt
│ │ │ ├── FlowUtils.kt
│ │ │ ├── Https.kt
│ │ │ ├── NetUtils.kt
│ │ │ ├── Scope.kt
│ │ │ ├── Suspend.kt
│ │ │ ├── TipUtils.kt
│ │ │ └── Uri.kt
│ └── okhttp3
│ │ └── OkHttpUtils.java
│ └── res
│ ├── drawable
│ ├── ic_limited_time.xml
│ └── ic_timing.xml
│ ├── values
│ └── strings.xml
│ └── xml
│ └── network_security_config.xml
├── sample
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── drake
│ │ └── net
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── drake
│ │ │ └── net
│ │ │ └── sample
│ │ │ ├── base
│ │ │ └── App.kt
│ │ │ ├── constants
│ │ │ ├── Api.kt
│ │ │ └── UserConfig.kt
│ │ │ ├── contract
│ │ │ └── AlbumSelectContract.kt
│ │ │ ├── converter
│ │ │ ├── FastJsonConverter.kt
│ │ │ ├── GsonConverter.kt
│ │ │ ├── MoshiConverter.kt
│ │ │ ├── ProtobufConverter.kt
│ │ │ └── SerializationConverter.kt
│ │ │ ├── interceptor
│ │ │ ├── EncryptDataInterceptor.kt
│ │ │ ├── GlobalHeaderInterceptor.kt
│ │ │ └── RefreshTokenInterceptor.kt
│ │ │ ├── mock
│ │ │ └── MockDispatcher.kt
│ │ │ ├── model
│ │ │ ├── ArrayData.kt
│ │ │ ├── BasicData.kt
│ │ │ ├── ConfigModel.kt
│ │ │ ├── GameModel.kt
│ │ │ ├── SubData.kt
│ │ │ ├── TokenModel.kt
│ │ │ └── UserInfoModel.kt
│ │ │ ├── ui
│ │ │ ├── activity
│ │ │ │ └── MainActivity.kt
│ │ │ └── fragment
│ │ │ │ ├── AsyncTaskFragment.kt
│ │ │ │ ├── AutoDialogFragment.kt
│ │ │ │ ├── CallbackRequestFragment.kt
│ │ │ │ ├── CoroutineScopeFragment.kt
│ │ │ │ ├── DownloadFileFragment.kt
│ │ │ │ ├── EditDebounceFragment.kt
│ │ │ │ ├── ErrorHandlerFragment.kt
│ │ │ │ ├── ExceptionTraceFragment.kt
│ │ │ │ ├── FastestFragment.kt
│ │ │ │ ├── HttpsCertificateFragment.kt
│ │ │ │ ├── InterceptorFragment.kt
│ │ │ │ ├── LimitedTimeFragment.kt
│ │ │ │ ├── ParallelNetworkFragment.kt
│ │ │ │ ├── PreviewCacheFragment.kt
│ │ │ │ ├── PullRefreshFragment.kt
│ │ │ │ ├── PushRefreshFragment.kt
│ │ │ │ ├── ReadCacheFragment.kt
│ │ │ │ ├── SimpleRequestFragment.kt
│ │ │ │ ├── StateLayoutFragment.kt
│ │ │ │ ├── SuperIntervalFragment.kt
│ │ │ │ ├── SwitchDispatcherFragment.kt
│ │ │ │ ├── SyncRequestFragment.kt
│ │ │ │ ├── TimingRequestFragment.kt
│ │ │ │ ├── UniqueRequestFragment.kt
│ │ │ │ ├── UploadFileFragment.kt
│ │ │ │ ├── ViewModelRequestFragment.kt
│ │ │ │ └── converter
│ │ │ │ ├── BaseConvertFragment.kt
│ │ │ │ ├── FastJsonConvertFragment.kt
│ │ │ │ ├── GsonConvertFragment.kt
│ │ │ │ ├── MoshiConvertFragment.kt
│ │ │ │ └── SerializationConvertFragment.kt
│ │ │ ├── utils
│ │ │ ├── AESUtils.kt
│ │ │ ├── HttpUtils.kt
│ │ │ └── RandomFileUtils.kt
│ │ │ └── vm
│ │ │ └── UserViewModel.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── bg_card.xml
│ │ ├── bg_empty.webp
│ │ ├── bg_error.webp
│ │ ├── bg_input.xml
│ │ ├── ic_async_task.xml
│ │ ├── ic_callback_request.xml
│ │ ├── ic_config_dialog.xml
│ │ ├── ic_convert.xml
│ │ ├── ic_debounce.xml
│ │ ├── ic_download_file.xml
│ │ ├── ic_error_handler.xml
│ │ ├── ic_exception_trace.xml
│ │ ├── ic_fastest.xml
│ │ ├── ic_https.xml
│ │ ├── ic_interceptor.xml
│ │ ├── ic_interval.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_menu.xml
│ │ ├── ic_parallel_network.xml
│ │ ├── ic_preview_cache.xml
│ │ ├── ic_pull_refresh.xml
│ │ ├── ic_push_refresh.xml
│ │ ├── ic_read_cache.xml
│ │ ├── ic_scope.xml
│ │ ├── ic_simple_request.xml
│ │ ├── ic_state_layout.xml
│ │ ├── ic_switch_dispatcher.xml
│ │ ├── ic_sync_request.xml
│ │ ├── ic_unique.xml
│ │ ├── ic_upload_file.xml
│ │ └── ic_view_model.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── fragment_async_task.xml
│ │ ├── fragment_auto_dialog.xml
│ │ ├── fragment_callback_request.xml
│ │ ├── fragment_coroutine_scope.xml
│ │ ├── fragment_custom_convert.xml
│ │ ├── fragment_download_file.xml
│ │ ├── fragment_edit_debounce.xml
│ │ ├── fragment_error_handler.xml
│ │ ├── fragment_exception_trace.xml
│ │ ├── fragment_fastest.xml
│ │ ├── fragment_https_certificate.xml
│ │ ├── fragment_interceptor.xml
│ │ ├── fragment_limited_time.xml
│ │ ├── fragment_parallel_network.xml
│ │ ├── fragment_pull_refresh.xml
│ │ ├── fragment_push_refresh.xml
│ │ ├── fragment_read_cache.xml
│ │ ├── fragment_simple_request.xml
│ │ ├── fragment_state_layout.xml
│ │ ├── fragment_super_interval.xml
│ │ ├── fragment_switch_dispatcher.xml
│ │ ├── fragment_sync_request.xml
│ │ ├── fragment_timing_request.xml
│ │ ├── fragment_unique_request.xml
│ │ ├── fragment_upload_file.xml
│ │ ├── fragment_view_model_request.xml
│ │ ├── item_game.xml
│ │ ├── item_pull_list.xml
│ │ ├── layout_drawer_nav_header.xml
│ │ ├── layout_empty.xml
│ │ ├── layout_error.xml
│ │ └── layout_loading.xml
│ │ ├── menu
│ │ ├── menu_converter.xml
│ │ ├── menu_download.xml
│ │ ├── menu_interval.xml
│ │ ├── menu_main.xml
│ │ └── menu_request_method.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── navigation
│ │ ├── nav_converter.xml
│ │ └── nav_main.xml
│ │ ├── raw
│ │ ├── array.json
│ │ ├── config.json
│ │ ├── data.json
│ │ ├── game.json
│ │ └── user.json
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── drake
│ └── net
│ └── ExampleUnitTest.kt
├── settings.gradle
└── signed
/.github/ISSUE_TEMPLATE/----.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 提交问题
3 | about: 详细的描述可以快速解决问题
4 | title: ''
5 | labels: 寻求帮助
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## 问题描述
11 |
12 | ### 期望行为
13 |
14 | ## 如何复现
15 |
16 | > fork仓库并复现问题可以快速解决, 猜测只会让问题晦涩难懂, 耽误所有人时间
17 |
18 | ## 截图
19 |
20 | 异常堆栈信息或者手机截图/视频(拖拽到输入框即可上传)
21 |
22 | ## 版本
23 | - Net:
24 | - OkHttp:
25 | - Android:
26 | - Gradle:
27 | - Android Studio:
28 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 | on:
3 | push:
4 | branches:
5 | - master
6 | jobs:
7 | deploy:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | - uses: actions/setup-python@v2
12 | with:
13 | python-version: 3.x
14 | - run: pip install mkdocs-material
15 | - run: mkdocs gh-deploy --force
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/navEditor.xml
5 | /.idea/assetWizardSettings.xml
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | .cxx
11 | /.idea/
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 劉強東
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 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | kotlin_version = '1.8.10'
6 | brv_version = '1.5.2'
7 | coroutine_version = '1.6.1'
8 | okhttp_version = "4.10.0"
9 | }
10 |
11 | repositories {
12 | mavenCentral()
13 | google()
14 | jcenter()
15 | }
16 | dependencies {
17 | classpath 'com.android.tools.build:gradle:7.1.3'
18 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
19 | classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
20 | classpath 'org.jetbrains.dokka:dokka-gradle-plugin:1.4.32'
21 | }
22 | }
23 |
24 | allprojects {
25 | repositories {
26 | mavenCentral()
27 | google()
28 | maven { url 'https://jitpack.io' }
29 | jcenter()
30 | }
31 | }
32 |
33 |
34 | task clean(type: Delete) {
35 | delete rootProject.buildDir
36 | }
37 |
--------------------------------------------------------------------------------
/docs/api/images/anchor-copy-button.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/api/images/arrow_down.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/api/images/copy-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/api/images/copy-successful-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/api/images/docs_logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/docs/api/images/footer-go-to-link.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/api/images/go-to-top-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/api/images/logo-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/api/scripts/clipboard.js:
--------------------------------------------------------------------------------
1 | window.addEventListener('load', () => {
2 | document.querySelectorAll('span.copy-icon').forEach(element => {
3 | element.addEventListener('click', (el) => copyElementsContentToClipboard(element));
4 | })
5 |
6 | document.querySelectorAll('span.anchor-icon').forEach(element => {
7 | element.addEventListener('click', (el) => {
8 | if(element.hasAttribute('pointing-to')){
9 | const location = hrefWithoutCurrentlyUsedAnchor() + '#' + element.getAttribute('pointing-to')
10 | copyTextToClipboard(element, location)
11 | }
12 | });
13 | })
14 | })
15 |
16 | const copyElementsContentToClipboard = (element) => {
17 | const selection = window.getSelection();
18 | const range = document.createRange();
19 | range.selectNodeContents(element.parentNode.parentNode);
20 | selection.removeAllRanges();
21 | selection.addRange(range);
22 |
23 | copyAndShowPopup(element, () => selection.removeAllRanges())
24 | }
25 |
26 | const copyTextToClipboard = (element, text) => {
27 | var textarea = document.createElement("textarea");
28 | textarea.textContent = text;
29 | textarea.style.position = "fixed";
30 | document.body.appendChild(textarea);
31 | textarea.select();
32 |
33 | copyAndShowPopup(element, () => document.body.removeChild(textarea))
34 | }
35 |
36 | const copyAndShowPopup = (element, after) => {
37 | try {
38 | document.execCommand('copy');
39 | element.nextElementSibling.classList.add('active-popup');
40 | setTimeout(() => {
41 | element.nextElementSibling.classList.remove('active-popup');
42 | }, 1200);
43 | } catch (e) {
44 | console.error('Failed to write to clipboard:', e)
45 | }
46 | finally {
47 | if(after) after()
48 | }
49 | }
50 |
51 | const hrefWithoutCurrentlyUsedAnchor = () => window.location.href.split('#')[0]
52 |
53 |
--------------------------------------------------------------------------------
/docs/api/scripts/sourceset_dependencies.js:
--------------------------------------------------------------------------------
1 | sourceset_dependencies='{":net:dokkaHtml/androidTestRelease":[],":net:dokkaHtml/debug":[],":net:dokkaHtml/main":[],":net:dokkaHtml/release":[],":net:dokkaHtml/testFixtures":[],":net:dokkaHtml/testFixturesDebug":[],":net:dokkaHtml/testFixturesRelease":[]}'
2 |
--------------------------------------------------------------------------------
/docs/api/styles/jetbrains-mono.css:
--------------------------------------------------------------------------------
1 | @font-face{
2 | font-family: 'JetBrains Mono';
3 | src: local('Iosevka Curly Medium'),
4 | url('https://raw.githubusercontent.com/liangjingkanji/liangjingkanji/master/font/iosevka-curly/iosevka-curly-medium.woff2') format('woff2');
5 | font-display: swap;
6 | font-weight: normal;
7 | font-style: normal;
8 | }
9 | @font-face{
10 | font-family: 'JetBrains Mono';
11 | src: local('Iosevka Curly Bold'),
12 | url('https://raw.githubusercontent.com/liangjingkanji/liangjingkanji/master/font/iosevka-curly/iosevka-curly-bold.woff2') format('woff2');
13 | font-display: swap;
14 | font-weight: bold;
15 | font-style: normal;
16 | }
17 | @font-face{
18 | font-family: 'HYZhengYuan';
19 | src: local('HYYouYuan-55W'),
20 | url('https://raw.githubusercontent.com/liangjingkanji/liangjingkanji/master/font/HYYouYuan/HYYouYuan-55W.ttf') format('truetype');
21 | font-display: swap;
22 | font-weight: normal;
23 | font-style: normal;
24 | }
25 | @font-face{
26 | font-family: 'HYZhengYuan';
27 | src: local('HYYouYuan-75W'),
28 | url('https://raw.githubusercontent.com/liangjingkanji/liangjingkanji/master/font/HYYouYuan/HYYouYuan-75W.ttf') format('truetype');
29 | font-display: swap;
30 | font-weight: bold;
31 | font-style: normal;
32 | }
--------------------------------------------------------------------------------
/docs/api/styles/logo-styles.css:
--------------------------------------------------------------------------------
1 | #logo {
2 | background-image: url(../images/docs_logo.svg);
3 | }
--------------------------------------------------------------------------------
/docs/auto-pull.md:
--------------------------------------------------------------------------------
1 | 首先请阅读上章节 [自动下拉刷新](auto-refresh.md), 已提及内容不再重复
2 |
3 |
4 | ## 自动分页
5 |
6 | 提供`addData()`来简化分页, 开发者可以借鉴实现
7 |
8 | ```kotlin hl_lines="2"
9 | page.onRefresh {
10 | scope {
11 | val data = Get(Api.PATH) {
12 | param("page", index)
13 | }.await().data
14 | addData(data.list) {
15 | index < data.total
16 | }
17 | }
18 | }.autoRefresh()
19 | ```
20 |
21 | ## 索引自增
22 | `index` 每次上拉加载自动++1, 刷新列表重置为`PageRefreshLayout.startIndex`
23 |
24 | ## 有更多页
25 |
26 | 根据`hasMore`返回结果是否关闭上拉加载, `isEmpty`决定是否显示`空数据`缺省页
27 |
28 | ```kotlin
29 | fun addData(
30 | data: List?,
31 | adapter: BindingAdapter? = null,
32 | isEmpty: () -> Boolean = { data.isNullOrEmpty() },
33 | hasMore: BindingAdapter.() -> Boolean = { true }
34 | )
35 | ```
36 |
37 |
38 | 1. [PageRefreshLayout 下拉刷新/上拉加载](https://liangjingkanji.github.io/BRV/refresh.html)
--------------------------------------------------------------------------------
/docs/auto-refresh.md:
--------------------------------------------------------------------------------
1 | !!! success "模块化依赖"
2 | 如果自己处理下拉刷新可跳过本章, Net可以仅仅作为简单的网络框架存在
3 |
4 |
5 | Net可依赖三方库 [BRV](https://github.com/liangjingkanji/BRV) 实现自动处理下拉刷新
6 |
7 |
8 |
9 | ```groovy
10 | implementation 'com.github.liangjingkanji:BRV:+' // 使用固定版本号替换+符号
11 | ```
12 |
13 | ## PageRefreshLayout
14 |
15 | ```xml
16 |
23 |
24 |
28 |
29 |
30 | ```
31 |
32 | ## 创建列表
33 |
34 | ```kotlin
35 | rv_push.linear().setup {
36 | addType(R.layout.item_list)
37 | }
38 | ```
39 |
40 | ## 网络请求
41 |
42 | 1. 请求开始, 显示下拉刷新动画
43 | 2. 请求成功, 显示`内容`缺省页
44 | 3. 请求失败, 显示`错误`缺省页
45 |
46 | ```kotlin hl_lines="2"
47 | page.onRefresh {
48 | scope {
49 | // 请求到数据设置到RecyclerView
50 | rv_push.models = Get(Api.PATH).await().data.list
51 | }
52 | }.autoRefresh()
53 | ```
54 |
55 | ## 生命周期
56 |
57 | | 生命周期 | 描述 |
58 | | -------- | -------------------------------------------------- |
59 | | 开始 | `showLoading/autoRefresh`触发`onRefresh`, 开始请求 |
60 | | 结束 | PageRefreshLayout被销毁, 请求自动取消 |
--------------------------------------------------------------------------------
/docs/callback.md:
--------------------------------------------------------------------------------
1 | Net支持OkHttp的原有的队列请求`Callback`
2 |
3 | !!! Failure "不推荐"
4 | Callback属于接口回调, 其代码冗余, 且无法支持并发请求
5 |
6 |
7 | ```kotlin
8 | Net.post(Api.PATH).enqueue(object : Callback {
9 | override fun onFailure(call: Call, e: IOException) {
10 | }
11 |
12 | override fun onResponse(call: Call, response: Response) {
13 | // 此处为子线程
14 | val body = response.body?.string() ?: "无数据"
15 | runMain {
16 | // 此处为主线程
17 | tv.text = body
18 | }
19 | }
20 | })
21 | ```
--------------------------------------------------------------------------------
/docs/cancel.md:
--------------------------------------------------------------------------------
1 | 部分场景开发者想手动取消请求
2 |
3 | ```kotlin
4 | downloadScope = scopeNetLife {
5 | // 下载文件
6 | val file = Get(Api.DOWNLOAD).await()
7 | }
8 |
9 | downloadScope.cancel() // 取消下载
10 | ```
11 |
12 | ## 任意位置取消
13 | 发起请求时指定`Id`
14 |
15 | ```kotlin
16 | scopeNetLife {
17 | tv.text = Get(Api.DOWNLOAD){
18 | setId("请求用户信息")
19 | }.await()
20 | }
21 | ```
22 |
23 | === "根据ID取消"
24 | ``` kotlin
25 | Net.cancelId("请求用户信息")
26 | ```
27 | === "根据Group取消"
28 | ``` kotlin
29 | Net.cancelGroup("请求分组名称")
30 | ```
31 |
32 | ## Group和Id区别
33 |
34 | | 函数 | 描述 |
35 | |-|-|
36 | | id | 请求唯一Id, 实际上重复也行, 但是取消请求时遍历到指定Id就会结束遍历 |
37 | | group | 允许多个请求使用相同group, 在取消请求时会遍历所有分组的请求
|
38 |
39 | !!! warning "作用域结束请求自动取消"
40 | 在`scopeXX()`作用域中发起请求时会默认使用当前协程错误处理器作为Group
41 | ```kotlin
42 | setGroup(coroutineContext[CoroutineExceptionHandler])
43 | ```
44 | 在作用域结束时 会`cancelGroup`, 所以如果你手动指定分组会导致无法自动取消
--------------------------------------------------------------------------------
/docs/converter.md:
--------------------------------------------------------------------------------
1 | Net支持请求返回的数据类型取决于你的转换器实现
2 |
3 | # Get<任何对象>(Api.PATH).await()
4 |
5 | 默认转换器支持返回以下数据类型
6 |
7 | | 函数 | 描述 |
8 | |-|-|
9 | | String | 字符串 |
10 | | ByteArray | 字节数组 |
11 | | ByteString | 更多功能的字符串对象 |
12 | | File | 文件对象, 这种情况其实应当称为[下载文件](download-file.md) |
13 | | Response | 所有响应信息(响应体/响应头/请求信息等) |
14 |
15 | 使用示例
16 |
17 | ```kotlin
18 | scopeNetLife {
19 | Get(Api.PATH).await().headers("响应头名称") // 返回响应头
20 | }
21 | ```
22 |
23 | ??? example "转换器实现非常简单"
24 | ```kotlin title="NetConverter.kt" linenums="1"
25 | interface NetConverter {
26 |
27 | @Throws(Throwable::class)
28 | fun onConvert(succeed: Type, response: Response): R?
29 |
30 | companion object DEFAULT : NetConverter {
31 | /**
32 | * 返回结果应当等于泛型对象, 可空
33 | * @param succeed 请求要求返回的泛型类型
34 | * @param response 请求响应对象
35 | */
36 | override fun onConvert(succeed: Type, response: Response): R? {
37 | return when {
38 | succeed === String::class.java && response.isSuccessful -> response.body?.string() as R
39 | succeed === ByteString::class.java && response.isSuccessful -> response.body?.byteString() as R
40 | succeed is GenericArrayType && succeed.genericComponentType === Byte::class.java && response.isSuccessful -> response.body?.bytes() as R
41 | succeed === File::class.java && response.isSuccessful -> response.file() as R
42 | succeed === Response::class.java -> response as R
43 | else -> throw ConvertException(response, "An exception occurred while converting the NetConverter.DEFAULT")
44 | }
45 | }
46 | }
47 | }
48 | ```
49 |
50 | 假设不支持你需要的数据类型, 例如JSON/ProtoBuf/Bitmap等请[自定义转换器](converter-customize.md#_3)
--------------------------------------------------------------------------------
/docs/cookie.md:
--------------------------------------------------------------------------------
1 | 可以实现OkHttp的`CookieJar`, 且提供持久化的[PersistentCookieJar](https://github.com/liangjingkanji/Net/blob/2abf07e1d003ef44574278fd2010f3375225d964/net/src/main/java/com/drake/net/cookie/PersistentCookieJar.kt)
2 |
3 | ```kotlin
4 | NetConfig.initialize(Api.HOST, this) {
5 | // 添加持久化Cookie
6 | cookieJar(PersistentCookieJar(this@App))
7 | }
8 | ```
9 |
10 | 可以手动增删Cookie
11 |
12 | | PersistentCookieJar | 描述 |
13 | |-|-|
14 | | addAll | 添加Cookie |
15 | | getAll | 获取某个域名的所有Cookie |
16 | | remove | 删除某个域名下所有或者指定名称的Cookie |
17 | | clear | 删除客户端全部Cookie |
18 |
19 |
20 | 可通过客户端获取到已配置cookieJar
21 | ```kotlin
22 | (NetConfig.okHttpClient.cookieJar as? PersistentCookieJar)?.clear()
23 | ```
24 |
25 | !!! note "隔绝Cookies共享"
26 | 为`PersistentCookieJar`指定不同`dbName`阻止不同的客户端共享Cookies
--------------------------------------------------------------------------------
/docs/coroutine-request.md:
--------------------------------------------------------------------------------
1 | Net的协程作用域会自动处理协程生命周期
2 |
3 | 在上章节已经介绍如何发起并发网络请求, 这里演示同时(并发)请求`一万次`, 然后取消全部
4 |
5 | ```kotlin
6 | val job = scopeNetLife {
7 | repeat(10000) {
8 | // 这里将返回的数据显示在TextView上
9 | launch {
10 | tv.text = Get(Api.PATH).await()
11 | }
12 | }
13 | }
14 | ```
15 |
16 |
17 | 等待五秒后取消请求
18 | ```kotlin
19 | thread {
20 | Thread.sleep(5000) // 等待五秒
21 | job.cancel()
22 | }
23 | ```
24 |
25 |
26 |
27 | Net主要使用协程请求, 但也支持其他方式发起请求
28 |
29 | - [同步请求](sync-request.md)
30 | - [回调请求](callback.md)
31 | - 协程请求
32 |
--------------------------------------------------------------------------------
/docs/css/extra.css:
--------------------------------------------------------------------------------
1 | @font-face{
2 | font-family: 'Iosevka Curly';
3 | src: local('Iosevka Curly Medium'),
4 | url('https://raw.githubusercontent.com/liangjingkanji/liangjingkanji/master/font/iosevka-curly/iosevka-curly-medium.woff2') format('woff2');
5 | font-display: swap;
6 | font-weight: normal;
7 | font-style: normal;
8 | }
9 | @font-face{
10 | font-family: 'Iosevka Curly';
11 | src: local('Iosevka Curly Bold'),
12 | url('https://raw.githubusercontent.com/liangjingkanji/liangjingkanji/master/font/iosevka-curly/iosevka-curly-bold.woff2') format('woff2');
13 | font-display: swap;
14 | font-weight: bold;
15 | font-style: normal;
16 | }
17 | @font-face{
18 | font-family: 'HYYouYuan';
19 | src: local('HYYouYuan-55W'),
20 | url('https://raw.githubusercontent.com/liangjingkanji/liangjingkanji/master/font/HYYouYuan/HYYouYuan-55W.ttf') format('truetype');
21 | font-display: swap;
22 | font-weight: normal;
23 | font-style: normal;
24 | }
25 | @font-face{
26 | font-family: 'HYYouYuan';
27 | src: local('HYYouYuan-75W'),
28 | url('https://raw.githubusercontent.com/liangjingkanji/liangjingkanji/master/font/HYYouYuan/HYYouYuan-75W.ttf') format('truetype');
29 | font-display: swap;
30 | font-weight: bold;
31 | font-style: normal;
32 | }
33 |
34 | * {
35 | -webkit-font-feature-settings: "liga" on, "calt" on;
36 | -webkit-font-smoothing: subpixel-antialiased;
37 | -moz-osx-font-smoothing: auto;
38 | text-rendering: optimizeLegibility;
39 | font-family: "Iosevka Curly", HYYouYuan !important;
40 | }
41 |
42 | code,
43 | .md-nav,
44 | .md-typeset code,
45 | .md-typeset {
46 | font-size: 14px !important;
47 | }
48 |
49 | .highlight span.filename,
50 | .md-typeset .admonition-title,
51 | .md-typeset summary {
52 | font-weight: normal;
53 | }
--------------------------------------------------------------------------------
/docs/debounce.md:
--------------------------------------------------------------------------------
1 | !!! question "节流"
2 | 在一定时间间隔内,只执行最后一次请求, 忽略其他多余的请求
3 |
4 | 搜索输入框一般都是输入完关键词后自动开始搜索
5 |
6 | 这个过程涉及到
7 |
8 | 1. 每次变化都搜索会导致服务器压力, 应在停止输入满足一定时间后自动搜索
9 | 2. 当产生新的搜索请求后应取消旧请求, 以防止旧数据覆盖新数据
10 | 3. 当输入内容没有变化(例复制粘贴重复内容到搜索框)不会发起搜索请求
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | ```kotlin
19 | var scope: CoroutineScope? = null
20 |
21 | // distinctUntilChanged 表示过滤掉重复结果
22 | binding.etInput.debounce().distinctUntilChanged().launchIn(this) {
23 | scope?.cancel() // 发起新的请求前取消旧的请求, 避免旧数据覆盖新数据
24 | scope = scopeNetLife { // 保存旧的请求到一个变量中
25 | tv.text = "请求中"
26 | tv.text = Get(Api.TIME).await()
27 | }
28 | }
29 | ```
30 |
31 | 指定参数设置节流阀超时时间
32 | ```kotlin
33 | fun EditText.debounce(timeoutMillis: Long = 800)
34 | ```
35 |
36 | 1. [示例-自动搜索分页列表](https://github.com/liangjingkanji/Net/blob/a78d46118666a3509d3fcc79c1d28ad81e2d5a57/sample/src/main/java/com/drake/net/sample/ui/fragment/EditDebounceFragment.kt)
--------------------------------------------------------------------------------
/docs/download-file.md:
--------------------------------------------------------------------------------
1 | 泛型改为`File`即可
2 |
3 | ```kotlin
4 | scopeNetLife {
5 | val file = Get(Api.DOWNLOAD).await()
6 | }
7 | ```
8 |
9 | ## 下载选项
10 |
11 | 丰富的下载定制方案, 并且在不断更新
12 |
13 | ```kotlin
14 | scopeNetLife {
15 | val file =
16 | Get("https://github.com/liangjingkanji/Net/releases/latest/download/net-sample.apk") {
17 | setDownloadFileName("net.apk")
18 | setDownloadDir(requireContext().filesDir)
19 | setDownloadMd5Verify()
20 | }.await()
21 | }
22 | ```
23 |
24 | | 配置选项 | 描述 |
25 | | --------------------------- | ------------------------------------------------------------ |
26 | | addDownloadListener | [下载进度监听器](progress.md) |
27 | | setDownloadFileName | 下载文件名 |
28 | | setDownloadDir | 下载目录 |
29 | | setDownloadMd5Verify | 下载文件MD5校验 |
30 | | setDownloadFileNameConflict | 下载文件同名冲突解决 |
31 | | setDownloadFileNameDecode | 文件名Url解码中文 |
32 | | setDownloadTempFile | 临时文件名 |
33 |
34 | ## 重复下载
35 |
36 | 防止重复下载有以下方式
37 |
38 | | 函数 | 描述 |
39 | | -------- | --------------------------------------------------------- |
40 | | 文件判断 | 判断本地是否存在同名文件 |
41 | | 缓存模式 | 开启缓存, 占用设备两份空间(缓存/下载成功文件都占空间) |
42 | | MD5校验 | 服务器返回`Content-MD5`, 客户端开启`setDownloadMd5Verify` |
--------------------------------------------------------------------------------
/docs/error-global.md:
--------------------------------------------------------------------------------
1 | 可实现`NetErrorHandler`接口来监听全局错误处理
2 |
3 | === "创建"
4 | ```kotlin
5 | class NetworkingErrorHandler : NetErrorHandler {
6 | override fun onError(e: Throwable) {
7 | // .... 其他错误
8 | if (e is ResponseException && e.tag == 401) { // 判断异常为token失效
9 | // 打开登录界面或者弹登录失效对话框
10 | }
11 | }
12 | }
13 | ```
14 | === "配置"
15 | ```kotlin
16 | NetConfig.initialize(Api.HOST, this) {
17 | setErrorHandler(NetworkingErrorHandler))
18 | }
19 | ```
20 |
21 | |NetErrorHandler|使用场景|触发位置|
22 | |-|-|-|
23 | |`onError`| 吐司错误信息 | `scopeNetLife/scopeDialog` |
24 | |`onStateError` | 要求错误显示在缺省页 |`PageRefreshLayout.scope/StateLayout.scope`|
25 |
26 |
27 | !!! warning "以下情况全局错误处理无效"
28 |
29 | 1. 异步任务作用域(`scope/scopeLife`)发生的错误
30 | 2. 使用[单例错误处理](error-single.md)处理的错误
--------------------------------------------------------------------------------
/docs/error-single.md:
--------------------------------------------------------------------------------
1 | 捕获当前请求/作用域错误, 将不会再被全局错误处理
2 |
3 | ## 捕获请求
4 |
5 | 一个请求发生错误会取消当前作用域内所有请求, 但单独捕获后不会再影响其他请求
6 |
7 | 例如
8 | ```kotlin
9 | scopeNetLife {
10 | Get("path").await() // 失败
11 | Get("path2").await() // 上面失败, 此处也不会执行
12 | }
13 | ```
14 |
15 | 捕获第一个协程避免终止后续执行
16 | ```kotlin
17 | scopeNetLife {
18 | try {
19 | Get("path").await() // 失败
20 | } catch(e:Exception) {
21 | }
22 | Get("path2").await() // 上面失败, 此处继续执行
23 | }
24 | ```
25 | 当然如果创建不同的作用域分别请求那是互不影响的
26 | ```kotlin
27 | scopeNetLife {
28 | Get("path").await() // 失败
29 | }
30 | scopeNetLife {
31 | Get("path2").await() // 上面失败, 此处完全不受影响
32 | }
33 | ```
34 |
35 |
36 |
37 | ## 捕获作用域
38 |
39 | ```kotlin
40 | scope {
41 | val data = Get("http://www.error.com/").await()
42 | }.catch {
43 | // 协程内发生错误回调, it为异常对象
44 | }.finally {
45 | // 协程执行完毕回调(不论成败), it为异常对象
46 | }
47 | ```
48 |
49 | | 函数 | 区别 |
50 | |-|-|
51 | | catch | 发生错误后回调, 取消不回调
不会再执行全局错误处理, 可使用`handleError`再次传递给全局 |
52 | | finally | 执行完毕回调(不论成败), 取消作用域时`it`为`CancellationException`
执行全局错误处理 |
--------------------------------------------------------------------------------
/docs/error.md:
--------------------------------------------------------------------------------
1 | Net有完善的错误处理机制, 具备捕获异常/取消请求/错误提示/追踪链路
2 |
3 | !!! success "收集网络日志"
4 | 在Net作用域内发生的异常都会被全局错误处理捕获, 可以将其筛选上传日志
5 |
6 |
7 | 以下位置抛出异常会被捕获
8 |
9 | | 函数 | 描述 |
10 | |-|-|
11 | | 作用域 | `scopeXX`代码块中 |
12 | | 拦截器 | `Interceptor/RequestInterceptor` |
13 | | 转换器 | `NetConverter` |
14 |
15 | 如果捕获到错误默认会执行以下操作
16 |
17 | 1. `Logcat`输出异常堆栈信息, [自定义异常抛出](error-throws.md)
18 | 2. `Toast`显示错误文本, [自定义错误提示](error-tip.md)
19 |
20 |
21 | !!! failure "捕获不到异常"
22 | 如果请求未执行`await()`, 那么即使发生错误也不会被捕获到
23 |
24 |
25 | 自定义请阅读[全局错误处理](error-global.md)
--------------------------------------------------------------------------------
/docs/https.md:
--------------------------------------------------------------------------------
1 | Net可快速配置Https证书, 或者使用OkHttp的方式
2 |
3 | ## 使用CA证书
4 |
5 | Https如果使用的CA证书, 不需要任何配置可以直接访问
6 |
7 | ```kotlin
8 | scopeNetLife {
9 | tv.text = Get(Api.PATH).await()
10 | }
11 | ```
12 |
13 | ## 信任所有证书
14 |
15 | 信任所有证书可以解决无法访问私有证书的Https地址问题
16 |
17 | === "全局配置"
18 |
19 | ```kotlin
20 | NetConfig.initialize(Api.HOST, this){
21 | trustSSLCertificate() // 信任所有证书
22 | }
23 | ```
24 | === "单例配置"
25 |
26 | ```kotlin
27 | scopeNetLife {
28 | Get(Api.PATH){
29 | setClient {
30 | trustSSLCertificate()
31 | }
32 | }.await()
33 | }
34 | ```
35 |
36 | ## 导入证书
37 |
38 | 私有证书可以放到任何位置, 只要读取到`InputStream`流对象即可
39 |
40 | === "全局配置"
41 |
42 | ```kotlin
43 | NetConfig.initialize(Api.HOST, this) {
44 | val privateCertificate = resources.assets.open("https.certificate")
45 | setSSLCertificate(privateCertificate)
46 | }
47 | ```
48 |
49 | === "单例配置"
50 |
51 | ```kotlin
52 | scopeNetLife {
53 | Get(Api.PATH) {
54 | setClient {
55 | val privateCertificate = resources.assets.open("https.certificate") // 这里的证书是放到应用的资产目录下
56 | setSSLCertificate(privateCertificate)
57 | }
58 | }.await()
59 | }
60 | ```
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/docs/img/book-open.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/img/code-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/docs/img/code-preview.png
--------------------------------------------------------------------------------
/docs/img/discussesions.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/img/issues.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/img/logo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/docs/img/logo.gif
--------------------------------------------------------------------------------
/docs/img/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/docs/img/preview.png
--------------------------------------------------------------------------------
/docs/interceptor.md:
--------------------------------------------------------------------------------
1 | 根据使用场景选择
2 |
3 | 1. `Interceptor`: 支持三方OkHttp拦截器组件, 允许修改请求/响应信息, 可以转发请求
4 | 2. `RequestInterceptor`: Net独有的轻量级拦截器, 允许修改全局请求头/请求参数, 无法转发请求
5 |
6 |
7 | !!! Failure "禁止无效封装"
8 | 不应为全局参数/加密等封装请求方法, 应自定义拦截器/转换器来实现, [常见拦截器示例](https://github.com/liangjingkanji/Net/tree/master/sample/src/main/java/com/drake/net/sample/interceptor)
9 |
10 |
11 |
12 | ## 拦截器
13 |
14 | 添加拦截器
15 |
16 | ```kotlin
17 | NetConfig.initialize(Api.HOST, this) {
18 | addInterceptor(RefreshTokenInterceptor())
19 | }
20 | ```
21 |
22 | 客户端token自动续期示例
23 |
24 | ```kotlin
25 | class RefreshTokenInterceptor : Interceptor {
26 | override fun intercept(chain: Interceptor.Chain): Response {
27 | val request = chain.request()
28 | val response = chain.proceed(request)
29 | return synchronized(RefreshTokenInterceptor::class.java) {
30 | if (response.code == 401 && UserConfig.isLogin && !request.url.encodedPath.contains(Api.Token)) {
31 | val tokenInfo = Net.get(Api.Token).execute() // 同步请求token
32 | if (tokenInfo.isExpired) {
33 | // token过期抛出异常, 由全局错误处理器处理, 在其中可以跳转到登陆界面提示用户重新登陆
34 | throw ResponseException(response, "登录状态失效")
35 | } else {
36 | UserConfig.token = tokenInfo.token
37 | }
38 | chain.proceed(request)
39 | } else {
40 | response
41 | }
42 | }
43 | }
44 | }
45 | ```
46 |
47 | ## 请求拦截器
48 |
49 | 轻量级拦截器(`RequestInterceptor`), 其Api更适合添加全局请求头/参数
50 |
51 | ```kotlin
52 | NetConfig.initialize(Api.HOST, this) {
53 | setRequestInterceptor(object : RequestInterceptor {
54 | override fun interceptor(request: BaseRequest) {
55 | request.param("client", "Net")
56 | request.setHeader("token", "123456")
57 | }
58 | })
59 | }
60 | ```
61 |
62 | 可以看出`setRequestInterceptor`仅支持一个, `addInterceptor`支持多个拦截器
--------------------------------------------------------------------------------
/docs/interval.md:
--------------------------------------------------------------------------------
1 | Net自带轮询器(计时器), 包含以下特性
2 |
3 | - 正计时
4 | - 倒计时
5 | - 指定次数
6 | - 支持开始/停止/暂停/继续/重置/切换开关
7 | - 仅页面显示时计时
8 | - 页面销毁自动取消
9 |
10 |
11 | === "限制次数/间隔"
12 | ```kotlin
13 | interval = Interval(100, 1, TimeUnit.SECONDS).life(this) // 自定义计数器个数的轮询器
14 | ```
15 | === "无限执行"
16 | ```kotlin
17 | interval = Interval(1, TimeUnit.SECONDS) // 每秒回调一次, 不会自动结束
18 | ```
19 | === "倒计时"
20 | ```kotlin
21 | // 倒计时轮询器, 当[start]]比[end]值大, 且end不等于-1时, 即为倒计时
22 | interval = Interval(1, 1, TimeUnit.SECONDS, 5).life(this)
23 | ```
24 |
25 | 监听轮询器
26 | ```kotlin
27 | interval.subscribe {
28 | tv.text = it.toString()
29 | }.finish {
30 | tv.text = "计时完成" // 最后一位数时同时回调 subscribe/finish
31 | }.start()
32 | ```
33 |
34 | | Interval | 描述 |
35 | |-|-|
36 | | start | 开始轮询器 |
37 | | stop | 停止 |
38 | | cancel | 取消, 区别于stop, 此函数执行后并不会回调finish |
39 | | pause | 暂停 |
40 | | resume | 继续 |
41 | | reset | 重置 |
42 | | switch | 切换开关 |
43 | | subscribe | 每次计时都会执行该回调 |
44 | | finish | 当计时器完成时执行该回调, 执行stop后也会回调 |
45 | | life | 指定生命周期自动取消轮询器 |
46 | | onlyResumed | 当界面不可见时暂停, 当界面可见时继续 |
47 |
48 | !!! failure "后台运行"
49 | 由于系统限制, 本工具无法保证一直后台运行
--------------------------------------------------------------------------------
/docs/issues.md:
--------------------------------------------------------------------------------
1 | Net最大的特点在于完美支持OkHttp的所有功能组件, 而OkHttp是Android主流网络解决方案
2 |
3 | 所以如果存在Net没有实现的功能可以谷歌搜索`"OkHttp如何实现XX"`, 然后可以很容易在Net中使用
4 |
5 | ## 低版本兼容
6 |
7 | 如果开发要求低至 Android 4.4 (API level 19)
8 | 请使用兼容版本: [Net-okhttp3](https://github.com/liangjingkanji/Net-okhttp3)
9 |
10 | 如果需更低版本支持请拉取仓库修改`minSdkVersion`后编译成aar使用
11 |
12 | ## 开发者交流
13 |
14 | - [反馈问题](https://github.com/liangjingkanji/Net/issues)
15 | - [其他开发者提及问题](https://github.com/liangjingkanji/Net/issues)
16 | - [交流社区](https://github.com/liangjingkanji/Net/discussions)
17 | -
18 |
--------------------------------------------------------------------------------
/docs/log-notice.md:
--------------------------------------------------------------------------------
1 | 使用三方库 [Chucker](https://github.com/ChuckerTeam/chucker) 在通知栏记录网络请求日志
2 |
3 |
4 | { width="300" }
5 | Chucker
6 |
7 |
8 | 依赖
9 |
10 | ```groovy
11 | implementation "com.github.chuckerteam.chucker:library:3.5.2"
12 | ```
13 |
14 | 添加拦截器
15 |
16 | ```kotlin
17 | NetConfig.initialize(Api.HOST, this) {
18 | // ...
19 | if (BuildConfig.DEBUG) {
20 | addInterceptor(
21 | ChuckerInterceptor.Builder(this@App)
22 | .collector(ChuckerCollector(this@App))
23 | .maxContentLength(250000L)
24 | .redactHeaders(emptySet())
25 | .alwaysReadResponseBody(false)
26 | .build()
27 | )
28 | }
29 | }
30 | ```
31 |
32 | 自定义功能浏览 [Chucker](https://github.com/ChuckerTeam/chucker) 官网
33 |
--------------------------------------------------------------------------------
/docs/model-generate.md:
--------------------------------------------------------------------------------
1 | 应该没人会完全手写数据模型吧? 建议使用`JSON To Kotlin Class` 插件完成
2 |
3 | ## 安装插件
4 |
5 | 在Plugins里面搜索kotlin关键词下载安装
6 |
7 |
8 |
9 | ## 使用插件
10 |
11 | 选中分包后, 使用快捷键或者鼠标右键打开填写Json界面
12 |
13 |
14 |
15 | 添加Json然后点击`Generate`生成
16 |
17 |
18 |
19 | 然后就会在选中的分包下生成一个数据模型类了
20 |
21 | !!! Warning "不要生成数组"
22 | 不要输入JSON数组生成(否则生成的类会继承ArrayList), 输入JSON对象, 请求使用`Get>`
23 |
24 | ## 高级设置
25 |
26 | 点击`Advanced`打开设置界面
27 |
28 | ### 可空选项
29 |
30 | 截图即为我的推荐配置
31 |
32 |
33 |
34 | 1. Keyword 创建的数据模型的字段是 Val还是Var
35 | 2. Type 字段是否是可空类型, 最后选项表示根据Json的值判断是否可空
36 | 3. 默认值的策略, 无法选择不介绍
37 |
38 | ### 使用的框架
39 |
40 | 生成数据模型时会兼容你使用的框架, 例如Moshi和ks可能需要注解, 然后会自动生成SerialName这些名称注解
41 |
42 |
43 |
44 | ### 其他
45 |
46 | 截图即为我的推荐配置
47 |
48 |
49 |
50 | 1. 是否使用注释, 既会将Json字符串作为注释保留在数据模型类中
51 | 2. 根据字母排序数据模型的字段
52 | 3. 使用内部类. 例如Json中的Json对象会作为内部类被创建在数据模型类中 (推荐开启, 保持高内聚低耦合)
53 | 4. 如果Json对象的字段都是原始类型则使用Map来表示
54 | 5. 只在需要时创建注解
55 | 6. 自动验证Json正确性(在填写时)
56 | 7. Json格式化时使用的空格数量
57 | 8. 指定一个类作为父类模板(既创建的数据模型都会继承该类)
58 |
59 | ### 扩展
60 |
61 | 截图即为我的推荐配置
62 |
63 |
64 |
65 | 1. 添加@Keep注解, 为防止被代码混淆
66 | 2. 注解和字段处于同一行, 便于美观
67 | 3. 使用Parcelable序列化
68 | 4. 为字段添加前缀/后缀
69 | 5. 为数据模型类添加前缀/后缀
--------------------------------------------------------------------------------
/docs/okhttp-client.md:
--------------------------------------------------------------------------------
1 | Net全局持有一个`OkHttpClient`对象发起请求
2 |
3 | ```kotlin
4 | object NetConfig {
5 | var okHttpClient: OkHttpClient
6 | }
7 | ```
8 |
9 | ## 全局
10 |
11 | ```kotlin
12 | NetConfig.initialize(Api.HOST, this) {
13 | // 此处this即为OkHttpClient.Builder
14 | }
15 | ```
16 |
17 |
18 | ## 单例
19 |
20 | 单独指定当前请求客户端
21 |
22 | === "修改全局客户端"
23 | ```kotlin
24 | scopeNetLife {
25 | tv_response.text = Get(Api.PATH) {
26 | setClient {
27 | // 此处this即为OkHttpClient.Builder
28 | trustCertificate()
29 | }
30 | }.await()
31 | }
32 | ```
33 | 在全局`OkHttpClient`基础上修改
34 |
35 | === "创建新客户端"
36 | ```kotlin
37 | scopeNetLife {
38 | tv_response.text = Get(Api.PATH) {
39 | okHttpClient = OkHttpClient.Builder().build()
40 | }.await()
41 | }
42 | ```
43 | 创建新的OkHttpClient, 一般不使用, 因为新OkHttpClient会重新创建线程池/连接池等造成内存消耗
--------------------------------------------------------------------------------
/docs/repeat-request.md:
--------------------------------------------------------------------------------
1 | 常用于筛选列表请求, 选择新的筛选条件时应将上次未完成的取消后再发起请求
2 |
3 | 在Net禁止重复请求仅2行代码
4 |
5 | ```kotlin hl_lines="4"
6 | var scope: AndroidScope? = null
7 |
8 | btnFilter.setOnClickListener {
9 | scope?.cancel()
10 |
11 | scope = scopeNetLife {
12 | val result = Post(Api.PATH).await()
13 | }
14 | }
15 | ```
16 |
17 | 当`scope`不为空时表示存在旧请求, 无论旧请求是否完成都可以调用`cancel()`保证取消即可
18 |
19 | 1. [取消请求](cancel.md)
20 | 2. 限制时间强制使用缓存, [配置缓存有效期](cache.md#_3)
--------------------------------------------------------------------------------
/docs/sync-request.md:
--------------------------------------------------------------------------------
1 | Net支持在当前线程执行阻塞线程的同步请求
2 |
3 | !!! question "什么是同步请求"
4 | 即上个请求结束才会发起下个请求, 实际上协程也可以实现但是他不会阻塞线程
5 |
6 | 同步请求应用场景一般是在拦截器(执行在子线程)中使用
7 |
8 | 因为Android主线程不允许发起网络请求, 这里创建一个子线程来演示
9 |
10 | === "返回数据"
11 |
12 | ```kotlin
13 | thread {
14 | val result = Net.post(Api.PATH).execute() // 网络请求不允许在主线程
15 | tv?.post {
16 | tv?.text = result // view要求在主线程更新
17 | }
18 | }
19 | ```
20 |
21 | === "返回Result"
22 |
23 | ```kotlin
24 | thread {
25 | val result = Net.post(Api.PATH).toResult().getOrDefault("请求发生错误, 我是默认值")
26 | tv?.post {
27 | tv?.text = result // view要求在主线程更新
28 | }
29 | }
30 | ```
31 |
32 | 1. `execute`在请求错误时会直接抛出异常
33 | 2. `toResult`不会抛出异常, 可`getOrThrow/exceptionOrNull`等返回异常对象
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/docs/timing.md:
--------------------------------------------------------------------------------
1 | 以下为提供实现定时/限时请求思路, 并不限定只有以下方式
2 |
3 |
4 | ## 限时请求
5 |
6 | 即超过指定时间后立即取消请求
7 |
8 | ```kotlin
9 | scopeDialog {
10 | // 当接口请求在100毫秒内没有完成会抛出异常TimeoutCancellationException
11 | withTimeout(100) {
12 | Get(Api.PATH).await()
13 | }
14 | }.catch {
15 | Log.e("日志", "请求错误", it) // catch无法接收到CancellationException异常
16 | }.finally {
17 | Log.e("日志", "请求完成", it) // TimeoutCancellationException属于CancellationException子类故只会被finally接收到
18 | if (it is TimeoutCancellationException) {
19 | toast("由于未在指定时间完成请求则取消请求")
20 | }
21 | }
22 | ```
23 |
24 | ## 定时请求
25 |
26 | 指定请求10次
27 |
28 | ```kotlin
29 | scopeNetLife {
30 | // 每秒请求一次, 总共执行10次
31 | repeat(20) {
32 | delay(1000)
33 | val data = Get(Api.PATH).await()
34 | if(it = 10) {
35 | return@repeat
36 | }
37 | }
38 | }
39 | ```
40 |
41 | 每秒无限循环请求, 根据某个条件`break`退出
42 |
43 | ```kotlin
44 | scopeNetLife {
45 | while (true) {
46 | delay(1.toDuration(DurationUnit.SECONDS))
47 | val data = Get(Api.PATH).await()
48 | if(data.type = 3) {
49 | break
50 | }
51 | }
52 | }
53 | ```
--------------------------------------------------------------------------------
/docs/track.md:
--------------------------------------------------------------------------------
1 | Net中网络请求异常会LogCat输出, 除非开发者修改全局错误处理
2 |
3 |
4 | 演示访问一个不存在的请求路径
5 | ```kotlin
6 | scopeNetLife {
7 | tv.text = Get("https://error.com/net/").await()
8 | }
9 | ```
10 |
11 | LogCat可以看到异常堆栈信息, 包含具体Url和代码位置
12 |
13 |
14 |
15 |
16 | ### 关闭日志
17 |
18 | 可以关闭日志打印
19 |
20 | ```kotlin
21 | NetConfig.initialize(Api.HOST, this) {
22 | setDebug(false) // 关闭日志, 一般使用 BuildConfig.DEBUG
23 | }
24 | ```
25 |
--------------------------------------------------------------------------------
/docs/upload-file.md:
--------------------------------------------------------------------------------
1 | ```kotlin
2 | scopeNetLife {
3 | Post(Api.UPLOAD) {
4 | param("fileName", assetsFile())
5 | }.await()
6 | }
7 | ```
8 |
9 | 使用`addUploadListener`添加上传进度监听器, 阅读[进度监听](progress.md)章节
10 |
11 | ## 指定类型
12 |
13 | 默认根据文件后缀名生成`MediaType`, 如果想自定义MediaType可以直接创建`RequestBody`
14 |
15 | ```kotlin
16 | scopeNetLife {
17 | Post(Api.UPLOAD) {
18 | param("file", assetsFile().toRequestBody("image/webp".toMediaType()))
19 | }.await()
20 | }
21 | ```
22 |
23 | ## 上传类型
24 |
25 | 自定义RequestBody可以实现任何数据类型的上传
26 |
27 | ```kotlin
28 | scopeNetLife {
29 | Post(Api.UPLOAD) {
30 | // 表单上传
31 | param("file", Uri)
32 | param("file", File)
33 | }.await()
34 | }
35 | ```
36 |
37 | 直接上传`InputStream`可能内存泄露, 建议你保存到文件后上传
38 |
39 | 1. [使用文件流上传文件](https://github.com/liangjingkanji/Net/discussions/190)
40 |
41 |
42 |
--------------------------------------------------------------------------------
/docs/view-model.md:
--------------------------------------------------------------------------------
1 | Net支持在ViewModel中创建网络请求/异步任务
2 |
3 | !!! Warning "不推荐"
4 | 1. 网络请求不一定要写在ViewModel
5 | 2. 网络请求不要写接口回调
6 | 3. 可以在Activity中直接返回请求结果
7 |
8 |
9 | ## 自动生命周期
10 |
11 | - [示例代码](https://github.com/liangjingkanji/Net/blob/c4d7c4cde6a34b9fa97a75cb357276b75432f8d1/sample/src/main/java/com/drake/net/sample/ui/fragment/ViewModelRequestFragment.kt)
12 |
13 | 使用`scopeXXLife()`创建作用域, 在ViewModel被销毁时自动取消请求
14 |
15 | ```kotlin
16 | class UserViewModel : ViewModel() {
17 |
18 | // 用户信息
19 | var userInfo: MutableLiveData = MutableLiveData()
20 |
21 | /**
22 | * 使用LiveData接受请求结果, 将该liveData直接使用DataBinding绑定到页面上, 会在请求成功自动更新视图
23 | */
24 | fun fetchUserInfo() = scopeNetLife {
25 | userInfo.value = Get(Api.GAME).await()
26 | }
27 |
28 | /**
29 | * 开始非阻塞异步任务
30 | * 返回Deferred, 调用await()才会返回结果
31 | */
32 | fun fetchList(scope: CoroutineScope) = scope.Get(Api.TEST)
33 |
34 | /**
35 | * 开始阻塞异步任务
36 | * 直接返回结果
37 | */
38 | suspend fun fetchPrecessData() = coroutineScope {
39 | val response = Get(Api.TEST).await()
40 | response + "处理数据"
41 | }
42 | }
43 | ```
44 |
45 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ## For more details on how to configure your build environment visit
2 | # http://www.gradle.org/docs/current/userguide/build_environment.html
3 | #
4 | # Specifies the JVM arguments used for the daemon process.
5 | # The setting is particularly useful for tweaking memory settings.
6 | # Default value: -Xmx1024m -XX:MaxPermSize=256m
7 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
8 | #
9 | # When configured, Gradle will run in incubating parallel mode.
10 | # This option should only be used with decoupled projects. More details, visit
11 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
12 | # org.gradle.parallel=true
13 | #Fri Mar 18 17:49:14 CST 2022
14 | kotlin.code.style=official
15 | kotlin.incremental=false
16 | org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
17 | android.useAndroidX=true
18 | android.enableJetifier=true
19 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jul 28 17:18:47 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/net/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/net/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.library"
2 | apply plugin: "kotlin-android"
3 | apply plugin: 'org.jetbrains.dokka'
4 |
5 | android {
6 | compileSdkVersion 30
7 | defaultConfig {
8 | minSdkVersion 19
9 | targetSdkVersion 30
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 | consumerProguardFiles "consumer-rules.pro"
15 | }
16 |
17 | kotlinOptions {
18 | freeCompilerArgs = ["-Xinline-classes", "-Xallow-result-return-type"]
19 | }
20 |
21 | dokkaHtml {
22 | outputDirectory.set(file("$rootDir/docs/api"))
23 | suppressInheritedMembers.set(true)
24 | moduleName.set("Net")
25 | }
26 | }
27 |
28 |
29 | dependencies {
30 | implementation 'androidx.appcompat:appcompat:1.3.1'
31 | implementation 'androidx.startup:startup-runtime:1.0.0'
32 | implementation 'androidx.documentfile:documentfile:1.0.1'
33 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2"
34 |
35 | compileOnly "com.squareup.okhttp3:okhttp:$okhttp_version"
36 |
37 | compileOnly "com.github.liangjingkanji:BRV:$brv_version"
38 | compileOnly "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
39 | compileOnly "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
40 | }
41 |
--------------------------------------------------------------------------------
/net/consumer-rules.pro:
--------------------------------------------------------------------------------
1 | -keep class okhttp3.** { *; }
2 | -keep class com.drake.net.exception.** { *; }
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/cache/CacheMode.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.cache
26 |
27 | enum class CacheMode {
28 |
29 | /** 只读取缓存, 本操作并不会请求网络故不存在写入缓存 */
30 | READ,
31 |
32 | /** 只请求网络, 强制写入缓存 */
33 | WRITE,
34 |
35 | /** 先从缓存读取,如果失败再从网络读取, 强制写入缓存 */
36 | READ_THEN_REQUEST,
37 |
38 | /** 先从网络读取,如果失败再从缓存读取, 强制写入缓存 */
39 | REQUEST_THEN_READ,
40 | }
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/exception/ConvertException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package com.drake.net.exception
25 |
26 | import okhttp3.Response
27 |
28 |
29 | /**
30 | * 转换数据异常
31 | * @param response 响应信息
32 | * @param message 错误描述信息
33 | * @param cause 错误原因
34 | * @param tag 可携带任意对象, 一般用于在转换器/拦截器中添加然后传递给错误处理器去使用判断
35 | */
36 | class ConvertException(
37 | response: Response,
38 | message: String? = null,
39 | cause: Throwable? = null,
40 | var tag: Any? = null
41 | ) : HttpResponseException(response, message, cause)
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/exception/DownloadFileException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.exception
26 |
27 | import okhttp3.Response
28 |
29 | /**
30 | * 下载文件异常
31 | * @param response 响应信息
32 | * @param message 错误描述信息
33 | * @param cause 错误原因
34 | * @param tag 可携带任意对象, 一般用于在转换器/拦截器中添加然后传递给错误处理器去使用判断
35 | */
36 | class DownloadFileException(
37 | response: Response,
38 | message: String? = null,
39 | cause: Throwable? = null,
40 | var tag: Any? = null
41 | ) : HttpResponseException(response, message, cause)
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/exception/HttpFailureException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.exception
26 |
27 | import okhttp3.Request
28 |
29 | /**
30 | * 该类表示Http请求在服务器响应之前失败
31 | * @param request 请求信息
32 | * @param message 错误描述信息
33 | * @param cause 错误原因
34 | */
35 | open class HttpFailureException(
36 | request: Request,
37 | message: String? = null,
38 | cause: Throwable? = null
39 | ) : NetException(request, message, cause)
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/exception/HttpResponseException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.exception
26 |
27 | import okhttp3.Response
28 |
29 | /**
30 | * 该类表示Http请求在服务器响应成功后失败
31 | * @param response 响应信息
32 | * @param message 错误描述信息
33 | * @param cause 错误原因
34 | *
35 | * @see ResponseException HttpStatusCode 200...299
36 | * @see RequestParamsException HttpStatusCode 400...499
37 | * @see ServerResponseException HttpStatusCode 500...599
38 | */
39 | open class HttpResponseException(
40 | open val response: Response,
41 | message: String? = null,
42 | cause: Throwable? = null
43 | ) : NetException(response.request, message, cause)
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/exception/NetConnectException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.exception
26 |
27 | import okhttp3.Request
28 |
29 | /**
30 | * 连接错误
31 | * @param request 请求信息
32 | * @param message 错误描述信息
33 | * @param cause 错误原因
34 | */
35 | class NetConnectException(
36 | request: Request,
37 | message: String? = null,
38 | cause: Throwable? = null
39 | ) : HttpFailureException(request, message, cause)
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/exception/NetSocketTimeoutException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.exception
26 |
27 | import okhttp3.Request
28 |
29 | /**
30 | * 请求过程中读取或者写入超时
31 | * @param request 请求信息
32 | * @param message 错误描述信息
33 | * @param cause 错误原因
34 | */
35 | class NetSocketTimeoutException(
36 | request: Request,
37 | message: String? = null,
38 | cause: Throwable? = null
39 | ) : HttpFailureException(request, message, cause)
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/exception/NetUnknownHostException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.exception
26 |
27 | import okhttp3.Request
28 |
29 | /**
30 | * 主机域名无法访问
31 | * @param request 请求信息
32 | * @param message 错误描述信息
33 | * @param cause 错误原因
34 | */
35 | class NetUnknownHostException(
36 | request: Request,
37 | message: String? = null,
38 | cause: Throwable? = null
39 | ) : HttpFailureException(request, message, cause)
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/exception/NetworkingException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.exception
26 |
27 | import okhttp3.Request
28 |
29 | /**
30 | * 无网络情况
31 | * @param request 请求信息
32 | * @param message 错误描述信息
33 | * @param cause 错误原因
34 | */
35 | class NetworkingException(
36 | request: Request,
37 | message: String? = null,
38 | cause: Throwable? = null
39 | ) : HttpFailureException(request, message, cause)
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/exception/NoCacheException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.exception
26 |
27 | import com.drake.net.cache.ForceCache
28 | import okhttp3.Request
29 |
30 | /**
31 | * 读取缓存失败
32 | * 仅当设置强制缓存模式[com.drake.net.cache.CacheMode.READ]和[com.drake.net.cache.CacheMode.REQUEST_THEN_READ]才会发生此异常
33 | * @param request 请求信息
34 | * @param message 错误描述信息
35 | * @param cause 错误原因
36 | */
37 | class NoCacheException(
38 | request: Request,
39 | message: String? = null,
40 | cause: Throwable? = null
41 | ) : NetException(request, message, cause) {
42 |
43 | override fun getLocalizedMessage(): String {
44 | return "cacheKey = " + ForceCache.key(request) + " " + super.getLocalizedMessage()
45 | }
46 | }
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/exception/RequestParamsException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.exception
26 |
27 | import okhttp3.Response
28 |
29 | /**
30 | * 400 - 499 客户端请求异常
31 | * @param response 响应信息
32 | * @param message 错误描述信息
33 | * @param cause 错误原因
34 | * @param tag 可携带任意对象, 一般用于在转换器/拦截器中添加然后传递给错误处理器去使用判断
35 | */
36 | class RequestParamsException(
37 | response: Response,
38 | message: String? = null,
39 | cause: Throwable? = null,
40 | var tag: Any? = null
41 | ) : HttpResponseException(response, message, cause)
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/exception/ResponseException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | @file:Suppress("MemberVisibilityCanBePrivate")
26 |
27 | package com.drake.net.exception
28 |
29 | import okhttp3.Response
30 |
31 | /**
32 | * 状态码在200..299, 但是返回数据不符合业务要求可以抛出该异常
33 | * @param response 响应信息
34 | * @param message 错误描述信息
35 | * @param cause 错误原因
36 | * @param tag 可携带任意对象, 一般用于在转换器/拦截器中添加然后传递给错误处理器去使用判断
37 | */
38 | class ResponseException(
39 | response: Response,
40 | message: String? = null,
41 | cause: Throwable? = null,
42 | var tag: Any? = null
43 | ) : HttpResponseException(response, message, cause)
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/exception/ServerResponseException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.exception
26 |
27 | import okhttp3.Response
28 |
29 |
30 | /**
31 | * >= 500 服务器异常
32 | * @param response 响应信息
33 | * @param message 错误描述信息
34 | * @param cause 错误原因
35 | * @param tag 可携带任意对象, 一般用于在转换器/拦截器中添加然后传递给错误处理器去使用判断
36 | */
37 | class ServerResponseException(
38 | response: Response,
39 | message: String? = null,
40 | cause: Throwable? = null,
41 | var tag: Any? = null
42 | ) : HttpResponseException(response, message, cause)
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/exception/URLParseException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.exception
26 |
27 | /**
28 | * URL地址错误
29 | *
30 | * @param message 错误描述信息
31 | * @param cause 错误原因
32 | */
33 | open class URLParseException(
34 | message: String? = null,
35 | cause: Throwable? = null,
36 | ) : Exception(message, cause) {
37 |
38 | var occurred: String = ""
39 |
40 | override fun getLocalizedMessage(): String? {
41 | return super.getLocalizedMessage() + occurred
42 | }
43 | }
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/interceptor/RequestInterceptor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.interceptor
26 |
27 | import com.drake.net.request.BaseRequest
28 |
29 | interface RequestInterceptor {
30 |
31 | /**
32 | * 当你使用Net发起请求的时候就会触发该拦截器
33 | * 该拦截器属于轻量级不具备重发的功能, 一般用于请求参数的修改
34 | * 请勿在这里进行请求重发可能会导致死循环
35 | */
36 | fun interceptor(request: BaseRequest)
37 | }
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/interfaces/ProgressListener.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package com.drake.net.interfaces
25 |
26 | import com.drake.net.component.Progress
27 |
28 | /**
29 | * 进度监听器, 可能为下载或上传
30 | *
31 | * @param interval 进度监听器刷新的间隔时间, 单位为毫秒, 默认值为500ms
32 | */
33 | abstract class ProgressListener(var interval: Long = 500) {
34 | // 上次进度变化的的开机时间
35 | var elapsedTime = 0L
36 |
37 | // 距离上次进度变化的新增字节数
38 | var intervalByteCount = 0L
39 |
40 | /**
41 | * 监听上传/下载进度回调函数
42 | * @param p 上传或者下载进度
43 | */
44 | abstract fun onProgress(p: Progress)
45 | }
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/internal/NetInitializer.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.internal
26 |
27 | import android.content.Context
28 | import androidx.startup.Initializer
29 | import com.drake.net.NetConfig
30 |
31 | /**
32 | * 使用AppStartup默认初始化[NetConfig.app], 仅应用主进程下有效
33 | * 在其他进程使用Net请手动在Application中初始化[NetConfig.initialize]
34 | */
35 | internal class NetInitializer : Initializer {
36 | override fun create(context: Context) {
37 | NetConfig.app = context
38 | }
39 |
40 | override fun dependencies(): MutableList>> {
41 | return mutableListOf()
42 | }
43 | }
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/log/MessageType.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.log
26 |
27 | enum class MessageType(var type: String) {
28 | REQUEST_URL("RQU"),
29 | REQUEST_TIME("RQT"),
30 | REQUEST_METHOD("RQM"),
31 | REQUEST_HEADER("RQH"),
32 | REQUEST_BODY("RQB"),
33 | REQUEST_END("RQD"),
34 | RESPONSE_TIME("RST"),
35 | RESPONSE_STATUS("RSS"),
36 | RESPONSE_HEADER("RSH"),
37 | RESPONSE_BODY("RSB"),
38 | RESPONSE_END("RSD"),
39 | RESPONSE_ERROR("REE"),
40 | UNKNOWN("UNKNOWN");
41 | }
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/okhttp/OkHttpExtension.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.okhttp
26 |
27 | import com.drake.net.interceptor.NetOkHttpInterceptor
28 | import okhttp3.OkHttpClient
29 |
30 | /**
31 | * Net要求经过该函数处理创建特殊的OkHttpClient
32 | */
33 | fun OkHttpClient.toNetOkhttp() = run {
34 | if (!interceptors.contains(NetOkHttpInterceptor)) {
35 | newBuilder().addInterceptor(NetOkHttpInterceptor).build()
36 | } else {
37 | this
38 | }
39 | }
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/reflect/TypeUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.reflect
26 |
27 | import kotlin.reflect.javaType
28 | import kotlin.reflect.typeOf
29 |
30 | @OptIn(ExperimentalStdlibApi::class)
31 | inline fun typeTokenOf() = typeOf().javaType
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/request/Method.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.request
26 |
27 | enum class Method {
28 | GET, HEAD, OPTIONS, TRACE, // Url request
29 | POST, DELETE, PUT, PATCH // Body request
30 | }
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/time/IntervalStatus.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.time
26 |
27 | /**
28 | * 计时器的状态
29 | */
30 | enum class IntervalStatus {
31 | STATE_ACTIVE, STATE_IDLE, STATE_PAUSE
32 | }
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/transform/DeferredTransform.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.transform
26 |
27 | import kotlinx.coroutines.Deferred
28 |
29 | /**
30 | * 可以将[Deferred]返回结果进行转换
31 | * [block]在[Deferred]执行成功返回结果时执行
32 | */
33 | fun Deferred.transform(block: (T) -> R): DeferredTransform {
34 | return DeferredTransform(this, block)
35 | }
36 |
37 | data class DeferredTransform(val deferred: Deferred, val block: (T) -> R)
--------------------------------------------------------------------------------
/net/src/main/java/com/drake/net/utils/TipUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * MIT License
3 | *
4 | * Copyright (c) 2023 劉強東 https://github.com/liangjingkanji
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 |
25 | package com.drake.net.utils
26 |
27 | import android.annotation.SuppressLint
28 | import android.widget.Toast
29 | import com.drake.net.NetConfig
30 |
31 | object TipUtils {
32 |
33 | private var toast: Toast? = null
34 |
35 | /**
36 | * 重复显示不会覆盖, 可以在子线程显示
37 | * 本方法会导致报内存泄露, 这是因为为了避免吐司反复显示导致重叠会长期持有Toast引用来保持单例导致, 可以无视或者自己实现吐司
38 | */
39 | @SuppressLint("ShowToast")
40 | @JvmStatic
41 | fun toast(message: String?) {
42 | message ?: return
43 | runMain {
44 | toast?.cancel()
45 | toast = Toast.makeText(NetConfig.app, message, Toast.LENGTH_SHORT)
46 | toast?.show()
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/net/src/main/res/drawable/ic_limited_time.xml:
--------------------------------------------------------------------------------
1 |
24 |
25 |
30 |
33 |
34 |
--------------------------------------------------------------------------------
/net/src/main/res/drawable/ic_timing.xml:
--------------------------------------------------------------------------------
1 |
24 |
25 |
30 |
33 |
36 |
37 |
--------------------------------------------------------------------------------
/net/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /release
3 |
--------------------------------------------------------------------------------
/sample/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/drake/net/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("com.drake.net", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
22 |
23 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/constants/Api.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.constants
2 |
3 |
4 | /*
5 | 建议请求路径都写在一个单例类中, 方便查找和替换
6 | */
7 | object Api {
8 | const val HOST = "http://127.0.0.1:8091"
9 |
10 | const val TEXT = "/text"
11 | const val DELAY = "/delay"
12 | const val UPLOAD = "/upload"
13 | const val GAME = "/game"
14 | const val DATA = "/data"
15 | const val ARRAY = "/array"
16 | const val CONFIG = "/config"
17 | const val USER_INFO = "/userInfo"
18 | const val TIME = "/time"
19 | const val Token = "/token"
20 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/constants/UserConfig.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.constants
2 |
3 | import com.drake.serialize.serialize.annotation.SerializeConfig
4 | import com.drake.serialize.serialize.serialLazy
5 |
6 |
7 | /**
8 | * 本单例类使用 https://github.com/liangjingkanji/Serialize 为字段提供持久化存储
9 | */
10 | @SerializeConfig(mmapID = "user_config")
11 | object UserConfig {
12 |
13 | var token by serialLazy(default = "6cad0ff06f5a214b9cfdf2a4a7c432339")
14 |
15 | var isLogin by serialLazy(default = false)
16 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/contract/AlbumSelectContract.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.contract
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.net.Uri
6 | import android.provider.MediaStore
7 | import androidx.activity.result.contract.ActivityResultContract
8 |
9 | class AlbumSelectContract : ActivityResultContract() {
10 |
11 | override fun createIntent(context: Context, input: Unit?): Intent {
12 | val intent = Intent(Intent.ACTION_PICK)
13 | intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
14 | return intent
15 | }
16 |
17 | class AlbumSelectResult(val code: Int, val uri: Uri?)
18 |
19 | override fun parseResult(resultCode: Int, intent: Intent?): AlbumSelectResult {
20 | return AlbumSelectResult(resultCode, intent?.data)
21 | }
22 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/converter/FastJsonConverter.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.converter
2 |
3 | import com.alibaba.fastjson.JSON
4 | import com.drake.net.convert.JSONConvert
5 | import org.json.JSONObject
6 | import java.lang.reflect.Type
7 |
8 | class FastJsonConverter : JSONConvert(code = "errorCode", message = "errorMsg", success = "0") {
9 |
10 | override fun String.parseBody(succeed: Type): R? {
11 | val string = try {
12 | JSONObject(this).getString("data")
13 | } catch (e: Exception) {
14 | this
15 | }
16 | return JSON.parseObject(string, succeed)
17 | }
18 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/converter/GsonConverter.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.converter
2 |
3 | import com.drake.net.convert.JSONConvert
4 | import com.google.gson.GsonBuilder
5 | import org.json.JSONObject
6 | import java.lang.reflect.Type
7 |
8 | class GsonConverter : JSONConvert(code = "errorCode", message = "errorMsg") {
9 | companion object {
10 | private val gson = GsonBuilder().serializeNulls().create()
11 | }
12 |
13 | override fun String.parseBody(succeed: Type): R? {
14 | val string = try {
15 | JSONObject(this).getString("data")
16 | } catch (e: Exception) {
17 | this
18 | }
19 | return gson.fromJson(string, succeed)
20 | }
21 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/converter/MoshiConverter.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.converter
2 |
3 | import com.drake.net.convert.JSONConvert
4 | import com.squareup.moshi.Moshi
5 | import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
6 | import org.json.JSONObject
7 | import java.lang.reflect.Type
8 |
9 | class MoshiConverter : JSONConvert(code = "errorCode", message = "errorMsg", success = "0") {
10 |
11 | companion object {
12 | private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
13 | }
14 |
15 | override fun String.parseBody(succeed: Type): R? {
16 | val string = try {
17 | JSONObject(this).getString("data")
18 | } catch (e: Exception) {
19 | this
20 | }
21 | return moshi.adapter(succeed).fromJson(string)
22 | }
23 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/converter/ProtobufConverter.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNCHECKED_CAST", "MemberVisibilityCanBePrivate")
2 |
3 | package com.drake.net.sample.converter
4 |
5 | import com.drake.net.convert.NetConverter
6 | import com.drake.net.exception.ConvertException
7 | import com.drake.net.exception.RequestParamsException
8 | import com.drake.net.exception.ServerResponseException
9 | import com.drake.net.request.kType
10 | import kotlinx.serialization.protobuf.ProtoBuf
11 | import kotlinx.serialization.serializer
12 | import okhttp3.Response
13 | import java.lang.reflect.Type
14 |
15 | class ProtobufConverter : NetConverter {
16 |
17 | override fun onConvert(succeed: Type, response: Response): R? {
18 | try {
19 | return NetConverter.onConvert(succeed, response)
20 | } catch (e: ConvertException) {
21 | val code = response.code
22 | when {
23 | code in 200..299 -> { // 请求成功
24 | val bytes = response.body?.bytes() ?: return null
25 | val kType = response.request.kType ?: throw ConvertException(response, "Request does not contain KType")
26 | return ProtoBuf.decodeFromByteArray(ProtoBuf.serializersModule.serializer(kType), bytes) as R
27 | }
28 | code in 400..499 -> throw RequestParamsException(response, code.toString()) // 请求参数错误
29 | code >= 500 -> throw ServerResponseException(response, code.toString()) // 服务器异常错误
30 | else -> throw ConvertException(response, message = "Http status code not within range")
31 | }
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/interceptor/EncryptDataInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.interceptor
2 |
3 | import com.drake.net.request.MediaConst
4 | import com.drake.net.sample.utils.AESUtils
5 | import okhttp3.Interceptor
6 | import okhttp3.RequestBody.Companion.toRequestBody
7 | import okhttp3.Response
8 | import okhttp3.ResponseBody.Companion.toResponseBody
9 | import okio.Buffer
10 |
11 | /**
12 | * 演示如何加密请求参数/解密响应数据
13 | */
14 | class EncryptDataInterceptor : Interceptor {
15 | override fun intercept(chain: Interceptor.Chain): Response {
16 | var request = chain.request()
17 |
18 | // 加密, 仅加密 POST请求/JSON参数类型
19 | if (request.method == "POST" && request.header("Content-Type") == MediaConst.JSON.toString()) {
20 | val body = request.body
21 | if (body != null) {
22 | val buff = Buffer()
23 | body.writeTo(buff)
24 | val json = buff.readUtf8()
25 | if (json.isNotBlank()) {
26 | val encryptJson = AESUtils.encrypt(json)
27 | val newBody = encryptJson.toRequestBody(MediaConst.JSON)
28 | request = request.newBuilder().post(newBody).build()
29 | }
30 | }
31 | }
32 |
33 | var response = chain.proceed(request)
34 |
35 | // 解密, 仅解密JSON响应类型
36 | if (response.header("Content-Type") == MediaConst.JSON.toString()) {
37 | val body = response.body
38 | if (body != null) {
39 | val json = body.string()
40 | if (json.isNotBlank()) {
41 | val decryptJson = AESUtils.decrypt(json)
42 | val requestBody = decryptJson.toResponseBody(MediaConst.JSON)
43 | response = response.newBuilder().body(requestBody).build()
44 | }
45 | }
46 | }
47 |
48 | return response
49 | }
50 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/interceptor/GlobalHeaderInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.interceptor
2 |
3 | import com.drake.net.interceptor.RequestInterceptor
4 | import com.drake.net.request.BaseRequest
5 | import com.drake.net.sample.constants.UserConfig
6 |
7 |
8 | /** 演示添加全局请求头/参数 */
9 | class GlobalHeaderInterceptor : RequestInterceptor {
10 |
11 | /** 本方法每次请求发起都会调用, 这里添加的参数可以是动态参数 */
12 | override fun interceptor(request: BaseRequest) {
13 | request.setHeader("client", "Android")
14 | request.setHeader("token", UserConfig.token)
15 | }
16 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/interceptor/RefreshTokenInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.interceptor
2 |
3 | import com.drake.net.Net
4 | import com.drake.net.exception.ResponseException
5 | import com.drake.net.sample.constants.Api
6 | import com.drake.net.sample.constants.UserConfig
7 | import com.drake.net.sample.model.TokenModel
8 | import okhttp3.Interceptor
9 | import okhttp3.Response
10 |
11 |
12 | /**
13 | * 客户端token自动续期示例
14 | */
15 | class RefreshTokenInterceptor : Interceptor {
16 | override fun intercept(chain: Interceptor.Chain): Response {
17 | val request = chain.request()
18 | val response = chain.proceed(request)
19 | return synchronized(RefreshTokenInterceptor::class.java) {
20 | if (response.code == 401 && UserConfig.isLogin && !request.url.encodedPath.contains(Api.Token)) {
21 | val tokenInfo = Net.get(Api.Token).execute() // 同步请求token
22 | if (tokenInfo.isExpired) {
23 | // token过期抛出异常, 由全局错误处理器处理, 在其中可以跳转到登陆界面提示用户重新登陆
24 | throw ResponseException(response, "登录状态失效")
25 | } else {
26 | UserConfig.token = tokenInfo.token
27 | }
28 | chain.proceed(request)
29 | } else {
30 | response
31 | }
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/model/ArrayData.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.model
2 |
3 | class ArrayData {
4 | var title: String = ""
5 | var name: String = ""
6 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/model/BasicData.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.model
2 |
3 | open class BasicData {
4 | val code: String = ""
5 | val msg: String = ""
6 | val data: T? = null
7 | }
8 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/model/ConfigModel.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.model
2 |
3 |
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class ConfigModel(
8 | var maintain: Boolean = false
9 | )
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/model/GameModel.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.model
2 |
3 | @kotlinx.serialization.Serializable
4 | data class GameModel(
5 | var total: Int = 0,
6 | var list: List = listOf()
7 | ) {
8 |
9 | @kotlinx.serialization.Serializable
10 | data class Data(
11 | var id: Int = 0,
12 | var img: String = "",
13 | var name: String = "",
14 | var label: List = listOf(),
15 | var price: String = "",
16 | var initialPrice: String = "",
17 | var grade: Int = 0,
18 | var discount: Double = 0.0,
19 | var endTime: Int = 0
20 | )
21 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/model/SubData.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.model
2 |
3 | class SubData : BasicData() {
4 | var title: String = ""
5 | var name: String = ""
6 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/model/TokenModel.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.model
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class TokenModel(
7 | var token: String = "",
8 | var isExpired: Boolean = false
9 | )
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/model/UserInfoModel.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.model
2 |
3 |
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class UserInfoModel(
8 | var userId: Int = 0,
9 | var username: String = "",
10 | var age: Int = 0,
11 | var balance: String = ""
12 | )
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/activity/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.activity
2 |
3 | import androidx.core.view.GravityCompat
4 | import androidx.navigation.findNavController
5 | import androidx.navigation.fragment.FragmentNavigator
6 | import androidx.navigation.ui.AppBarConfiguration
7 | import androidx.navigation.ui.setupWithNavController
8 | import com.bumptech.glide.Glide
9 | import com.drake.engine.base.EngineActivity
10 | import com.drake.net.sample.R
11 | import com.drake.net.sample.databinding.ActivityMainBinding
12 | import com.drake.statusbar.immersive
13 |
14 | /**
15 | * 以下代码设置导航, 和框架本身无关无需关心, 请查看[com.drake.net.sample.ui.fragment]内的Fragment
16 | */
17 | class MainActivity : EngineActivity(R.layout.activity_main) {
18 |
19 | override fun initView() {
20 | immersive(binding.toolbar, true)
21 | setSupportActionBar(binding.toolbar)
22 | val navController = findNavController(R.id.nav)
23 |
24 | Glide.with(this)
25 | .load("https://avatars.githubusercontent.com/u/21078112?v=4")
26 | .circleCrop()
27 | .into(binding.drawerNav.getHeaderView(0).findViewById(R.id.iv))
28 |
29 | binding.toolbar.setupWithNavController(
30 | navController,
31 | AppBarConfiguration(binding.drawerNav.menu, binding.drawer)
32 | )
33 | navController.addOnDestinationChangedListener { _, destination, _ ->
34 | binding.toolbar.subtitle =
35 | (destination as FragmentNavigator.Destination).className.substringAfterLast('.')
36 | }
37 | binding.drawerNav.setupWithNavController(navController)
38 | }
39 |
40 | override fun initData() {
41 | }
42 |
43 | override fun onBackPressed() {
44 | if (binding.drawer.isDrawerOpen(GravityCompat.START)) binding.drawer.closeDrawers() else super.onBackPressed()
45 | }
46 | }
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/AsyncTaskFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import com.drake.engine.base.EngineFragment
4 | import com.drake.net.sample.R
5 | import com.drake.net.sample.databinding.FragmentAsyncTaskBinding
6 | import com.drake.net.utils.scope
7 | import kotlinx.coroutines.*
8 |
9 | class AsyncTaskFragment : EngineFragment(R.layout.fragment_async_task) {
10 |
11 | override fun initView() {
12 | scope {
13 | binding.tvFragment.text = withContext(Dispatchers.IO) {
14 | delay(2000)
15 | "结果"
16 | }
17 | }
18 | }
19 |
20 | override fun initData() {
21 | }
22 |
23 | /**
24 | * 抽出异步任务为一个函数
25 | */
26 | private suspend fun withDownloadFile() = withContext(Dispatchers.IO) {
27 | delay(200)
28 | "结果"
29 | }
30 |
31 | private fun CoroutineScope.asyncDownloadFile() = async {
32 | "结果"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/AutoDialogFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import com.drake.engine.base.EngineFragment
4 | import com.drake.net.Post
5 | import com.drake.net.sample.R
6 | import com.drake.net.sample.constants.Api
7 | import com.drake.net.sample.databinding.FragmentAutoDialogBinding
8 | import com.drake.net.utils.scopeDialog
9 | import com.drake.tooltip.toast
10 | import kotlinx.coroutines.CancellationException
11 |
12 |
13 | class AutoDialogFragment :
14 | EngineFragment(R.layout.fragment_auto_dialog) {
15 |
16 | override fun initView() {
17 | scopeDialog {
18 | binding.tvFragment.text = Post(Api.DELAY) {
19 | param("username", "你的账号")
20 | param("password", "123456")
21 | }.await()
22 | }.finally {
23 | // 关闭对话框后执行的异常
24 | if (it is CancellationException) {
25 | toast("对话框被关闭, 网络请求自动取消") // 这里存在Handler吐司崩溃, 如果不想处理就直接使用我的吐司库 https://github.com/liangjingkanji/Tooltip
26 | }
27 | }
28 | }
29 |
30 | override fun initData() {
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/CallbackRequestFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import com.drake.engine.base.EngineFragment
4 | import com.drake.net.Net
5 | import com.drake.net.sample.R
6 | import com.drake.net.sample.constants.Api
7 | import com.drake.net.sample.databinding.FragmentCallbackRequestBinding
8 | import com.drake.net.utils.runMain
9 | import okhttp3.Call
10 | import okhttp3.Callback
11 | import okhttp3.Response
12 | import java.io.IOException
13 |
14 | class CallbackRequestFragment :
15 | EngineFragment(R.layout.fragment_callback_request) {
16 |
17 | override fun initData() {
18 | }
19 |
20 | override fun initView() {
21 | // Net同样支持OkHttp原始的队列任务
22 | Net.post(Api.TEXT).enqueue(object : Callback {
23 | override fun onFailure(call: Call, e: IOException) {
24 | }
25 |
26 | override fun onResponse(call: Call, response: Response) {
27 | // 此处为子线程
28 | val body = response.body?.string() ?: "无数据"
29 | runMain {
30 | // 此处为主线程
31 | binding.tvFragment.text = body
32 | }
33 | }
34 | })
35 | }
36 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/CoroutineScopeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import androidx.lifecycle.Lifecycle
4 | import com.drake.engine.base.EngineFragment
5 | import com.drake.net.sample.R
6 | import com.drake.net.sample.databinding.FragmentCoroutineScopeBinding
7 | import com.drake.net.utils.scope
8 | import com.drake.net.utils.scopeLife
9 | import com.drake.net.utils.scopeNet
10 | import com.drake.net.utils.scopeNetLife
11 | import kotlinx.coroutines.delay
12 |
13 |
14 | class CoroutineScopeFragment :
15 | EngineFragment(R.layout.fragment_coroutine_scope) {
16 | override fun initData() {
17 | // 其作用域在应用进程销毁时才会被动取消
18 | scope {
19 |
20 | }
21 |
22 | // 其作用域在Activity或者Fragment销毁(onDestroy)时被动取消 [scopeNetLife]
23 | scopeLife {
24 | delay(2000)
25 | binding.tvFragment.text = "任务结束"
26 | }
27 |
28 | // 自定义取消跟随的生命周期, 失去焦点时立即取消作用域
29 | scopeLife(Lifecycle.Event.ON_PAUSE) {
30 |
31 | }
32 |
33 | // 此作用域会捕捉发生的异常, 如果是网络异常会进入网络异常的全局处理函数, 例如自动弹出吐司 [NetConfig.onError]
34 | scopeNet {
35 |
36 | }
37 |
38 | // 自动网络处理 + 生命周期管理
39 | scopeNetLife {
40 |
41 | }
42 | }
43 |
44 | override fun initView() {
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/ErrorHandlerFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import com.drake.engine.base.EngineFragment
4 | import com.drake.net.Get
5 | import com.drake.net.sample.R
6 | import com.drake.net.sample.databinding.FragmentErrorHandlerBinding
7 | import com.drake.net.utils.scopeNetLife
8 |
9 |
10 | class ErrorHandlerFragment :
11 | EngineFragment(R.layout.fragment_error_handler) {
12 |
13 | override fun initView() {
14 | scopeNetLife {
15 | // 该请求是错误的路径会在控制台打印出错误信息
16 | Get("error").await()
17 | }.catch {
18 | // 重写该函数后, 错误不会流到[NetConfig.onError]中的全局错误处理, 在App.kt中可以自定义该全局处理, 同时包含onStateError
19 | binding.tvFragment.text = it.message
20 | }
21 | }
22 |
23 | override fun initData() {
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/ExceptionTraceFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import com.drake.engine.base.EngineFragment
4 | import com.drake.net.Get
5 | import com.drake.net.sample.R
6 | import com.drake.net.sample.databinding.FragmentExceptionTraceBinding
7 | import com.drake.net.utils.scopeNetLife
8 |
9 |
10 | class ExceptionTraceFragment :
11 | EngineFragment(R.layout.fragment_exception_trace) {
12 |
13 | override fun initView() {
14 | scopeNetLife {
15 | // 这是一个错误的地址, 请查看LogCat的错误信息, 在[initNet]函数中的[onError]回调中你也可以进行自定义错误信息打印
16 | binding.tvFragment.text =
17 | Get("https://githuberror.com/liangjingkanji/Net/").await()
18 | }
19 | }
20 |
21 | override fun initData() {
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/HttpsCertificateFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import android.view.View
4 | import com.drake.engine.base.EngineFragment
5 | import com.drake.net.Get
6 | import com.drake.net.okhttp.setSSLCertificate
7 | import com.drake.net.okhttp.trustSSLCertificate
8 | import com.drake.net.sample.R
9 | import com.drake.net.sample.databinding.FragmentHttpsCertificateBinding
10 | import com.drake.net.utils.scopeNetLife
11 | import com.drake.tooltip.toast
12 | import okhttp3.OkHttpClient
13 |
14 | class HttpsCertificateFragment :
15 | EngineFragment(R.layout.fragment_https_certificate) {
16 |
17 | override fun initView() {
18 | binding.btnTrustCertificate.setOnClickListener(this::trustAllCertificate)
19 | binding.btnImportCertificate.setOnClickListener(this::importCertificate)
20 | }
21 |
22 | override fun initData() {
23 | }
24 |
25 | /**
26 | * 信任全部证书
27 | * 大部分情况下还是建议在Application中配置一次全局的证书
28 | */
29 | private fun trustAllCertificate(view: View) {
30 | scopeNetLife {
31 | binding.tvResponse.text = Get("https://github.com/liangjingkanji/Net/") {
32 | // 构建新的客户端
33 | okHttpClient = OkHttpClient.Builder().trustSSLCertificate().build()
34 | }.await()
35 | }
36 | }
37 |
38 | /**
39 | * 导入私有证书
40 | */
41 | private fun importCertificate(view: View) {
42 | scopeNetLife {
43 | Get("https://github.com/liangjingkanji/Net/") {
44 | // 使用现在客户端
45 | setClient {
46 | val privateCertificate = resources.assets.open("https.certificate")
47 | setSSLCertificate(privateCertificate)
48 | }
49 | }.await()
50 | }.catch {
51 | toast("作者没有证书, 只是演示代码, O(∩_∩)O哈哈~")
52 | }
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/InterceptorFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import com.drake.engine.base.EngineFragment
4 | import com.drake.net.Get
5 | import com.drake.net.sample.R
6 | import com.drake.net.sample.constants.Api
7 | import com.drake.net.sample.databinding.FragmentInterceptorBinding
8 | import com.drake.net.utils.scopeNetLife
9 |
10 |
11 | class InterceptorFragment :
12 | EngineFragment(R.layout.fragment_interceptor) {
13 |
14 | override fun initView() {
15 | scopeNetLife {
16 | binding.tvFragment.text = Get(Api.TEXT) {
17 | // 拦截器只支持全局, 无法单例, 请查看[com.drake.net.sample.interceptor.NetInterceptor]
18 | }.await()
19 | }
20 | }
21 |
22 | override fun initData() {
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/LimitedTimeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import android.util.Log
4 | import android.view.View
5 | import com.drake.engine.base.EngineFragment
6 | import com.drake.net.Get
7 | import com.drake.net.sample.R
8 | import com.drake.net.sample.constants.Api
9 | import com.drake.net.sample.databinding.FragmentLimitedTimeBinding
10 | import com.drake.net.utils.scopeDialog
11 | import com.drake.tooltip.toast
12 | import kotlinx.coroutines.TimeoutCancellationException
13 | import kotlinx.coroutines.withTimeout
14 |
15 | class LimitedTimeFragment : EngineFragment(R.layout.fragment_limited_time) {
16 | override fun initView() {
17 | binding.v = this
18 | }
19 |
20 | override fun initData() {
21 | }
22 |
23 | override fun onClick(v: View) {
24 | scopeDialog {
25 | // 当接口请求在100毫秒内没有完成会抛出异常TimeoutCancellationException
26 | withTimeout(100) {
27 | Get(Api.TEXT).await()
28 | }
29 | }.catch {
30 | Log.e("日志", "catch", it) // catch无法接收到CancellationException异常
31 | }.finally {
32 | Log.e("日志", "finally", it) // TimeoutCancellationException属于CancellationException子类故只会被finally接收到
33 | if (it is TimeoutCancellationException) {
34 | toast("由于未在指定时间完成请求则取消请求")
35 | }
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/ParallelNetworkFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import com.drake.engine.base.EngineFragment
4 | import com.drake.net.Get
5 | import com.drake.net.Post
6 | import com.drake.net.Trace
7 | import com.drake.net.sample.R
8 | import com.drake.net.sample.constants.Api
9 | import com.drake.net.sample.databinding.FragmentParallelNetworkBinding
10 | import com.drake.net.utils.scopeNetLife
11 |
12 |
13 | class ParallelNetworkFragment :
14 | EngineFragment(R.layout.fragment_parallel_network) {
15 |
16 | override fun initView() {
17 | scopeNetLife {
18 |
19 | // 同时发起三个请求
20 | val deferred = Get(Api.TEXT)
21 | val deferred1 = Post(Api.TEXT)
22 | val deferred2 = Trace(Api.TEXT)
23 |
24 | // 同时接收三个请求数据
25 | deferred.await()
26 | deferred1.await()
27 | deferred2.await()
28 | }
29 | }
30 |
31 | override fun initData() {
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/PreviewCacheFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import android.util.Log
4 | import com.drake.engine.base.EngineFragment
5 | import com.drake.net.Get
6 | import com.drake.net.cache.CacheMode
7 | import com.drake.net.sample.R
8 | import com.drake.net.sample.constants.Api
9 | import com.drake.net.sample.databinding.FragmentReadCacheBinding
10 | import com.drake.net.utils.scopeNetLife
11 |
12 |
13 | /** 预览缓存. 其实不仅仅是双重加载缓存/网络也可以用于回退请求, 可以执行两次作用域并且忽略preview{}内的所有错误 */
14 | class PreviewCacheFragment : EngineFragment(R.layout.fragment_read_cache) {
15 |
16 | override fun initView() {
17 |
18 | // 一般用于秒开首页或者回退加载数据. 我们可以在preview{}只加载缓存. 然后再执行scopeNetLife来请求网络, 做到缓存+网络双重加载的效果
19 |
20 | scopeNetLife {
21 | // 然后执行这里(网络请求)
22 | binding.tvFragment.text = Get(Api.TEXT) {
23 | setCacheMode(CacheMode.WRITE)
24 | }.await()
25 | Log.d("日志", "网络请求")
26 | }.preview(true) {
27 | // 先执行这里(仅读缓存), 任何异常都视为读取缓存失败
28 | binding.tvFragment.text = Get(Api.TEXT) {
29 | setCacheMode(CacheMode.READ)
30 | }.await()
31 | Log.d("日志", "读取缓存")
32 | }
33 | }
34 |
35 | override fun initData() {
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/PullRefreshFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import com.drake.brv.utils.linear
4 | import com.drake.brv.utils.setup
5 | import com.drake.engine.base.EngineFragment
6 | import com.drake.net.Get
7 | import com.drake.net.sample.R
8 | import com.drake.net.sample.constants.Api
9 | import com.drake.net.sample.databinding.FragmentPullRefreshBinding
10 | import com.drake.net.sample.model.GameModel
11 | import com.drake.net.utils.scope
12 |
13 |
14 | class PullRefreshFragment :
15 | EngineFragment(R.layout.fragment_pull_refresh) {
16 |
17 | override fun initView() {
18 | binding.rv.linear().setup {
19 | addType(R.layout.item_pull_list)
20 | }
21 |
22 | binding.page.onRefresh {
23 | scope {
24 | val response = Get(String.format(Api.GAME, index)).await()
25 | addData(response.list) {
26 | itemCount < response.total
27 | }
28 | }
29 | }.autoRefresh()
30 | }
31 |
32 | override fun initData() {
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/PushRefreshFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import com.drake.brv.utils.linear
4 | import com.drake.brv.utils.models
5 | import com.drake.brv.utils.setup
6 | import com.drake.engine.base.EngineFragment
7 | import com.drake.net.Get
8 | import com.drake.net.sample.R
9 | import com.drake.net.sample.constants.Api
10 | import com.drake.net.sample.databinding.FragmentPushRefreshBinding
11 | import com.drake.net.sample.model.GameModel
12 | import com.drake.net.utils.scope
13 |
14 | /** 本页面已禁用上拉加载(添加xml属性app:srlEnableLoadMore="false"), 只允许下拉刷新 */
15 | class PushRefreshFragment :
16 | EngineFragment(R.layout.fragment_push_refresh) {
17 |
18 | override fun initView() {
19 | binding.rv.linear().setup {
20 | addType(R.layout.item_game)
21 | }
22 |
23 | binding.page.onRefresh {
24 | scope {
25 | binding.rv.models = Get(Api.GAME).await().list
26 | }
27 | // }.autoRefresh() // 首次下拉刷新
28 | }.showLoading() // 首次加载缺省页
29 | }
30 |
31 | override fun initData() {
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/ReadCacheFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import com.drake.engine.base.EngineFragment
4 | import com.drake.net.Post
5 | import com.drake.net.cache.CacheMode
6 | import com.drake.net.sample.R
7 | import com.drake.net.sample.constants.Api
8 | import com.drake.net.sample.databinding.FragmentReadCacheBinding
9 | import com.drake.net.utils.scopeNetLife
10 |
11 |
12 | /**
13 | * 默认支持Http标准缓存协议
14 | * 如果需要自定义缓存模式来强制读写缓存,可以使用[CacheMode], 这会覆盖默认的Http标准缓存协议.
15 | * 可以缓存任何数据, 包括文件. 并且遵守LRU缓存策略限制最大缓存空间
16 | */
17 | class ReadCacheFragment : EngineFragment(R.layout.fragment_read_cache) {
18 |
19 | override fun initView() {
20 | scopeNetLife {
21 | binding.tvFragment.text =
22 | Post(Api.TEXT) {
23 | setCacheMode(CacheMode.REQUEST_THEN_READ) // 请求网络失败会读取缓存, 请断网测试
24 | // setCacheKey("自定义缓存KEY")
25 | }.await()
26 | }
27 | }
28 |
29 | override fun initData() {
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/StateLayoutFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import com.drake.engine.base.EngineFragment
4 | import com.drake.net.Get
5 | import com.drake.net.sample.R
6 | import com.drake.net.sample.constants.Api
7 | import com.drake.net.sample.databinding.FragmentStateLayoutBinding
8 | import com.drake.net.utils.scope
9 |
10 |
11 | class StateLayoutFragment :
12 | EngineFragment(R.layout.fragment_state_layout) {
13 |
14 | override fun initData() {
15 | }
16 |
17 | override fun initView() {
18 | binding.state.onRefresh {
19 | scope {
20 | binding.tvFragment.text = Get(Api.TEXT).await()
21 | }
22 | }.showLoading()
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/SuperIntervalFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import android.view.Menu
4 | import android.view.MenuInflater
5 | import android.view.MenuItem
6 | import com.drake.engine.base.EngineFragment
7 | import com.drake.net.sample.R
8 | import com.drake.net.sample.databinding.FragmentSuperIntervalBinding
9 | import com.drake.net.time.Interval
10 | import java.util.concurrent.TimeUnit
11 |
12 |
13 | class SuperIntervalFragment :
14 | EngineFragment(R.layout.fragment_super_interval) {
15 |
16 | private lateinit var interval: Interval // 轮询器
17 |
18 | override fun initView() {
19 | setHasOptionsMenu(true)
20 | // 自定义计数器个数的轮询器, 当[start]]比[end]值大, 且end不等于-1时, 即为倒计时
21 | interval = Interval(0, 1, TimeUnit.SECONDS, 10).life(this)
22 | // interval = Interval(1, TimeUnit.SECONDS) // 每秒回调一次, 不会自动结束
23 | interval.subscribe {
24 | binding.tvFragment.text = it.toString()
25 | }.finish {
26 | binding.tvFragment.text = "计时完成"
27 | }.start()
28 | }
29 |
30 | override fun initData() {
31 | }
32 |
33 | override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
34 | super.onCreateOptionsMenu(menu, inflater)
35 | inflater.inflate(R.menu.menu_interval, menu)
36 | }
37 |
38 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
39 | when (item.itemId) {
40 | R.id.start -> interval.start()
41 | R.id.pause -> interval.pause()
42 | R.id.resume -> interval.resume()
43 | R.id.reset -> interval.reset()
44 | R.id.switch_interval -> interval.switch()
45 | R.id.stop -> interval.stop()
46 | R.id.cancel -> interval.cancel()
47 | }
48 | return super.onOptionsItemSelected(item)
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/SwitchDispatcherFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import com.drake.engine.base.EngineFragment
4 | import com.drake.net.sample.R
5 | import com.drake.net.sample.databinding.FragmentSwitchDispatcherBinding
6 | import com.drake.net.utils.scopeLife
7 | import com.drake.net.utils.withIO
8 | import com.drake.net.utils.withMain
9 | import kotlinx.coroutines.launch
10 |
11 |
12 | class SwitchDispatcherFragment :
13 | EngineFragment(R.layout.fragment_switch_dispatcher) {
14 |
15 | override fun initView() {
16 | scopeLife {
17 |
18 | // 点击函数名查看更多相关函数
19 | launch {
20 | val data = withMain {
21 | "异步调度器切换到主线程"
22 | }
23 | }
24 |
25 | val data = withIO {
26 | "主线程切换到IO调度器"
27 | }
28 | }
29 | }
30 |
31 | override fun initData() {
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/SyncRequestFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import com.drake.engine.base.EngineFragment
4 | import com.drake.net.Net
5 | import com.drake.net.sample.R
6 | import com.drake.net.sample.constants.Api
7 | import com.drake.net.sample.databinding.FragmentSyncRequestBinding
8 | import kotlin.concurrent.thread
9 |
10 | class SyncRequestFragment :
11 | EngineFragment(R.layout.fragment_sync_request) {
12 |
13 | override fun initView() {
14 | thread { // 网络请求不允许在主线程
15 | val result = try {
16 | Net.post(Api.TEXT).execute()
17 | } catch (e: Exception) { // 同步请求失败会导致崩溃要求捕获异常
18 | "请求错误 = ${e.message}"
19 | }
20 |
21 | // val result = Net.post(Api.BANNER).toResult().getOrDefault("请求发生错误, 我这是默认值")
22 |
23 | binding.tvFragment?.post { // 这里用?号是避免界面被销毁依然赋值
24 | binding.tvFragment?.text = result // view要求在主线程更新
25 | }
26 | }
27 | }
28 |
29 | override fun initData() {
30 | }
31 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/UniqueRequestFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import android.util.Log
4 | import com.drake.engine.base.EngineFragment
5 | import com.drake.net.Post
6 | import com.drake.net.sample.R
7 | import com.drake.net.sample.constants.Api
8 | import com.drake.net.sample.databinding.FragmentUniqueRequestBinding
9 | import com.drake.net.scope.AndroidScope
10 | import com.drake.net.utils.scopeNetLife
11 |
12 | class UniqueRequestFragment :
13 | EngineFragment(R.layout.fragment_unique_request) {
14 |
15 | private var scope: AndroidScope? = null
16 |
17 | override fun initView() {
18 | binding.btnRequest.setOnClickListener {
19 | binding.tvResult.text = "请求中"
20 | scope?.cancel() // 如果存在则取消
21 |
22 | scope = scopeNetLife {
23 | val result = Post(Api.TEXT).await()
24 | Log.d("日志", "请求到结果") // 你一直重复点击"发起请求"按钮会发现永远无法拿到请求结果, 因为每次发起新的请求会取消未完成的
25 | binding.tvResult.text = result
26 | }
27 | }
28 | }
29 |
30 | override fun initData() {
31 | }
32 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/ViewModelRequestFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment
2 |
3 | import androidx.fragment.app.viewModels
4 | import com.drake.engine.base.EngineFragment
5 | import com.drake.net.sample.R
6 | import com.drake.net.sample.databinding.FragmentViewModelRequestBinding
7 | import com.drake.net.sample.utils.HttpUtils
8 | import com.drake.net.sample.vm.UserViewModel
9 | import com.drake.net.utils.scopeNetLife
10 |
11 | class ViewModelRequestFragment :
12 | EngineFragment(R.layout.fragment_view_model_request) {
13 |
14 | private val userViewModel: UserViewModel by viewModels() // 创建ViewModel
15 |
16 | override fun initView() {
17 |
18 | // 直接将用户信息绑定到视图上
19 | binding.lifecycleOwner = this
20 | binding.m = userViewModel
21 |
22 | // 动作开始拉取服务器数据
23 | binding.btnFetchUserinfo.setOnClickListener {
24 | userViewModel.fetchUserInfo()
25 | }
26 | }
27 |
28 | override fun initData() {
29 |
30 | scopeNetLife {
31 | val configAsync = HttpUtils.getConfigAsync(this)
32 | // 经常使用的请求可以封装函数
33 | val userInfo = HttpUtils.getUser()
34 | configAsync.await() // 实际上在getUser之前就发起请求, 此处只是等待结果, 这就是并发请求
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/converter/BaseConvertFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment.converter
2 |
3 | import android.os.Bundle
4 | import android.view.Menu
5 | import android.view.MenuInflater
6 | import android.view.MenuItem
7 | import androidx.annotation.LayoutRes
8 | import androidx.databinding.ViewDataBinding
9 | import androidx.navigation.fragment.findNavController
10 | import androidx.navigation.ui.onNavDestinationSelected
11 | import com.drake.engine.base.EngineFragment
12 | import com.drake.net.sample.R
13 |
14 | abstract class BaseConvertFragment(@LayoutRes contentLayoutId: Int = 0) :
15 | EngineFragment(contentLayoutId) {
16 |
17 | override fun onCreate(savedInstanceState: Bundle?) {
18 | super.onCreate(savedInstanceState)
19 | setHasOptionsMenu(true)
20 | }
21 |
22 | override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
23 | super.onCreateOptionsMenu(menu, inflater)
24 | inflater.inflate(R.menu.menu_converter, menu)
25 | }
26 |
27 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
28 | item.onNavDestinationSelected(findNavController())
29 | return true
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/converter/FastJsonConvertFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment.converter
2 |
3 | import com.drake.net.Get
4 | import com.drake.net.sample.R
5 | import com.drake.net.sample.constants.Api
6 | import com.drake.net.sample.converter.FastJsonConverter
7 | import com.drake.net.sample.databinding.FragmentCustomConvertBinding
8 | import com.drake.net.sample.model.GameModel
9 | import com.drake.net.utils.scopeNetLife
10 |
11 |
12 | class FastJsonConvertFragment :
13 | BaseConvertFragment(R.layout.fragment_custom_convert) {
14 |
15 | override fun initView() {
16 | binding.tvConvertTip.text = """
17 | 1. 阿里巴巴出品的Json解析库
18 | 2. 引入kotlin-reflect库可以支持kotlin默认值
19 | """.trimIndent()
20 |
21 | scopeNetLife {
22 | binding.tvFragment.text = Get(Api.GAME) {
23 | converter = FastJsonConverter() // 单例转换器, 此时会忽略全局转换器
24 | }.await().list[0].name
25 | }
26 | }
27 |
28 | override fun initData() {
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/converter/GsonConvertFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment.converter
2 |
3 | import com.drake.net.Get
4 | import com.drake.net.sample.R
5 | import com.drake.net.sample.constants.Api
6 | import com.drake.net.sample.converter.GsonConverter
7 | import com.drake.net.sample.databinding.FragmentCustomConvertBinding
8 | import com.drake.net.sample.model.GameModel
9 | import com.drake.net.utils.scopeNetLife
10 |
11 |
12 | class GsonConvertFragment :
13 | BaseConvertFragment(R.layout.fragment_custom_convert) {
14 |
15 | override fun initView() {
16 | binding.tvConvertTip.text = """
17 | 1. Google官方出品
18 | 2. Json解析库Java上的老牌解析库
19 | 3. 不支持Kotlin构造参数默认值
20 | 4. 支持动态解析
21 | """.trimIndent()
22 |
23 | scopeNetLife {
24 | binding.tvFragment.text = Get(Api.GAME) {
25 | converter = GsonConverter() // 单例转换器, 此时会忽略全局转换器, 在Net中可以直接解析List等嵌套泛型数据
26 | }.await().list[0].name
27 | }
28 | }
29 |
30 | override fun initData() {
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/converter/MoshiConvertFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment.converter
2 |
3 | import com.drake.net.Get
4 | import com.drake.net.sample.R
5 | import com.drake.net.sample.constants.Api
6 | import com.drake.net.sample.converter.MoshiConverter
7 | import com.drake.net.sample.databinding.FragmentCustomConvertBinding
8 | import com.drake.net.sample.model.GameModel
9 | import com.drake.net.utils.scopeNetLife
10 |
11 |
12 | class MoshiConvertFragment :
13 | BaseConvertFragment(R.layout.fragment_custom_convert) {
14 |
15 | override fun initView() {
16 | binding.tvConvertTip.text = """
17 | 1. Square出品的JSON解析库
18 | 2. 支持Kotlin构造默认值
19 | 3. 具备注解和反射两种使用方式
20 | 4. 非可选类型反序列化时赋值Null会抛出异常
21 | 5, 不支持动态解析
22 | """.trimIndent()
23 |
24 | scopeNetLife {
25 | binding.tvFragment.text = Get(Api.GAME) {
26 | converter = MoshiConverter() // 单例转换器, 此时会忽略全局转换器
27 | }.await().list[0].name
28 | }
29 | }
30 |
31 | override fun initData() {
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/ui/fragment/converter/SerializationConvertFragment.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.ui.fragment.converter
2 |
3 | import com.drake.net.Get
4 | import com.drake.net.sample.R
5 | import com.drake.net.sample.constants.Api
6 | import com.drake.net.sample.converter.SerializationConverter
7 | import com.drake.net.sample.databinding.FragmentCustomConvertBinding
8 | import com.drake.net.sample.model.GameModel
9 | import com.drake.net.utils.scopeNetLife
10 |
11 | class SerializationConvertFragment :
12 | BaseConvertFragment(R.layout.fragment_custom_convert) {
13 |
14 | override fun initView() {
15 | binding.tvConvertTip.text = """
16 | 1. kotlin官方出品, 推荐使用
17 | 2. kotlinx.serialization 是Kotlin上是最完美的序列化工具
18 | 3. 支持多种反序列化数据类型Pair/枚举/Map...
19 | 4. 多配置选项
20 | 5. 支持动态解析
21 | 6. 支持ProtoBuf/CBOR/JSON等数据
22 | """.trimIndent()
23 |
24 | scopeNetLife {
25 | val data = Get(Api.GAME) {
26 | // 该转换器直接解析JSON中的data字段, 而非返回的整个JSON字符串
27 | converter = SerializationConverter() // 单例转换器, 此时会忽略全局转换器
28 | }.await()
29 |
30 | binding.tvFragment.text = data.list[0].name
31 | }
32 | }
33 |
34 | override fun initData() {
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/utils/AESUtils.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.utils
2 |
3 | import okio.ByteString
4 | import okio.ByteString.Companion.decodeHex
5 | import javax.crypto.Cipher
6 | import javax.crypto.spec.SecretKeySpec
7 |
8 | object AESUtils {
9 |
10 | private const val KEY = "123456789"
11 | private const val IV = "123456789"
12 |
13 | fun encrypt(data: String): String {
14 | val key = KEY.decodeHex()
15 | val iv = IV.decodeHex()
16 | val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
17 | val keySpec = SecretKeySpec(key.toByteArray(), "AES")
18 | val ivSpec = javax.crypto.spec.IvParameterSpec(iv.toByteArray())
19 | cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
20 | val encrypted = cipher.doFinal(data.toByteArray())
21 | return ByteString.of(*encrypted).hex()
22 | }
23 |
24 | fun decrypt(data: String): String {
25 | val key = KEY.decodeHex()
26 | val iv = IV.decodeHex()
27 | val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
28 | val keySpec = SecretKeySpec(key.toByteArray(), "AES")
29 | val ivSpec = javax.crypto.spec.IvParameterSpec(iv.toByteArray())
30 | cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec)
31 | val encrypted = cipher.doFinal(data.decodeHex().toByteArray())
32 | return String(encrypted)
33 | }
34 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/utils/HttpUtils.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.utils
2 |
3 | import com.drake.net.Get
4 | import com.drake.net.sample.constants.Api
5 | import com.drake.net.sample.model.ConfigModel
6 | import com.drake.net.sample.model.UserInfoModel
7 | import kotlinx.coroutines.CoroutineScope
8 | import kotlinx.coroutines.coroutineScope
9 |
10 |
11 | /**
12 | * 常用的请求方法建议写到一个工具类中
13 | */
14 | object HttpUtils {
15 |
16 | /**
17 | * 获取配置信息
18 | *
19 | * 本方法需要再调用await()才会返回结果, 属于异步方法
20 | */
21 | fun getConfigAsync(scope: CoroutineScope) = scope.Get(Api.CONFIG)
22 |
23 | /**
24 | * 获取用户信息
25 | * 阻塞返回可直接返回结果
26 | *
27 | * @param userId 如果为空表示请求自身用户信息
28 | */
29 | suspend fun getUser(userId: String? = null) = coroutineScope {
30 | Get(Api.USER_INFO) {
31 | param("userId", userId)
32 | }.await()
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/sample/src/main/java/com/drake/net/sample/vm/UserViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net.sample.vm
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.ViewModel
5 | import androidx.lifecycle.scopeNetLife
6 | import com.drake.net.Get
7 | import com.drake.net.sample.constants.Api
8 | import kotlinx.coroutines.CoroutineScope
9 | import kotlinx.coroutines.coroutineScope
10 |
11 |
12 | /**
13 | * 不要将请求结果抛来抛去, 增加代码复杂度
14 | */
15 | class UserViewModel : ViewModel() {
16 |
17 | // 用户信息
18 | var userInfo: MutableLiveData = MutableLiveData()
19 |
20 | /**
21 | * 使用LiveData接受请求结果, 将该liveData直接使用DataBinding绑定到页面上, 会在请求成功自动更新视图
22 | */
23 | fun fetchUserInfo() = scopeNetLife {
24 | userInfo.value = Get(Api.GAME).await()
25 | }
26 |
27 | /**
28 | * 开始非阻塞异步任务
29 | * 返回Deferred, 调用await()才会返回结果
30 | */
31 | fun fetchList(scope: CoroutineScope) = scope.Get(Api.TEXT)
32 |
33 | /**
34 | * 开始阻塞异步任务
35 | * 直接返回结果
36 | */
37 | suspend fun fetchPrecessData() = coroutineScope {
38 | val response = Get(Api.TEXT).await()
39 | response + "处理数据"
40 | }
41 | }
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/bg_card.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/bg_empty.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/sample/src/main/res/drawable/bg_empty.webp
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/bg_error.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/sample/src/main/res/drawable/bg_error.webp
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/bg_input.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_async_task.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_callback_request.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_config_dialog.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_convert.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_debounce.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_download_file.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_error_handler.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_exception_trace.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_fastest.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_https.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_interceptor.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_interval.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_menu.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_parallel_network.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_pull_refresh.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_push_refresh.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_read_cache.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_scope.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_simple_request.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_state_layout.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_switch_dispatcher.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_sync_request.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_unique.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_upload_file.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_view_model.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
13 |
17 |
18 |
25 |
26 |
33 |
34 |
35 |
36 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_async_task.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_auto_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_callback_request.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
10 |
11 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_coroutine_scope.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_custom_convert.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
21 |
22 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_download_file.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
12 |
13 |
27 |
28 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_edit_debounce.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
12 |
13 |
20 |
21 |
22 |
27 |
28 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_error_handler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_exception_trace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_fastest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_https_certificate.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
12 |
13 |
18 |
19 |
24 |
25 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_interceptor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_limited_time.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
18 |
19 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_parallel_network.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_pull_refresh.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
12 |
13 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_push_refresh.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
14 |
15 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_read_cache.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_simple_request.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_state_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
12 |
13 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_super_interval.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_switch_dispatcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
12 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_sync_request.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
10 |
11 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_timing_request.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
20 |
21 |
27 |
28 |
34 |
35 |
41 |
42 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_unique_request.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
11 |
12 |
20 |
21 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_upload_file.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
13 |
14 |
19 |
20 |
25 |
26 |
35 |
36 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_view_model_request.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
19 |
20 |
26 |
27 |
35 |
36 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/item_game.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
16 |
17 |
24 |
25 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/item_pull_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
16 |
17 |
24 |
25 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/layout_drawer_nav_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
10 |
11 |
21 |
22 |
26 |
27 |
35 |
36 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/layout_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
18 |
19 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/layout_error.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
20 |
21 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/layout_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/menu_converter.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/menu_download.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/menu_interval.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/menu_request_method.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/sample/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/sample/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/sample/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/navigation/nav_converter.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
17 |
21 |
25 |
--------------------------------------------------------------------------------
/sample/src/main/res/raw/array.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "标题",
4 | "name": "名称"
5 | },
6 | {
7 | "title": "标题",
8 | "name": "名称"
9 | },
10 | {
11 | "title": "标题",
12 | "name": "名称"
13 | },
14 | {
15 | "title": "标题",
16 | "name": "名称"
17 | }
18 | ]
--------------------------------------------------------------------------------
/sample/src/main/res/raw/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "maintain": false
3 | }
--------------------------------------------------------------------------------
/sample/src/main/res/raw/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": 0,
3 | "msg": "OK",
4 | "data": {
5 | "title": "标题",
6 | "name": "名称"
7 | }
8 | }
--------------------------------------------------------------------------------
/sample/src/main/res/raw/user.json:
--------------------------------------------------------------------------------
1 | {
2 | "userId": 23123132,
3 | "username": "test_account",
4 | "age": 17,
5 | "balance": "123.24"
6 | }
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #2FA8E6
5 | #2FA8E6
6 | #F44336
7 |
8 |
9 | #339e9e9e
10 | #fafafa
11 | #737373
12 |
13 |
14 | #333333
15 | #999999
16 |
17 |
18 | #FFFFFF
19 | #F44336
20 | #000000
21 |
22 |
23 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Net
3 |
4 | Hello blank fragment
5 | assertk.Assert
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/sample/src/test/java/com/drake/net/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.drake.net
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':sample', ':net'
2 | rootProject.name='Net'
3 |
--------------------------------------------------------------------------------
/signed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liangjingkanji/Net/0d2ab60f67df6442c613d8055c59f3a60c524022/signed
--------------------------------------------------------------------------------