Android 在动画结束回调onAnimationEnd()中remove view的崩溃解决方法及源码分析

问题:

问题描述起来很简单,就是在动画结束的时候,调用父view删除子view,出现崩溃,信息如下:

java.lang.NullPointerException
Attempt to read from field 'int android.view.View.mViewFlags' on a null object reference
 android.view.ViewGroup.dispatchDraw(ViewGroup.java:4111)
 android.view.View.updateDisplayListIfDirty(View.java:19073)
 android.view.View.draw(View.java:19935)
 android.view.ViewGroup.drawChild(ViewGroup.java:4333)
 android.view.ViewGroup.dispatchDraw(ViewGroup.java:4112)

下面是问题的核心代码

		//设置动画回调
        animation.setAnimationListener(new Animation.AnimationListener(){
            @Override
            public void onAnimationStart(Animation animation) {
            }
            @Override
            public void onAnimationEnd(Animation animation) {
                //container 是fromView 的父view,是一个viewGroup
                container.removeViewInLayout(fromView);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
        //执行动画
        fromView.startAnimation(animation);

源码分析:

不想看源码的同学 ,也可以直接去下面看解决方法。

问题出在哪里,就在哪里断点,看看到底是什么问题。

下面先把断点调试的代码,截图出来,方便看到具体的值。两图的代码都是dispatchDraw函数里的,
在这里插入图片描述

在这里插入图片描述

下面的代码,是上面代码的文字版本

   @Override
    protected void dispatchDraw(Canvas canvas) {
        ...省略若干代码.....
        final int childrenCount = mChildrenCount;
        final View[] children = mChildren;

       ...省略若干代码.....

        // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
        // draw reordering internally
        final ArrayList<View> preorderedList = usingRenderNodeProperties
                ? null : buildOrderedChildList();
        final boolean customOrder = preorderedList == null
                && isChildrenDrawingOrderEnabled();
        for (int i = 0; i < childrenCount; i++) {
            while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
                final View transientChild = mTransientViews.get(transientIndex);
                if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                        transientChild.getAnimation() != null) {
                    more |= drawChild(canvas, transientChild, drawingTime);
                }
                transientIndex++;
                if (transientIndex >= transientCount) {
                    transientIndex = -1;
                }
            }

            final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
            final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
            //这里发生了空指针异常,child为null
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                more |= drawChild(canvas, child, drawingTime);
            }
        }
        ...省略若干代码.....
   }     

我们看到,直接原因是child 为null,导致获取child.mViewFlags 出现NullPointerException

代码再往上找,函数getAndVerifyPreorderedView 来获取child,具体的参数情况,是 children 里的个数是2,但是childIndex是2,得到的结果肯定null。

childIndex 是通过函数getAndVerifyPreorderedIndex(childrenCount, i, customOrder)来获取的,根据当前的参数情况,childIndex 取得是i 的值,i的值是在循环中根据childrenCount来递增的。

继续跟进childrenCount,在dispatchDraw()函数的前面进行赋值

        final int childrenCount = mChildrenCount;
        final View[] children = mChildren;

mChildrenCount 就是mChildren的数量,凡是在mChildren中添加或者删除view,mChildrenCount 都会相应变化。

通过上面的分析,大概知道原因就是开始时mChildrenCount的值是3,赋给了childrenCount ,mChildren里面也是3个view。继续往下执行的时候,出现了mChildren 里面的view被删除了一个,mChildrenCount的值也变成了2。于是就出现了上面的崩溃。

那为什么view 会少了一个呢?
我们接着看代码

            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                more |= drawChild(canvas, child, drawingTime);
            }

在dispatchDraw中会执行到这个代码,绘制子view,

    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }

注意这里调用draw是三个参数的,和平时看的measure,layout,draw的draw函数不是同一个

View.java 中的函数

    /**
     * This method is called by ViewGroup.drawChild() to have each child view draw itself.
     *
     * This is where the View specializes rendering behavior based on layer type,
     * and hardware acceleration.
     */
    boolean draw(Canvas canvas, ViewGroup parent, long drawingTime){
    
        ...省略若干代码.....
         //获取是否有动画
        final Animation a = getAnimation();
        if (a != null) {
           //若果有动画,需要应用(处理)遗留的动画
            more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
            concatMatrix = a.willChangeTransformationMatrix();
            if (concatMatrix) {
                mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
            }
            transformToApply = parent.getChildTransformation();
        } 
      ...省略若干代码.....

}

Animation.java 中的函数

    /**
     * Gets the transformation to apply at a specified point in time. Implementations of this
     * method should always replace the specified Transformation or document they are doing
     * otherwise.
     *
     * @param currentTime Where we are in the animation. This is wall clock time.
     * @param outTransformation A transformation object that is provided by the
     *        caller and will be filled in by the animation.
     * @param scale Scaling factor to apply to any inputs to the transform operation, such
     *        pivot points being rotated or scaled around.
     * @return True if the animation is still running
     */
    public boolean getTransformation(long currentTime, Transformation outTransformation,
            float scale) {
        mScaleFactor = scale;
        return getTransformation(currentTime, outTransformation);
    }

