Android 開發中遇到的 bug(10)

前言

記錄開發中遇到的 bug,不再讓自己重複地被同樣的 bug 折磨。

正文

1. Caused by: java.lang.IllegalStateException: Not allowed to start service Intent app is in background

時間:2019年12月4日21:49:41
問題描述:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.iekie.free.clean/com.iekie.free.clean.ui.activity.MainActivity}: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.iekie.free.clean/.ui.service.NotificationUpdateService }: app is in background uid UidRecord{c5e652f u0a118 TRNB idle procs:1 seq(0,0,0)}
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3139)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3282)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1970)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7156)
    at java.lang.reflect.Method.invoke(Method.java)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:975)
Caused by: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.iekie.free.clean/.ui.service.NotificationUpdateService }: app is in background uid UidRecord{c5e652f u0a118 TRNB idle procs:1 seq(0,0,0)}
    at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1666)
    at android.app.ContextImpl.startService(ContextImpl.java:1611)
    at android.content.ContextWrapper.startService(ContextWrapper.java:677)
    at com.iekie.free.clean.ui.service.NotificationUpdateService.a(NotificationUpdateService.java:7)
    at com.iekie.free.clean.ui.util.NotificationUtils.b(NotificationUtils.java:17)
    at com.iekie.free.clean.ui.activity.MainActivity.t(MainActivity.java:4)
    at com.iekie.free.clean.ui.activity.MainActivity.onCreate(MainActivity.java:24)
    at android.app.Activity.performCreate(Activity.java:7335)
    at android.app.Activity.performCreate(Activity.java:7326)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1275)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3119)
    ... 11 more
java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.iekie.free.clean/.ui.service.NotificationUpdateService }: app is in background uid UidRecord{c5e652f u0a118 TRNB idle procs:1 seq(0,0,0)}
    at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1666)
    at android.app.ContextImpl.startService(ContextImpl.java:1611)
    at android.content.ContextWrapper.startService(ContextWrapper.java:677)
    at com.iekie.free.clean.ui.service.NotificationUpdateService.a(NotificationUpdateService.java:7)
    at com.iekie.free.clean.ui.util.NotificationUtils.b(NotificationUtils.java:17)
    at com.iekie.free.clean.ui.activity.MainActivity.t(MainActivity.java:4)
    at com.iekie.free.clean.ui.activity.MainActivity.onCreate(MainActivity.java:24)
    at android.app.Activity.performCreate(Activity.java:7335)
    at android.app.Activity.performCreate(Activity.java:7326)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1275)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3119)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3282)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1970)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7156)
    at java.lang.reflect.Method.invoke(Method.java)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:975)

解決辦法:
在 API 26 以上,需要使用 startForegroundService 方式啓動

    public static void start(Context context) {
        Intent starter = new Intent(context, NotificationUpdateService.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(starter);
        } else {
            context.startService(starter);
        }
    }

2. Context.startForegroundService() did not then call Service.startForeground()

時間:2019年12月15日11:52:11
問題描述

12-15 11:51:36.892 E/AndroidRuntime(30893): FATAL EXCEPTION: main
12-15 11:51:36.892 E/AndroidRuntime(30893): Process: com.iekie.free.clean, PID: 30893
12-15 11:51:36.892 E/AndroidRuntime(30893): android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
12-15 11:51:36.892 E/AndroidRuntime(30893): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1848)
12-15 11:51:36.892 E/AndroidRuntime(30893): 	at android.os.Handler.dispatchMessage(Handler.java:105)
12-15 11:51:36.892 E/AndroidRuntime(30893): 	at android.os.Looper.loop(Looper.java:164)
12-15 11:51:36.892 E/AndroidRuntime(30893): 	at android.app.ActivityThread.main(ActivityThread.java:6695)
12-15 11:51:36.892 E/AndroidRuntime(30893): 	at java.lang.reflect.Method.invoke(Native Method)
12-15 11:51:36.892 E/AndroidRuntime(30893): 	at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
12-15 11:51:36.892 E/AndroidRuntime(30893): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:772)

解決辦法

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            showNotify();
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    private void showNotify() {
        startForeground(NotificationUtils.NOTIFICATION_ID_PERMANENT, NotificationUtils.getInstance().createNotification(null));
    }

