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

Posted 初一十五啊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面霸养成记;50万字2022最新Android11位大厂面试专题相关的知识,希望对你有一定的参考价值。

前言

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

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

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

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

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

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

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

关注公众号:初一十五a
提前解锁 《整套50W字Android体系PDF》,让学习更贴近未来实战。

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

八丶小米

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:

静态注册广播:

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

新建完要在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关键字修饰

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

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 等;
  • 视频、音频等多媒体通信;
  • 广播通信;

关注公众号:初一十五a
提前解锁 《整套50W字Android体系PDF》,让学习更贴近未来实战。

以上是关于面霸养成记;50万字2022最新Android11位大厂面试专题的主要内容,如果未能解决你的问题,请参考以下文章

面霸养成记;50W字Android面试文档致敬2022摸爬滚打的一年

含泪刷128道面试题,50万字2022最新Android11位大厂面试专题

软件测试面霸是如何养成的,他的路子真的野

年薪50万的大数据分析师养成记

年薪50万的大数据分析师养成记摘抄

年薪50万的大数据分析师养成记摘抄