由于近期许多小伙伴开端面试了,所以在许多的刷题,也有许多问到我有没有一些大厂面试题或许常见的面试题,字节参阅一下,于是乎花了一周时刻整理出这份 《2022Android十一位大厂面试真题》 结合之前的 《腾讯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专题百度篇

一丶百度篇

1.关于OKhttpRetrofit

Retrofit

①根本运用流程
界说HTTP API,用于描述恳求
public interface GitHubService {
     @GET("users/{user}/repos")
     Call<List<Repo>> listRepos(@Path("user") String user);
}
创立Retrofit并生成API的完结

留意: 办法上面的注解标明恳求的接口部分,回来类型是恳求的回来值类型,办法的参数便是恳求的参数)

// 1.Retrofit构建进程
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
// 2.创立网络恳求接口类实例进程
GitHubService service = retrofit.create(GitHubService.class);
调用API办法,生成Call,履行恳求
// 3.生成并履行恳求进程
Call<List<Repo>> repos = service.listRepos("octocat");
repos.execute() or repos.enqueue()

Retrofit的根本运用流程很简练,可是简练并不代表简略,Retrofit为了完结这种简练的运用流程,内部运用了优异的架构规划和许多的规划方式,在剖析过Retrofit最新版的源码和许多优异的Retrofit源码剖析文章后发现,要想实在了解Retrofit内部的中心源码流程和规划思想,首要,需求对这九大规划方式有一定的了解,如下:

  • Retrofit构建进程 制作者方式、工厂办法方式

  • 创立网络恳求接口实例进程 外观方式、署理方式、单例方式、战略方式、装饰方式(制作者方式)

  • 生成并履行恳求进程 适配器方式(署理方式、装饰方式)

其次,需求对OKHttp源码有一定的了解。让咱们按以上流程去深化Retrofit源码内部,领会它带给咱们的规划之美

②Retrofit构建进程
Retrofit中心目标解析

首要Retrofit中有一个全局变量十分要害,在V2.5之前的版本,运用的是LinkedHashMap(),它是一个网络恳求装备目标,是由网络恳求接口中办法注解进行解析后得到的。

public final class Retrofit {
    // 网络恳求装备目标,存储网络恳求相关的装备,如网络恳求的办法、数据转换器、网络恳求适配器、网络恳求工厂、基地址等
    private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();

Retrofit运用了制作者方式经过内部类Builder类树立一个Retrofit实例,如下:

public static final class Builder {
    // 平台类型目标(Platform -> Android)
    private final Platform platform;
    // 网络恳求工厂,默许运用OkHttpCall(工厂办法方式)
    private @Nullable okhttp3.Call.Factory callFactory;
    // 网络恳求的url地址
    private @Nullable HttpUrl baseUrl;
    // 数据转换器工厂的调集
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    // 网络恳求适配器工厂的调集,默许是ExecutorCallAdapterFactory
    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    // 回调办法履行器,在 Android 上默许是封装了 handler 的 MainThreadExecutor, 默许效果是:切换线程(子线程 -> 主线程)
    private @Nullable Executor callbackExecutor;
    // 一个开关,为true则会缓存创立的ServiceMethod
    private boolean validateEagerly;
Builder内部结构

下面看看Builder内部结构做了什么。

public static final class Builder {
    ...
    Builder(Platform platform) {
        this.platform = platform;
    }
    public Builder() {
        this(Platform.get());
    }
    ...
}
class Platform {
    private static final Platform PLATFORM = findPlatform();
    static Platform get() {
      return PLATFORM;
    }
    private static Platform findPlatform() {
      try {
        // 运用JVM加载类的办法判别是否是Android平台
        Class.forName("android.os.Build");
        if (Build.VERSION.SDK_INT != 0) {
          return new Android();
        }
      } catch (ClassNotFoundException ignored) {
      }
      try {
        // 一起支撑Java平台
        Class.forName("java.util.Optional");
        return new Java8();
      } catch (ClassNotFoundException ignored) {
      }
      return new Platform();
    }
static class Android extends Platform {
    ...
    @Override public Executor defaultCallbackExecutor() {
        //切换线程(子线程 -> 主线程)
        return new MainThreadExecutor();
    }
    // 创立默许的网络恳求适配器工厂,假如是Android7.0或Java8上,则使
    // 用了并发包中的CompletableFuture确保了回调的同步
    // 在Retrofit中供给了四种CallAdapterFactory(战略方式):
    // ExecutorCallAdapterFactory(默许)、GuavaCallAdapterFactory、
    // va8CallAdapterFactory、RxJavaCallAdapterFactory
    @Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
        @Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      ExecutorCallAdapterFactory executorFactory = new   ExecutorCallAdapterFactory(callbackExecutor);
      return Build.VERSION.SDK_INT >= 24
        ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
        : singletonList(executorFactory);
    }
    ...
    @Override List<? extends Converter.Factory> defaultConverterFactories() {
      return Build.VERSION.SDK_INT >= 24
          ? singletonList(OptionalConverterFactory.INSTANCE)
          : Collections.<Converter.Factory>emptyList();
    }
    ...
    static class MainThreadExecutor implements Executor {
        // 获取Android 主线程的Handler 
        private final Handler handler = new Handler(Looper.getMainLooper());
        @Override public void execute(Runnable r) {
            // 在UI线程对网络恳求回来数据处理
            handler.post(r);
        }
    }
}

能够看到,在Builder内部结构时设置了默许Platform、callAdapterFactories和callbackExecutor。

增加baseUrl

很简略,便是将String类型的url转换为OkHttp的HttpUrl进程如下:

/**
 * Set the API base URL.
 *
 * @see #baseUrl(HttpUrl)
 */
public Builder baseUrl(String baseUrl) {
    checkNotNull(baseUrl, "baseUrl == null");
    return baseUrl(HttpUrl.get(baseUrl));
}
public Builder baseUrl(HttpUrl baseUrl) {
    checkNotNull(baseUrl, "baseUrl == null");
    List<String> pathSegments = baseUrl.pathSegments();
    if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
      throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
    }
    this.baseUrl = baseUrl;
    return this;
}

增加GsonConverterFactory

首要,看到GsonConverterFactory.creat()的源码。

public final class GsonConverterFactory extends Converter.Factory {
    public static GsonConverterFactory create() {
        return create(new Gson());
    }
    public static GsonConverterFactory create(Gson gson) {
        if (gson == null) throw new NullPointerException("gson ==   null");
        return new GsonConverterFactory(gson);
    }
    private final Gson gson;
    // 创立了一个含有Gson目标实例的GsonConverterFactory
    private GsonConverterFactory(Gson gson) {
        this.gson = gson;
    }

然后,看看addConverterFactory()办法内部。

public Builder addConverterFactory(Converter.Factory factory) {
    converterFactories.add(checkNotNull(factory, "factory null"));
    return this;
}

可知,这一步是将一个含有Gson目标实例的GsonConverterFactory放入到了数据转换器工厂converterFactories里。

build进程
public Retrofit build() {
    if (baseUrl == null) {
      throw new IllegalStateException("Base URL required.");
    }
    okhttp3.Call.Factory callFactory = this.callFactory;
    if (callFactory == null) {
        // 默许运用okhttp
         callFactory = new OkHttpClient();
    }
    Executor callbackExecutor = this.callbackExecutor;
    if (callbackExecutor == null) {
        // Android默许的callbackExecutor
        callbackExecutor = platform.defaultCallbackExecutor();
    }
    // Make a defensive copy of the adapters and add the defaultCall adapter.
    List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
    // 增加默许适配器工厂在调集尾部
    callAdapterFactories.addAll(platform.defaultCallAdapterFactorisca  llbackExecutor));
    // Make a defensive copy of the converters.
    List<Converter.Factory> converterFactories = new ArrayList<>(
        1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
    // Add the built-in converter factory first. This prevents overriding its behavior but also
    // ensures correct behavior when using converters thatconsumeall types.
    converterFactories.add(new BuiltInConverters());
    converterFactories.addAll(this.converterFactories);
    converterFactories.addAll(platform.defaultConverterFactories();
    return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
        unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}

能够看到,终究咱们在Builder类中看到的6大中心目标都现已装备到Retrofit目标中了

③创立网络恳求接口实例进程

retrofit.create()运用了外观方式和署理方式创立了网络恳求的接口实例,咱们剖析下create办法。

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
        // 判别是否需求提早缓存ServiceMethod目标
        eagerlyValidateMethods(service);
    }
    // 运用动态署理拿到恳求接口一切注解装备后,创立网络恳求接口实例
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new  Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();
          private final Object[] emptyArgs = new Object[0];
          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
    });
 }
private void eagerlyValidateMethods(Class<?> service) {
  Platform platform = Platform.get();
  for (Method method : service.getDeclaredMethods()) {
    if (!platform.isDefaultMethod(method)) {
      loadServiceMethod(method);
    }
  }
}

持续看看loadServiceMethod的内部流程

ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
            // 解析注解装备得到了ServiceMethod
            result = ServiceMethod.parseAnnotations(this, method);
            // 能够看到,终究参加到ConcurrentHashMap缓存中
            serviceMethodCache.put(method, result);
      }
    }
    return result;
}
abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method   method) {
        // 经过RequestFactory解析注解装备(工厂方式、内部运用了制作者方式)
        RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
        Type returnType = method.getGenericReturnType();
        if (Utils.hasUnresolvableType(returnType)) {
          throw methodError(method,
              "Method return type must not include a type variable or wildcard: %s", returnType);
        }
        if (returnType == void.class) {
          throw methodError(method, "Service methods cannot return void.");
        }
        // 终究是经过HttpServiceMethod构建的恳求办法
        return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
    }
    abstract T invoke(Object[] args);
}
恳求结构中心流程

依据RequestFactory#Builder结构办法和parseAnnotations办法的源码,可知的它的效果便是用来解析注解装备的。

Builder(Retrofit retrofit, Method method) {
    this.retrofit = retrofit;
    this.method = method;
    // 获取网络恳求接口办法里的注释
    this.methodAnnotations = method.getAnnotations();
    // 获取网络恳求接口办法里的参数类型       
    this.parameterTypes = method.getGenericParameterTypes();
    // 获取网络恳求接口办法里的注解内容    
    this.parameterAnnotationsArray = method.getParameterAnnotations();
}

接着看HttpServiceMethod.parseAnnotations()的内部流程。

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    //1.依据网络恳求接口办法的回来值和注解类型,
    // 从Retrofit目标中获取对应的网络恳求适配器
    CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit,method);
    // 得到呼应类型
    Type responseType = callAdapter.responseType();
    ...
    //2.依据网络恳求接口办法的回来值和注解类型从Retrofit目标中获取对应的数据转换器 
    Converter<ResponseBody, ResponseT>responseConverter =
        createResponseConverter(retrofit,method, responseType);
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    return newHttpServiceMethod<>(requestFactory, callFactory, callAdapter,responseConverter);
}

createCallAdapter(retrofit, method)