3. Permission Denial: startForeground requires android.permission.FOREGROUND_SERVICE

時間:2019年12月15日12:06:17
問題描述:在9.0機子上出現這個問題,

java.lang.SecurityException: Permission Denial: startForeground from pid=1824, uid=10479 requires android.permission.FOREGROUND_SERVICE
    at android.os.Parcel.createException(Parcel.java:1942)
    at android.os.Parcel.readException(Parcel.java:1910)
    at android.os.Parcel.readException(Parcel.java:1860)
    at android.app.IActivityManager$Stub$Proxy.setServiceForeground(IActivityManager.java:5198)
    at android.app.Service.startForeground(Service.java:695)
    at com.example.app.services.AudioService.setUpMediaNotification(AudioService.java:372)
    at com.example.app.services.AudioService.setUpAndStartAudioFeed(AudioService.java:328)
    at com.example.app.services.AudioService.onStartCommand(AudioService.java:228)
    at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3667)
    at android.app.ActivityThread.access$1600(ActivityThread.java:199)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1681)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:193)
    at android.app.ActivityThread.main(ActivityThread.java:6669)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

解決辦法:
查看 Test your Android 9 app,在清單中添加權限:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

4. BottomSheetDialog 無法顯示透明背景

時間:2019年12月15日09:44:20
問題描述: 應用中有一個底部彈窗,需要背景帶有圓角,但實際上卻看不到設置的圓角。

問題分析: 開始以爲是背景圓角的背景被忽略了,於是把帶圓角的背景設置爲紅色,查看一下效果:

發現自己設置的圓角背景是有的,但是 BottomSheetDialog 自己是帶有白色背景的,這樣才造成我原來設置的白色圓角背景看不見。所以,需要去掉 BottomSheetDialog 自帶的白色背景,改爲透明背景。
查看一下 BottomSheetDialog 的代碼,關鍵的地方是下邊的方法:

private View wrapInBottomSheet(int layoutResId, View view, LayoutParams params) {
		// 填充 design_bottom_sheet_dialog.xml
        FrameLayout container = (FrameLayout)View.inflate(this.getContext(), layout.design_bottom_sheet_dialog, (ViewGroup)null);
        CoordinatorLayout coordinator = (CoordinatorLayout)container.findViewById(id.coordinator);
        if (layoutResId != 0 && view == null) {
            view = this.getLayoutInflater().inflate(layoutResId, coordinator, false);
        }
		// 找到 design_bottom_sheet 這個 id 對應的控件 bottomSheet
        FrameLayout bottomSheet = (FrameLayout)coordinator.findViewById(id.design_bottom_sheet);
        this.behavior = BottomSheetBehavior.from(bottomSheet);
        this.behavior.setBottomSheetCallback(this.bottomSheetCallback);
        this.behavior.setHideable(this.cancelable);
        // 把我們的 view 添加進去
        if (params == null) {
            bottomSheet.addView(view);
        } else {
            bottomSheet.addView(view, params);
        }

        coordinator.findViewById(id.touch_outside).setOnClickListener(new OnClickListener() {
            public void onClick(View view) {
                if (BottomSheetDialog.this.cancelable && BottomSheetDialog.this.isShowing() && BottomSheetDialog.this.shouldWindowCloseOnTouchOutside()) {
                    BottomSheetDialog.this.cancel();
                }

            }
        });
        ViewCompat.setAccessibilityDelegate(bottomSheet, new AccessibilityDelegateCompat() {
            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
                super.onInitializeAccessibilityNodeInfo(host, info);
                if (BottomSheetDialog.this.cancelable) {
                    info.addAction(1048576);
                    info.setDismissable(true);
                } else {
                    info.setDismissable(false);
                }

            }

            public boolean performAccessibilityAction(View host, int action, Bundle args) {
                if (action == 1048576 && BottomSheetDialog.this.cancelable) {
                    BottomSheetDialog.this.cancel();
                    return true;
                } else {
                    return super.performAccessibilityAction(host, action, args);
                }
            }
        });
        bottomSheet.setOnTouchListener(new OnTouchListener() {
            public boolean onTouch(View view, MotionEvent event) {
                return true;
            }
        });
        return container;
    }

再去查看一下 design_bottom_sheet_dialog.xml:

