水平有限,如果存在問(wèn)題歡迎大家訪問(wèn)我的博客批評(píng)指正.
ionic應(yīng)用兼容android9的經(jīng)歷
文中Android P就是Android 9,這一塊原因大家自己google.
突然,給用戶(hù)開(kāi)發(fā)的App無(wú)法在Android9與Android10上正常使用,甚至10上面無(wú)法進(jìn)行安裝,不得不拉取很早之前的代碼進(jìn)行問(wèn)題排查.
此前是ionic4與Angular的混合開(kāi)發(fā)項(xiàng)目,所以我首先想到的事Android9以上api進(jìn)行了變動(dòng),如果是權(quán)限好處理,如果是api并且涉及到第三方cordova插件那么就有點(diǎn)麻煩,不能等到插件作者自己更新的情況下我們只能自己基于源碼去修改.就像我之前想找一個(gè)支持中文TTS的cordova插件,網(wǎng)上能找到的幾乎全部是英文的.最后只能自己封裝(中文TTS插件),所以如果了解cordova插件原理及懂一些java知識(shí),第二點(diǎn)也不麻煩.
好在我們這次的問(wèn)題出在自己本身身上,就是那種好解決的,我們現(xiàn)在我們添加的android平臺(tái)源碼里面改動(dòng).
問(wèn)題解決
Apache HTTP client 類(lèi)丟失
將 compileSdkVersion 升級(jí)到 28(Android9) 之后,如果在項(xiàng)目中用到了 Apache HTTP client 的相關(guān)類(lèi),就會(huì)拋出找不到這些類(lèi)的錯(cuò)誤。這是因?yàn)楣俜揭呀?jīng)啟動(dòng)類(lèi)加載器中將其移除,如果仍然需要使用 Apache HTTP client,可以在AndroidManifest.xml文件中加入:
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
具體再application節(jié)點(diǎn)下.
網(wǎng)絡(luò)無(wú)法連接
CLEARTEXT communication to life.115.com not permitted by network security policy
原因: Android P以后 限制了明文流量的網(wǎng)絡(luò)請(qǐng)求,非加密的流量請(qǐng)求都會(huì)被系統(tǒng)禁止掉
方案一
既然不讓用明文,那我們將http換成https就可以解決問(wèn)題了,這也是以后的趨勢(shì),大勢(shì)所趨.
方案二
考慮到有些請(qǐng)求是和客戶(hù)打交道,太麻煩,那么還有其他解決辦法.
在資源文件新建xml目錄(res下建立xml目錄,一般自己存在),新建一個(gè)文件network_security_config.xml(一般也存在),添加或修改原有內(nèi)容:
全部放開(kāi)
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
或者允許部分:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!--<base-config cleartextTrafficPermitted="true" />-->
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">sample.domain.com</domain>
</domain-config>
</network-security-config>
然后在AndroidManifest.xml的application節(jié)點(diǎn)屬性上添加:
<application
...
android:networkSecurityConfig="@xml/network_security_config">
其它方案:
不要使用太高的targetversion,26以及以前的版本還是可以正常訪問(wèn)http.
在AndroidMainifest的加入android:usesCleartextTraffic
<application
android:usesCleartextTraffic="true"/>
Android 9使用前臺(tái)服務(wù)報(bào)異常
原因:在安卓P版本之后,必須要授予FOREGROUND_SERVICE權(quán)限,才能夠使用前臺(tái)服務(wù),否則會(huì)拋出異常。
那就在AndroidManifest.xml中manifest添加權(quán)限唄:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
到這一步,我在Android P的問(wèn)題其實(shí)已經(jīng)解決了,Android 10的一會(huì)再說(shuō),但是我還是把我解決過(guò)程中網(wǎng)上大家提的其他問(wèn)題列舉一下.
劉海屏適配(原生代碼)
比如我們需要全屏顯示的時(shí)候,Google在api28中已經(jīng)做了處理。如下面代碼:
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
//android p 劉海屏適配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {//加版本判斷,28以下會(huì)報(bào)錯(cuò)
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.layoutInDisplayCutoutMode
= WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(lp);
}
layoutInDisplayCutoutMode值說(shuō)明:
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:默認(rèn)情況下,全屏窗口不會(huì)使用到挖孔區(qū)域,非全屏窗口可正常使用挖孔區(qū)域。
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS:窗口聲明使用挖孔區(qū)域
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:窗口聲明使用挖孔區(qū)域
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:窗口聲明不使用挖孔區(qū)域
限制靜態(tài)廣播的接收
升級(jí)安卓P之后,隱式廣播將會(huì)被全面禁止,在AndroidManifest中注冊(cè)的Receiver將不能夠生效,如果你的清單文件中有如下的監(jiān)聽(tīng)器:
<receiver android:name="com.yanghaoyi.receiver.UpdateReceiver">
<intent-filter>
<action android:name="com.yanghaoyi.action.ACTION_UPDATE" />
</intent-filter>
</receiver>
你需要移除上面的代碼,并在應(yīng)用中進(jìn)行動(dòng)態(tài)注冊(cè),例如:
private void registerReceiver(){
myReceiver = new MyReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(TOAST_ACTION);
registerReceiver(myReceiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myReceiver);
}
非全屏透明Activity禁用設(shè)置orientation
java.lang.IllegalStateException: Only fullscreen activities can request orientation
非全屏透明頁(yè)面不允許設(shè)置方向,否則會(huì)拋異常.
如果一個(gè)Activity的Style符合下面三個(gè)條件之一,認(rèn)為不是“fullscreen”:
“windowIsTranslucent”為true;
“windowIsTranslucent”為false,但“windowSwipeToDismiss”為true;
“windowIsFloating“為true;
解決方案:android:windowIsTranslucent設(shè)置為false。
ionic兼容優(yōu)化
上面我采用了添加xml打開(kāi)明文訪問(wèn),前臺(tái)權(quán)限以及Apache HTTP client 類(lèi)丟失解決方案,每次添加完平臺(tái)都得改一次代碼,是不是相當(dāng)不方便,那么ionic本身的config.xml就派上用場(chǎng)了.
將上文提到的network_security_config.xml文件放在resources/android/xml目錄下.
并且在config.xml的widget標(biāo)簽上添加屬性:
<widget ... xmlns:android="http://schemas.android.com/apk/res/android">
...
</widget>
在config.xml的android平臺(tái)配置里面添加:
<platform name="android">
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application" xmlns:android="http://schemas.android.com/apk/res/android">
<application android:networkSecurityConfig="@xml/network_security_config" />
</edit-config>
<config-file parent="/manifest" target="app/src/main/AndroidManifest.xml">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
</config-file>
<config-file parent="/manifest/application" target="app/src/main/AndroidManifest.xml">
<uses-library android:name="org.apache.http.legacy" android:required="false" />
</config-file>
</platform>
然后就不用擔(dān)心每次刪除平臺(tái)后再次添加重新修改代碼了.
Android 10的一些問(wèn)題
在Android 10的一些手機(jī)上,比如華為,不知道是因?yàn)槭謾C(jī)自己設(shè)置的原因還是android升級(jí)了,不允許安裝非正式版app,所以我們?cè)诎l(fā)送測(cè)試版(debug)版本的apk安裝會(huì)失敗,因?yàn)橛袝r(shí)候并不是我們都是整個(gè)開(kāi)發(fā)完上應(yīng)用市場(chǎng)的,客戶(hù)可能需要提前體驗(yàn)或者看效果.而每個(gè)手機(jī)關(guān)閉設(shè)置又不一樣,反正華為的沒(méi)找見(jiàn),那么我們發(fā)布時(shí)應(yīng)該將我們的app進(jìn)行正式簽名,防止客戶(hù)安裝失敗.
而且簽名也是我們app正式發(fā)布的必須流程.
具體簽名流程請(qǐng)參考我的Android簽名一文.
INSTALL_FAILED_NO_MATCHING_ABIS(虛擬機(jī)的問(wèn)題)
有時(shí)候我們使用虛擬機(jī)測(cè)試應(yīng)用是可能因?yàn)榧軜?gòu)的問(wèn)題,報(bào)錯(cuò):
Installation failed with message Failed to finalize session : INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113. It is possible that this issue is resolved by uninstalling an existing version of the apk if it is present, and then re-installing.
解決方案:
在Android studio 的build.gradle文件里(model:app):在android中添加以下內(nèi)容:
android{
...
splits {
abi {
enable true
reset()
include 'x86', 'armeabi-v7a','x86_64'
universalApk true
}
}
}