private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT>     createCallAdapter(
      Retrofit retrofit, Method method) {
    // 获取网络恳求接口里办法的回来值类型
    Type returnType = method.getGenericReturnType();
    // 获取网络恳求接口接口里的注解
    Annotation[] annotations = method.getAnnotations();
    try {
      //noinspection unchecked
      return (CallAdapter<ResponseT, ReturnT>)  retrofit.callAdapter(returnType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create call adapter for %s", returnType);
    }
}
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
  Annotation[] annotations) {
    ...
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    // 遍历 CallAdapter.Factory 调集寻找适宜的工厂
    for (int i = start, count = callAdapterFactories.size(); i <count; i++) {
        CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
        if (adapter != null) {
          return adapter;
        }
    }
}

createResponseConverter(Retrofit retrofit, Method method, Type responseType)

 private static <ResponseT> Converter<ResponseBody, ResponseT>  createResponseConverter(
     Retrofit retrofit, Method method, Type responseType) {
   Annotation[] annotations = method.getAnnotations();
   try {
     return retrofit.responseBodyConverter(responseType,annotations);
   } catch (RuntimeException e) { // Wide exception range because    factories are user code.
     throw methodError(method, e, "Unable to create converter for%s",   responseType);
   }
}
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
    return nextResponseBodyConverter(null, type, annotations);
}
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
  @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
...
int start = converterFactories.indexOf(skipPast) + 1;
// 遍历 Converter.Factory 调集并寻找适宜的工厂, 这儿是GsonResponseBodyConverter
for (int i = start, count = converterFactories.size(); i < count; i++) {
  Converter<ResponseBody, ?> converter =
      converterFactories.get(i).responseBodyConverter(type, annotations, this);
  if (converter != null) {
    //noinspection unchecked
    return (Converter<ResponseBody, T>) converter;
  }
}

履行HttpServiceMethod的invoke办法

@Override ReturnT invoke(Object[] args) {
    return callAdapter.adapt(
        new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}

终究在adapt中创立了一个ExecutorCallbackCall目标,它是一个装饰者,而在它内部实在去履行网络恳求的仍是OkHttpCall。

④创立网络恳求接口类实例并履行恳求进程
service.listRepos()
1、Call<List<Repo>> repos = service.listRepos("octocat");

service目标是动态署理目标Proxy.newProxyInstance(),当调用getCall()时会被 它阻拦,然后调用自身的InvocationHandler#invoke(),得到终究的Call目标。

同步履行流程 repos.execute()
@Override public Response<T> execute() throws IOException {
    okhttp3.Call call;
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
      if (creationFailure != null) {
        if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else if (creationFailure instanceof RuntimeException) {
          throw (RuntimeException) creationFailure;
        } else {
          throw (Error) creationFailure;
        }
      }
      call = rawCall;
      if (call == null) {
        try {
          // 创立一个OkHttp的Request目标恳求
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException | Error e) {
          throwIfFatal(e); //  Do not assign a fatal error to     creationFailure.
          creationFailure = e;
          throw e;
        }
      }
    }
    if (canceled) {
      call.cancel();
    }
    // 调用OkHttpCall的execute()发送网络恳求(同步),
    // 并解析网络恳求回来的数据
    return parseResponse(call.execute());
}
private okhttp3.Call createRawCall() throws IOException {
    // 创立 一个okhttp3.Request
    okhttp3.Call call =
    callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
}
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body(); 
    // Remove the body's source (the only stateful object) so we can   pass the response along.
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();    
    // 依据呼应回来的状况码进行处理    
    int code = rawResponse.code();
    if (code < 200 || code >= 300) {
      try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }    
    if (code == 204 || code == 205) {
      rawBody.close();
      return Response.success(null, rawResponse);
    }    
    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      // 将呼应体转为Java目标
      T body = responseConverter.convert(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // If the underlying source threw an exception, propagate that     rather than indicating it was
      // a runtime exception.
      catchingBody.throwIfCaught();
      throw e;
    }
}
异步恳求流程 reponse.enqueque
@Override
public void enqueue(final Callback<T> callback) {
    // 运用静态署理 delegate进行异步恳求 
    delegate.enqueue(new Callback<T>() {
      @Override 
      public void onResponse(Call<T> call, finalResponse<T>response) {
        // 线程切换,在主线程显现成果
        callbackExecutor.execute(new Runnable() {
            @Override 
             public void run() {
            if (delegate.isCanceled()) {
              callback.onFailure(ExecutorCallbackCall.this, newIOException("Canceled"));
            } else {
              callback.onResponse(ExecutorCallbackCall.this,respons);
            }
          }
        });
      }
      @Override 
      public void onFailure(Call<T> call, final Throwable t) {
        callbackExecutor.execute(new Runnable() {
          @Override public void run() {
            callback.onFailure(ExecutorCallbackCall.this, t);
          }
        });
      }
    });
}

看看 delegate.enqueue 内部流程。

@Override
public void enqueue(final Callback<T> callback) {
    okhttp3.Call call;
    Throwable failure;
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          // 创立OkHttp的Request目标,再封装成OkHttp.call
          // 办法同发送同步恳求,此处上面已剖析
          call = rawCall = createRawCall(); 
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
@Override public void enqueue(final Callback<T> callback) {
  checkNotNull(callback, "callback == null");
  okhttp3.Call call;
  Throwable failure;
  ...
  call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
      Response<T> response;
      try {
        // 此处上面已剖析
        response = parseResponse(rawResponse);
      } catch (Throwable e) {
        throwIfFatal(e);
        callFailure(e);
        return;
      }
      try {
        callback.onResponse(OkHttpCall.this, response);
      } catch (Throwable t) {
        t.printStackTrace();
      }
    }
    @Override public void onFailure(okhttp3.Call call, IOException e) {
      callFailure(e);
    }
    private void callFailure(Throwable e) {
      try {
        callback.onFailure(OkHttpCall.this, e);
      } catch (Throwable t) {
        t.printStackTrace();
      }
    }
  });
}

⑤Retrofit源码流程图

主张咱们自己自动配合着Retrofit最新版的源码一步步去彻底地知道它,只需这样,你才干看到它实在的心里,附上一张Retrofit源码流程图,要留意的是,这是V2.5之前版本的流程,可是,在看完上面的源码剖析后,咱们知道,主体流程是没有改动的。

12W字;2022最新Android11位大厂面试专题(一)

从本质上来说,Retrofit虽然仅仅一个RESTful 的HTTP 网络恳求结构的封装库。可是,它内部经过 许多的规划方式 封装了 OkHttp,让运用者感到它十分简练、易懂。它内部首要是用动态署理的办法,动态将网络恳求接口的注解解析成HTTP恳求,终究履行恳求的进程。

OKhttp

①OKHttp网络结构

OKHttp内部的大致恳求流程图如下所示:

12W字;2022最新Android11位大厂面试专题(一)

如下为运用OKHttp进行Get恳求的进程:

//1.新建OKHttpClient客户端
OkHttpClient client = new OkHttpClient();
//新建一个Request目标
Request request = new Request.Builder()
        .url(url)
        .build();
//2.Response为OKHttp中的呼应
Response response = client.newCall(request).execute();
新建OKHttpClient客户端
OkHttpClient client = new OkHttpClient();
public OkHttpClient() {
    this(new Builder());
}
OkHttpClient(Builder builder) {
    ....
}

能够看到,OkHttpClient运用了制作者方式,Builder里边的可装备参数如下:

public static final class Builder {
    Dispatcher dispatcher;// 分发器
    @Nullable Proxy proxy;
    List<Protocol> protocols;
    List<ConnectionSpec> connectionSpecs;// 传输层版本和衔接协议
    final List<Interceptor> interceptors = new ArrayList<>();// 阻拦器
    final List<Interceptor> networkInterceptors = new ArrayList<>();
    EventListener.Factory eventListenerFactory;
    ProxySelector proxySelector;
    CookieJar cookieJar;
    @Nullable Cache cache;
    @Nullable InternalCache internalCache;// 内部缓存
    SocketFactory socketFactory;
    @Nullable SSLSocketFactory sslSocketFactory;// 安全套接层socket 工厂,用于HTTPS
    @Nullable CertificateChainCleaner certificateChainCleaner;// 验证承认呼应证书 适用 HTTPS 恳求衔接的主机名。
    HostnameVerifier hostnameVerifier;// 验证承认呼应证书 适用 HTTPS 恳求衔接的主机名。  
    CertificatePinner certificatePinner;// 证书确认,运用CertificatePinner来束缚哪些认证组织被信任。
    Authenticator proxyAuthenticator;// 署理身份验证
    Authenticator authenticator;// 身份验证
    ConnectionPool connectionPool;// 衔接池
    Dns dns;
    boolean followSslRedirects; // 安全套接层重定向
    boolean followRedirects;// 本地重定向
    boolean retryOnConnectionFailure;// 重试衔接失败
    int callTimeout;
    int connectTimeout;
    int readTimeout;
    int writeTimeout;
    int pingInterval;
    // 这儿是默许装备的构建参数
    public Builder() {
        dispatcher = new Dispatcher();
        protocols = DEFAULT_PROTOCOLS;
        connectionSpecs = DEFAULT_CONNECTION_SPECS;
        ...
    }
    // 这儿传入自己装备的构建参数
    Builder(OkHttpClient okHttpClient) {
        this.dispatcher = okHttpClient.dispatcher;
        this.proxy = okHttpClient.proxy;
        this.protocols = okHttpClient.protocols;
        this.connectionSpecs = okHttpClient.connectionSpecs;
        this.interceptors.addAll(okHttpClient.interceptors);
        this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
        ...
    }
同步恳求流程
Response response = client.newCall(request).execute();
/**
* Prepares the {@code request} to be executed at   some point in the future.
*/
@Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}
// RealCall为实在的恳求履行者
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
}
@Override public Response execute() throws IOException {
    synchronized (this) {
        // 每个Call只能履行一次
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
    }
    captureCallStackTrace();
    timeout.enter();
    eventListener.callStart(this);
    try {
        // 通知dispatcher现已进入履行状况
        client.dispatcher().executed(this);
        // 经过一系列的阻拦器恳求处理和呼应处理得到终究的回来成果
        Response result = getResponseWithInterceptorChain();
        if (result == null) throw new IOException("Canceled");
        return result;
    } catch (IOException e) {
        e = timeoutExit(e);
        eventListener.callFailed(this, e);
        throw e;
    } finally {
        // 通知 dispatcher 自己现已履行结束
        client.dispatcher().finished(this);
    }
}
Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    // 在装备 OkHttpClient 时设置的 interceptors;
    interceptors.addAll(client.interceptors());
    // 负责失败重试以及重定向
    interceptors.add(retryAndFollowUpInterceptor);
    // 恳求时,对必要的Header进行一些增加,接纳呼应时,移除必要的Header
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    // 负责读取缓存直接回来、更新缓存
    interceptors.add(new CacheInterceptor(client.internalCache()));
    // 负责和服务器树立衔接
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
        // 装备 OkHttpClient 时设置的 networkInterceptors
        interceptors.addAll(client.networkInterceptors());
    }
    // 负责向服务器发送恳求数据、从服务器读取呼应数据
    interceptors.add(new CallServerInterceptor(forWebSocket));
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
    // 运用职责链方式开启链式调用
    return chain.proceed(originalRequest);
}
// StreamAllocation 目标,它相当于一个管理类,维护了服务器衔接、并发流
// 和恳求之间的联系,该类还会初始化一个 Socket 衔接目标,获取输入/输出流目标。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
  RealConnection connection) throws IOException {
    ...
    // Call the next interceptor in the chain.
    // 实例化下一个阻拦器对应的RealIterceptorChain目标
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    // 得到当时的阻拦器
    Interceptor interceptor = interceptors.get(index);
    // 调用当时阻拦器的intercept()办法,并将下一个阻拦器的RealIterceptorChain目标传递下去,终究得到呼应
    Response response = interceptor.intercept(next);
    ...
    return response;
}
异步恳求流程
Request request = new Request.Builder()
    .url("http://publicobject.com/helloworld.txt")
    .build();
