IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 我在纵横的面试总结 -> 正文阅读

[移动开发]我在纵横的面试总结

ViewPager嵌套ViewPager滑动冲突的解决

总结

 *  1。点下的时候,要记录下手指按下的位置
 *  2。滑动的时候,通过滑动的距离,判断是往哪个方向滑动
 *  3。左滑的时候,到末尾不拦截,false;不到末尾拦截,true
 *      右滑的时候,到开头不拦截,false;不到开头拦截,false
 * 4. getParent().requestDisallowInterceptTouchEvent(disallowIntercept);的作用就是给
 *      给ViewGroup设置一个标识位,不调用本省的onInterceptTouchEvent()方法
package com.joyy.android_project;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.ViewPager;

/**
 * Time:2022/4/2 20:22
 * Author:
 * Description:
 */
public class RollViewPager extends ViewPager {
    public RollViewPager(@NonNull Context context) {
        super(context);
    }

    public RollViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    int downX = 0;
    int downY = 0;

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // y轴方向考虑移动整个模块,让其支持下拉刷新

        // 在用系统的事件处理之前,先让自定义的viewpager满足我们定义的规则
        // viewpager选中最后一个点的时候,由右向左滑动,需要让整个模块进行翻转
        // viewpager选中第一个点的时候,由左向右滑动,需要让整个模块进行翻转
        // 其余情况,翻转viewpager的图片
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = (int) ev.getX(); // 记录按下的位置
                downY = (int) ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                int moveX = (int) ev.getX();
                int moveY = (int) ev.getY();
                if (Math.abs(moveX - downX) < Math.abs(moveY - downY)) {
                    // y轴上的偏移量比x轴上的偏移量更多,可能会出发下拉刷新,需要响应事件的是大的模块
                    // 请求不拦截触摸事件(不是这样的,不拦截)
                    //让viewpager告知其父容器要拦截响应事件
                    requestParentIntercept(false);
                } else {
                    // x轴偏移量大于y轴的偏移量的情况
                    if (moveX - downX < 0) { // 由右向左移动,最后一个点,翻转整个模块
                        if (getCurrentItem() == getAdapter().getCount() - 1) {
                            //让viewpager告知其父容器要拦截响应事件
                            requestParentIntercept(false);
                        } else if (getCurrentItem() < getAdapter().getCount() - 1) {
                            //让viewpager告知其父容器不要拦截响应事件
                            requestParentIntercept(true);
                        }
                    } else { // 由左向右移动,第一个点,翻转整个模块
                        if (getCurrentItem() == 0) {
                            //让viewpager告知其父容器要拦截响应事件
                            requestParentIntercept(false);
                        } else if (getCurrentItem() > 0) {
                            //让viewpager告知其父容器不要拦截响应事件
                            requestParentIntercept(true);
                        }
                    }
                }
        }

        return super.dispatchTouchEvent(ev);
    }

    private void requestParentIntercept(boolean disallowIntercept) {
        Log.i("RollViewPager", "disallowIntercept : " + disallowIntercept);
        getParent().requestDisallowInterceptTouchEvent(disallowIntercept);
    }

    /**
     * 总结:
     *  1。点下的时候,要记录下手指按下的位置
     *  2。滑动的时候,通过滑动的距离,判断是往哪个方向滑动
     *  3。左滑的时候,到末尾不拦截,false;不到末尾拦截,true
     *      右滑的时候,到开头不拦截,false;不到开头拦截,false
     *  4. getParent().requestDisallowInterceptTouchEvent(disallowIntercept);的作用就是给
     *      给ViewGroup设置一个标识位,不调用本省的onInterceptTouchEvent()方法
     *  
     * if (actionMasked == MotionEvent.ACTION_DOWN
     *         || mFirstTouchTarget != null) {
     *     // 这里判断子View是否允许父View被打断
     *     final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
     *     if (!disallowIntercept) {
     *         intercepted = onInterceptTouchEvent(ev);
     *         ev.setAction(action); // restore action in case it was changed
     *     } else {
     *         intercepted = false;
     *     }
     * } else {
     *     // There are no touch targets and this action is not an initial down
     *     // so this view group continues to intercept touches.
     *     intercepted = true;
     * }
     */
}

翻转Activity(Fragment)的生命周期

在这里插入图片描述
在这里插入图片描述

