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 版本才对。
使用开源库,有大版本号升级时,一定要去查看一下版本更新。大版本号,意味着有大的更新。

最后

代码出错了,关键是要仔细查看日志。能够仔细地查看日志,就离解决问题很近了。

发布了78 篇原创文章 · 获赞 46 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/willway_wang/article/details/103394456