client.newCall(request).enqueue(new Callback() {
    @Override 
    public void onFailure(Call call, IOException e) {
      e.printStackTrace();
    }
    @Override 
    public void onResponse(Call call, Response response) throws IOException {
        ...
    }
void enqueue(AsyncCall call) {
    synchronized (this) {
        readyAsyncCalls.add(call);
    }
    promoteAndExecute();
}
// 正在预备中的异步恳求行列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
// 运行中的异步恳求
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
// 同步恳求
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
// Promotes eligible calls from {@link #readyAsyncCalls} to {@link #runningAsyncCalls} and runs
// them on the executor service. Must not be called with synchronization because executing calls
// can call into user code.
private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));
    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();
        // 假如其间的runningAsynCalls不满,且call占用的host小于最大数量,则将call参加到runningAsyncCalls中履行,
        // 一起运用线程池履行call;否者将call参加到readyAsyncCalls中。
        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
        i.remove();
        executableCalls.add(asyncCall);
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }
    return isRunning;
}

终究,咱们在看看AsynCall的代码。

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;
    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }
    String host() {
      return originalRequest.url().host();
    }
    Request request() {
      return originalRequest;
    }
    RealCall get() {
      return RealCall.this;
    }
    /**
     * Attempt to enqueue this async call on {@code    executorService}. This will attempt to clean up
     * if the executor has been shut down by reporting    the call as failed.
     */
    void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        executorService.execute(this);
        success = true;
      } catch (RejectedExecutionException e) {
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        eventListener.callFailed(RealCall.this, ioException);
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
          client.dispatcher().finished(this); // This call is no longer running!
        }
      }
    }
    @Override protected void execute() {
      boolean signalledCallback = false;
      timeout.enter();
      try {
        // 跟同步履行相同,终究都会调用到这儿
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new   IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this,   response);
        }
      } catch (IOException e) {
        e = timeoutExit(e);
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure   for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
}

从上面的源码能够知道,阻拦链的处理OKHttp帮咱们默许做了五步阻拦处理,其间RetryAndFollowUpInterceptor、BridgeInterceptor、CallServerInterceptor内部的源码很简练易懂,此处不再多说。

网络恳求缓存处理之CacheInterceptor
@Override public Response intercept(Chain chain) throws IOException {
    // 依据request得到cache中缓存的response
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;
    long now = System.currentTimeMillis();
    // request判别缓存的战略,是否要运用了网络,缓存或两者都运用
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(),     cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;
    if (cache != null) {
      cache.trackResponse(strategy);
    }
    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache   candidate wasn't applicable. Close it.
    }
    // If we're forbidden from using the network and the cache is insufficient, fail.
    if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(Util.EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }
    Response networkResponse = null;
    try {
        // 调用下一个阻拦器,决议从网络上来得到response
        networkResponse = chain.proceed(networkRequest);
    } finally {
        // If we're crashing on I/O or otherwise,   don't leak the cache body.
        if (networkResponse == null && cacheCandidate != null) {
          closeQuietly(cacheCandidate.body());
        }
    }
    // If we have a cache response too, then we're doing a conditional get.
    // 假如本地现已存在cacheResponse,那么让它和网络得到的networkResponse做比较,决议是否来更新缓存的cacheResponse
    if (cacheResponse != null) {
        if (networkResponse.code() == HTTP_NOT_MODIFIED)   {
          Response response = cacheResponse.newBuilder()
                  .headers(combine(cacheResponse.headers(), networkResponse.headers()))
                  .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
                  .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
                  .cacheResponse(stripBody(cacheResponse))
                  .networkResponse(stripBody(networkResponse))
              .build();
          networkResponse.body().close();
          // Update the cache after combining headers but before stripping the
          // Content-Encoding header (as performed by initContentStream()).
          cache.trackConditionalCacheHit();
          cache.update(cacheResponse, response);
          return response;
        } else {
          closeQuietly(cacheResponse.body());
        }
    }
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();
    if (cache != null) {
      if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response,   networkRequest)) {
        // Offer this request to the cache.
        // 缓存未经缓存过的response
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
      }
      if (HttpMethod.invalidatesCache(networkRequest.method())) {
        try {
          cache.remove(networkRequest);
        } catch (IOException ignored) {
          // The cache cannot be written.
        }
      }
    }
    return response;
}

缓存阻拦器会依据恳求的信息和缓存的呼应的信息来判别是否存在缓存可用,假如有能够运用的缓存,那么就回来该缓存给用户,否则就持续运用职责链方式来从服务器中获取呼应。当获取到呼应的时分,又会把呼应缓存到磁盘上面。

ConnectInterceptor之衔接池
@Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    // We need the network to satisfy this request.     Possibly for validating a conditional GET.
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    // HttpCodec是对 HTTP 协议操作的笼统,有两个完结:Http1Codec和Http2Codec,顾名思义,它们别离对应 HTTP/1.1 和 HTTP/2 版本的完结。在这个办法的内部完结衔接池的复用处理
    HttpCodec httpCodec = streamAllocation.newStream(client, chain,     doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
// Returns a connection to host a new stream. This // prefers the existing connection if it exists,
// then the pool, finally building a new connection.
// 调用 streamAllocation 的 newStream() 办法的时分,终究会经过一系列
// 的判别到达 StreamAllocation 中的 findConnection() 办法
private RealConnection findConnection(int   connectTimeout, int readTimeout, int writeTimeout,
    int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
      ...
      // Attempt to use an already-allocated connection. We need to be careful here because our
      // already-allocated connection may have been restricted from creating new streams.
      // 测验运用已分配的衔接,现已分配的衔接或许现已被约束创立新的流
      releasedConnection = this.connection;
      // 开释当时衔接的资源,假如该衔接现已被约束创立新的流,就回来一个Socket以关闭衔接
      toClose = releaseIfNoNewStreams();
      if (this.connection != null) {
        // We had an already-allocated connection and it's good.
        result = this.connection;
        releasedConnection = null;
      }
      if (!reportedAcquired) {
        // If the connection was never reported acquired, don't report it as released!
        // 假如该衔接从未被符号为取得,不要符号为发布状况,reportedAcquired 经过 acquire()   办法修正
        releasedConnection = null;
      }
      if (result == null) {
        // Attempt to get a connection from the pool.
        // 测验供衔接池中获取一个衔接
        Internal.instance.get(connectionPool, address, this, null);
        if (connection != null) {
          foundPooledConnection = true;
          result = connection;
        } else {
          selectedRoute = route;
        }
      }
    }
    // 关闭衔接
    closeQuietly(toClose);
    if (releasedConnection != null) {
      eventListener.connectionReleased(call, releasedConnection);
    }
    if (foundPooledConnection) {
      eventListener.connectionAcquired(call, result);
    }
    if (result != null) {
      // If we found an already-allocated or pooled connection, we're done.
      // 假如现已从衔接池中获取到了一个衔接,就将其回来
      return result;
    }
    // If we need a route selection, make one. This   is a blocking operation.
    boolean newRouteSelection = false;
    if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
      newRouteSelection = true;
      routeSelection = routeSelector.next();
    }
    synchronized (connectionPool) {
      if (canceled) throw new IOException("Canceled");
      if (newRouteSelection) {
        // Now that we have a set of IP addresses,   make another attempt at getting a   connection from
        // the pool. This could match due to   connection coalescing.
         // 依据一系列的 IP地址从衔接池中获取一个链接
        List<Route> routes = routeSelection.getAll();
        for (int i = 0, size = routes.size(); i < size;i++) {
          Route route = routes.get(i);
          // 从衔接池中获取一个衔接
          Internal.instance.get(connectionPool, address, this, route);
          if (connection != null) {
            foundPooledConnection = true;
            result = connection;
            this.route = route;
            break;
          }
        }
      }
      if (!foundPooledConnection) {
        if (selectedRoute == null) {
          selectedRoute = routeSelection.next();
        }
        // Create a connection and assign it to this allocation immediately. This makes it   possible
        // for an asynchronous cancel() to interrupt the handshake we're about to do.
        // 在衔接池中假如没有该衔接,则创立一个新的衔接,并将其分配,这样咱们就能够在握手之前进行终端
        route = selectedRoute;
        refusedStreamCount = 0;
        result = new RealConnection(connectionPool, selectedRoute);
        acquire(result, false);
      }
    }
    // If we found a pooled connection on the 2nd time around, we're done.
    if (foundPooledConnection) {
    // 假如咱们在第2次的时分发现了一个池衔接,那么咱们就将其回来
      eventListener.connectionAcquired(call, result);
      return result;
    }
    // Do TCP + TLS handshakes. This is a blocking     operation.
     // 进行 TCP 和 TLS 握手
    result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
      connectionRetryEnabled, call, eventListener);
    routeDatabase().connected(result.route());
    Socket socket = null;
    synchronized (connectionPool) {
      reportedAcquired = true;
      // Pool the connection.
      // 将该衔接放进衔接池中
      Internal.instance.put(connectionPool, result);
      // If another multiplexed connection to the same   address was created concurrently, then
      // release this connection and acquire that one.
      // 假如一起创立了另一个到同一地址的多路复用衔接,开释这个衔接并获取那个衔接
      if (result.isMultiplexed()) {
        socket = Internal.instance.deduplicate(connectionPool, address, this);
        result = connection;
      }
    }
    closeQuietly(socket);
    eventListener.connectionAcquired(call, result);
    return result;
}

从以上的源码剖析可知:

  • 判别当时的衔接是否能够运用:流是否现已被关闭,而且现已被约束创立新的流;
  • 假如当时的衔接无法运用,就从衔接池中获取一个衔接;
  • 衔接池中也没有发现可用的衔接,创立一个新的衔接,并进行握手,然后将其放到衔接池中。

在从衔接池中获取一个衔接的时分,运用了 Internal 的 get() 办法。Internal 有一个静态的实例,会在 OkHttpClient 的静态代码快中被初始化。咱们会在 Internal 的 get() 中调用衔接池的 get() 办法来得到一个衔接。而且,从中咱们明白了衔接复用的一个好处便是省去了进行 TCP 和 TLS 握手的一个进程。由于树立衔接自身也是需求消耗一些时刻的,衔接被复用之后能够提高咱们网络拜访的效率。

接下来详细剖析下ConnectionPool是怎么完结衔接管理的。