Activity的onSaveInstanceState()方法

Activity的onSaveInstanceState()方法
Android onSaveInstanceState()和onRestoreInstanceState()调用时机

在手机内存不足时系统会回收掉不在栈顶位置的app

Activity的OnSaveInstanceState()方法,它只有具备以下条件的时候才会触发:

  • 当按下HOME键的时候
  • 长按HOME键,选择运行程序的时候
  • 按下电源(关闭屏幕显示)时候
  • 从Activity中启动其他Activity时候
  • 屏幕方向切换时候(横竖屏切换)
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.d(TAG,"我正在被销毁,我保存了一些临时数据");
        String tempData="Something you just typed";
        outState.putString("data_key",tempData);
    }

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);
        Log.d(TAG,"onCreate----正在创建活动");
        if (savedInstanceState!=null){
            String tempDate=savedInstanceState.getString("data_key");
            Log.d(TAG,tempDate);
        }
    }

onRestoreInstanceState什么时候被调用?

  • 只有在Activity确实被系统回收,重新创建Activity的情况下才会被调用
  1. 被系统回收过的生命周期
    onPause -> onSaveInstanceState -> onStop -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume
  2. 没有被回收过的生命周期
    onPause -> onSaveInstanceState -> onStop -> onRestart -> onStart -> onResume

onCreate()里也有Bundle参数,可以用来恢复数据,它和onRestoreInstanceState有什么区别?
3. 因为onSaveInstanceState不一定会调用,所以onCreate里的Bundle参数可能为空,如果在onCreate来恢复数据,一定要做非空判断
4. 而onRestoreInstanceState的Bundle参数一定不为空,因为它只有在上次activity被回收了才会调用。
而且onRestoreInstanceState是在onStart之后被调用的。
onRestoreInstanceState可以不调用super,但onCreate必须调用super

onSaveInstanceState都保留了什么参数
Activity 状态保存 OnSaveInstanceState参数解析
Android 学习笔记之实时保存数据-现场保护onSaveInstanceState()
Android常识面试题,onSaveInstanceState
Activity 状态保存 OnSaveInstanceState参数解析
【Android 应用开发】Activity 状态保存 OnSaveInstanceState参数解析
Fragment 生命周期和使用

/**
     * Called to retrieve per-instance state from an activity before being killed
     * so that the state can be restored in {@link #onCreate} or
     * {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method
     * will be passed to both).
     *
     * <p>This method is called before an activity may be killed so that when it
     * comes back some time in the future it can restore its state.  For example,
     * if activity B is launched in front of activity A, and at some point activity
     * A is killed to reclaim resources, activity A will have a chance to save the
     * current state of its user interface via this method so that when the user
     * returns to activity A, the state of the user interface can be restored
     * via {@link #onCreate} or {@link #onRestoreInstanceState}.
     *
     * <p>Do not confuse this method with activity lifecycle callbacks such as {@link #onPause},
     * which is always called when the user no longer actively interacts with an activity, or
     * {@link #onStop} which is called when activity becomes invisible. One example of when
     * {@link #onPause} and {@link #onStop} is called and not this method is when a user navigates
     * back from activity B to activity A: there is no need to call {@link #onSaveInstanceState}
     * on B because that particular instance will never be restored,
     * so the system avoids calling it.  An example when {@link #onPause} is called and
     * not {@link #onSaveInstanceState} is when activity B is launched in front of activity A:
     * the system may avoid calling {@link #onSaveInstanceState} on activity A if it isn't
     * killed during the lifetime of B since the state of the user interface of
     * A will stay intact.
     *
     * <p>The default implementation takes care of most of the UI per-instance
     * state for you by calling {@link android.view.View#onSaveInstanceState()} on each
     * view in the hierarchy that has an id, and by saving the id of the currently
     * focused view (all of which is restored by the default implementation of
     * {@link #onRestoreInstanceState}).  If you override this method to save additional
     * information not captured by each individual view, you will likely want to
     * call through to the default implementation, otherwise be prepared to save
     * all of the state of each view yourself.
     *
     * <p>If called, this method will occur after {@link #onStop} for applications
     * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}.
     * For applications targeting earlier platform versions this method will occur
     * before {@link #onStop} and there are no guarantees about whether it will
     * occur before or after {@link #onPause}.
     *
     * @param outState Bundle in which to place your saved state.
     *
     * @see #onCreate
     * @see #onRestoreInstanceState
     * @see #onPause
     */
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

        outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        if (mAutoFillResetNeeded) {
            outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
            getAutofillManager().onSaveInstanceState(outState);
        }
        dispatchActivitySaveInstanceState(outState);
    }