接着调用getTransformation,这里调用到AnimationSet.java 里的函数

    /**
     * The transformation of an animation set is the concatenation of all of its
     * component animations.
     *
     * @see android.view.animation.Animation#getTransformation
     */
    @Override
    public boolean getTransformation(long currentTime, Transformation t) {
       ...省略若干代码.....
        boolean ended = true;
        if (ended != mEnded) {
            if (mListener != null) {
                // 这里调用了动画结束的回调
                mListener.onAnimationEnd(this);
            }
            mEnded = ended;
        }
     ...省略若干代码.....
        return more;
    }

到这里原因就彻底搞清楚了

解决办法:

知道了原因,再来解决就很简单了。以最开始的核心问题代码,来演示如何解决。

问题出现remove view的时候,在dispatchDraw 中改变了viewGroup已有的子view的数量,导致只有N个view,最大索引是N-1,想要获取第N个view,出现了异常。

那么我们可以考虑不在本次执行中,remove view。在下一次的loop消息中执行remove 操作,那么就通过post 或 handler 发送消息来操作view

提供两种解决方法:

第一种:

		//设置动画回调
        animation.setAnimationListener(new Animation.AnimationListener(){
            @Override
            public void onAnimationStart(Animation animation) {
            }
            @Override
            public void onAnimationEnd(Animation animation) {
                 //get the parentView...
                 container.post(new Runnable() {
                        public void run () {
                         // it works without the runOnUiThread, but all UI updates must 
                         // be done on the UI thread
                          activity.runOnUiThread(new Runnable() {
                               public void run() {
                                 //container 是fromView 的父view,是一个viewGroup
                                 container.removeViewInLayout(fromView);
                               }
                           });
                       }
                }
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
        //执行动画
        fromView.startAnimation(animation);

第二种:

        //执行动画
        fromView.startAnimation(animation);
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                try {
                    container.removeViewInLayout(fromView);
                } catch (Exception ignored) {
                }
           }
         },animation.getDuration());

结语:

后来分析完源码,在动画结束后,删除view 应该也算是一个合理的需求,于是上网一搜,果然有人也遇到这个问题,第一种解决方法,就是这里的
Android - remove View when its animation finished

已标记关键词 清除标记
<div><p>使用tinkerpatch平台,补丁合成成功了,也生效了。但是报下面log上的错误。实时统计也统计不到合成成功或者说统计不准确,只有下载成功数。 机型:会议平板(市面比较少见) 系统:原生的Android 5.1 应用类型:Luancher。应用生命周期与系统相同。只有在开机的时候,启动Launcher,合成补丁文件。 该错误只有在合成补丁的当次出现,其余情况下没有出现。并未引起应用崩溃,现在发现最直观的影响是上报不成功。 log:</p> <pre><code> 02-15 11:11:01.732 928-928/? E/ActivityThread: Failed to find provider info for com.tinker.debug.debugprovider 02-15 11:11:01.880 928-928/? E/AndroidRuntime: FATAL EXCEPTION: main Process: android.upedu.launcher, PID: 928 java.lang.RuntimeException: Unable to create application com.tinkerpatch.sdk.loader.TinkerPatchReflectApplication: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.util.ArrayList.size()' on a null object reference at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4582) at android.app.ActivityThread.access$1500(ActivityThread.java:151) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1380) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5280) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:963) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:758) Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.util.ArrayList.size()' on a null object reference at android.upedu.launcher.utils.CommonUtil.isServiceWorked(Unknown Source) at android.upedu.launcher.utils.CommonUtil.startMultitaskService(Unknown Source) at android.upedu.launcher.OnlineApp.onCreate(Unknown Source) at com.tinkerpatch.sdk.loader.TinkerPatchReflectApplication.onCreate(Unknown Source) at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1012) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4579) at android.app.ActivityThread.access$1500(ActivityThread.java:151)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1380)  at android.os.Handler.dispatchMessage(Handler.java:102)  at android.os.Looper.loop(Looper.java:135)  at android.app.ActivityThread.main(ActivityThread.java:5280)  at java.lang.reflect.Method.invoke(Native Method)  at java.lang.reflect.Method.invoke(Method.java:372)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:963)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:758)  02-15 11:11:01.880 928-928/? E/AndroidRuntime: Error reporting crash android.os.DeadObjectException at android.os.BinderProxy.transactNative(Native Method) at android.os.BinderProxy.transact(Binder.java:496) at android.app.ActivityManagerProxy.handleApplicationCrash(ActivityManagerNative.java:4211) at com.android.internal.os.RuntimeInit$UncaughtHandler.uncaughtException(RuntimeInit.java:89) at com.tencent.tinker.loader.TinkerUncaughtHandler.uncaughtException(Unknown Source) at com.tinkerpatch.sdk.tinker.a.a.uncaughtException(Unknown Source) at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:693) at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:690) 02-15 11:12:06.774 2238-2238/android.upedu.launcher W/Tinker.TinkerLoader: tryLoadPatchFiles:isEnabledForResource:true 02-15 11:12:06.789 2238-2238/android.upedu.launcher D/Tinker.TinkerInternals: same fingerprint:cvtouchup/cvtouchup/cvtouchup:5.1.1/LMY49F/cvtouch01161612:userdebug/test-keys 02-15 11:12:06.790 2238-2238/android.upedu.launcher W/Tinker.TinkerLoader: tinker safe mode preferName:tinker_own_config_android.upedu.launcher count:1 02-15 11:12:06.805 2238-2238/android.upedu.launcher I/Tinker.TinkerDexLoader: classloader: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/android.upedu.launcher-1/base.apk"],nativeLibraryDirectories=[/data/app/android.upedu.launcher-1/lib/arm, /vendor/lib, /system/lib]]] 02-15 11:12:06.834 2365-2365/? I/dex2oat: /system/bin/dex2oat --runtime-arg -classpath --runtime-arg --instruction-set=arm --instruction-set-features=div --runtime-arg -Xrelocate --boot-image=/system/framework/boot.art --dex-file=/data/user/0/android.upedu.launcher/tinker/patch-45d44dda/dex/classes.dex.jar --oat-fd=20 --oat-location=/data/user/0/android.upedu.launcher/tinker/patch-45d44dda/odex/classes.dex.dex --runtime-arg -Xms64m --runtime-arg -Xmx512m 02-15 11:12:11.856 2499-2499/? I/dex2oat: /system/bin/dex2oat --runtime-arg -classpath --runtime-arg --instruction-set=arm --instruction-set-features=div --runtime-arg -Xrelocate --boot-image=/system/framework/boot.art --dex-file=/data/user/0/android.upedu.launcher/tinker/patch-45d44dda/dex/test.dex.jar --oat-fd=20 --oat-location=/data/user/0/android.upedu.launcher/tinker/patch-45d44dda/odex/test.dex.dex --runtime-arg -Xms64m --runtime-arg -Xmx512m 02-15 11:12:11.874 2238-2238/android.upedu.launcher I/Tinker.ClassLoaderAdder: after loaded classloader: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/user/0/android.upedu.launcher/tinker/patch-45d44dda/dex/classes.dex.jar", zip file "/data/user/0/android.upedu.launcher/tinker/patch-45d44dda/dex/test.dex.jar", zip file "/data/app/android.upedu.launcher-1/base.apk"],nativeLibraryDirectories=[/data/app/android.upedu.launcher-1/lib/arm, /vendor/lib, /system/lib]]], dex size:2 02-15 11:12:11.875 2238-2238/android.upedu.launcher W/Tinker.ClassLoaderAdder: checkDexInstall result:true 02-15 11:12:11.876 2238-2238/android.upedu.launcher W/Tinker.ResourcePatcher: try to clear typedArray cache! 02-15 11:12:11.876 2238-2238/android.upedu.launcher E/Tinker.ResourcePatcher: checkResUpdate success, found test resource assets file only_use_to_test_tinker_resource.txt 02-15 11:12:11.877 2238-2238/android.upedu.launcher I/Tinker.ResourceLoader: monkeyPatchExistingResources resource file:/data/user/0/android.upedu.launcher/tinker/patch-45d44dda/res/resources.apk, use time: 2 02-15 11:12:11.877 2238-2238/android.upedu.launcher I/Tinker.TinkerLoader: tryLoadPatchFiles: load end, ok! 02-15 11:12:11.880 2238-2238/android.upedu.launcher D/Tinker.DefaultAppLike: onBaseContextAttached: 02-15 11:12:11.883 2238-2238/android.upedu.launcher I/Tinker.ReflectApp: with app application from manifest applicationName:android.upedu.launcher.OnlineApp 02-15 11:12:11.885 2238-2238/android.upedu.launcher E/Tinker.ReflectApp: replaceApplicationLike delegateClass:class com.tinkerpatch.sdk.loader.TinkerPatchApplicationLike 02-15 11:12:11.885 2238-2238/android.upedu.launcher D/Tinker.DefaultAppLike: onCreate 02-15 11:12:11.895 2238-2238/android.upedu.launcher I/Tinker.ServerUtils: with app key from manifest appKey:bce9c166cd7ae416 02-15 11:12:11.896 2238-2238/android.upedu.launcher I/Tinker.ServerUtils: with app version from manifest appVersion:1.43 02-15 11:12:11.897 2238-2238/android.upedu.launcher I/Tinker.VersionInfo: readVersionInfo file path:/data/user/0/android.upedu.launcher/tinker_server/version.info, appVersion: 1.43, uuid:d5df078a-2678-4b6d-8e1e-8ab74cce8717, abi:armeabi-v7a, patchVersion:6, patchMd5:45d44dda5e512e7b91893e523c8423ab, grayValue:5, crashTimes:1, retryTimes:0 02-15 11:12:11.900 2238-2238/android.upedu.launcher E/ActivityThread: Failed to find provider info for com.tinker.debug.debugprovider 02-15 11:12:11.900 2238-2238/android.upedu.launcher D/Tinker.Debugger: debugger not attached cu == null 02-15 11:12:11.900 2238-2238/android.upedu.launcher I/Tinker.ServerClient: installTinkerServer, debug value: false, appVersion: 1.43, appKey: bce9c166cd7ae416 02-15 11:12:11.903 2238-2238/android.upedu.launcher W/Tinker.Tinker: tinker patch directory: /data/user/0/android.upedu.launcher/tinker 02-15 11:12:11.906 2238-2238/android.upedu.launcher I/Tinker.Tinker: try to install tinker, isEnable: true, version: 1.7.7 02-15 11:12:11.908 2238-2238/android.upedu.launcher I/Tinker.TinkerLoadResult: parseTinkerResult loadCode:0, systemOTA:false 02-15 11:12:11.908 2238-2238/android.upedu.launcher I/Tinker.TinkerLoadResult: parseTinkerResult oldVersion:45d44dda5e512e7b91893e523c8423ab, newVersion:45d44dda5e512e7b91893e523c8423ab, current:45d44dda5e512e7b91893e523c8423ab 02-15 11:12:11.908 2238-2238/android.upedu.launcher I/Tinker.TinkerLoadResult: oh yeah, tinker load all success 02-15 11:12:11.908 2238-2238/android.upedu.launcher I/Tinker.DefaultLoadReporter: patch loadReporter onLoadResult: patch load result, path:/data/user/0/android.upedu.launcher/tinker, code:0, cost:4402 02-15 11:12:11.912 2238-2238/android.upedu.launcher W/ContextImpl: Calling a method in the system process without a qualified user: android.app.ContextImpl.startService:1709 android.content.ContextWrapper.startService:516 android.upedu.launcher.utils.CommonUtil.startMultitaskService:-1 android.upedu.launcher.OnlineApp.onCreate:-1 com.tinkerpatch.sdk.loader.TinkerPatchReflectApplication.onCreate:-1 02-15 11:12:12.305 2514-2514/android.upedu.launcher:channel W/Tinker.TinkerLoader: tryLoadPatchFiles:isEnabledForResource:true 02-15 11:12:12.310 2514-2514/android.upedu.launcher:channel D/Tinker.TinkerInternals: same fingerprint:cvtouchup/cvtouchup/cvtouchup:5.1.1/LMY49F/cvtouch01161612:userdebug/test-keys 02-15 11:12:12.310 2514-2514/android.upedu.launcher:channel W/Tinker.TinkerLoader: tinker safe mode preferName:tinker_own_config_android.upedu.launcher:channel count:1 02-15 11:12:12.332 2514-2514/android.upedu.launcher:channel I/Tinker.TinkerDexLoader: classloader: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/android.upedu.launcher-1/base.apk"],nativeLibraryDirectories=[/data/app/android.upedu.launcher-1/lib/arm, /vendor/lib, /system/lib]]] 02-15 11:12:12.335 2514-2514/android.upedu.launcher:channel I/Tinker.ClassLoaderAdder: after loaded classloader: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/user/0/android.upedu.launcher/tinker/patch-45d44dda/dex/classes.dex.jar", zip file "/data/user/0/android.upedu.launcher/tinker/patch-45d44dda/dex/test.dex.jar", zip file "/data/app/android.upedu.launcher-1/base.apk"],nativeLibraryDirectories=[/data/app/android.upedu.launcher-1/lib/arm, /vendor/lib, /system/lib]]], dex size:2 02-15 11:12:12.335 2514-2514/android.upedu.launcher:channel W/Tinker.ClassLoaderAdder: checkDexInstall result:true 02-15 11:12:12.337 2514-2514/android.upedu.launcher:channel W/Tinker.ResourcePatcher: try to clear typedArray cache! 02-15 11:12:12.337 2514-2514/android.upedu.launcher:channel E/Tinker.ResourcePatcher: checkResUpdate success, found test resource assets file only_use_to_test_tinker_resource.txt 02-15 11:12:12.337 2514-2514/android.upedu.launcher:channel I/Tinker.ResourceLoader: monkeyPatchExistingResources resource file:/data/user/0/android.upedu.launcher/tinker/patch-45d44dda/res/resources.apk, use time: 1 02-15 11:12:12.338 2514-2514/android.upedu.launcher:channel I/Tinker.TinkerLoader: tryLoadPatchFiles: load end, ok! 02-15 11:12:12.339 2514-2514/android.upedu.launcher:channel D/Tinker.DefaultAppLike: onBaseContextAttached: 02-15 11:12:12.344 2514-2514/android.upedu.launcher:channel I/Tinker.ReflectApp: with app application from manifest applicationName:android.upedu.launcher.OnlineApp 02-15 11:12:12.348 2514-2514/android.upedu.launcher:channel E/Tinker.ReflectApp: replaceApplicationLike delegateClass:class com.tinkerpatch.sdk.loader.TinkerPatchApplicationLike 02-15 11:12:12.348 2514-2514/android.upedu.launcher:channel D/Tinker.DefaultAppLike: onCreate 02-15 11:12:12.359 2514-2514/android.upedu.launcher:channel I/Tinker.ServerUtils: with app key from manifest appKey:bce9c166cd7ae416 02-15 11:12:12.360 2514-2514/android.upedu.launcher:channel I/Tinker.ServerUtils: with app version from manifest appVersion:1.43 02-15 11:12:12.361 2514-2514/android.upedu.launcher:channel I/Tinker.VersionInfo: readVersionInfo file path:/data/user/0/android.upedu.launcher/tinker_server/version.info, appVersion: 1.43, uuid:d5df078a-2678-4b6d-8e1e-8ab74cce8717, abi:armeabi-v7a, patchVersion:6, patchMd5:45d44dda5e512e7b91893e523c8423ab, grayValue:5, crashTimes:1, retryTimes:0 02-15 11:12:12.364 2514-2514/android.upedu.launcher:channel E/ActivityThread: Failed to find provider info for com.tinker.debug.debugprovider 02-15 11:12:12.364 2514-2514/android.upedu.launcher:channel D/Tinker.Debugger: debugger not attached cu == null 02-15 11:12:12.364 2514-2514/android.upedu.launcher:channel I/Tinker.ServerClient: installTinkerServer, debug value: false, appVersion: 1.43, appKey: bce9c166cd7ae416 02-15 11:12:12.368 2514-2514/android.upedu.launcher:channel W/Tinker.Tinker: tinker patch directory: /data/user/0/android.upedu.launcher/tinker 02-15 11:12:12.371 2514-2514/android.upedu.launcher:channel I/Tinker.Tinker: try to install tinker, isEnable: true, version: 1.7.7 02-15 11:12:12.373 2514-2514/android.upedu.launcher:channel I/Tinker.TinkerLoadResult: parseTinkerResult loadCode:0, systemOTA:false 02-15 11:12:12.373 2514-2514/android.upedu.launcher:channel I/Tinker.TinkerLoadResult: parseTinkerResult oldVersion:45d44dda5e512e7b91893e523c8423ab, newVersion:45d44dda5e512e7b91893e523c8423ab, current:45d44dda5e512e7b91893e523c8423ab 02-15 11:12:12.373 2514-2514/android.upedu.launcher:channel I/Tinker.TinkerLoadResult: oh yeah, tinker load all success 02-15 11:12:12.373 2514-2514/android.upedu.launcher:channel I/Tinker.DefaultLoadReporter: patch loadReporter onLoadResult: patch load result, path:/data/user/0/android.upedu.launcher/tinker, code:0, cost:58 02-15 11:12:12.959 2514-2514/android.upedu.launcher:channel D/Tinker.UpgradePatchRetry: onPatchRetryLoad retry is not main process, just return 02-15 11:12:14.299 2238-2238/android.upedu.launcher E/Tinker: 网络连接成功,开始请求补丁文件 02-15 11:12:14.669 2238-2238/android.upedu.launcher W/Tinker.UpgradePatchRetry: onPatchRetryLoad patch file: /data/user/0/android.upedu.launcher/tinker_temp/temp.apk is not exist, just return 02-15 11:12:14.683 2238-2238/android.upedu.launcher D/Tinker.UrlConnectionFetcher: loadData from url: http://q.tinkerpatch.com/bce9c166cd7ae416/1.43?d=d5df078a-2678-4b6d-8e1e-8ab74cce8717&v=1487128334683, method:GET, body:null 02-15 11:12:14.693 2238-2238/android.upedu.launcher D/Tinker.UrlConnectionFetcher: loadData from url: http://q.tinkerpatch.com/bce9c166cd7ae416/1.43?d=d5df078a-2678-4b6d-8e1e-8ab74cce8717&v=1487128334693, method:GET, body:null 02-15 11:12:14.872 2238-2651/android.upedu.launcher D/Tinker.UrlConnectionFetcher: response code 200 msg: OK 02-15 11:12:14.872 2238-2651/android.upedu.launcher E/Tinker.ClientImpl: tinker server sync respond:{"v":6} 02-15 11:12:14.872 2238-2651/android.upedu.launcher I/Tinker.ClientImpl: sync response in update:version:6 grayValue:null conditions: pause:false rollback:false 02-15 11:12:14.872 2238-2651/android.upedu.launcher I/Tinker.VersionInfo: VersionCheck: target version 6 is not latest. current version is 6 02-15 11:12:14.872 2238-2651/android.upedu.launcher I/Tinker.ClientImpl: Needn't update, sync response is: version:6 grayValue:null conditions: pause:false rollback:false gray: 5 02-15 11:12:14.959 2238-2651/android.upedu.launcher D/Tinker.UrlConnectionFetcher: response code 200 msg: OK 02-15 11:12:14.959 2238-2651/android.upedu.launcher E/Tinker.ClientImpl: tinker server sync respond:{"v":6} 02-15 11:12:14.959 2238-2651/android.upedu.launcher I/Tinker.ClientImpl: sync response in update:version:6 grayValue:null conditions: pause:false rollback:false 02-15 11:12:14.959 2238-2651/android.upedu.launcher I/Tinker.VersionInfo: VersionCheck: target version 6 is not latest. current version is 6 02-15 11:12:14.959 2238-2651/android.upedu.launcher I/Tinker.ClientImpl: Needn't update, sync response is: version:6 grayValue:null conditions: pause:false rollback:false gray: 5 </code></pre> <p>application:</p> <pre><code>java package android.upedu.launcher; import android.annotation.SuppressLint; import android.app.Activity; import android.app.ActivityManager; import android.app.Application; import android.content.Context; import android.graphics.Bitmap; import android.os.Environment; import android.support.multidex.MultiDex; import android.upedu.launcher.activity.CrashActivity; import android.upedu.launcher.net.HttpUtils; import android.upedu.launcher.utils.CommonUtil; import android.upedu.launcher.utils.SharedPreferencesDownLoadUtil; import android.upedu.launcher.utils.SharedPreferencesUtil; import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.DisplayImageOptions.Builder; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.assist.QueueProcessingType; import com.nostra13.universalimageloader.core.download.BaseImageDownloader; import com.tencent.tinker.loader.app.ApplicationLike; import com.tinkerpatch.sdk.TinkerPatch; import com.tinkerpatch.sdk.loader.TinkerPatchApplicationLike; import com.umeng.analytics.MobclickAgent; import com.umeng.message.IUmengCallback; import com.umeng.message.IUmengRegisterCallback; import com.umeng.message.PushAgent; import java.io.File; import java.util.LinkedList; import cat.ereza.customactivityoncrash.CustomActivityOnCrash; public class OnlineApp extends Application { public Context context; ("StaticFieldLeak") public static OnlineApp myApp; private static final String KEY_HEADS_UP = "headsUp"; // public ExecutorService instance; private final LinkedList<activity> mActivitys = new LinkedList<>(); //显示图片的配置 public Builder builder; //ImageLoader对象 public ImageLoader imageLoader; private DisplayImageOptions options; private static final String TAG = "Tinker.Application"; private ApplicationLike tinkerApplicationLike; protected void attachBaseContext(Context base) { super.attachBaseContext(base); //you must install multiDex whatever tinker is installed! MultiDex.install(base); } public void onCreate() { super.onCreate(); myApp = this; context = getApplicationContext(); initTinker();//初始化tinker //初始化时先在文件删除pushToken SharedPreferencesUtil.getInstance().editPutString("pushToken", ""); CommonUtil.startMultitaskService(context);//初始化多任务的service // instance = Executors.newFixedThreadPool(10); //volley初始化 diskLruCache初始化 HttpUtils.getInstance(context); //初始化文件下载 SharedPreferencesDownLoadUtil.getInstance().editClear(); //ACRA初始化 CustomActivityOnCrash.install(this); CustomActivityOnCrash.setErrorActivityClass(CrashActivity.class); //友盟 PushAgent mPushAgent = PushAgent.getInstance(this); //注册推送服务,每次调用register方法都会回调该接口 mPushAgent.register(new IUmengRegisterCallback() { public void onSuccess(String deviceToken) { //注册成功会返回device token SharedPreferencesUtil.getInstance().editPutString("pushToken", deviceToken); } public void onFailure(String s, String s1) { } }); //添加推送SDK自己的日志打印关闭方法 mPushAgent.setDebugMode(false); //在用户未登录时不通过Push去推送 mPushAgent.disable(new IUmengCallback() { public void onSuccess() { } public void onFailure(String s, String s1) { } }); //// MobclickAgent.setScenarioType(context, MobclickAgent.EScenarioType.E_UM_NORMAL); MobclickAgent.setDebugMode(true); //显示图片的配置 builder = new Builder() .cacheInMemory(true) .cacheOnDisk(true) .bitmapConfig(Bitmap.Config.RGB_565); options=new DisplayImageOptions.Builder() .cacheInMemory(true) .cacheOnDisk(true) .bitmapConfig(Bitmap.Config.RGB_565) .build(); final File cacheDir = new File(Environment.getExternalStorageDirectory().getPath() + "/Upedu/picCache"); imageLoader = ImageLoader.getInstance(); ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context) .memoryCacheExtraOptions(480, 800) // default = device screen dimensions .diskCacheExtraOptions(480, 800, null) .threadPoolSize(3) // default .threadPriority(Thread.NORM_PRIORITY - 1) // default .tasksProcessingOrder(QueueProcessingType.FIFO) // default .denyCacheImageMultipleSizesInMemory() .memoryCache(new LruMemoryCache(2 * 1024 * 1024)) .memoryCacheSize(2 * 1024 * 1024) .memoryCacheSizePercentage(13) // default .diskCacheFileCount(500) .diskCacheSize(200 * 1024 * 1024) // .discCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default .imageDownloader(new BaseImageDownloader(context)) // default // .imageDecoder(new BaseImageDecoder()) // default .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default .writeDebugLogs() .defaultDisplayImageOptions(options) .build(); imageLoader.init(config);//全局初始化此配置 //用于控制splash界面的显示 SharedPreferencesUtil.getInstance().editPutBoolean("isStartuped", true); //添加本地崩溃日志 注意在打包的时候需要注释掉 // CrashHandler crashHandler = CrashHandler.getInstance() ; // crashHandler.init(this) ; } private void initTinker(){ //我们可以从这里获得Tinker加载过程的信息 tinkerApplicationLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike(); //开始检查是否有补丁,这里配置的是每隔访问3小时服务器是否有更新。 TinkerPatch.init(tinkerApplicationLike) .reflectPatchLibrary(); TinkerPatch.with().fetchPatchUpdate(true); } /** * 获得当前进程的名字 * * context * 进程号 */ public static String getCurProcessName(Context context) { int pid = android.os.Process.myPid(); ActivityManager activityManager = (ActivityManager) context .getSystemService(Context.ACTIVITY_SERVICE); for (ActivityManager.RunningAppProcessInfo appProcess : activityManager .getRunningAppProcesses()) { if (appProcess.pid == pid) { return appProcess.processName; } } return null; } /** * Activity开启时添加Activity到集合 * * activity */ public void addActivity(Activity activity) { mActivitys.addFirst(activity); } /** * Activity退出时清除集合的Activity. * * activity */ public void removeActivity(Activity activity) { mActivitys.remove(activity); } public void exit() { for (Activity activity : mActivitys) { activity.finish(); } } } </activity></code></pre> <p>CommonUtil:</p> <pre><code>java package android.upedu.launcher.utils; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.content.res.AssetManager; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.StateListDrawable; import android.os.Bundle; import android.upedu.launcher.base.BaseFragment; import android.upedu.launcher.service.MultitaskService; import android.view.View; import android.view.ViewGroup; import android.widget.GridView; import android.widget.ListAdapter; import android.widget.ListView; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; public class CommonUtil { /** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) */ public static int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } /** * 根据手机的分辨率从 px(像素) 的单位 转成为 dp */ public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } public static int getScreenPicHeight(int screenWidth, Bitmap bitmap) { int picWidth = bitmap.getWidth(); int picHeight = bitmap.getHeight(); int picScreenHeight = 0; picScreenHeight = (screenWidth * picHeight) / picWidth; return picScreenHeight; } private static Drawable createDrawable(Drawable d, Paint p) { BitmapDrawable bd = (BitmapDrawable) d; Bitmap b = bd.getBitmap(); Bitmap bitmap = Bitmap.createBitmap(bd.getIntrinsicWidth(), bd.getIntrinsicHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); canvas.drawBitmap(b, 0, 0, p); // 关键代码,使用新的Paint画原图, return new BitmapDrawable(bitmap); } /** * 设置Selector。 本次只增加点击变暗的效果,注释的代码为更多的效果 */ public static StateListDrawable createSLD(Context context, Drawable drawable) { StateListDrawable bg = new StateListDrawable(); int brightness = 50 - 127; ColorMatrix cMatrix = new ColorMatrix(); cMatrix.set(new float[]{1, 0, 0, 0, brightness, 0, 1, 0, 0, brightness,// 改变亮度 0, 0, 1, 0, brightness, 0, 0, 0, 1, 0}); Paint paint = new Paint(); paint.setColorFilter(new ColorMatrixColorFilter(cMatrix)); Drawable normal = drawable; Drawable pressed = createDrawable(drawable, paint); bg.addState(new int[]{android.R.attr.state_pressed,}, pressed); bg.addState(new int[]{android.R.attr.state_focused,}, pressed); bg.addState(new int[]{android.R.attr.state_selected}, pressed); bg.addState(new int[]{}, normal); return bg; } /** * 从assets目录获取图片 * * ct ct * fileName fileName * 返回值 */ public static Bitmap getImageFromAssetsFile(Context ct, String fileName) { Bitmap image = null; AssetManager am = ct.getAssets(); try { InputStream is = am.open(fileName); image = BitmapFactory.decodeStream(is); is.close(); } catch (IOException e) { e.printStackTrace(); } return image; } // public static <params progress result> void executeAsyncTask( // AsyncTask<params progress result> task, Params... params) { // if (Build.VERSION.SDK_INT >= 11) { // task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); // } else { // task.execute(params); // } // } // public static boolean hasToken(Context ct) { // String token = SharePrefUtil.getString(ct, "token", ""); // if (StringUtil.isEmpty(token)) { // return false; // } else { // return true; // } // } public static void setListViewHeightBasedOnChildren(ListView listView) { // 获取ListView对应的Adapter ListAdapter listAdapter = listView.getAdapter(); if (listAdapter == null) { return; } int totalHeight = 0; for (int i = 0; i < listAdapter.getCount(); i++) { // listAdapter.getCount()返回数据项的数目 View listItem = listAdapter.getView(i, null, listView); listItem.measure(0, 0); // 计算子项View 的宽高 totalHeight += listItem.getMeasuredHeight(); // 统计所有子项的总高度 } ViewGroup.LayoutParams params = listView.getLayoutParams(); params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); // listView.getDividerHeight()获取子项间分隔符占用的高度 // params.height最后得到整个ListView完整显示需要的高度 listView.setLayoutParams(params); } public static void setGridViewHeightBasedOnChildren(Context context, GridView gridView, int numColumns) { ListAdapter gridAdapter = gridView.getAdapter(); if (gridAdapter == null) { // pre-condition return; } View gridItem = gridAdapter.getView(0, null, gridView); gridItem.measure(0, 0); int totalHeight = gridItem.getMeasuredHeight() * ((gridAdapter.getCount() - 1) / numColumns + 1); ViewGroup.LayoutParams params = gridView.getLayoutParams(); params.height = totalHeight + CommonUtil.dip2px(context, 20); gridView.setLayoutParams(params); } public static void saveMyBitmap(Bitmap mBitmap, String picPath) { File f = new File(picPath); FileOutputStream fOut = null; try { fOut = new FileOutputStream(f); if (fOut != null) { mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fOut); fOut.flush(); fOut.close(); } } catch (FileNotFoundException e) { e.printStackTrace(); }catch(IOException e){ e.printStackTrace(); } } /** * 向指定的fragment传递string类型的数据 * fragment fragment * key key * string string * 返回值 */ public static BaseFragment addBundleString(BaseFragment fragment, String key, String string){ Bundle args = new Bundle(); args.putString(key, string); fragment.setArguments(args); return fragment; } /** * 向指定的fragment传递对象类型的数据(必须实现Serializable接口) * fragment fragment * key key * object object * <t> 泛型 * 返回值 */ public static <t> BaseFragment addBundleSerializable(BaseFragment fragment,String key,T object){ Bundle args = new Bundle(); args.putSerializable(key, (Serializable) object); fragment.setArguments(args); return fragment; } /** * 启动MultitaskService * context 上下文 */ public static void startMultitaskService(Context context){ if(!CommonUtil.isServiceWorked("android.upedu.launcher.service.MultitaskService",context)) { Intent service = new Intent(context, MultitaskService.class); context.startService(service); } } /** * 判断多任务service是否存活 * serviceName service名 * true存在 */ public static boolean isServiceWorked(String serviceName,Context context) { ActivityManager mManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); ArrayList<activitymanager.runningserviceinfo> runningService = (ArrayList<activitymanager.runningserviceinfo>) mManager.getRunningServices(Integer.MAX_VALUE); for (int i = 0; i < runningService.size(); i++) { if (runningService.get(i).service.getClassName().equals(serviceName)) { return true; } } return false; } } </activitymanager.runningserviceinfo></activitymanager.runningserviceinfo></t></t></params></params></code></pre> <p>tinkerpatch管理端截图: <img alt="image" src="https://img-blog.csdnimg.cn/img_convert/da0acac3cf663ef4795a0038b8c35e02.png" /></p><p>该提问来源于开源项目:TinkerPatch/tinkerpatch-sdk</p></div>
> debug运行后,在初始化的setContentView(R.layout.activity_mlogin);方法出现如下错误: # java.lang.RuntimeException: Unable to start activity ComponentInfo{com.medex.Activity.release/com.medex.Activity.miha.MLoginActivity}: java.lang.NullPointerException: Attempt to read from field 'android.os.MessageQueue android.os.Looper.mQueue' on a null object reference at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2793) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2864) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1567) at android.os.Handler.dispatchMessage(Handler.java:105) at android.os.Looper.loop(Looper.java:156) at android.app.ActivityThread.main(ActivityThread.java:6523) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:941) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:831) # Caused by: java.lang.NullPointerException: Attempt to read from field 'android.os.MessageQueue android.os.Looper.mQueue' on a null object reference at android.os.Handler.<init>(Handler.java:236) at android.view.accessibility.AccessibilityManager$MyHandler.<init>(AccessibilityManager.java:686) at android.view.accessibility.AccessibilityManager.<init>(AccessibilityManager.java:221) at android.view.accessibility.AccessibilityManager.getInstance(AccessibilityManager.java:205) at android.view.View.setFlags(View.java:11440) at android.view.ViewGroup.initViewGroup(ViewGroup.java:590) at android.view.ViewGroup.<init>(ViewGroup.java:579) at android.view.ViewGroup.<init>(ViewGroup.java:574) at android.view.ViewGroup.<init>(ViewGroup.java:570) at android.view.ViewGroup.<init>(ViewGroup.java:566) at android.widget.FrameLayout.<init>(FrameLayout.java:78) at com.android.internal.policy.DecorView.<init>(DecorView.java:247) at com.android.internal.policy.PhoneWindow.generateDecor(PhoneWindow.java:2401) at com.android.internal.policy.PhoneWindow.installDecor(PhoneWindow.java:2804) at com.android.internal.policy.PhoneWindow.setContentView(PhoneWindow.java:443) at com.android.internal.policy.HwPhoneWindow.setContentView(HwPhoneWindow.java:280) at android.app.Activity.setContentView(Activity.java:2515) at com.medex.Activity.Base.a.onCreate(Unknown Source) at android.app.Activity.performCreate(Activity.java:6910) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2746) ... 9 more
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:设计师小姐姐 返回首页