面霸养成记;50万字2022最新Android11位大厂面试专题(六)

2,346 阅读10分钟

前言

面试专题前面的百度篇,腾讯篇,阿里篇,京东篇,bilibili篇,网易篇,字节篇,小红书,小米九大板块已经更新完了,还剩下2个专题~持续更新中。

1.12W字;2022最新Android11位大厂面试专题(一)百度篇

2.12W字;2022最新Android11位大厂面试专题(二)阿里篇

3.12W字;2022最新Android11位大厂面试专题(三)腾讯篇

4.面霸养成记;50万字Android面试文档(四五)字节,京东篇

5.面霸养成记;50万字Android面试文档(六七)网易,Bilibili篇

6.面霸养成记;50万字Android面试文档(八九)小红书,小米篇

一共50W字的文档,面试专题12W字只是一小部分,字数限制,分几篇更。

关注公众号:Android苦做舟

提前解锁 《整套50W字Android体系PDF》,让学习更贴近未来实战。

总共囊括

1.腾讯Android开发笔记(33W字)

2.2022最新Android十一位大厂面试专题(12W字)

3.音视频经典面试题(6W字)

4.Jetpack全家桶

5.Android 性能监控框架Matrix

6.JVM

7.车载应用开发

共十一模块,今天来更新第八丶九专题小米篇和小红书篇

八丶小米

1.handler机制

(第二章第3题)

2.一个线程中几个handler同时发送一个相同的消息,怎么分辨哪个消息是哪个handler发送的

(每个msg都有对应的target,这个target其实就是对应handler的引用。所以是通过msg.target区分的 )

3.Android服务的生命周期

服务生命周期具有以下回调

  • onCreate()

在首次创建服务以设置您可能需要的初始配置时执行。仅在服务尚未运行时才执行此方法。

  • onStartCommand()

每次执行startService()都由另一个组件(例如Activity或BroadcastReceiver)调用。使用此方法时,服务将一直运行,直到您调用stopSelf()或为止stopService()。请注意,无论您调用多少次onStartCommand(),方法stopSelf()和都stopService()必须仅被调用一次才能停止服务。

  • onBind()

在组件调用bindService()并返回IBInder的实例时执行,以提供与Service的通信通道。bindService()只要有绑定到它的客户端,对的调用将使服务保持运行。

  • onDestroy()

在不再使用该服务并允许处置已分配的资源时执行。

请务必注意,在服务的生命周期中可能会调用其他回调,例如onConfigurationChanged()andonLowMemory()

4.Android持久化存储的方式

内部存储、网络存储、SharedPreferences、SQLite、外部存储(SD卡)。

5.Android广播使用

动态注册广播:

MainActivity中:

public class MainActivity extends AppCompatActivity {
 
    private MyReceiver myReceiver;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn1 = findViewById(R.id.btn);
        myReceiver = new MyReceiver();
 
        registerReceiver(myReceiver,new IntentFilter("wk"));  //IntentFilter的参数为广播接受的类型(自定义)
        btn1.setOnClickListener((v)-> sendBroadcast(new Intent("wk"))); //Intent的参数为发送广播的类型(自定义)
    }
 
    class MyReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "我是动态注册的广播", Toast.LENGTH_SHORT).show();
        }
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(myReceiver);  //Activity销毁时反注册,以免内存泄漏
    }
}

点击按钮后在MainActivity中发送广播,MyReceiver就能接受到广播Toast:

静态注册广播:

直接在包名按右键新建一个:

image.png

新建完要在Manifest定义这个广播接收什么类型:

    <application
      
        .....
 
        <receiver
            android:name=".MyReceiver2"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="wk"/>  //定义广播接收的类型(自定义)
            </intent-filter>
        </receiver>
 
        .....
 
    </application>

发送广播时定义好发送的类型就能接收到广播了

6.自定义view哪个构造方法是不能不重写的

setContentView源码可以看到普通view的创建方法是createViewFromTag。找到view = createView(context, name, null, attrs);点进去看到根节点的view是通过反射获取的,然后获取根节点的构造方法clazz.getConstructor(mConstructorSignature) mConstructorSignature是两个参数的数组。所以自定义View一定要重写两个参数的构造函数