OkHttp 的缓存管理分红两个进程,一边当咱们创立了一个新的衔接的时分,咱们要把它放进缓存里边;另一边,咱们还要来对缓存进行整理。在 ConnectionPool 中,当咱们向衔接池中缓存一个衔接的时分,只需调用双端行列的 add() 办法,将其参加到双端行列即可,而整理衔接缓存的操作则交给线程池来守时履行。

private final Deque<RealConnection> connections = new ArrayDeque<>();
void put(RealConnection connection) {
assert (Thread.holdsLock(this));
    if (!cleanupRunning) {
      cleanupRunning = true;
      // 运用线程池履行整理使命
      executor.execute(cleanupRunnable);
    }
    // 将新建的衔接刺进到双端行列中
    connections.add(connection);
}
 private final Runnable cleanupRunnable = new Runnable() {
@Override public void run() {
    while (true) {
        // 内部调用 cleanup() 办法来整理无效的衔接
        long waitNanos = cleanup(System.nanoTime());
        if (waitNanos == -1) return;
        if (waitNanos > 0) {
          long waitMillis = waitNanos / 1000000L;
          waitNanos -= (waitMillis * 1000000L);
          synchronized (ConnectionPool.this) {
            try {
              ConnectionPool.this.wait(waitMillis, (int) waitNanos);
            } catch (InterruptedException ignored) {
            }
          }
        }
    }
};
long cleanup(long now) {
    int inUseConnectionCount = 0;
    int idleConnectionCount = 0;
    RealConnection longestIdleConnection = null;
    long longestIdleDurationNs = Long.MIN_VALUE;
    // Find either a connection to evict, or the time that the next eviction is due.
    synchronized (this) {
        // 遍历一切的衔接
        for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
          RealConnection connection = i.next();
          // If the connection is in use, keep     searching.
          // 遍历一切的衔接
          if (pruneAndGetAllocationCount(connection, now) > 0) {
            inUseConnectionCount++;
            continue;
          }
          idleConnectionCount++;
          // If the connection is ready to be     evicted,     we're done.
          // 假如找到了一个能够被整理的衔接,会测验去寻找搁置时刻最久的衔接来开释
          long idleDurationNs = now - connection.idleAtNanos;
          if (idleDurationNs > longestIdleDurationNs) {
            longestIdleDurationNs = idleDurationNs;
            longestIdleConnection = connection;
          }
        }
        // maxIdleConnections 标明最大答应的搁置的衔接的数量,keepAliveDurationNs标明衔接答应存活的最长的时刻。
        // 默许空闲衔接最大数目为5个,keepalive 时刻最长为5分钟。
        if (longestIdleDurationNs >= this.keepAliveDurationNs
            || idleConnectionCount > this.maxIdleConnections) {
          // We've found a connection to evict. Remove it from the list, then close it     below (outside
          // of the synchronized block).
          // 该衔接的时长超出了最大的活泼时长或许搁置的衔接数量超出了最大答应的范围,直接移除
          connections.remove(longestIdleConnection);
        } else if (idleConnectionCount > 0) {
          // A connection will be ready to evict soon.
          // 搁置的衔接的数量大于0,停顿指定的时刻(等会儿会将其整理掉,现在还不是时分)
          return keepAliveDurationNs - longestIdleDurationNs;
        } else if (inUseConnectionCount > 0) {
          // All connections are in use. It'll be at least the keep alive duration 'til we run again.
          // 一切的衔接都在运用中,5分钟后再整理
          return keepAliveDurationNs;
        } else {
          // No connections, idle or in use.
           // 没有衔接
          cleanupRunning = false;
          return -1;
      }
}

从以上的源码剖析可知,首要会对缓存中的衔接进行遍历,以寻找一个搁置时刻最长的衔接,然后依据该衔接的搁置时长和最大答应的衔接数量等参数来决议是否应该整理该衔接。一起留意上面的办法的回来值是一个时刻,假如搁置时刻最长的衔接仍然需求一段时刻才干被整理的时分,会回来这段时刻的时刻差,然后会在这段时刻之后再次对衔接池进行整理。

经过上面临OKHttp内部作业机制的一系列剖析,相信你现已对OKHttp现已有了一个比较深化的了解了。首要,咱们会在恳求的时分初始化一个Call的实例,然后履行它的execute()办法或enqueue()办法,内部终究都会履行到getResponseWithInterceptorChain()办法,这个办法里边经过阻拦器组成的职责链,依次经过用户自界说一般阻拦器、重试阻拦器、桥接阻拦器、缓存阻拦器、衔接阻拦器和用户自界说网络阻拦器以及拜访服务器阻拦器等阻拦处理进程,来获取到一个呼应并交给用户。

其间,除了OKHttp的内部恳求流程这点之外,缓存和衔接这两部分内容也是两个很重要的点,相信经过解说,咱们对这三部分要点内容现已有了自己的了解。

2.View的制作流程

Android 中 Activity 是作为运用程序的载体存在,代表着一个完好的用户界面,供给了一个窗口来制作各种视图,当 Activity 启动时,咱们会经过 setContentView 办法来设置一个内容视图,这个内容视图便是用户看到的界面。那么 View 和 activity 是怎么关联在一起的呢 ?

①Android的UI层级制作体系

12W字;2022最新Android11位大厂面试专题(一)

上图是View与Activity之间的联系,先介绍一下上面这张图

  • PhoneWindow:每个Activity都会创立一个Window用来承载View的显现,Window是一个笼统类,PhoneWindow是Window的仅有完结类,该类中包含一个DecorView。
  • DecorView:最顶层的View,该View承继自 FrameLayout,它的内部包含两部分,一部分是ActionBar ,另一部分ContentView,
  • ContentView:咱们 setContentView() 中传入的布局,就在该View中加载显现
  • ViewRootImpl:视图层次结构的顶部。一个 Window 对应着一个 ViewRootImpl 和 一个DecorView,经过该实例对DecorView进行控制,终究经过履行ViewRootImpl的performTraversals()开启整个View树的制作,

②View的加载流程

  • 当调用 Activity 的setContentView 办法后会调用PhoneWindow 类的setContentView办法
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}
  • PhoneWindow类的setContentView办法中终究会生成一个DecorView目标
@Override
public void setContentView(int layoutResID) {
    if (mContentParent == null) {
         //在这儿生成一个DecorView
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
    ...
}
private void installDecor() {
    mForceDecorInstall = false;
    //mDecor  为DecorView
    if (mDecor == null) {
        mDecor = generateDecor(-1);
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }
    ...
 }
protected DecorView generateDecor(int featureId) {
   ...
   // 在这儿直接 new 了一个DecorView
   return new DecorView(context, featureId, this, getAttributes());
}
  • DecorView容器中包含根布局,根布局中包含一个id为content的FrameLayout布局,Activity加载布局的xml终究经过LayoutInflater将xml文件中的内容解析成View层级体系,终究填加到id为content的FrameLayout布局中。
protected ViewGroup generateLayout(DecorView decor) {
    //做一些窗体款式的判别
       ...
     //给窗体进行装饰
    int layoutResource;
    int features = getLocalFeatures();
    // System.out.println("Features: 0x" + Integer.toHexString(features));
    //加载系统布局 判别究竟是加载那个布局
    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
        layoutResource = R.layout.screen_swipe_dismiss;
        setCloseOnSwipeEnabled(true);
    }  
...
    mDecor.startChanging();
    //将加载到的根底布局增加到mDecor中
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    //经过系统的content的资源ID去进行实例化这个控件
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    if (contentParent == null) {
        throw new RuntimeException("Window couldn't find content container view");
    }
}

到此,Actvity的制作完结

③View的视图制作流程剖析

  • DecorView被加载到Window中 在ActivityThread的 handleResumeActivity() 办法中经过WindowManager将DecorView加载到Window中,经过ActivityThread中一下代码能够得到应征
final void handleResumeActivity(IBinder token,
        boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
      ...
      //在此处履行Activity的onResume办法
    r = performResumeActivity(token, clearHide, reason);
    if (r != null) {
        final Activity a = r.activity;
        if (localLOGV) Slog.v(
            TAG, "Resume " + r + " started activity: " +
            a.mStartedActivity + ", hideForNow: " + r.hideForNow
            + ", finished: " + a.mFinished);
        final int forwardBit = isForward ?
                WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
        boolean willBeVisible = !a.mStartedActivity;
        if (!willBeVisible) {
            try {
                willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                        a.getActivityToken());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        if (r.window == null && !a.mFinished && willBeVisible) {
            //获取window目标
            r.window = r.activity.getWindow();
            //获取DecorView
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            //获取WindowManager,在这儿getWindowManager()实质上获取的是ViewManager的子类目标WindowManager
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                 //获取ViewRootImpl目标
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient && !a.mWindowAdded) {
                a.mWindowAdded = true;
                //在这儿WindowManager将DecorView增加到PhoneWindow中
                wm.addView(decor, l);
            }
        } 

总结:在ActivityThread的handleResumeActivity办法中WindowManager将DecorView增加到PhoneWindow中,addView()办法履行时将视图增加的动作交给了ViewRootImpl处理,终究在ViewRootImpl的performTraversals中开端View树的制作

④ViewRootImpl的performTraversals()办法完结详细的视图制作流程

private void performTraversals() {
    if (!mStopped || mReportNextDraw) {
        ...
        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
          ...
         // Ask host how big it wants to be
         //View制作:开端丈量 View的丈量时递归逐层丈量,由父布局与子布局一起承认子View的丈量方式,在子布局丈量结束时承认副布局的宽高,
         //在此办法履行结束后才可获取到View的宽高,否侧获取的宽高都为0
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
       }
    if (didLayout) {
    //开端摆放,该办法是ViewGroup中的办法,例如 LinerLayout...
        performLayout(lp, mWidth, mHeight);
    }
    if (!cancelDraw && !newSurface) {
        //开端制作,履行View的onDraw()办法
        performDraw();
    }
}

下面开端对performMeasure(),performLayout(),performDraw()进行解析

  • performMeasure()
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

经过以上这段代码,咱们能够看到两个重要的参数 childWidthMeasureSpec,childHeightMeasureSpec,这两个Int类型的参数包含了View的丈量方式和宽高信息,因此在onMeasure()办法中咱们能够经过该参数获取到丈量方式,和宽高信息,咱们在onMeasue中设置宽高信息也是经过MeasureSpec设置,

 */
public static class MeasureSpec {
    //int类型占4个字节,其间高2位标明尺度丈量方式,低30位标明详细的宽高信息
    private static final int MODE_SHIFT = 30;
    private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
    /** @hide */
    @IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
    @Retention(RetentionPolicy.SOURCE)
    public @interface MeasureSpecMode {}
    //如下所示是MeasureSpec中的三种方式:UNSPECIFIED、EXACTLY、AT_MOST 
    //UNSPECIFIED:未指定方式,父容器不约束View的巨细,一般用于系统内部的丈量
    public static final int UNSPECIFIED = 0 << MODE_SHIFT;
    //AT_MOST:最大方式,对应于在xml文件中指定控件巨细为wrap_content特色,子View的终究巨细是父View指定的巨细值,而且子View的巨细不能大于这个值
    public static final int EXACTLY     = 1 << MODE_SHIFT;
    //EXACTLY :准确方式,对应于在xml文件中指定控件为match_parent特色或许是详细的数值,父容器丈量出View所需的详细巨细
    public static final int AT_MOST     = 2 << MODE_SHIFT;
    //获取丈量方式
    @MeasureSpecMode
    public static int getMode(int measureSpec) {
        //noinspection ResourceType
        return (measureSpec & MODE_MASK);
    }
   //获取宽高信息
    public static int getSize(int measureSpec) {
        return (measureSpec & ~MODE_MASK);
    }
     ...
}

performMeasure()会持续调用mView.measure()办法

 public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        boolean optical = isLayoutModeOptical(this);
        if (optical != isLayoutModeOptical(mParent)) {
            Insets insets = getOpticalInsets();
            int oWidth  = insets.left + insets.right;
            int oHeight = insets.top  + insets.bottom;
            //依据原有宽高核算获取不同方式下的详细宽高值
            widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);
            heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
        }
        ...
        if (forceLayout || needsLayout) {
            // first clears the measured dimension flag
            mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
            resolveRtlPropertiesIfNeeded();
            int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
            if (cacheIndex < 0 || sIgnoreMeasureCache) {
                // measure ourselves, this should set the measured dimension flag back
                //在该办法中子控件完结详细的丈量
                onMeasure(widthMeasureSpec, heightMeasureSpec);
                ...
            } 
         ...
    }

从上述代码片段中能够看到履行到了onMeasure()办法,假如该控件为View的话,丈量到此结束,假如是ViewGroup的话,会持续循环获取一切子View,调用子View的measure办法,下面以LinearLayout为例,持续看

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mOrientation == VERTICAL) {
        measureVertical(widthMeasureSpec, heightMeasureSpec);
    } else {
        measureHorizontal(widthMeasureSpec, heightMeasureSpec);
    }
}

