1.问题
-
用户切换改变网络的过程中,应用概率会出现崩溃,日志如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
API level: '30' OS version: '11' ABI list: 'arm64-v8a,armeabi-v7a,armeabi' Manufacturer: 'OnePlus' Brand: 'OnePlus' Model: 'GM1900' Build fingerprint: 'OnePlus/OnePlus7_CH/OnePlus7:11/RKQ1.201022.002/2108161921:user/release-keys' pid: 29126, tid: 29126, name: main >>> package name <<< java stacktrace: java.lang.SecurityException: getDataNetworkTypeForSubscriber at android.os.Parcel.createExceptionOrNull(Parcel.java:2373) at android.os.Parcel.createException(Parcel.java:2357) at android.os.Parcel.readException(Parcel.java:2340) at android.os.Parcel.readException(Parcel.java:2282) at com.android.internal.telephony.ITelephony$Stub$Proxy.getNetworkTypeForSubscriber(ITelephony.java:8803) at android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:3070) at android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:3034) at j.k.b0.a.e.b.c(Unknown Source:51) at j.k.b.q.c.w.onCreateView(:14) at androidx.fragment.app.Fragment.performCreateView(Unknown Source:15) at h.l.d.q.V(:18) at h.l.d.q.T(:1) at h.l.d.q.U(Unknown Source:47) at h.l.d.a.n(Unknown Source:182) at h.l.d.q.E(:7) at h.l.d.q.b0(Unknown Source:84) at h.l.d.q.D(Unknown Source:31) at h.l.d.a.e(:2) at h.l.d.v.finishUpdate(Unknown Source:12) at androidx.viewpager.widget.ViewPager.r(:2) at androidx.viewpager.widget.ViewPager.onMeasure(:1) at android.view.View.measure(View.java:25671) at androidx.constraintlayout.widget.ConstraintLayout.e(:2) at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(:70) at android.view.View.measure(View.java:25671) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6987) at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) at androidx.appcompat.widget.ContentFrameLayout.onMeasure(Unknown Source:154) at android.view.View.measure(View.java:25671) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6987) at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552) at android.widget.LinearLayout.measureVertical(LinearLayout.java:842) at android.widget.LinearLayout.onMeasure(LinearLayout.java:721) at android.view.View.measure(View.java:25671) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6987) at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) at android.view.View.measure(View.java:25671) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6987) at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552) at android.widget.LinearLayout.measureVertical(LinearLayout.java:842) at android.widget.LinearLayout.onMeasure(LinearLayout.java:721) at android.view.View.measure(View.java:25671) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6987) at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) at com.android.internal.policy.DecorView.onMeasure(DecorView.java:776) at android.view.View.measure(View.java:25671) at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3713) at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:2496) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2771) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2182) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8730) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1352) at android.view.Choreographer.doCallbacks(Choreographer.java:1149) at android.view.Choreographer.doFrame(Choreographer.java:1049) at android.view.Choreographer$FrameHandler.handleMessage(Choreographer.java:1275) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:233) at android.app.ActivityThread.main(ActivityThread.java:8010) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:631) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:978) |
另外一种场景就是集成了华为的二维码扫描 com.huawei.hms:scanplus:1.3.2.300 ,这个 SDK 比较奇葩的地方在于,使用 Wi-Fi 网络情况下是不会出现问题的。
但是在手机流量的情况下,不申请 android.permission.READ_PHONE_STATE 权限,初始化这个 SDK 会导致应用闪退。
崩溃堆栈如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
java.lang.SecurityException: getNetworkTypeForSubscriber at android.os.Parcel.createExceptionOrNull(Parcel.java:2437) at android.os.Parcel.createException(Parcel.java:2421) at android.os.Parcel.readException(Parcel.java:2404) at android.os.Parcel.readException(Parcel.java:2346) at com.android.internal.telephony.ITelephony$Stub$Proxy.getNetworkTypeForSubscriber(ITelephony.java:9325) at android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:3002) at android.telephony.TelephonyManager.getNetworkType(TelephonyManager.java:2964) at com.huawei.hms.mlkit.common.ha.d.b(HianalyticsLogUtils.java:68) at com.huawei.hms.mlkit.common.ha.HianalyticsLogProvider.logEnd(HianalyticsLogProvider.java:6315) at com.huawei.hms.ml.camera.g$a.a(HiAnalyticsThread.java:109) at com.huawei.hms.ml.camera.g$a.handleMessage(HiAnalyticsThread.java:78) at android.os.Handler.dispatchMessage(Handler.java:114) at android.os.Looper.loopOnce(Looper.java:206) at android.os.Looper.loop(Looper.java:296) at com.huawei.hms.ml.camera.g.run(HiAnalyticsThread.java:51) |
2.分析
-
根据 SecurityException: getDataNetworkTypeForSubscriber 可以看到,这是一个安全性异常,所以猜测应用在 Android11 的权限有关,由于缺少该权限导致无法访问接口而异常。
-
找到网络状态检测方法,可以看到调用了 TelephonyManager.getNetworkType()接口获取网络类型,该方法是需要 READ_PHONE_STATE 权限的,该方法上面也有 RequiresPermission 注解声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
@RequiresPermission(value = "android.permission.READ_PHONE_STATE") public int getNetworkState(Context context) { if (null == mConnectivityManager) { // 为空则认为无网络 return NETWORK_NONE; } // 获取网络类型,如果为空,返回无网络 NetworkInfo activeNetInfo = mConnectivityManager.getActiveNetworkInfo(); if (activeNetInfo == null || !activeNetInfo.isAvailable()) { return NETWORK_NONE; } // 判断是否为WIFI NetworkInfo wifiInfo = mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); if (null != wifiInfo) { NetworkInfo.State state = wifiInfo.getState(); if (null != state) { if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) { return NETWORK_WIFI; } } } // 若不是WIFI,则去判断是2G、3G、4G网 TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); int networkType = telephonyManager.getNetworkType(); ... } |
-
进一步分析,接口的调用堆栈为:
1 2 3 4 5 6 7 8 9 10 |
TelephonyManager.getNetworkType ->PhoneInterfaceManager.getNetworkTypeForSubscriber->TelephonyPermissions.checkCallingOrSelfReadPhoneState->TelephonyPermissions.checkReadPhoneState->ContextImpl.enforcePermission->ContextImpl.checkPermission->PermissionManager.checkPermission->PermissionTelephonyManager.getNetworkType -> PhoneInterfaceManager.getNetworkTypeForSubscriber-> TelephonyPermissions.checkCallingOrSelfReadPhoneState-> TelephonyPermissions.checkReadPhoneState-> ContextImpl.enforcePermission-> ContextImpl.checkPermission-> PermissionManager.checkPermission-> PermissionManager.checkPermissionUncached-> ActivityManager.getService-> IActivityManager.checkPermission-> |
3.解决方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
private void requestPermission() { LogUtil.printE(HomeActivity.class, ">>> etrunc requestPermission SDK-VERSION= " + Build.VERSION.SDK_INT); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { //Android 11 授权读写权限 XXPermissions.with(HomeActivity.this) // 不适配 Android 11 可以这样写 //.permission(Permission.Group.STORAGE) // 适配 Android 11 需要这样写,这里无需再写 Permission.Group.STORAGE .permission(Permission.MANAGE_EXTERNAL_STORAGE, Permission.READ_PHONE_STATE) .request(new OnPermissionCallback() { @Override public void onGranted(List<String> permissions, boolean all) { if (all) { ToastUtil.showToast(getApplication(), R.string.permission_success_tip); } } @Override public void onDenied(List<String> permissions, boolean never) { if (never) { ToastUtil.showToast(getApplication(), R.string.permissions_refuse_authorization); // 如果是被永久拒绝就跳转到应用权限系统设置页面 XXPermissions.startPermissionActivity(HomeActivity.this, permissions); } else { ToastUtil.showToast(getApplication(), R.string.permissions_error); } } }); } } |