<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2015 The Android Open Source Project
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
-->
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

  <androidx.coordinatorlayout.widget.CoordinatorLayout
      android:id="@+id/coordinator"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:fitsSystemWindows="true">

    <View
        android:id="@+id/touch_outside"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:importantForAccessibility="no"
        android:soundEffectsEnabled="false"
        tools:ignore="UnusedAttribute"/>

    <FrameLayout
        android:id="@+id/design_bottom_sheet"
        style="?attr/bottomSheetStyle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal|top"
        app:layout_behavior="@string/bottom_sheet_behavior"/>

  </androidx.coordinatorlayout.widget.CoordinatorLayout>

</FrameLayout>

可以看到 design_bottom_sheet 這個 id 下設置了一個 style="?attr/bottomSheetStyle" 這裏面應該負責設置了顏色。通過查找,重新定義主題:

    <style name="CustomBottomSheetDialogTheme" parent="Theme.Design.Light.BottomSheetDialog">
        <item name="bottomSheetStyle">@style/CustomBottomSheetStyle</item>
    </style>

    <style name="CustomBottomSheetStyle" parent="Widget.Design.BottomSheet.Modal">
        <item name="android:background">@android:color/transparent</item>
    </style>

並通過構造設置主題:

new BottomSheetDialog(this, R.style.CustomBottomSheetDialogTheme);

運行後解決了問題。
還可以通過下面的方法解決:

BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(this);
bottomSheetDialog.setContentView(R.layout.bottome_sheet);
// 設置背景透明
bottomSheetDialog.getWindow().findViewById(R.id.design_bottom_sheet)
                .setBackgroundResource(android.R.color.transparent);

參考:https://stackoverflow.com/questions/37104960/bottomsheetdialog-with-transparent-background

5. WheelPicker 的 setSelectedItemPosition 方法不生效

時間:2019年12月15日10:35:54
問題描述: WheelPicker 設置了選中的位置,但是在顯示出來之後,實際的位置與預期的位置不一致。

weightBottomSheet = new BottomSheetDialog(this);
weightBottomSheet.setContentView(R.layout.bottome_sheet);
weightBottomSheet.getWindow().findViewById(R.id.design_bottom_sheet)
        .setBackgroundResource(android.R.color.transparent);
weightWheelPicker = weightBottomSheet.findViewById(R.id.wheel_picker);
ImageView ivBack = weightBottomSheet.findViewById(R.id.ivBack);
ivBack.setOnClickListener(v -> weightBottomSheet.dismiss());
TextView tvFinish = weightBottomSheet.findViewById(R.id.tvFinish);
tvFinish.setOnClickListener(v -> {
    currentWeightIndex = weightWheelPicker.getCurrentItemPosition();
    binding.csivWeight.setValue(weightList.get(currentWeightIndex));
    weightBottomSheet.dismiss();
    setupCurrentWater();
});
// 在這裏設置選中的位置
weightWheelPicker.setSelectedItemPosition(currentWeightIndex,false);
weightWheelPicker.setData(weightList);

在點擊事件裏,顯示出來:

public void onClick(View v) {
    if (v == binding.csivWeight) {
        weightBottomSheet.show();
    }
}

解決辦法:
把選中位置的代碼放到顯示的點擊事件裏:

public void onClick(View v) {
    if (v == binding.csivWeight) {
    	weightWheelPicker.setSelectedItemPosition(currentWeightIndex,false);
        weightBottomSheet.show();
    }
}

解決了這個問題。

6. EventBus 發送事件不應該使用 code 來區分事件

時間:2019年12月15日10:46:43
問題描述:項目中使用 EventBus 發送事件,有的頁面可能需要註冊多個時間,同學們會想到使用同一個事件類,接收到以後,再通過攜帶過來的 code 區分具體該做什麼處理。
問題分析:以現實生活中的快遞爲例來說明,三位買家分別在北京,上海,深圳居住,分別向鄭州的賣家購買了蘋果,香蕉,橘子。那麼按照上面按 code 來區分的寫法,鄭州的賣家會把蘋果羣發給三位買家,北京的買家會接收,上海,深圳的買家一看不是它們的貨物,會怎麼辦呢?
再回到我們的代碼裏面,三個頁面需要監聽不同的事件,如果通過 code 來區分事件的話,在 EventBus 發送事件時,三個頁面都會收到事件而只有一個頁面會真正需要這個事件,這會造成什麼?白髮了 2 個事件。
解決辦法:還是看一下快遞的例子,賣家是怎樣給買家發貨的?填上明確的地址信息,收貨人,再發出去。代碼中也應該這樣操作,就是創建明確的事件,不通過 code 來區分。