LinearLayout经过不同的摆放布局履行不同的丈量办法,以measureVertical为例,向下看

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
    //获取子View的个数
    final int count = getVirtualChildCount();
        ...
    //循环获取一切子View
    for (int i = 0; i < count; ++i) {
        //获取子View
        final View child = getVirtualChildAt(i);
        //调用子View的measure办法
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    ....
}

至此,View的丈量流程结束

⑤View的layout流程剖析

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
        int desiredWindowHeight) {
        final View host = mView;
          // 在此处调用mView的layout()摆放开端
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
     }
/*  
 *@param l view 左边际相关于父布局左边际间隔 
 *@param t view 上边际相关于父布局上边际方位 
 *@param r view 右边际相关于父布局左边际间隔 
 *@param b view 下边际相关于父布局上边际间隔 
 */  
public void layout(int l, int t, int r, int b) {
      ...
   //记录 view 原始方位  
    int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;
  //调用 setFrame 办法 设置新的 mLeft、mTop、mBottom、mRight 值,  
  //设置 View 自身四个极点方位  
  //并回来 changed 用于判别 view 布局是否改动  
    boolean changed = isLayoutModeOptical(mParent) ?
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
 //第二步,假如 view 方位改动那么调用 onLayout 办法设置子 view 方位 
    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
        //开端调用 onLayout  在此处依据子View的宽高及相关规矩进行摆放
        onLayout(changed, l, t, r, b);
          ...
            }
        }
    }
}

⑥View的Draw流程剖析

private void performDraw() {
        ...
          //调用draw办法
        draw(fullRedrawNeeded);
        ...
    }
private void draw(boolean fullRedrawNeeded) {
        ...
     //View的制作流程调用的   drawSoftware() 该办法
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
    return;
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty) {
    final Canvas canvas;
       ...
    //初始化画布
    canvas = mSurface.lockCanvas(dirty);
    ...
    //开端调用ViewGroup 和  View的draw办法
    mView.draw(canvas);
    ...
}
public void draw(Canvas canvas) {
    drawBackground(canvas);
    //ViewGroup  默许是不会调用OnDraw办法的
    if (!dirtyOpaque) onDraw(canvas);
    //这个办法首要是ViewGroup循环调用 drawChild()进行对子View的制作
    dispatchDraw(canvas); 
}
protected void onDraw(Canvas canvas) {
}

View的onDraw办法仅仅一个模版,详细完结办法,交由咱们这些开发者去进行完结

至此,View的制作流程结束

  • requestLayout从头制作视图 子View调用requestLayout办法,会符号当时View及父容器,一起逐层向上提交,直到ViewRootImpl处理该事情,ViewRootImpl会调用三大流程,从measure开端,关于每一个含有符号位的view及其子View都会进行丈量、布局、制作。
  • invalidate在UI线程中从头制作视图

当子View调用了invalidate办法后,会为该View增加一个符号位,一起不断向父容器恳求改写,父容器经过核算得出自身需求重绘的区域,直到传递到ViewRootImpl中,终究触发performTraversals办法,进行开端View树重绘流程(只制作需求重绘的视图)。

  • postInvalidate在非UI线程中从头制作视图

这个办法与invalidate办法的效果是相同的,都是使View树重绘,但两者的运用条件不同,postInvalidate是在非UI线程中调用,invalidate则是在UI线程中调用。

3.讲一下接触的传递机制

①接触时刻的几种类型

  • ACTION_DOWN
  • ACTION_MOVE
  • ACTION_UP

很简略也便是你手指的按下 移动 松开的这三个中心的操作!

②接触事情的三个中心办法

1,分发 Dispatch 也便是对应着 dispatchTouchEvent 办法。

一般来说,这个办法标明当时的这个视图是处理这个事情仍是持续分发这个事情,当这个办法的回来值为True的时分标明,事情现已被消费了,不会再持续的往下传递了。可是这个视图是ViewGroup或许它的子类,则会调用onInterceptTouchEvent来判别是否持续分发该事情。

2,阻拦 Intercept

事情阻拦对应着onInterceptTouchEvent办法,这个办法只在ViewGroup及其子类中存在,在activity和view中是不存在这类办法的。

3 消费 Consume

事情消费对应着onTouchEvent这个办法,一般事情的逻辑便是在这儿面进行处理的。

总结一下: activity中有 dispatchTouchEvent 和 onTouchEvent这两个办法 viewGroup和它的子类 拥有dispatchTouchEvent,onInterceptTouch和onTouchEvent这个三个办法 view只需dispatchTouchEvent和onTouchEvent两个办法。

③View事情的传递机制

//activity 部分代码
@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"dispatchTouchEvent ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"dispatchTouchEvent ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"dispatchTouchEvent ACTION_UP");
                break;
            case MotionEvent.ACTION_CANCEL:
                Log.e(TAG,"dispatchTouchEvent ACTION_CANCEL");
                break;
             default:
                 break;
        }
        return super.dispatchTouchEvent(ev);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"onTouchEvent Action_DOWN");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"onTouchEvent ACTION_UP");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"onTouchEvent ACTION_MOVE");
                break;
            case MotionEvent.ACTION_CANCEL:
                Log.e(TAG,"onTouchEvent ACTION_CANCEL");
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (v.getId()){
            case R.id.textView:
            switch(event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    Log.e(TAG,"MyTextView onTouch Action_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.e(TAG,"MyTextView onTouch ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.e(TAG,"MyTextView onTouch ACTION_UP");
                    break;
                default:
                    break;
            }
            default:
                break;
        }
        return false;
    }
    //MyTextView部分代码
     //分配事情的办法,在这儿决议是否要分配事情
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"dispatchTouchEvent ACTION_MOVE");
                break;
            case MotionEvent.ACTION_BUTTON_PRESS:
                Log.e(TAG,"dispatchTouchEvent ACTION_BUTTON_PRESS");
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"dispatchTouchEvent ACTION_DOWN");
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"dispatchTouchEvent ACTION_UP");
            case MotionEvent.ACTION_CANCEL:
                Log.e(TAG,"dispatchTouchEvent ACTION_CANCEL");
            default:
                break;
        }
        return super.dispatchTouchEvent(event);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                // 假如点击
//                performClick();
                Log.e(TAG,"onTouchEvent ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"onTouchEvent ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"onTouchEvent ACTION_UP");
                break;
            case MotionEvent.ACTION_CANCEL:
                Log.e(TAG,"onTouchEvent ACTION_CANCEL");
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

12W字;2022最新Android11位大厂面试专题(一)

12W字;2022最新Android11位大厂面试专题(一)
从打印出来的Log来看,点击事情的 第一步是走Activity的dispatchTouchEvent的ACTION_DOWN->再进入MyTextView的dispatchTouchEvent 第二步是走Activity的onTouchEvent的onTouch ACTION_DOWN-再进入MyTextView的onTouch ACTION_DOWN 第二步是走Activity的onTouchEvent的onTouch ACTION_MOVE-再进入MyTextView的onTouch ACTION_MOVE 这很明显的论述了点击事情和接触事情的传递次序

12W字;2022最新Android11位大厂面试专题(一)

1,经过上面的图咱们能够剖析到,接触事情的传递流程是从 dispatchTouchEvent开发的,假如不进行人为干涉,(也便是默许回来父类的同名函数),则事情将会依照嵌套层次从外层向内层传递,到达最内的View层View, 就有它的onTouchEvent办法进处理。该办法假如能消费该事情,则回来true,假如处理不了,则回来false,这时分事情就会向外层传递,并有外层的View的onTouchEvent办法进行处理。 2,假如事情在向内层传递的进程中由于人为干涉的原因,事情处理函数回来true,则会导致事情提早被消费掉,内层View将不会受到这个事情。 3,View控件的事情出发次序是先履行onTouch办法,终究才履行onClick办法的,假如onTouch回来True,则事情不会持续传递,终究也不会调用onClick办法,假如onTouch回来为False,则事情会持续传递下去。

④ViewGroup 事情的传递机制

ViewGroup是作为View控件的容器,咱们常见的ViewGroup有:LinnerLayout,Relativeyout, ListView, ScrollView.这儿咱们来完结一个比方。

