androidrequest
⑴ android6.0動態許可權代碼(Mpermissions、REQUEST_CAMERA)報錯
應該是你的編譯版本低於23,在build.gradle(app)修改你的compileSdkVersion使其大於或等於23【圖片上是編譯版本是25】,然後重新構建一下工程(rebuild)
⑵ android false怎麼判斷是拒絕了還是允許了
Activity中requestPermissions是用來再次請求許可權的,調用之前要先用checkSelfPermission檢查許可權是否已經獲取,activity重寫onRequestPermissionsResult方法來監聽請求的結果,還有方法來判斷是否需要自己定義請求許可權的說明窗口。而且Android6.0之後許可權管理機制有變化了,有些許可權必須在代碼中去請求,你的app兼容6.0及以上的話你就要去了解這部分知識。建議你去Github上搜Nammu去看看,這個開源庫就是處理許可權請求這些事的,你可以用這個庫來方便自己。
⑶ Android invalidate/postInvalidate/requestLayout-徹底釐清
系列文章:
Android Activity創建到View的顯示過程
Android Activity 與View 的互動思考
Android invalidate/postInvalidate/requestLayout-徹底釐清
Android 容易遺漏的刷新小細節
前幾篇分析了Measure、Layout、Draw 過程,這三個過程在第一次展示View的時候都會調用。那之後更改了View的屬性呢?比如更改顏色、更換文字內容、更換圖片等,還會走這三個過程嗎?循著這個思路,來分析Invalidate/RequestLayout流程。
通過本篇文章,你將了解到:
MyView 默認展示一塊紅色的矩形區域,暴露給外界的方法:setColor
用以改變繪制的顏色。顏色改變後,需要重新執行onDraw(xx)才能看到改變後的效果,通過invalidate()方法觸發onDraw(xx)調用。
接下來看看invalidate()方法是怎麼觸發onDraw(xx)方法執行的。
invalidate顧名思義:使某個東西無效。在這里表示使當前繪制內容無效,需要重新繪制。當然,一般來說常常簡單稱作:刷新。
invalidate()是View.java 里的方法。
從上可知,當前要刷新的View確定了刷新區域後即調用了父布局的invalidateChild(xx)方法。該方法為ViewGroup里的final方法。
由上可知,在該方法里區分了硬體加速繪制與軟體繪制,分別來看看兩者區別:
硬體加速繪制分支
如果該Window支持硬體加速,則走下邊流程:
onDescendantInvalidated 方法的目的是不斷向上尋找其父布局,並將父布局PFLAG_DRAWING_CACHE_VALID 標記清空,也就是繪制緩存清空。
而我們知道,根View的mParent指向ViewRootImpl對象,因此來看看它裡面的onDescendantInvalidated()方法:
做個小結:
用圖表示硬體加速繪制的invaldiate流程:
軟體繪制分支
如果該Window不支持硬體加速,那麼走軟體繪制分支:
parent.invalidateChildInParent(location, dirty) 返回mParent,只要mParent不為空那麼一直調用invalidateChildInParent(xx),實際上這也是遍歷ViewTree過程,來看看關鍵invalidateChildInParent(xx):
與硬體加速繪制一致,最終調用ViewRootImpl invalidateChildInParent(xx),來看看實現:
做個小結:
用圖表示軟體繪制invalidate流程:
上述分析了硬體加速繪制與軟體繪制時invalidate的不同,它們的最終目的都是為了重走Draw過程。重走Draw過程通過調用scheleTraversals() 觸發的,來看看是如何觸發的。
想了解更多硬體加速繪制請移步:
Android 自定義View之Draw過程(中)
觸發Draw過程
scheleTraversals 詳細分析在這篇文章:
Android Activity創建到View的顯示過程
三大流程真正開啟在ViewRootImpl->performTraversals(),在該方法里根據一定的條件執行了Measure(測量)、Layout(擺放)、Draw(繪制)。
本次著重分析如何觸發Draw過程。
可以看出,invalidate 最終觸發了Draw過程。
可以看出,啟用硬體加速繪制可以避免不必要的繪制。
關於硬體加速繪制與軟體繪制詳細區別,請移步系列文章:
Android 自定義View之Draw過程(上)
最後,用圖表示invalidate流程:
顧名思義,重新請求布局。
來看看View.requestLayout()方法:
可以看出,這個遞歸調用和invalidate一樣的套路,向上尋找其父布局,一直到ViewRootImpl為止,給每個布局設置PFLAG_FORCE_LAYOUT和PFLAG_INVALIDATED標記。
查看ViewRootImpl requestLayout()
很明顯,requestLayout目的很單純:
和invalidate一樣的配方,當刷新信號來到之時,調用doTraversal()->performTraversals(),而在performTraversals()里真正執行三大流程。
由此可見:
之前設置的PFLAG_FORCE_LAYOUT標記有啥用呢?
回憶一下measure 過程:
PFLAG_FORCE_LAYOUT 標記打上之後,會觸發onMeasure()測量自身及其子布局。
試想一下,假設View的尺寸改變了,變大了,那麼調用了requestLayout後因為走了Measure、Layout 過程,測量、擺放倒是重新設置了,但是不調用Draw出不來效果啊。實際上,View layout時候已經考慮到了。
在View.layout(xx)->setFrame(xx)里
也就是說:
關於measure、layout 過程更深入的分析,請移步:
用圖表示requestLayout過程:
結合requestLayout和invalidate與View三大流程關系,有如下圖:
總結一下:
上面僅僅說明了單個布局Invalidate/RequestLayout聯系,那麼如果父布局調用了invalidate,那麼子布局會走重繪過程嗎?接下來列舉這些關系。
子布局Invalidate
如果是軟體繪制或者父布局開啟了軟體緩存繪制,父布局會走重繪過程(前提是WILL_NOT_DRAW標記沒設置)。
子布局RequestLayout
父布局會重走Measure、Layout過程。
父布局Invalidate
如果是軟體繪制,則子布局會走重繪過程。
父布局RequestLayout
如果父布局尺寸發生了改變,則會觸發子布局Measure過程、Layout過程。
在Activity onCreate里創建子線程並展示對話框:
答案是可以的,接下來分析為什麼可以。
在分析ViewRootImpl里requestLayout/invalidate過程中,發現其內部調用了checkThread()方法:
問題的關鍵是mThread是什麼?從哪裡來?
而創建ViewRootImpl對象是在調用WindowManager.addView(xx)過程中創建的。
關於WindowManager/Window 請移步: Window/WindowManager 不可不知之事
現在回過頭來看Dialog創建就比較明朗了:
實際上,"子線程不能更新ui" 更合理的表述應為:View只能被構建了ViewTree的線程操作。只是通常來說,Activity 構建ViewTree的線程被稱作UI(主)線程,因此才會有上述說法。
既然invalidate()只能主線程調用(硬體加速條件下,不調用checkThread()),那如果想在子線程調用呢?當然想到的是先通過Handler切換到主線程,再執行invalidate(),但是每次這么寫有點冗餘,幸好,View里提供了postInvalidate:
切到ViewRootImpl.java
發現了真相:
本文基於Android 10.0
⑷ android volley stringrequest post中的getparams怎麼把json數據提交上去
1.客戶端以普通的post方式進行提交,服務端返回字元串
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
StringRequest stringRequest = new StringRequest(Request.Method.POST,httpurl,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d(TAG, "response -> " + response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, error.getMessage(), error);
}
}) {
@Override
protected Map<String, String> getParams() {
//在這里設置需要post的參數
Map<String, String> map = new HashMap<String, String>();
map.put("name1", "value1");
map.put("name2", "value2");
return params;
}
};
requestQueue.add(stringRequest);
2.客戶端以json串的post請求方式進行提交,服務端返回json串
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
Map<String, String> map = new HashMap<String, String>();
map.put("name1", "value1");
map.put("name2", "value2");
JSONObject jsonObject = new JSONObject(params);
JsonRequest<JSONObject> jsonRequest = new JsonObjectRequest(Method.POST,httpurl, jsonObject,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.d(TAG, "response -> " + response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, error.getMessage(), error);
}
})
{
//注意此處override的getParams()方法,在此處設置post需要提交的參數根本不起作用
//必須象上面那樣,構成JSONObject當做實參傳入JsonObjectRequest對象里
//所以這個方法在此處是不需要的
// @Override
// protected Map<String, String> getParams() {
// Map<String, String> map = new HashMap<String, String>();
// map.put("name1", "value1");
// map.put("name2", "value2");
// return params;
// }