7. Git 錯誤地把一個目錄提交到了遠程

時間:2019年12月16日09:20:22
問題描述:AndroidStudio 開發,有一個 .idea 目錄,剛開始只是把 .idea 目錄下的幾個文件添加進了 .gitignore 文件中,但是後來 .idea 目錄下又生成了新的文件,錯誤地把它們也提交到了遠程。
解決辦法
在 .ignore 文件中添加一行,表示忽略掉 .idea/ 整個目錄:

/.idea

打開 git bash 窗口:

git rm -r -n --cached ".idea/" //-n:加上這個參數,執行命令時,是不會刪除任何文件,而是展示此命令要刪除的文件列表預覽。
git rm -r --cached  ".idea/"      //最終執行命令. 
git commit -m "remove .idea folder all file out of control"    //提交
git push origin master   //提交到遠程服務器

8. 項目中使用 File.getPath(),File.getAbsolutePath(),File.getCanonicalPath() 混亂

時間:2019年12月16日09:30:53
問題描述:項目中使用了上述三種獲取路徑的方法,但是並沒有區分清楚它們的不同之處。
解決辦法

package com.test;

import java.io.File;

/**
 * @author wangzhichao
 * @since 2019/12/16
 */
public class Test {
    public static void main(String[] args) throws Exception {
        String pathname = "..\\demo.txt";
        System.out.println(pathname+":");
        File file = new File(pathname);
        System.out.println("file.getPath()=" + file.getPath());
        System.out.println("file.getAbsolutePath()=" + file.getAbsolutePath());
        System.out.println("file.getCanonicalPath()=" + file.getCanonicalPath());
        System.out.println();

        pathname = ".\\demo.txt";
        System.out.println(pathname+":");
        file = new File(pathname);
        System.out.println("file.getPath()=" + file.getPath());
        System.out.println("file.getAbsolutePath()=" + file.getAbsolutePath());
        System.out.println("file.getCanonicalPath()=" + file.getCanonicalPath());
        System.out.println();

        pathname = "G:\\AndroidWorkspaces\\Think4JavaExamples\\app\\src\\main\\java\\com\\test\\demo.txt";
        System.out.println(pathname+":");
        file = new File(pathname);
        System.out.println("file.getPath()=" + file.getPath());
        System.out.println("file.getAbsolutePath()=" + file.getAbsolutePath());
        System.out.println("file.getCanonicalPath()=" + file.getCanonicalPath());

    }
}

運行一下:

..\demo.txt:
file.getPath()=..\demo.txt
file.getAbsolutePath()=G:\AndroidWorkspaces\Think4JavaExamples\..\demo.txt
file.getCanonicalPath()=G:\AndroidWorkspaces\demo.txt

.\demo.txt:
file.getPath()=.\demo.txt
file.getAbsolutePath()=G:\AndroidWorkspaces\Think4JavaExamples\.\demo.txt
file.getCanonicalPath()=G:\AndroidWorkspaces\Think4JavaExamples\demo.txt

G:\AndroidWorkspaces\Think4JavaExamples\app\src\main\java\com\test\demo.txt:
file.getPath()=G:\AndroidWorkspaces\Think4JavaExamples\app\src\main\java\com\test\demo.txt
file.getAbsolutePath()=G:\AndroidWorkspaces\Think4JavaExamples\app\src\main\java\com\test\demo.txt
file.getCanonicalPath()=G:\AndroidWorkspaces\Think4JavaExamples\app\src\main\java\com\test\demo.txt

它們的區別如下:
File.getPath() 獲取的是傳入 File 構造的那個路徑。
File.getAbsolutePath() 獲取的是定義時的路徑對應的相對路徑,但不會處理. 和 … 的情況。
File.getCanonicalPath() 獲取的是規範化的絕對路徑,會處理 . 和 … 的情況。但是,它是會拋出異常的,需要處理一下。

9. Trying to instantiate a class xxx that is not a Fragment