//在上面的根底上咱们自界说了RelativeRelayout这个相对布局
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
public class MyRelativeLayout extends RelativeLayout {
    private static final String TAG = "MyRelayoutLayout";
    public MyRelativeLayout(Context context) {
        super(context);
    }
    public MyRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i(TAG,"MyRelativeLayout dispatchTouch ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i(TAG, "MyRelaytiveLayout dispatchTouch ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i(TAG,"MyRelativeLayout dispatchTouch ACTION_UP");
                break;
            default:
                break;
            }
        return super.dispatchTouchEvent(ev);
    }
    /**
     *这个办法是ViewGroup特有的办法
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i(TAG, "MyRelativeLayout onInterceptTouchEvent ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i(TAG, "MyRelativeLayout onInterceptTouchEvent ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i(TAG, "MyRelativeLayout onInterceptTouchEvent ACTION_UP");
                break;
            default:
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i(TAG, "MyRelativeLayout onTouchEvent ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i(TAG, "MyRelativeLayout onTouchEvent ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i(TAG, "MyRelativeLayout onTouchEvent ACTION_UP");
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }
}

这儿我么进运用自己的相对布局

<?xml version="1.0" encoding="utf-8"?>
<com.example.administrator.testactivity.MyRelativeLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <com.example.administrator.testactivity.MyTextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.181"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.15" />
    <Button
        android:id="@+id/button"
        android:visibility="gone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="44dp"
        android:layout_marginTop="8dp"
        android:text="Button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/textView"
        app:layout_constraintTop_toTopOf="parent" />
</com.example.administrator.testactivity.MyRelativeLayout>

接触屏幕上的TextView,咱们能够看到打印出来的Log信息如下

12W字;2022最新Android11位大厂面试专题(一)

其实仔细的读者能够发现,中心也便是多了一道门而已也便是RelativeLayout ,这个咱们做个ViewGroup的事情传递的总结:

ViewGroup经过onInterceptTouch办法对时刻进行阻拦,假如该办法回来True,则事情不会持续传递给子View,假如回来false或许是super.onInterceptTouch,则会持续传递给子View。

在子View中对事情进行消费后ViewGroup将接受不到任何事情, 那么当我这儿插一句嘴,当咱们再开发嵌套View的时分,比方说是在ScrollView中嵌套了一个ListView,那么咱们是否能够采纳这种办法,当ListView接纳了接触事情后就不会持续向下传递,那么就不会呈现滑动冲突这样的问题了。

4.自界说View首要重写哪个办法

A.View()

B.onDraw()

C.onCreate()

D.ViewGroup()

答案: onDraw()

5.讲一下战略方式和状况方式的各自的特色和差异

战略方式一般用于单个算法,而状况方式的每个状况子类中需求包含一切本来的语境类(Context)中的一切办法的详细完结。

差异:

状况方式将各个状况所对应的操作别离开来,即关于不同的状况,由不同的子类完结详细操作,不同状况的切换由子类完结,当发现传入参数不是自己这个状况所对应的参数,则自己给Context类切换状况;而战略方式是直接依靠注入到Context类的参数进行选择战略,不存在切换状况的操作。

联络:

状况方式和战略方式都是为具有多种或许景象规划的方式,把不同的处理景象笼统为一个相同的接口,契合对扩展开放,对修正关闭的准则。

还有便是,战略方式更具有一般性一些,在实践中,能够用战略方式来封装简直任何类型的规矩,只需在剖析进程中听到需求在不同实践运用不同的业务规矩,就能够考虑运用战略方式处理,在这点上战略方式是包含状况方式的功用的,战略方式是一个重要的规划方式。

6.说一下线程安全,线程同步的原子性,可见性和有序性

①界说:什么是线程安全性

当多个线程拜访某个类时,不论运行时环境选用 何种调度办法 或许这些进程将怎么替换履行,而且在主调代码中不需求任何额定的同步或协同,这个类都能表现出正确的行为,那么就称这个类便是线程安全的。

②线程安全性的三个体现

  • 原子性:供给互斥拜访,同一时刻只能有一个线程对数据进行操作(Atomic、CAS算法、synchronized、Lock)
  • 可见性:一个主内存的线程假如进行了修正,能够及时被其他线程调查到(synchronized、volatile)
  • 有序性:假如两个线程不能从 happens-before准则 调查出来,那么就不能调查他们的有序性,虚拟机能够随意的对他们进行重排序,导致其调查调查成果杂乱无序(happens-before准则)

③线程安全性:原子性

3.1、原子性 — Atomic包

在Java jdk中里边供给了许多Atomic类

  • AtomicXXX:CAS、Unsafe.compareAndSwapInt
  • AtomicLong、LongAdder
  • AtomicReference、AtomicReferenceFieldUpdater
  • AtomicStampReference:CAS的ABA问题

由于CAS原语的直接操作与核算机底层的联络很大,CAS原语有三个参数,内存地址期望值新值。咱们在Java中一般不去直接写CAS相关的代码,JDK为咱们封装在AtomicXXX中,因此,咱们直接运用就能够了。

咱们在 java.util.concurrent.atomic 目录中能够看到咱们这些类,包下供给了AtomicBooleanAtomicLongAtomicInteger三种原子更新根本类型和一个比较好玩的类AtomicReference,这些类都有一个一起点,都支撑CAS,以AtomicInteger为要点解说。

12W字;2022最新Android11位大厂面试专题(一)

3.1.1、AtomicInteger

AtomicInteger是一个供给原子操作的Integer类,经过线程安全的办法操作加减

以下是AtomicIntege根本包含的办法:

public final int getAndSet(int newValue)       //给AtomicInteger设置newValue并回来加oldValue
public final boolean compareAndSet(int expect, int update)    //假如输入的值和期望值持平就set并回来true/false
public final int getAndIncrement()     //对AtomicInteger原子的加1并回来当时自增前的value
public final int getAndDecrement()   //对AtomicInteger原子的减1并回来自减之前的的value
public final int getAndAdd(int delta)   //对AtomicInteger原子的加上delta值并返加之前的value
public final int incrementAndGet()   //对AtomicInteger原子的加1并回来加1后的值
public final int decrementAndGet()    //对AtomicInteger原子的减1并回来减1后的值
public final int addAndGet(int delta)   //给AtomicInteger原子的加上指定的delta值并回来加后的值

示例:

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public class AtomicIntegerExample {
    // 恳求总数
    public static int clientTotal = 5000;
    // 一起并发履行的线程数
    public static int threadTotal = 200;
    public static AtomicInteger count = new AtomicInteger(0);
    public static void main(String[] args) throws Exception {
     //获取线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //界说信号量
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count.get());
    }
    private static void add() {
        count.incrementAndGet();
    }
}

这儿咱们运用恳求总数为:5000,一起履行的并发线程数为:200,咱们终究需求得到成果为:5000,这个履行成果才算正确。

查看回来成果:

13:43:26.473 [main] INFO com.mmall.concurrency.example.atomic.AtomicIntegerExample - count:5000

终究成果是 5000标明是线程安全的。

咱们来看看 AtomicInteger底层代码中究竟为咱们做了什么?首要咱们来看 AtomicInteger.incrementAndGet()办法

public class AtomicInteger extends Number implements java.io.Serializable{
/**
     *  对AtomicInteger原子的加1并回来加1后的值
     * @return 更新的值
     */
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
}

AtomicInteger调用了java底层的 unsafe.getAndAddInt()办法,这儿是完结CAS 的要害。

incrementAndGet()是将自增后的值回来,还有一个办法getAndIncrement()是将自增前的值回来,别离对应++ii++操作。相同的decrementAndGet()getAndDecrement()则对--ii--操作。

Unsafe类是在sun.misc包下,不属于Java标准。可是许多Java的根底类库,包含一些被广泛运用的高功能开发库都是依据 Unsafe类开发的,比方Netty、Cassandra、Hadoop、Kafka等。Unsafe类在提高Java运行效率,增强Java言语底层操作 能力方面起了很大的效果。Unsafe类使Java拥有了像C言语的指针相同操作内存空间的能力,一起也带来了指针的问题。 过度的运用Unsafe类会使得出错的几率变大,因此Java官方并不主张运用的,官方文档也简直没有。通常咱们最好也不 要运用Unsafe类,除非有清晰的意图,而且也要对它有深化的了解才行。

再来看 Unsafe.getAndAddInt()办法

 /*
  * 其间getIntVolatile和compareAndSwapInt都是native办法
  * getIntVolatile是获取当时的期望值
  * compareAndSwapInt便是咱们平常说的CAS(compare and swap),经过比较假如内存区的值没有改动,那么就用新值直接给该内存区赋值
  */
    public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
        return var5;
    }
    public native int getIntVolatile(Object var1, long var2);
    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

咱们能够看到getAndAddInt(Object var1, long var2, int var4),传进来的第一个参数是当时的一个目标,也便是咱们的:count.incrementAndGet(),在getAndAddInt()中,var1便是count,var2便是当时的值,比方当时循环中count值为 2,var4为每次递加1

其次getAndAddInt()办法中涉及到的两个办法调用都界说为native,即java底层完结的本地办法

  • getIntVolatile():获取保存当时目标count的主存地址的引证(留意不是目标的值,是引证)。
  • compareAndSwapInt():比较当时目标的值和底层该目标的值是否持平,假如持平,则将当时目标值加1,假如不持平,则从头去获取底层该目标的值,这个办法的完结便是CPU的CAS(compare and swap)操作。

咱们知道volatile具有一致性的特征,可是它不具有原子性,为什么AtomicInteger却一起具有一致性和原子性,本来在AtomicInteger源码中完结了这样一串代码:private volatile int value;,在AtomicInteger内部完结就运用了volatile要害字,这便是为什么履行CAS操作的时分,从底层获取的数据便是最新的数据:

假如当时要保存的值和内存中最新的值不持平的话,阐明在这个进程中被其他线程修正了,只能获取更新当时值为最新值,再那这个当时值再去和从头去内存获取的最新值比较,直到二者持平的时分,才完结+1的进程.

运用AtomicInteger的好处在于,它不同于sychronized要害字或lock用锁的方式来完结原子性,加锁会影响功能,而是选用循环比较的方式来提高功能。

3.1.2、AtomicLong

AtomicLong是效果是对长整形进行原子操作,依靠底层的cas来确保原子性的更新数据,在要增加或许削减的时分,会运用死循环不断地cas到特定的值,从而到达更新数据的意图。

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicLong;
@Slf4j
public class AtomicLongExample {
    // 恳求总数
    public static int clientTotal = 5000;
    // 一起并发履行的线程数
    public static int threadTotal = 200;
    public static AtomicLong count = new AtomicLong(0);
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count.get());
    }
    private static void add() {
        count.incrementAndGet();
        // count.getAndIncrement();
    }
}

履行成果:

14:59:38.978 [main] INFO com.mmall.concurrency.example.atomic.AtomicLongExample - count:5000

终究成果是 5000标明是线程安全的。

3.1.3、AtomicBoolean

AtomicBoolean位于java.util.concurrent.atomic包下,是java供给给的能够确保数据的原子性操作的一个类

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
@Slf4j
public class AtomicBooleanExample {
    private static AtomicBoolean isHappened = new AtomicBoolean(false);
    // 恳求总数
    public static int clientTotal = 5000;
    // 一起并发履行的线程数
    public static int threadTotal = 200;
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    test();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("isHappened:{}", isHappened.get());
    }
    private static void test() {
        if (isHappened.compareAndSet(false, true)) {
            log.info("execute");
        }
    }
}

回来成果:

15:04:54.954 [pool-1-thread-2] INFO com.mmall.concurrency.example.atomic.AtomicBooleanExample - execute
15:04:54.971 [main] INFO com.mmall.concurrency.example.atomic.AtomicBooleanExample - isHappened:true

这儿咱们发现log.info("execute");,在代码中只履行了一次,而且isHappened:true的值为true,这是为啥呢?

这儿是由于当程序第一次compareAndSet()的时分,使得isHappend变为了true,由于原子性的联系,没有其他线程进行搅扰,经过运用AtomicBoolean,咱们使某段代码只履行一次。