自定义ViewPager

总结:

  1. 通过addView添加子View
  2. 通过onMeasure测量子View的宽高
  3. 通过layout布局子View的位置
  4. 通过onTouchEvent来拦截事件
    4.1 ACTION_DOWN的时候记录按下的位置
    4.2 ACTION_MOVE的时候,判断方向,并通过scrollTo来移动位置
    4.3 ACTION_UP的时候,计算要回落的点,invalidate会调用computeScroll方法,通过Scroller移动到相应的位置
package com.joyy.android_project;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;

/**
 * Time:2022/4/3 17:02
 * Author:
 * Description:
 */
public class MyViewPager extends ViewGroup {

    private void log(String msg) {
        Log.i("MyViewPager", msg);
    }

    private GestureDetector mGestureDetector;
    private int scrollX;
    private int position;
    private Scroller mScroller;
    private int originalX = 0;
    private int originalY = 0;

    public MyViewPager(Context context) {
        super(context);
        initView(context);
    }

    public MyViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public MyViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    // public MyViewPager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    //     super(context, attrs, defStyleAttr, defStyleRes);
    //     initView(context);
    // }

    private void initView(Context context) {
        mScroller = new Scroller(context);
        mGestureDetector = new GestureDetector(
            context,
            new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                    // 当有手指在屏幕上滑动的时候回调
                    // distanceX为正时候,向左移动,为负时,向右移动
                    // 移动屏幕的方法scrollBy,很重要,这个方法会调用onScrollChanged方法,并刷新视图
                    // dx表示x方向上移动的距离,dy表示y方向上移动的距离。
                    scrollBy((int) distanceX, 0);
                    return super.onScroll(e1, e2, distanceX, distanceY);
                }
            }
        );
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // 如果左右滑动,就需要拦截,上下滑动,不需要拦截
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                originalX = (int) ev.getX();
                originalY = (int) ev.getY();
                //这个时候还需要把将ACTION_DOWN传递给手势识别器,因为拦截了MOVE的事件后,DOWN的事件还是要给手势识别器处理,否则会丢失事件,滑动的时候会存在bug。
                mGestureDetector.onTouchEvent(ev);
                break;
            case MotionEvent.ACTION_MOVE:
                // 上下滑动拦截,左右滑动不拦截
                int currentX = (int) ev.getX();
                int currentY = (int) ev.getY();

                int dx = currentX - originalX;
                int dy = currentY - originalY;

                if (Math.abs(dx) > Math.abs(dy)) {
                    // 左右滑动
                    return true; // 中断事件传递,不允许孩子响应事件
                } else {
                    return false;
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 将触摸事件传递手势识别器
        mGestureDetector.onTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                scrollX = getScrollX(); // 相对于初始位置滑动的距离
                // 你滑动的距离加上屏幕的一半,除以屏幕宽度,就是当前图片显示的position,
                // 如果你滑动距离超过了屏幕的一半,这个position就+1
                position = (getScrollX() + getWidth() / 2) / getWidth();
                // 滑动到最后一张的时候,不能出边界
                if (position >= getChildCount()) {
                    position = getChildCount() - 1;
                }
                if (position < 0) {
                    position = 0;
                }
                break;
            case MotionEvent.ACTION_UP:
                // 绝对滑动,直接滑到指定的x,y的位置,较迟钝
                // scrollTo(position*getWidth(), 0);
                // 滚动,startX, startY为开始滚动的位置,dx,dy为滚动的偏移量
                mScroller.startScroll(scrollX, 0, -(scrollX - position * getWidth()), 0);
                // 使用invalidate这个方法会执行一个回调方法computeScroll, 我们来重写这个方法
                invalidate();
                break;
        }
        //return super.onTouchEvent(event);
        return true;
    }

    @Override
    public void computeScroll() {
        //super.computeScroll();
        log("computeScroll");
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), 0);
            postInvalidate();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        log("onMeasure");
        for (int i = 0; i < getChildCount(); i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        log("onLayout");
        for (int i = 0; i < getChildCount(); i++) {
            View view = getChildAt(i);
            view.layout(i * getWidth(), 0, (i + 1) * getWidth(), getHeight());
        }
    }

}