時間:2019年12月17日22:19:45
問題描述
錯誤日誌:

2019-12-17 22:20:54.118 11934-11934/com.example.startactivityforresultdemo E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.startactivityforresultdemo, PID: 11934
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.startactivityforresultdemo/com.example.startactivityforresultdemo.FirstActivity}: android.view.InflateException: Binary XML file line #25: Binary XML file line #25: Error inflating class fragment
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2724)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2789)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1527)
        at android.os.Handler.dispatchMessage(Handler.java:110)
        at android.os.Looper.loop(Looper.java:203)
        at android.app.ActivityThread.main(ActivityThread.java:6251)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)
     Caused by: android.view.InflateException: Binary XML file line #25: Binary XML file line #25: Error inflating class fragment
     Caused by: android.view.InflateException: Binary XML file line #25: Error inflating class fragment
     Caused by: android.app.Fragment$InstantiationException: Trying to instantiate a class com.example.startactivityforresultdemo.FirstFragmentFragment that is not a Fragment
        at android.app.Fragment.instantiate(Fragment.java:617)
        at android.app.Fragment.instantiate(Fragment.java:593)
        at android.app.FragmentManagerImpl.onCreateView(FragmentManager.java:2302)
        at android.app.FragmentController.onCreateView(FragmentController.java:98)
        at android.app.Activity.onCreateView(Activity.java:5886)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:777)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:727)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:858)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:518)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:426)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:377)
        at com.android.internal.policy.PhoneWindow.setContentView(PhoneWindow.java:424)
        at android.app.Activity.setContentView(Activity.java:2416)
        at androidx.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:303)
        at androidx.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:284)
        at com.example.startactivityforresultdemo.FirstActivity.onCreate(FirstActivity.java:31)
        at android.app.Activity.performCreate(Activity.java:6666)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2677)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2789)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1527)
        at android.os.Handler.dispatchMessage(Handler.java:110)
        at android.os.Looper.loop(Looper.java:203)
        at android.app.ActivityThread.main(ActivityThread.java:6251)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)
     Caused by: java.lang.ClassCastException
        at android.app.Fragment.instantiate(Fragment.java:618)
        at android.app.Fragment.instantiate(Fragment.java:593) 
        at android.app.FragmentManagerImpl.onCreateView(FragmentManager.java:2302) 
        at android.app.FragmentController.onCreateView(FragmentController.java:98) 
        at android.app.Activity.onCreateView(Activity.java:5886) 
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:777) 
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:727) 
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:858) 
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821) 
        at android.view.LayoutInflater.inflate(LayoutInflater.java:518) 
        at android.view.LayoutInflater.inflate(LayoutInflater.java:426) 
        at android.view.LayoutInflater.inflate(LayoutInflater.java:377) 
        at com.android.internal.policy.PhoneWindow.setContentView(PhoneWindow.java:424) 
        at android.app.Activity.setContentView(Activity.java:2416) 
        at androidx.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:303) 
        at androidx.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:284) 
        at com.example.startactivityforresultdemo.FirstActivity.onCreate(FirstActivity.java:31) 
        at android.app.Activity.performCreate(Activity.java:6666) 
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) 
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2677) 
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2789) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1527) 
        at android.os.Handler.dispatchMessage(Handler.java:110) 
        at android.os.Looper.loop(Looper.java:203) 
        at android.app.ActivityThread.main(ActivityThread.java:6251) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924) 

在一個繼承於 Activity 的頁面,通過 fragment 標籤加載了一個繼承於 androidx.fragment.app.Fragment 的 Fragment 後,報出了這個錯誤。
問題分析
查看日誌,定位報錯的地方在 android.app.Fragment.instantiate() 這個方法裏。看一下源碼:

  Class<?> clazz = sClassMap.get(fname);
  if (clazz == null) {
         // Class not found in the cache, see if it's real, and try to add it
         clazz = context.getClassLoader().loadClass(fname);
         if (!Fragment.class.isAssignableFrom(clazz)) {
                 throw new InstantiationException("Trying to instantiate a class " + fname
                            + " that is not a Fragment", new ClassCastException());
         }
         sClassMap.put(fname, clazz);
  }