/**
     * Creates a view from a tag name using the supplied attribute set.
     * <p>
     * <strong>Note:</strong> Default visibility so the BridgeInflater can
     * override it.
     *
     * @param parent the parent view, used to inflate layout params
     * @param name the name of the XML tag used to define the view
     * @param context the inflation context for the view, typically the
     *                {@code parent} or base layout inflater context
     * @param attrs the attribute set for the XML tag used to define the view
     * @param ignoreThemeAttr {@code true} to ignore the {@code android:theme}
     *                        attribute (if set) for the view being inflated,
     *                        {@code false} otherwise
     */
    @UnsupportedAppUsage
    View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }

        // Apply a theme wrapper, if allowed and one is specified.
        if (!ignoreThemeAttr) {
            final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
            final int themeResId = ta.getResourceId(0, 0);
            if (themeResId != 0) {
                context = new ContextThemeWrapper(context, themeResId);
            }
            ta.recycle();
        }

        try {
            View view = tryCreateView(parent, name, context, attrs);

            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(context, parent, name, attrs);
                    } else {
                        view = createView(context, name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
        } catch (InflateException e) {
            throw e;

        } catch (ClassNotFoundException e) {
            final InflateException ie = new InflateException(
                    getParserStateDescription(context, attrs)
                    + ": Error inflating class " + name, e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;

        } catch (Exception e) {
            final InflateException ie = new InflateException(
                    getParserStateDescription(context, attrs)
                    + ": Error inflating class " + name, e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;
        }
    }

 /**
     * Low-level function for instantiating a view by name. This attempts to
     * instantiate a view class of the given <var>name</var> found in this
     * LayoutInflater's ClassLoader.
     *
     * <p>
     * There are two things that can happen in an error case: either the
     * exception describing the error will be thrown, or a null will be
     * returned. You must deal with both possibilities -- the former will happen
     * the first time createView() is called for a class of a particular name,
     * the latter every time there-after for that class name.
     *
     * @param viewContext The context used as the context parameter of the View constructor
     * @param name The full name of the class to be instantiated.
     * @param attrs The XML attributes supplied for this instance.
     *
     * @return View The newly instantiated view, or null.
     */
    @Nullable
    public final View createView(@NonNull Context viewContext, @NonNull String name,
            @Nullable String prefix, @Nullable AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        Objects.requireNonNull(viewContext);
        Objects.requireNonNull(name);
        Constructor<? extends View> constructor = sConstructorMap.get(name);
        if (constructor != null && !verifyClassLoader(constructor)) {
            constructor = null;
            sConstructorMap.remove(name);
        }
        Class<? extends View> clazz = null;

        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);

            if (constructor == null) {
                // Class not found in the cache, see if it's real, and try to add it
                clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
                        mContext.getClassLoader()).asSubclass(View.class);

                if (mFilter != null && clazz != null) {
                    boolean allowed = mFilter.onLoadClass(clazz);
                    if (!allowed) {
                        failNotAllowed(name, prefix, viewContext, attrs);
                    }
                }
                constructor = clazz.getConstructor(mConstructorSignature);
                constructor.setAccessible(true);
                sConstructorMap.put(name, constructor);
            } else {
                // If we have a filter, apply it to cached constructor
                if (mFilter != null) {
                    // Have we seen this name before?
                    Boolean allowedState = mFilterMap.get(name);
                    if (allowedState == null) {
                        // New class -- remember whether it is allowed
                        clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
                                mContext.getClassLoader()).asSubclass(View.class);

                        boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
                        mFilterMap.put(name, allowed);
                        if (!allowed) {
                            failNotAllowed(name, prefix, viewContext, attrs);
                        }
                    } else if (allowedState.equals(Boolean.FALSE)) {
                        failNotAllowed(name, prefix, viewContext, attrs);
                    }
                }
            }

            Object lastContext = mConstructorArgs[0];
            mConstructorArgs[0] = viewContext;
            Object[] args = mConstructorArgs;
            args[1] = attrs;

            try {
                final View view = constructor.newInstance(args);
                if (view instanceof ViewStub) {
                    // Use the same context when inflating ViewStub later.
                    final ViewStub viewStub = (ViewStub) view;
                    viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
                }
                return view;
            } finally {
                mConstructorArgs[0] = lastContext;
            }
        } catch (NoSuchMethodException e) {
            final InflateException ie = new InflateException(
                    getParserStateDescription(viewContext, attrs)
                    + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;

        } catch (ClassCastException e) {
            // If loaded class is not a View subclass
            final InflateException ie = new InflateException(
                    getParserStateDescription(viewContext, attrs)
                    + ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;
        } catch (ClassNotFoundException e) {
            // If loadClass fails, we should propagate the exception.
            throw e;
        } catch (Exception e) {
            final InflateException ie = new InflateException(
                    getParserStateDescription(viewContext, attrs) + ": Error inflating class "
                            + (clazz == null ? "<unknown>" : clazz.getName()), e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
  

7.java怎么使得一个同步方法变为异步方法

构造一个异步调用

1.使用wait和notify方法

2.使用条件锁

3.Future

4.使用CountDownLatch

5.使用CyclicBarrier

8.java 抽象类和接口的区别

1、声明方式不同。抽象类为abstract class,接口类为interface

2、继承抽象类关键字为extends,实现接口关键字为implements

3、继承抽象类仅支持单继承,实现接口可以多实现

单继承:classB Extends classA 接口多实现:Interface implements Interface0, Interface1, interface2…… java中为什么要单继承,多实现? 若为多继承,那么当多个父类中有重复的属性或者方法时,子类的调用结果会含糊不清,因此用了单继承。 为什么可以多实现呢? 通过实现接口拓展了类的功能,若实现的多个接口中有重复的方法也没关系,因为实现类中必须重写接口中的方法,所以调用时还是调用的实现类中重写的方法。那么各个接口中重复的变量又是怎么回事呢?

4、抽象类可以有构造方法,接口中不能有构造方法。 5、抽象类可以有成员变量,接口类只能有常量(static)。 6、抽象类可以有成员方法,接口中只可以有抽象方法。 7、抽象类中增加方法可以影响子类,接口中增加方法通常影响子类(JDK1.8增加default方法不影响子类) 8、从JDK1.8开始允许接口中出现非抽象方法,但需要default关键字修饰

  • 主要减少了代码牵一发而动全身的弊端.

image.png

9.抽象类的方法一定要继承吗

当父类的抽象类中有抽象方法时,继承的子类有两种选择,第一:定义子类是抽象类,就不需要实现抽象方法;第二:实现所有的抽象方法,不需要定义子类为抽象类;

当父类的抽象类中没有抽象方法时,子类直接继承不用做任何操作。

九丶小红书

1.Java深拷贝和浅拷贝的区别

关于深拷贝和浅拷贝区别

  • 浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
  • 深拷贝 :深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。

2.自定义View是怎么绘制的?ViewGroup呢

(第一章第2题)

3.Handler机制

(第二章第3题)

4.TCP和UDP的区别

①连接

  • TCP 是面向连接的传输层协议,传输数据前先要建立连接。
  • UDP 是不需要连接,即刻传输数据。

②服务对象

  • TCP 是一对一的两点服务,即一条连接只有两个端点。
  • UDP 支持一对一、一对多、多对多的交互通信

③可靠性

  • TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
  • UDP 是尽最大努力交付,不保证可靠交付数据。

④拥塞控制、流量控制

  • TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
  • UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。

⑤首部开销

  • TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变长的。
  • UDP 首部只有 8 个字节,并且是固定不变的,开销较小。

⑥传输方式

  • TCP 是流式传输,没有边界,但保证顺序和可靠。
  • UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。

⑦分片不同

  • TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
  • UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层。

TCP 和 UDP 应用场景:

由于 TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:

  • FTP 文件传输;
  • HTTP / HTTPS;

由于 UDP 面向无连接,它可以随时发送数据,再加上UDP本身的处理既简单又高效,因此经常用于:

  • 包总量较少的通信,如 DNSSNMP 等;
  • 视频、音频等多媒体通信;
  • 广播通信;

一共50W字的文档,面试专题12W字只是一小部分,字数限制,分几篇更。

关注公众号:Android苦做舟

提前解锁 《整套50W字Android体系PDF》,让学习更贴近未来实战。

总共囊括

1.腾讯Android开发笔记(33W字)

2.2022最新Android十一位大厂面试专题(12W字)

3.音视频经典面试题(6W字)

4.Jetpack全家桶

5.Android 性能监控框架Matrix

6.JVM

7.车载应用开发