requestLayout, invalidate, postInvalidate

requestLayout()的执行流程
在这里插入图片描述

强引用、软引用、弱引用(实例)

Java基础篇 - 强引用、弱引用、软引用和虚引用

强引用:

  • 强引用是使用最普遍的引用。如果有强引用,那垃圾回收器绝不会回收它。
  • 当内存空间不足时,Java虚拟机宁愿抛出OutOfMemeoryError,也不会回收。

软引用:

  • 如果一个对象是软引用,则内存空间充足时,垃圾回收器就不会回收它;如果内存空间不足时,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。
  • 软引用可用来实现内存敏感的高速缓存。
  • 软引用可以和一个引用队列(ReferenceQueue)联合使用。如果软引用所引用的对象被垃圾回收,JAVA虚拟机就会把这个软引用加入到与之关联的引用队列中。
  • 注意:软引用对象在jvm内存不够的时候才会被回收,我们调用System.gc()方法只是起通知作用,JVM什么时候扫描回收对象是JVM自己的状态决定。就算扫描到软应用对象也不一定会回收它,只有内存不够时才会被回收。
  • 垃圾回收线程就在虚拟机抛出OutOfMemeoryError之前回收软引用对象,而且虚拟机会尽可能优先回收长时间闲置不用的软引用对象。对那些刚构建的或刚使用过的“较新的”软对象会被虚拟机尽可能保留,这就是引入引用队列ReferenceQueue的原因。

弱引用:

  • 弱引用和软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快的发现那些只具有弱引用的对象。

  • 注意:如果一个对象是偶尔(很少)的使用,并且希望在使用时候随时获取到,但由不想影响此对象的垃圾收集,那么你应该用WeakReference来记住此对象。

    引用类型被垃圾回收时间用途生存时间
    强引用从来不会对象的一般状态JVM停止运行时终止
    软引用当内存不足时对象缓存内存不足时终止
    弱引用正常垃圾回收时对象缓存垃圾回收后终止
    虚引用正常垃圾回收时跟踪对象的垃圾回收垃圾回收后终止

软引用

public class SoftDemo {
    public static void main(String[] args) {
        ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
        String str = new String("abc");
        SoftReference<String>softReference = new SoftReference<>(str, referenceQueue);
        str = null;
        System.gc();
        System.out.println(softReference.get());
        Reference reference = referenceQueue.poll();
        System.out.println(reference);
    }
}

弱引用

package com.flannery;


import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.LinkedList;

// https://blog.csdn.net/baidu_22254181/article/details/82555485

public class WeakDemo {
    // 弱引用队列
    private final static ReferenceQueue<GCTarget> REFERENCE_QUEUE
            = new ReferenceQueue<>();

    public static void main(String[] args) {
        LinkedList<GCTargetWeakReference> gcTargetList = new LinkedList<>();

        // 创建弱引用的对象,一次加入链表中
        for (int i = 0; i < 5; i++) {
            GCTarget gcTarget = new GCTarget(String.valueOf(i));
            GCTargetWeakReference weakReference = new GCTargetWeakReference(gcTarget, REFERENCE_QUEUE);
            gcTargetList.add(weakReference);

            System.out.println("Just created GCTargetWeakReference obj: " +
                    gcTargetList.getLast());
        }

        // 通知gc进行回收
        System.gc();
        try {
            // 休息几分钟,等待上面的垃圾回收线程运行完成
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 检查关联的引用队列是否为空
        Reference<? extends GCTarget> reference;
        while ((reference = REFERENCE_QUEUE.poll()) != null) {
            if (reference instanceof GCTargetWeakReference) {
                System.out.println("In queue, id is: " + ((GCTargetWeakReference) (reference)).id);
            }
        }
    }
}

class GCTarget {
    // 对象的ID
    public String id;
    //占用内存空间
    byte[] buffer = new byte[1024];

    public GCTarget(String id) {
        this.id = id;
    }

    @Override
    protected void finalize() throws Throwable {
        //super.finalize();
        System.out.println("Finalizing GCTarget, id is : " + id);
    }
}

class GCTargetWeakReference extends WeakReference<GCTarget> {