clazz 對象是通過傳入的參數 fname,加載出來的,它是一個 androidx.fragment.app.Fragment 的一個子類。
Fragment.class 它所在的包是 package android.app;
再來看一下 Class.isAssignableFrom(Class clazz),是Class類的方法,主要用於判斷此Class對象表示的類或接口是否與指定的Class參數表示的類或接口相同,或者是它們的超類或超接口。很明顯,結果是 false,所以拋出了異常。
解決辦法:使用繼承於 android.app.Fragment 加載。

10. Caused by: java.lang.IllegalStateException: Expected Android API level 21+ but was 19

時間:2019年12月19日21:40:00
問題描述

java.lang.ExceptionInInitializerError
        at okhttp3.internal.platform.Platform$Companion.findPlatform(Platform.kt:211)
        at okhttp3.internal.platform.Platform$Companion.access$findPlatform(Platform.kt:179)
        at okhttp3.internal.platform.Platform.<clinit>(Platform.kt:180)
        at okhttp3.OkHttpClient.<init>(OkHttpClient.kt:219)
        at okhttp3.OkHttpClient.<init>(OkHttpClient.kt:211)
        at com.didichuxing.doraemonkit.util.DoraemonStatisticsUtil.uploadUserInfo(DoraemonStatisticsUtil.java:51)
        at com.didichuxing.doraemonkit.DoraemonKit.install(DoraemonKit.java:392)
        at com.didichuxing.doraemonkit.DoraemonKit.install(DoraemonKit.java:127)
        at com.prdsff.veryclean.MainApplication.onCreate(MainApplication.java:43)
        at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1009)
        at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4611)
        at android.app.ActivityThread.access$1500(ActivityThread.java:153)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1405)
        at android.os.Handler.dispatchMessage(Handler.java:110)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:5373)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:829)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:645)
        at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.IllegalStateException: Expected Android API level 21+ but was 19
        at okhttp3.internal.platform.AndroidPlatform.<clinit>(AndroidPlatform.kt:232)
        at okhttp3.internal.platform.Platform$Companion.findPlatform(Platform.kt:211) 
        at okhttp3.internal.platform.Platform$Companion.access$findPlatform(Platform.kt:179) 
        at okhttp3.internal.platform.Platform.<clinit>(Platform.kt:180) 
        at okhttp3.OkHttpClient.<init>(OkHttpClient.kt:219) 
        at okhttp3.OkHttpClient.<init>(OkHttpClient.kt:211) 
        at com.didichuxing.doraemonkit.util.DoraemonStatisticsUtil.uploadUserInfo(DoraemonStatisticsUtil.java:51) 
        at com.didichuxing.doraemonkit.DoraemonKit.install(DoraemonKit.java:392) 
        at com.didichuxing.doraemonkit.DoraemonKit.install(DoraemonKit.java:127) 
        at com.prdsff.veryclean.MainApplication.onCreate(MainApplication.java:43) 
        at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1009) 
        at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4611) 
        at android.app.ActivityThread.access$1500(ActivityThread.java:153) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1405) 
        at android.os.Handler.dispatchMessage(Handler.java:110) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:5373) 
        at java.lang.reflect.Method.invokeNative(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:515) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:829) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:645) 
        at dalvik.system.NativeStart.main(Native Method) 

使用 Doraemonkit 在 API 19 的手機上報出這個錯誤。
問題分析:定位一下,是 okhttp 拋出的異常。我使用的 okhttp 版本是 4.2.1,定位到代碼:

    val isSupported: Boolean = when {
      !isAndroid -> false
      else -> {
        // Fail Fast
        check(
            Build.VERSION.SDK_INT >= 21) { "Expected Android API level 21+ but was ${Build.VERSION.SDK_INT}" }

        true
      }
    }

官方確實毫不猶豫拋出了異常。查看 okhttp 的 Requirements

OkHttp works on Android 5.0+ (API level 21+) and on Java 8+.
The OkHttp 3.12.x branch supports Android 2.3+ (API level 9+) and Java 7+.

從這裏看出應該使用 3.12.x 版本纔對。
使用開源庫,有大版本號升級時,一定要去查看一下版本更新。大版本號,意味着有大的更新。

最後

代碼出錯了,關鍵是要仔細查看日誌。能夠仔細地查看日誌,就離解決問題很近了。

發佈了76 篇原創文章 · 獲贊 44 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章