xUtilsAndroid 工具包
## xUtils3簡(jiǎn)介
xUtils 包含了orm, http(s), image, view注解, 但依然很輕量級(jí)(251K), 并且特性強(qiáng)大, 方便擴(kuò)展.
#### 1. `orm`: 高效穩(wěn)定的orm工具, 使得http接口實(shí)現(xiàn)時(shí)更方便的支持cookie和緩存.
* 靈活的, 類似linq表達(dá)式的接口.
* 和greenDao一致的性能.
#### 2. `http(s)`: 基于UrlConnection, Android4.4以后底層為okHttp實(shí)現(xiàn).
* 請(qǐng)求協(xié)議支持11種謂詞: GET,POST,PUT,PATCH,HEAD,MOVE,COPY,DELETE,OPTIONS,TRACE,CONNECT
* 支持超大文件(超過(guò)2G)上傳
* 支持?jǐn)帱c(diǎn)下載(如果服務(wù)端支持Range參數(shù),客戶端自動(dòng)處理斷點(diǎn)下載)
* 支持cookie(實(shí)現(xiàn)了domain, path, expiry等特性)
* 支持緩存(實(shí)現(xiàn)了Cache-Control, Last-Modified, ETag等特性, 緩存內(nèi)容過(guò)多時(shí)使用過(guò)期時(shí)間+LRU雙重機(jī)制清理)
* 支持異步和同步(可結(jié)合RxJava使用)調(diào)用
#### 3. `image`: 有了`http(s)`及其下載緩存的支持, `image`模塊的實(shí)現(xiàn)相當(dāng)?shù)暮?jiǎn)潔.
* 支持內(nèi)存緩存, 磁盤(pán)緩存(縮略圖和原圖), 并且支持回收被view持有, 但被MemCache移除的圖片, 減少頁(yè)面回退時(shí)的閃爍.
* 支持在ListView滑動(dòng)時(shí), 自動(dòng)停止被回收復(fù)用的item對(duì)應(yīng)的下載任務(wù)(再次下載時(shí)斷點(diǎn)續(xù)傳)
* 支持webp, gif(部分比較老的系統(tǒng)只展示靜態(tài)圖)
* 支持圓角, 圓形, 方形等裁剪, 支持自動(dòng)旋轉(zhuǎn)...
#### 4. `view注解`: view注解模塊僅僅400多行代碼卻靈活的支持了各種View注入和事件綁定.
* 事件注解支持且不受混淆影響...
* 支持綁定擁有多個(gè)方法的listener
#### 使用Gradle構(gòu)建時(shí)添加以下依賴即可:
```javascript
implementation 'org.xutils:xutils:3.9.0'
```
#### 混淆配置參考示例項(xiàng)目sample的配置
[這里可以下載aar文件](http://dl.bintray.com/wyouflf/maven/org/xutils/xutils/)
### 常見(jiàn)問(wèn)題:
1. 更好的管理圖片緩存: https://github.com/wyouflf/xUtils3/issues/149
2. Cookie的使用: https://github.com/wyouflf/xUtils3/issues/125
3. 關(guān)于query參數(shù)? http請(qǐng)求可以通過(guò) header, url, body(請(qǐng)求體)傳參; query參數(shù)是url中問(wèn)號(hào)(?)后面的參數(shù).
4. 關(guān)于body參數(shù)? body參數(shù)只有PUT, POST, PATCH, DELETE(老版本RFC2616文檔沒(méi)有明確指出它是否支持, 所以暫時(shí)支持)請(qǐng)求支持.
5. 自定義Http參數(shù)對(duì)象和結(jié)果解析: https://github.com/wyouflf/xUtils3/issues/191
6. 設(shè)置了http超時(shí)時(shí)間為5s但任然等待15s左右: GET請(qǐng)求失敗后默認(rèn)會(huì)重試2次, 可以通過(guò)setMaxRetryCount(0)來(lái)防止請(qǐng)求自動(dòng)重試.
7. @Event注解同一個(gè)id子類的事件會(huì)覆蓋父類, onClickListener和onItemClickListener默認(rèn)屏蔽了雙擊這種手機(jī)上不常用操作, 如需要雙擊支持可以自己setOnClickListener.
#### 使用前配置
##### 需要的權(quán)限
```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!-- 可選 -->
```
##### 初始化
```java
// 在application的onCreate中初始化
@Override
public void onCreate() {
super.onCreate();
x.Ext.init(this);
x.Ext.setDebug(BuildConfig.DEBUG); // 是否輸出debug日志, 開(kāi)啟debug會(huì)影響性能.
...
}
```
### 使用@Event事件注解(@ContentView, @ViewInject等更多示例參考sample項(xiàng)目)
```java
/**
* 1. 方法必須私有限定,
* 2. 方法參數(shù)形式必須和type對(duì)應(yīng)的Listener接口一致.
* 3. 注解參數(shù)value支持?jǐn)?shù)組: value={id1, id2, id3}
* 4. 其它參數(shù)說(shuō)明見(jiàn){@link org.xutils.event.annotation.Event}類的說(shuō)明.
**/
@Event(value = R.id.btn_test1,
type = View.OnClickListener.class/*可選參數(shù), 默認(rèn)是View.OnClickListener.class*/)
private void onTest1Click(View view) {
...
}
```
### 使用數(shù)據(jù)庫(kù)(更多示例參考sample項(xiàng)目)
```java
Parent test = db.selector(Parent.class)
.where("id", "in", new int[]{1, 3, 6})
.or("age", "<", 29)
.findFirst();
long count = db.selector(Parent.class)
.where("name", "LIKE", "w%")
.and("age", ">", 32)
.count();
List<Parent> testList = db.selector(Parent.class)
.where("id", "between", new String[]{"1", "5"})
.findAll();
List<DbModel> list = db.selector(Child.class)
.where("age", "<", 18)
.groupBy("parentId")
.having(WhereBuilder.b("COUNT(parentId)", ">", 1))
.select("parentId, COUNT(parentId) as childNum")
.findAll();
```
### 訪問(wèn)網(wǎng)絡(luò)(更多示例參考sample項(xiàng)目)
#### 如果你只需要一個(gè)簡(jiǎn)單的網(wǎng)絡(luò)請(qǐng)求:
```java
@Event(value = R.id.btn_test2)
private void onTest2Click(View view) {
RequestParams params = new RequestParams("https://www.baidu.com/s");
// params.setSslSocketFactory(...); // 如果需要自定義SSL
params.addQueryStringParameter("wd", "xUtils");
x.http().get(params, new Callback.CommonCallback<String>() {
@Override
public void onSuccess(String result) {
Toast.makeText(x.app(), result, Toast.LENGTH_LONG).show();
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();
}
@Override
public void onCancelled(CancelledException cex) {
Toast.makeText(x.app(), "cancelled", Toast.LENGTH_LONG).show();
}
@Override
public void onFinished() {
}
});
}
````
#### json或protobuf類型請(qǐng)求的處理
```java
/**
* 自定義實(shí)體參數(shù)類請(qǐng)參考:
* 請(qǐng)求注解 {@link org.xutils.http.annotation.HttpRequest}
* 請(qǐng)求注解處理模板接口 {@link org.xutils.http.app.ParamsBuilder}
*
* 需要自定義類型作為callback的泛型時(shí), 參考:
* 響應(yīng)注解 {@link org.xutils.http.annotation.HttpResponse}
* 響應(yīng)注解處理模板接口 {@link org.xutils.http.app.ResponseParser}
*
* 示例: 查看 org.xutils.sample.http 包里的代碼
*/
JsonDemoParams params = new JsonDemoParams();
params.wd = "xUtils";
// 有上傳文件時(shí)使用multipart表單, 否則上傳原始文件流.
// params.setMultipart(true);
// 上傳文件方式 1
// params.uploadFile = new File("/sdcard/test.txt");
// 上傳文件方式 2
// params.addBodyParameter("uploadFile", new File("/sdcard/test.txt"));
Callback.Cancelable cancelable
= x.http().get(params,
/**
* 1. callback的泛型:
* callback參數(shù)默認(rèn)支持的泛型類型參見(jiàn){@link org.xutils.http.loader.LoaderFactory},
* 例如: 指定泛型為File則可實(shí)現(xiàn)文件下載, 使用params.setSaveFilePath(path)指定文件保存的全路徑, 默認(rèn)支持?jǐn)帱c(diǎn)續(xù)傳(采用了文件鎖防止多線程/進(jìn)程修改文件,及文件末端校驗(yàn)續(xù)傳文件的一致性).
*
* 自定義callback的泛型支持方案1, 自定義某一Class的轉(zhuǎn)換(不夠靈活):
* 結(jié)合PrepareCallback的兩個(gè)泛型參數(shù), 第一個(gè)泛型參數(shù)類型使用LoaderFactory已經(jīng)支持的, 第二個(gè)泛型參數(shù)作為最終輸出, 需要在prepare方法中自己實(shí)現(xiàn).
* 一個(gè)稍復(fù)雜的例子可以參考{@link org.xutils.image.ImageLoader}
*
* 自定義callback的泛型支持方案2, 自定義一類數(shù)據(jù)的自動(dòng)轉(zhuǎn)化:
* 將注解@HttpResponse加到自定義返回值類型上, 實(shí)現(xiàn)自定義ResponseParser接口來(lái)統(tǒng)一轉(zhuǎn)換.
* 如果返回值是json/xml/protobuf等數(shù)據(jù)格式, 那么利用第三方的json/xml/protobuf等工具將十分容易定義自己的ResponseParser.
* 如示例代碼{@link org.xutils.sample.http.JsonDemoResponse}, 可直接使用JsonDemoResponse作為callback的泛型.
*
* 2. callback的組合:
* 可以用基類或接口組合個(gè)種類的Callback, 見(jiàn){@link org.xutils.common.Callback}.
* 例如:
* a. 組合使用CacheCallback將使請(qǐng)求檢測(cè)緩存或?qū)⒔Y(jié)果存入緩存(僅GET和POST請(qǐng)求生效).
* b. 組合使用PrepareCallback的prepare方法將為callback提供一次后臺(tái)執(zhí)行耗時(shí)任務(wù)的機(jī)會(huì), 然后將結(jié)果給onCache或onSuccess.
* c. 組合使用ProgressCallback將提供進(jìn)度回調(diào).
* 可參考{@link org.xutils.image.ImageLoader} 或 示例代碼中的 {@link org.xutils.sample.download.DownloadCallback}
*
* 3. 請(qǐng)求過(guò)程攔截或記錄日志: 參考 {@link org.xutils.http.app.RequestTracker}
*
* 4. 請(qǐng)求Header獲取: 參考 {@link org.xutils.sample.http.JsonResponseParser} 或 {@link org.xutils.http.app.RequestInterceptListener}
*
* 5. 其他(線程池, 超時(shí), 重定向, 重試, 代理等): 參考 {@link org.xutils.http.RequestParams}
*
**/
new Callback.CommonCallback<JsonDemoResponse>() {
@Override
public void onSuccess(JsonDemoResponse result) {
Toast.makeText(x.app(), result.toString(), Toast.LENGTH_LONG).show();
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
//Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();
if (ex instanceof HttpException) { // 網(wǎng)絡(luò)錯(cuò)誤
HttpException httpEx = (HttpException) ex;
int responseCode = httpEx.getCode();
String responseMsg = httpEx.getMessage();
String errorResult = httpEx.getResult();
// ...
} else { // 其他錯(cuò)誤
// ...
}
Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();
}
@Override
public void onCancelled(CancelledException cex) {
Toast.makeText(x.app(), "cancelled", Toast.LENGTH_LONG).show();
}
@Override
public void onFinished() {
}
});
// cancelable.cancel(); // 取消請(qǐng)求
```
#### 帶有緩存的請(qǐng)求示例:
```java
JsonDemoParams params = new JsonDemoParams();
params.wd = "xUtils";
// 默認(rèn)緩存存活時(shí)間, 單位:毫秒.(如果服務(wù)沒(méi)有返回有效的max-age或Expires)
params.setCacheMaxAge(1000 * 60);
Callback.Cancelable cancelable
// 使用CacheCallback, xUtils將為該請(qǐng)求緩存數(shù)據(jù).
= x.http().get(params, new Callback.CacheCallback<JsonDemoResponse>() {
private boolean hasError = false;
private String result = null;
@Override
public boolean onCache(JsonDemoResponse result) {
// 得到緩存數(shù)據(jù), 緩存過(guò)期后不會(huì)進(jìn)入這個(gè)方法.
// 如果服務(wù)端沒(méi)有返回過(guò)期時(shí)間, 參考params.setCacheMaxAge(maxAge)方法.
//
// * 客戶端會(huì)根據(jù)服務(wù)端返回的 header 中 max-age 或 expires 來(lái)確定本地緩存是否給 onCache 方法.
// 如果服務(wù)端沒(méi)有返回 max-age 或 expires, 那么緩存將一直保存, 除非這里自己定義了返回false的
// 邏輯, 那么xUtils將請(qǐng)求新數(shù)據(jù), 來(lái)覆蓋它.
//
// * 如果信任該緩存返回 true, 將不再請(qǐng)求網(wǎng)絡(luò);
// 返回 false 繼續(xù)請(qǐng)求網(wǎng)絡(luò), 但會(huì)在請(qǐng)求頭中加上ETag, Last-Modified等信息,
// 如果服務(wù)端返回304, 則表示數(shù)據(jù)沒(méi)有更新, 不繼續(xù)加載數(shù)據(jù).
//
this.result = result;
return false; // true: 信任緩存數(shù)據(jù), 不在發(fā)起網(wǎng)絡(luò)請(qǐng)求; false不信任緩存數(shù)據(jù).
}
@Override
public void onSuccess(JsonDemoResponse result) {
// 注意: 如果服務(wù)返回304 或 onCache 選擇了信任緩存, 這時(shí)result為null.
if (result != null) {
this.result = result;
}
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
hasError = true;
Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();
if (ex instanceof HttpException) { // 網(wǎng)絡(luò)錯(cuò)誤
HttpException httpEx = (HttpException) ex;
int responseCode = httpEx.getCode();
String responseMsg = httpEx.getMessage();
String errorResult = httpEx.getResult();
// ...
} else { // 其他錯(cuò)誤
// ...
}
}
@Override
public void onCancelled(CancelledException cex) {
Toast.makeText(x.app(), "cancelled", Toast.LENGTH_LONG).show();
}
@Override
public void onFinished() {
if (!hasError && result != null) {
// 成功獲取數(shù)據(jù)
Toast.makeText(x.app(), result, Toast.LENGTH_LONG).show();
}
}
});
```
### 綁定圖片(更多示例參考sample項(xiàng)目)
```java
x.image().bind(imageView, url, imageOptions);
// assets file
x.image().bind(imageView, "assets://test.gif", imageOptions);
// resources file
x.image().bind(imageView, "res://" + R.minimap.test, imageOptions);
// local file
x.image().bind(imageView, new File("/sdcard/test.gif").toURI().toString(), imageOptions);
x.image().bind(imageView, "/sdcard/test.gif", imageOptions);
x.image().bind(imageView, "file:///sdcard/test.gif", imageOptions);
x.image().bind(imageView, "file:/sdcard/test.gif", imageOptions);
x.image().bind(imageView, url, imageOptions, new Callback.CommonCallback<Drawable>() {...});
x.image().loadDrawable(url, imageOptions, new Callback.CommonCallback<Drawable>() {...});
// 用來(lái)獲取緩存文件
x.image().loadFile(url, imageOptions, new Callback.CommonCallback<File>() {...});
```
評(píng)論
圖片
表情