3.1.4、AtomicReference

AtomicReferenceAtomicInteger十分相似,不同之处就在于AtomicInteger是对整数的封装,底层选用的是compareAndSwapInt完结CAS,比较的是数值是否持平,而AtomicReference则对应一般的目标引证,底层运用的是compareAndSwapObject完结CAS,比较的是两个目标的地址是否持平。也便是它能够确保你在修正目标引证时的线程安全性。

多个线程之间的操作不管选用何种履行时序或替换办法,都要确保不变性条件不被损坏,要保持状况的一致性,就需求在单个原子操作中更新相关的状况变量。

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j
public class AtomicReferenceExample {
    private static AtomicReference<Integer> count = new AtomicReference<>(0);
    public static void main(String[] args) {
        count.compareAndSet(0, 2); 
        count.compareAndSet(0, 1);
        count.compareAndSet(1, 3); 
        count.compareAndSet(2, 4); 
        count.compareAndSet(3, 5); 
        log.info("count:{}", count.get());
    }
}

咱们觉得咱们输出的成果会是多少?

回来成果:

15:26:59.680 [main] INFO com.mmall.concurrency.example.atomic.AtomicReferenceExample - count:4

为什么是4呢? 首要咱们 要说的是public final boolean compareAndSet(V expect, V update) 这个办法,这个办法首要的效果是经过比对两个目标,然后更新为新的目标,这儿的比对两个目标,比对的办法不是equals而是==,意味着比对的是内存的中地址。

1、首要咱们创立count的初始化为0 2、在main办法中 count.compareAndSet(0, 2);,判别count为0时赋值为2 3、在count.compareAndSet(0, 1); count.compareAndSet(1, 3);判别count是否为1或许0,由于上一步咱们现已赋值为2了,所以判别不建立 4、在count.compareAndSet(2, 4);判别count是否为2,等式建立 5、最好输出成果为4

count.compareAndSet(0, 2); //count=0?赋值 2,判别建立,此刻count=0,更新后为2
count.compareAndSet(0, 1); //count=0?赋值 1,判别不建立,此刻count=2
count.compareAndSet(1, 3); //count=1?赋值 3,判别不建立,此刻count=2
count.compareAndSet(2, 4); //count=2?赋值 4,判别建立,此刻count=2,更新后count=4
count.compareAndSet(3, 5); //count=3?赋值 5,判别不建立,此刻count=4

所以咱们输出成果为:4

3.1.5、CAS中ABA问题的处理

CAS并非完美的,它会导致ABA问题,例如:当时内存的值一开端是A,被另外一个线程先改为B然后再改为A,那么当时线程拜访的时分发现是A,则认为它没有被其他线程拜访过。在某些场景下这样是存在错误风险的。比方在链表中。 怎么处理这个ABA问题呢,大多数状况下达观锁的完结都会经过引进一个版本号符号这个目标,每次修正版本号都会变话,比方运用时刻戳作为版本号,这样就能够很好的处理ABA问题。 在JDK中供给了AtomicStampedReference类来处理这个问题,这个类维护了一个int类型的符号stamp,每次更新数据的时分顺带更新一下stamp。

3.2、原子性 — synchronized

synchronized是一种同步锁,经过锁完结原子操作。 1、润饰代码块:大括号括起来的代码,效果于调用的目标 2、润饰办法:整个办法,效果于调用的目标 3、润饰静态办法:整个静态办法,效果于一切目标 4、润饰类:括号括起来的部分,效果于一切目标

3.3、原子性 — 对比
  • Atomic:竞赛剧烈时能保持常态,比Lock功能好, 只能同步一个值
  • synchronized:不行中止锁,合适竞赛不剧烈,可读性好的状况
  • Lock:可中止锁,多样化同步,竞赛剧烈时能保持常态

④线程安全性:可见性

简介:一个线程对主内存的修正能够及时被其他线程调查到

导致同享变量在线程间不行见的原因: 1.线程穿插履行 2.从头排序结合线程穿插履行 3.同享变量更新后的值没有在作业内存中与主内存间及时更新

4.1 可见性 — syncronized

JMM关于syncronized的两条规定:

  • 线程解锁前,有必要把同享变量的最新值改写到主内存中
  • 线程加锁时,将清空作业内存中同享变量的值,从而使得运用同享变量时需求从主内存中从头读取最新的值(留意:加锁与解锁是同一把锁) 由于syncronized能够确保原子性及可见性,变量只需被syncronized润饰,就能够定心的运用
4.2 可见性 — volatile

经过参加内存屏障制止重排序优化来完结可见性。 详细完结进程:

  • volatile变量写操作时,会在写操作后参加一条store屏障指令,将本地内存中的同享变量值改写到主内存
  • volatile变量读操作时,会在读操作前参加一条load屏障指令,从主内存中读取同享变量

volatile不能确保操作的原子性,也便是不能确保线程安全性, 假如需求运用volatile有必要满足以下两个条件:

  • 对变量的写操作不依靠与变量当时的值。
  • 该变量没有包含在具有其他变量的不变的式子中。

所以volatile润饰的变量合适作为状况符号量。

注:以下图片为资猜中获取,如有雷同,纯属巧合

12W字;2022最新Android11位大厂面试专题(一)

12W字;2022最新Android11位大厂面试专题(一)

示例:

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
public class VolatileExample {
    // 恳求总数
    public static int clientTotal = 5000;
    // 一起并发履行的线程数
    public static int threadTotal = 200;
    public static volatile int count = 0;
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count);
    }
    private static void add() {
        count++;
    }
}

回来成果:

16:12:01.404 [main] INFO com.mmall.concurrency.example.count.VolatileExample4 - count:4986

经过履行代码咱们能够发现,回来成果并不是咱们想看到的5000,阐明这个是线程不安全的类

首要是由于当咱们履行conut++时分红了三步: 1、取出当时内存count值,这时count值时最新的 2、+1操作 3、从头写回主存

例如:有两个线程一起在履行count++,两个内存都履行了第一步,比方当时count值为99,它们都读到了这个count值,然后两个线程别离履行了+1,并写回主存,这样就丢掉了一次+1的操作。

⑤线程安全性:有序性

  • 在JMM中,答应编译器和处理器对指令进行重排序,可是重排序进程不会影响到单线程程序的履行,却会影响到多线程并发履行的正确性。
  • 经过volatilesynchronizedlock确保有序性
5.1 happens-before准则
  • 程序次第规矩:一个线程内,依照代码次序,书写在前面的操作先行产生于书写在后面的操作
  • 确认规矩:一个unLock操作先行产生于后面临同一个锁的Lock()操作,也便是说只需先解锁才干对下面的线程进行加锁
  • volatile变量规矩:对一个变量的写操作先行产生于后面临这个变量的读操作
  • 传递规矩:假如操作A先行产生与操作B,而操作B先行产生于操作C,则操作A先行产生于操作C
  • 线程启动规矩Thread目标start()办法先行产生于此线程的每一个动作,一个线程只需履行了start()办法后才干做其他的操作
  • 线程终端规矩:对线程interrupt()办法的调用先行产生与被中止线程的代码检测到中止事情的产生(只需履行了interrupt()办法才干够检测到中止事情的产生)
  • 线程完结规矩:线程中一切操作都先行产生于线程的停止检测,咱们能够经过Thread.join()办法结束,Thread.isAlive()的回来值手段检测到线程现已停止履行
  • 目标完结规矩:一个目标的初始化完结先行产生于他的finalize()办法的开端