    String id;

    public GCTargetWeakReference(GCTarget referent) {
        super(referent);
        this.id = referent.id;
    }

    public GCTargetWeakReference(GCTarget referent, ReferenceQueue<? super GCTarget> q) {
        super(referent, q);
        this.id = referent.id;
    }
}

Activity启动模式

Activity的四种启动模式
Activity的四种启动模式
Activity的四种启动模式详解

四种启动模式:

  1. standard模式
  • 标准模式
  • 特点:每一次都糊创建一个新的Activity, 这哥新的Activity总放在栈顶;
  1. singleTop模式
    每当需要启动Activity时,系统首先检查栈顶的Activity是否存在一样的Activity,如果存在,则直接使用栈顶已经存在的Activity,否则新建一个Activity。

  2. singleTask模式
    每当启动一个Activity时,系统会检查栈中是否存在该Activity的实例,如果存在,否则该实例的newInstance()方法重用该Activity,并把上面的Activity销毁,使其处于激活状态–栈顶,否则重新创建一个新的实例

  3. singleInstance模式
    每当需要启动一个Activity时,系统会检查栈中是否存在一样的Activity实例,如果存在,则会调用newIntent()给它开一个单间,即重新开一个线程存放,这种模式只会创建一次,即会调用一次onCreate()方法,除非Activity被销毁。
    比较耗资源,并且使用该模式会存在bug–startActivityForResult时,会报错

查看栈
dumpsys activity activities | grep “ActivityRecord”

  • standart
  • singleTop
  • singleTask
  • singleInstnce

Activity的Flags

  1. FLAG_ACTIVITY_NEW_TASK
  2. FLAG_ACTIVITY_SINGLE_TOP
  3. FLAG_ACTIVITY_CLEAN_TOP
  4. FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

启动模式的实际应用场景

  1. SingleTask模式的运用场景
    最常见的应用场景就是保持我们应用开启后仅仅有一个Activity的实例。最典型的样例就是应用中展示的主页(Home页)。
    假设用户在主页跳转到其他页面,运行多次操作后想返回到主页,假设不使用SingleTask模式,在点击返回的过程中会多次看到主页,这明显就是设计不合理了。

  2. SingleTop模式的运用场景
    假设你在当前的Activity中又要启动同类型的Activity,此时建议将此类型Activity的启动模式指定为SingleTop,能够降低Activity的创建,节省内存!

LauchModeInstance
standard邮件、mainfest中没有配置就默认标准模式
singleTop登录页面、WXPayEntryActivity、WXEntryActivity 、推送通知栏
singleTask程序模块逻辑入口:主页面(Fragment的containerActivity)、WebView页面、扫一扫页面、电商中:购物界面,确认订单界面,付款界面
singleInstance系统Launcher、锁屏键、来电显示等系统应用

事件分发

Handler使用引起的内存泄漏原因及解决办法

Handler使用引起的内存泄漏原因以及解决办法
都 2021 年了,还有人在研究 Handler?
面试问Handler内存泄露的场景,别就只知道静态内部类&弱引用!

发生内存泄漏的原因

  1. 当一个android应用程序启动的时候,frameworks会自动为这个应用程序在主线程创建一个Looper对象。这个被创建的Looper对象也有它主要的工作,它主要的工作就是不断地处理消息队列中的消息对象。在android应用程序中,所有主要的框架事件(例如Activity的生命周期方法,按钮的点击事件等等)都包含在消息对象里面,然后被添加到Looper要处理的消息队列中,主线程的Looper一直存在于整个应用程序的生命周期中。
  2. 当一个Handler在主线程中被初始化。那它就一直都和Looper的消息队列相关联着。当消息被发送到Looper关联的消息队列的时候,会持有一个Handler的引用,以便于当Looper处理消息的时候,框架可以调用Handler的handleMessage(Message msg)。
  3. 在java中,非静态的内部类和匿名内部类都会隐式的持有一个外部类的引用。静态内部类则不会持有外部类的引用。

ThreadPoolExcutors什么时候不会使用到非线程池

使用ThreadPoolExecutor遇到的核心线程被阻塞,非核心线程未按照预期运行问题

项目表述方面

先有架构,要讲故事

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-07 22:51:12  更:2022-04-07 22:53:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 20:39:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码