⑥线程安全性:总结

  • 原子性Atomic包、CAS算法、synchronizedLock 原子性做了互斥办法,同一个线程只能有一个进行操作`
  • 可见性synchronizedvolatile 一个主内存的线程假如进行了修正,能够及时被其他线程调查到,介绍了volatile怎么被调查到的`
  • 有序性happens-before准则 happens-before准则,调查成果,假如两个线程不能偶从happens-before准则调查出来,那么就不能调查他们的有序性,虚拟机能够随意的对他们进行重排序`

7.讲一下两层查看锁中的 volatile 要害字的效果

volatile要害字 的效果

  • volatile 变量会运用内存屏障来避免指令重排。
  • 修正 volatile 变量时会强制将修正后的数据改写到主内存中。
  • volatile 变量修正后,其他线程引证到的该值将会失效,再次运用此变量时,需求到主内存从头获取。

②new 并不是一个原子操作,可分为三步:

  • 分配内存空间
  • 调用结构器办法,履行初始化
  • 将内存地址的引证赋值给变量。其间2、3在指令重排是颠倒次序为 1>3>2

③总结

在没有 volatile 要害字润饰的状况下; 当 线程A 第一次获取锁开端 new 目标时,产生了 指令重排; cup履行了 new 的 第三步 变量获取了地址的引证,即变量不是 null; 此刻 线程B 刚好进入办法开端履行 DCL 的第一个判别,发现变量不是空便开端拜访此变量; 但变量内部并未进行初始化操作。或许导致程序报错。 这也就解释了书中的注释:volatile 确保当 uniqueInstance变量 被初始化成实例时,多个线程能够正确的处理该 uniqueInstance变量

8.讲一下jvm的仓库

①概述

JAVA在程序运行时,在内存中区分5片空间进行数据的存储。别离是:

1:寄存器

2:本地办法区

3:办法区

4:栈

5:堆。

能够把堆了解为一家餐厅,里边有200张桌子,也便是说最多包容200桌客人就餐,来一批客人就为他们组织一些桌子,假如某天来的客人特别多,超过200桌了,哪就不能在招待超出的客人了。

当然,进来吃饭的客人不能是一起的,有的早有的晚,先吃好的客人,老板会组织给他们结账走人,然后空出来的桌子又能招待新的客人。

这儿,堆便是餐桌,最大容量200桌便是堆内存的巨细,老板就相当于GC(废物收回)给客人组织桌子就相当于Java创立目标的时分分配堆内存,结账就相当于GC收回目标占用的空间。

接着把栈比作一座废井,这口井多年不用现已没水了,主人现在把它作为存储酿酒的当地,存酒的时分就用绳子勾着酒坛子慢慢放下去,后面在存酒就一坛一坛的堆着放上去,取酒的时分就先取最上面的坛子。

②堆内存

什么是堆内存?

堆内存是Java内存中的一种,它的效果是用于存储Java中的目标和数组,当咱们new一个目标或许创立一个数组的时分,就会在堆内存中开辟一段空间给它,用于存放。

堆内存的特色是什么?

  • 堆起始能够相似的看做是管道,或许是平常去排队买票的状况差不多,所以堆内存的特色便是:先进先出,后进后出,也便是你先排队好,你先买票
  • 堆能够动态地分配内存巨细,生计期也不必事先告诉编译器,由于它是在运行时动态分配内存的,但缺点是,由于要在运行时动态分配内存,存取速度较慢。

new目标在堆中怎么分配

由Java虚拟机的自动废物收回器来管理

③栈内存

什么是栈内存?

栈内存是Java的另一种内存,首要是用来履行程序的,比方:根本类型的变量和目标的引证变量栈内存的特色

  • 栈内存就好像是一个矿泉水瓶,往里边放东西,那么先放入的沉入底部,所以它的特色是:先进后出,后进先出
  • 存取速度比堆要快,仅次于寄存器,栈数据能够同享,但缺点是,存在栈中的数据巨细与生计有必要是确认的,缺乏灵活性。

栈内存分配机制:

栈内存能够称为一级缓存,由废物收回器自动收回。

数据同享:

比方:

  int a = 3;
  int b = 3;

④栈和堆的差异

JVM是依据仓库的虚拟机,JVM为新创立的线程都分配一个仓库,也便是说,关于一个Java程序来说,它的运行便是经过对仓库的操作来完结的。仓库以帧为单位保存线程的状况。JVM对仓库只进行两种操作:以帧为单位的压栈和出栈操作。

差异:

  • 堆内存用来存放有new创立的目标和数组
  • 栈内存用来存放办法或许局部变量等
  • 堆是先进先出,后进后出
  • 栈是先进后出,后进先出
  • 同享性不同(栈内存是线程私有的,堆内存是一切线程共有的)

9.二叉树的层次遍历

本文首要介绍了二叉树的按层遍历。而且别离用如下三种办法完结:

  • 哈希表结合LinkedList
  • 运用系统自带的LinkedList
  • 自界说行列

以上办法仅仅空间复杂度有所差异,时刻复杂度上都是相同的。

①示例二叉树

12W字;2022最新Android11位大厂面试专题(一)

这个二叉树按层次遍历的成果便是 1->2->3->4->5->6->7->8->9->10->11->12->13

②数据结构

public static class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {
    }
    TreeNode(int val) {
        this.val = val;
    }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

③流程

整个进程最中心的当地便是需求记录当时层什么时分遍历结束以及当时弹出的节点在第几层。

办法1中,运用哈希表来存每个节点当时地点的层,头节点默许在第0层,且将头节点首要入行列,然后在弹出的进程中,将弹出节点的子节点放入哈希表,且把层数设置为当时节点的层数+1,一起把子节点放入行列,然后进行相同的行列弹出操作,直到行列空。

办法1的完好代码如下

  public static List<List<Integer>> levelOrder(TreeNode head) {
        if (head == null) {
            return new ArrayList<>();
        }
        List<List<Integer>> ans = new ArrayList<>();
        // 记录某个节点在第几层
        Map<TreeNode, Integer> map = new HashMap<>();
        Queue<TreeNode> queue = new LinkedList<>();
        // 当时是第几层
        int curLevel = 0;
        TreeNode cur = head;
        queue.offer(cur);
        map.put(cur, curLevel);
        List<Integer> levelRecords = new ArrayList<>();
        while (!queue.isEmpty()) {
            TreeNode c = queue.poll();
            int level = map.get(c);
            if (c.left != null) {
                queue.offer(c.left);
                map.put(c.left, level + 1);
            }
            if (c.right != null) {
                queue.offer(c.right);
                map.put(c.right, level + 1);
            }
            if (curLevel == level) {
                levelRecords.add(c.val);
            } else {
                ans.add(levelRecords);
                levelRecords = new ArrayList<>();
                levelRecords.add(c.val);
                curLevel = level;
            }
        }
        // 记得要存终究一层的数据
        ans.add(levelRecords);
        return ans;
    }

办法2省掉了一个哈希表,运用了两个变量来判别层数的改动,别离是:

// 遍历到的当时层的终究一个方位
TreeNode curEnd; 
// 下一层的终究一个方位
TreeNode nextEnd;

在行列每次弹出元素的时分,设置nextEnd变量,一起,假如弹出的元素等于curEnd,阐明现已到当时层的结尾了,就能够收集这一层的答案了。

办法2的完好代码如下

public static List<List<Integer>> levelOrder2(TreeNode head) {
        if (head == null) {
            return new ArrayList<>();
        }
        List<List<Integer>> ans = new ArrayList<>();
        List<Integer> levelRecords = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        TreeNode curEnd = head;
        TreeNode nextEnd = null;
        queue.offer(curEnd);
        while (!queue.isEmpty()) {
            TreeNode c = queue.poll();
            levelRecords.add(c.val);
            if (c.left != null) {
                queue.offer(c.left);
                // 弹出的时分,设置nextEnd
                nextEnd = c.left;
            }
            if (c.right != null) {
                queue.offer(c.right);
               // 弹出的时分,设置nextEnd
                nextEnd = c.right;
            }
            if (c == curEnd) {
                // 行将要来到新的一层了
                curEnd = nextEnd;
                ans.add(levelRecords);
                levelRecords = new ArrayList<>();
            }
        }
        return ans;
    }

办法3仅仅把办法2中的链表和行列换成自己完结的链表和行列结构,大思路上和办法2相同,咱们能够自己完结一个链表和行列,完结最简略的poll和offer办法即可,自界说的链表如下:

  // 自界说链表
  public static class MyNode {
        public TreeNode data;
        public MyNode next;
        public MyNode(TreeNode node) {
            data = node;
        }
    }
    // 自界说行列
    public static class MyQueue {
        public MyNode front;
        public MyNode end;
        public int size;
        public MyQueue() {
            front = null;
            end = null;
        }
        public void offer(MyNode c) {
            size++;
            if (front == null) {
                front = c;
            } else {
                end.next = c;
            }
            end = c;
        }
        public boolean isEmpty() {
            return size == 0;
        }
        public MyNode poll() {
            size--;
            MyNode ans = front;
            front = front.next;
            ans.next = null;
            return ans;
        }
    }

然后把办法2中的Java自带的LinkedList换成咱们自己完结的链表和行列,完好代码如下

    public static List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> ans = new ArrayList<>();
        if (root == null) {
            return ans;
        }
        MyNode head = new MyNode(root);
        MyQueue queue = new MyQueue();
        queue.offer(head);
        MyNode curEnd = head;
        MyNode nextEnd = null;
        List<Integer> item = new ArrayList<>();
        MyNode t;
        while (!queue.isEmpty()) {
            MyNode c = queue.poll();
            if (c.data.left != null) {
                t = new MyNode(c.data.left);
                queue.offer(t);
                nextEnd = t;
            }
            if (c.data.right != null) {
                t = new MyNode(c.data.right);
                queue.offer(t);
                nextEnd = t;
            }
            item.add(c.data.val);
            if (curEnd.data == c.data) {
                ans.add(item);
                item = new ArrayList<>();
                curEnd = nextEnd;
            }
        }
        return ans;
    }

10.自界说Navigator切换fragment

①运用场景:

运用Navigation完结fragment间的跳转操作

②问题描述:

Navigation 完结 fragment 间的跳转用的是replace()办法,此办法会移除本来的fragment,再增加新的fragment,所以回到上一个fragment时就需求从头走一遍生命周期流程,从头加载数据。

③处理方案:

剖析 NavController类 中的navigate 源码

private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
        ...
        Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
                node.getNavigatorName());  依据节点称号生成不同的navigator
        Bundle finalArgs = node.addInDefaultArgs(args);
        NavDestination newDest = navigator.navigate(node, finalArgs,
                navOptions, navigatorExtras);  调用navigator 中的 navigate办法
       ...
    }

getNavigator 源码如下

private final HashMap<String, Navigator<? extends NavDestination>> mNavigators =
        new HashMap<>();
public <T extends Navigator<?>> T getNavigator(@NonNull String name) {
    if (!validateName(name)) {
        throw new IllegalArgumentException("navigator name cannot be an empty string");
    }
    Navigator<? extends NavDestination> navigator = mNavigators.get(name); // 依据传入的node获取不同的navigator
    if (navigator == null) {
        throw new IllegalStateException("Could not find Navigator with name \"" + name
                + "\". You must call NavController.addNavigator() for each navigation type.");
    }
    return (T) navigator;
}

因此,想要调用自界说navigate()办法就需求 自界说一个Navigator类,一起改动fragment节点称号,将fragment节点称号与Navigator类作为key,value 增加到 HashMap类型的mNavigators中。

④自界说Navigator

@Navigator.Name("custom_fragment")  // 节点称号界说为custom_fragment,作为 mNavigatorProvider 变量的 key
class CustomNavigator(  //作为 mNavigatorProvider 变量的 value
    private val context: Context,
    private val manager: FragmentManager,
    private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {
    override fun navigate(  // 重写 navigate 办法
        destination: Destination,
        args: Bundle?,
        navOptions: NavOptions?,
        navigatorExtras: Navigator.Extras?
    ): NavDestination? {
        val tag = destination.id.toString() // 跳转意图地
        val transaction = manager.beginTransaction() // 开启fragment业务
        val currentFragment = manager.primaryNavigationFragment // navigation 顶层fragment
        if (currentFragment != null) {
            transaction.hide(currentFragment)   // 躲藏当时fragment
        }
        var fragment = manager.findFragmentByTag(tag)   // 找到意图地fragment
        if (fragment == null) { // fragment未被初始化
            val className = destination.className
            fragment = manager.fragmentFactory.instantiate(context.classLoader, className) // 实例化 fragment
            transaction.add(containerId, fragment, tag) // 将碎片增加到容器中
        } else { // fragment 现已初始化过了
            transaction.show(fragment) // 显现fragment
        }
        transaction.setPrimaryNavigationFragment(fragment) //将fragment 设置为顶层fragment
        transaction.setReorderingAllowed(true)
        transaction.commitNow() // 提交业务
        return destination // 回来意图地,用于监听
    }
}

⑤修正节点称号

由于节点称号界说为custom_fragment,所以修正为<custom_fragment>

<navigation 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/my_nav"
    app:startDestination="@id/home_dest"
    tools:ignore="UnusedNavigation">
    <custom_fragment
        android:id="@+id/home_dest"
        android:name="com.cl.androidstudy.ui.home.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" />
    <custom_fragment
        android:id="@+id/system_dest"
        android:name="com.cl.navicationtest.SystemFragment"
        android:label="fragment_system"
        tools:layout="@layout/fragment_system" />
    <custom_fragment
        android:id="@+id/square_dest"
        android:name="com.cl.navicationtest.SquareFragment"
        android:label="fragment_square"
        tools:layout="@layout/fragment_square" />
    <custom_fragment
        android:id="@+id/me_dest"
        android:name="com.cl.androidstudy.ui.me.MeFragment"
        android:label="fragment_me"
        tools:layout="@layout/fragment_me" />
</navigation>

⑥逻辑代码

val navController = Navigation.findNavController(this, R.id.fragment) // 创立navController
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment)!!
val navigator = CustomNavigator(
    this,
    navHostFragment.childFragmentManager,
    R.id.fragment
)// 生成自界说Navigator目标
navController.navigatorProvider.addNavigator("custom_fragment", navigator) // 增加 key, value
navController.setGraph(R.navigation.my_nav)  // 要在 CustomNavigator 类被加载之后增加graph,否则找不到 custom_fragment节点

11.Navigation运用和源码剖析

12.volatile的效果

一共50W字的文档,面试专题12W字仅仅一小部分,字数约束,分几篇更。

关注大众号:Android苦做舟

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

一共包含

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

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

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

4.Jetpack全家桶

5.Android 功能监控结构Matrix

6.JVM

7.车载运用开发