La colección más completa de las últimas preguntas de la entrevista de Android en 2022 en la historia (incluidas las respuestas y el código fuente)

inserte la descripción de la imagen aquí
Oro tres plata cuatro, es la temporada dorada anual de saltos de trabajo nuevamente, presumiblemente puede haber muchos programadores de Android comenzando a prepararse, a la vuelta de la esquina. Sin embargo, la mayoría de las preguntas de la entrevista en Internet están desactualizadas o incluso llenas de errores, llenas de lagunas. Hoy, combiné mi experiencia de entrevista durante este período de tiempo y los intercambios entre los principales fabricantes y expertos en varios grupos de tecnología Android para resumir el últimas preguntas de la entrevista de 2022. Una colección de preguntas de la entrevista de Android. Entonces, sin más preámbulos, vayamos directo al grano.

prefacio

Lo primero que hay que declarar es: el propósito de las preguntas de la entrevista no es dejar que todos reciten las preguntas, sino ayudar a que todos revisen desde diferentes dimensiones y aprendan unos de otros. Vamos a ir al grano:

Resumen de las preguntas de la entrevista.

1. Cuatro componentes principales de Android: Actividad, Servicio, BroadcastReceiver, ContentProvider. Sus funciones son respectivamente:

Actividad—>Cooperar con View para mostrar la interfaz
Servicio—>Ejecutar en segundo plano durante mucho tiempo sin interactuar directamente con los usuarios
BroadcastReceiver—>Recibir transmisión
ContentProvider—>Proporcionar datos para que los usen otros módulos

2. ¿Cuántos modos de lanzamiento tiene Actividad? ¿Cuáles son las características de cada uno?

1. Estándar (el modo de inicio predeterminado)
Cuando enviamos una intención para iniciar la actividad, la actividad siempre se crea como una nueva instancia.
2. singleTop (multiplexación superior de la pila)
Si desea iniciar una Actividad que existe en la pila de tareas y está en la parte superior de la pila de tareas, no se creará una nueva instancia de Actividad, pero el método onNewIntent() de la Actividad sí lo hará. ser llamado para evitar la parte superior de la pila La actividad se crea repetidamente. Si ya existe una instancia de la actividad llamada pero no está en la parte superior de la pila, la actividad llamada se seguirá creando.
3. SingleTask (multiplexación en pila)
La actividad solo tendrá una instancia en la pila de tareas. Si la Actividad que se iniciará ya existe en la Tarea de la pila de tareas, no se creará una nueva instancia de Actividad, pero se reutilizará la Actividad existente, se llamará al método onNewIntent() y todas las demás actividades por encima de la pila de tareas de Actividad ser borrado
4.singleInstance (modo de instancia única)
La actividad con este modo solo se puede ubicar en una sola pila de tareas y no puede tener otras actividades. Cualquier otra actividad iniciada desde esta actividad se colocará en otras pilas de tareas.

3. ¿Cuántos tipos de Servicio hay? ¿Cuáles son los escenarios de aplicación para cada uno?

método método de inicio manera de parar Métodos para comunicarse con componentes iniciados ciclo vital
Comienza el servicio Después de llamar al método startService() en otros componentes, el servicio está en estado iniciado Después de llamar al método stopSelf() en el servicio, o de que otros componentes llamen al método stopService(), el servicio dejará de ejecutarse. No se proporciona ningún método de comunicación predeterminado y el servicio se ejecuta de forma independiente después de iniciar el servicio Una vez iniciado, el servicio puede ejecutarse indefinidamente en segundo plano, incluso si el componente que inició el servicio ha sido destruido, no se verá afectado hasta que se detenga.
enlazarServicio Después de llamar al método bindService() en otros componentes, el servicio está en estado iniciado Después de que todos los componentes vinculados al servicio se destruyan, o todos llamen al método unbindService(), el servicio dejará de ejecutarse. Puede comunicarse a través de ServiceConnection, los componentes pueden interactuar con el servicio, enviar solicitudes, obtener resultados e incluso usar IPC para realizar estas operaciones en todos los procesos. Cuando todos los componentes vinculados a él no estén vinculados (tal vez el componente se destruya o puede llamar al método unbindService()), el servicio se detendrá

4. ¿Cuál es el escenario de aplicación de LaunchMode?

Hay cuatro tipos de LaunchMode, a saber, Standard, SingleTop, SingleTask y SingleInstance. El principio de implementación de cada modo se describe en detalle en el primer piso. Los escenarios de uso específicos se describen a continuación:

  • Estándar: El
    modo estándar es el modo de inicio predeterminado del sistema. Generalmente, la mayoría de las páginas de nuestra aplicación están compuestas por páginas de este modo. El escenario más común es: en aplicaciones sociales, haga clic para ver el usuario Información->ver usuario Ventiladores A- >Seleccione y vea la información del usuario B de los ventiladores -> vea los ventiladores del usuario A... En este caso, generalmente necesitamos mantener todas las secuencias de ejecución de las páginas de la pila de actividad de operación del usuario.
  • SingleTop:
    el modo SingleTop se usa comúnmente en los comportamientos de la barra de notificaciones en las aplicaciones sociales. Por ejemplo, cuando un usuario de la aplicación recibe varios mensajes de inserción solicitados por amigos, el usuario debe hacer clic en la notificación de inserción para ingresar a la página de información personal del solicitante. y establezca la página de información en el modo SingleTop. Se puede mejorar la reutilización.
  • SingleTask:
    el modo SingleTask generalmente se usa como la página de inicio de la aplicación, como la página de inicio del navegador. El usuario puede iniciar el navegador desde varias aplicaciones, pero la interfaz principal solo se inicia una vez. En otros casos, se usará onNewIntent. , y se borrarán otras páginas de la interfaz principal.
  • SingleInstance:
    el modo SingleInstance se usa a menudo en aplicaciones con operaciones de pila independientes, como la página de recordatorio del despertador. Cuando ve un video en la aplicación A, suena el despertador. Hace clic en la notificación de alarma e ingresa a la página de detalles del recordatorio. y luego haga clic en Atrás para volver a la página. Vaya a la página de video de A, para que no interfiera demasiado con las operaciones anteriores del usuario.

5. Servicio de fondo y primer plano

Esto implica la clasificación de Servicio.
Si se clasifica por si es insensible o no, el Servicio se puede dividir en primer plano y segundo plano. El Servicio de primer plano permitirá que el usuario perciba que tal cosa se está ejecutando en segundo plano a través de una notificación.
Por ejemplo, las aplicaciones de música, mientras reproducen música en segundo plano, pueden encontrar que siempre se muestra una notificación en primer plano, lo que permite a los usuarios saber que existe un servicio relacionado con la música en segundo plano.
En Android 8.0, Google requiere que si el programa está en segundo plano, no se puede crear el servicio en segundo plano y el servicio en segundo plano que se inició se detendrá después de un cierto período de tiempo.
Por lo tanto, se recomienda utilizar el Servicio de primer plano, que tiene una prioridad más alta y no es fácil de destruir. El método de uso es el siguiente:

startForegroundService(intent);
    public void onCreate() {
        super.onCreate();
        Notification notification = new Notification.Builder(this)
                .setChannelId(CHANNEL_ID)
                .setContentTitle("主服务")//标题
                .setContentText("运行中...")//内容
                .setSmallIcon(R.mipmap.ic_launcher)
                .build();
        startForeground(1,notification);
    }  

    <!--android 9.0上使用前台服务,需要添加权限-->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

¿Qué pasa con las tareas en segundo plano? La recomendación oficial es usar JobScheduler.
6. Hable acerca de
la programación de tareas de JobScheduler Se lanzó JobScheduler, Android5.0. (Algunos amigos pueden no estar familiarizados, de hecho, también se implementa a través del Servicio, que se discutirá más adelante) Lo que
puede hacer es realizar la ejecución automática de tareas según los requisitos especificados. Por ejemplo, se ejecutará automáticamente en segundo plano en diversas circunstancias, como la hora especificada, cuando la red sea Wi-Fi, cuando el dispositivo esté inactivo y cuando se esté cargando.
Así que Google permitió que reemplazara algunas de las funciones del Servicio en segundo plano, casos de uso:

  • Primero, crea un JobService:
public class MyJobService extends JobService {

    @Override
    public boolean onStartJob(JobParameters params) {
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}
  • Luego, registre este servicio (porque JobService también es un Servicio)
<service android:name=".MyJobService"
    android:permission="android.permission.BIND_JOB_SERVICE" />
  • Finalmente, crea un JobInfo y ejecuta
JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);  
 ComponentName jobService = new ComponentName(this, MyJobService.class);

 JobInfo jobInfo = new JobInfo.Builder(ID, jobService) 
         .setMinimumLatency(5000)// 任务最少延迟时间 
         .setOverrideDeadline(60000)// 任务deadline,当到期没达到指定条件也会开始执行 
         .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)// 网络条件,默认值NETWORK_TYPE_NONE
         .setRequiresCharging(true)// 是否充电 
         .setRequiresDeviceIdle(false)// 设备是否空闲
         .setPersisted(true) //设备重启后是否继续执行
         .setBackoffCriteria(3000,JobInfo.BACKOFF_POLICY_LINEAR) //设置退避/重试策略
         .build();  
 scheduler.schedule(jobInfo);

Simplemente ponga el principio:

JobSchedulerService es un servicio que se inicia en SystemServer y luego atraviesa las tareas inconclusas, encuentra el JobService correspondiente a través de Binder, ejecuta el método onStartJob y completa la tarea. Para más detalles, véase el análisis del enlace de referencia.
Entonces, sé que después de 5.0, si necesita ejecutar tareas en segundo plano, especialmente tareas que deben cumplir ciertas condiciones, como la alimentación de la red, etc., puede usar JobScheduler.
Algunas personas pueden preguntar, ¿qué debo hacer antes de 5.0?
Puede usar GcmNetworkManager o BroadcastReceiver para manejar los requisitos de la tarea en algunos casos.
Google también tuvo esto en cuenta, por lo que combinó JobScheduler después de 5.0 con GcmNetworkManager, GcmNetworkManager, AlarmManager antes de 5.0 y API relacionadas con tareas para diseñar WorkManager.
7. Hable sobre WorkManager

WorkManager es una API que le permite programar fácilmente tareas asincrónicas diferibles que deberían ejecutarse incluso después de cerrar la aplicación o reiniciar el dispositivo.

Como miembro de Jetpack, no es muy nuevo. Su esencia es combinar las API existentes relacionadas con la programación de tareas y luego ejecutar estas tareas de acuerdo con los requisitos de la versión. Hay una imagen en el sitio web oficial: Entonces, ¿qué puede hacer WorkManager?

? ?

1. Puede funcionar bien para algunas restricciones de tareas, como tareas que deben realizarse en condiciones como la red, el estado inactivo del dispositivo y suficiente espacio de almacenamiento.
2. Puede realizar tareas de forma repetida, única y estable. Incluyendo la capacidad de continuar la tarea después de reiniciar el dispositivo.
3. Puede definir la relación cohesiva de diferentes tareas de trabajo. Por ejemplo, establezca una tarea tras otra.
En resumen, es una gran herramienta para realizar tareas en segundo plano.

8. Hable sobre la diferencia entre startService y bindService, el ciclo de vida y los escenarios de uso.

  1. diferencia en el ciclo de vida

Al ejecutar startService, Service experimentará onCreate->onStartCommand. Al ejecutar stopService, llama directamente al método onDestroy. Si la persona que llama no detiene el Servicio, el Servicio siempre se ejecutará en segundo plano, y la próxima vez que la persona que llama se despierte, aún puede detener el Servicio.

Al ejecutar bindService, el servicio experimentará onCreate->onBind. En este momento, la persona que llama está vinculada al Servicio. Cuando la persona que llama llama al método unbindService o el Contexto de la persona que llama no existe (por ejemplo, si la Actividad ha finalizado), el Servicio llamará a onUnbind->onDestroy. La llamada unión aquí significa que los dos coexisten y mueren.

Si se llama a startService varias veces, el Servicio solo se puede crear una vez, es decir, el método onCreate del Servicio solo se llamará una vez. Pero cada vez que se llama a startService, se llamará al método onStartCommand. El método onStart de Servicio quedó obsoleto en la API 5 y fue reemplazado por el método onStartCommand.

Cuando se ejecuta bindService por primera vez, se llamará a los métodos onCreate y onBind, pero cuando se ejecuta bindService varias veces, los métodos onCreate y onBind no se llamarán varias veces, es decir, el servicio no se creará y atado varias veces.

  1. ¿Cómo obtiene la persona que llama el método del servicio enlazado?

El método de devolución de llamada onBind devolverá una instancia de la interfaz IBinder al cliente.IBinder le permite al cliente devolver la llamada al método del servicio, como obtener el estado de ejecución del Servicio u otras operaciones. Necesitamos que el objeto IBinder devuelva un objeto de servicio específico para operar, por lo que el objeto de servicio específico primero debe implementar el objeto Binder.

  1. ¿Qué pasa con el uso de startService y bindService?
    Si un Servicio se inicia y vincula nuevamente, el Servicio siempre se ejecutará en segundo plano. En primer lugar, no importa cómo se llame, onCreate siempre se llamará una sola vez. Correspondiente a cuántas veces se llama a startService, cuántas veces se llamará al método onStart del Servicio. La terminación del Servicio debe llamar a unbindService y stopService al mismo tiempo. Independientemente del orden de llamada de startService y bindService, si llama primero a unbindService, el servicio no terminará automáticamente en este momento, y luego se llamará a stopService antes de que termine el servicio; si se llama primero a stopService, el servicio no terminará en este momento, y llame a unbindService o antes. El servicio se detendrá automáticamente después de que el Contexto que llama a bindService ya no exista (como cuando finaliza la Actividad).

Entonces, ¿bajo qué circunstancias usa startService y bindService?

Si solo desea iniciar un servicio en segundo plano para una tarea a largo plazo, utilice startService. Si aún desea ponerse en contacto con el Servicio en ejecución, hay dos formas: una es usar la transmisión y la otra es usar bindService. La desventaja del primero es que si la comunicación es más frecuente, es fácil causar problemas de rendimiento, mientras que el segundo no tiene estos problemas. Por lo tanto, en este caso, startService y bindService deben usarse juntos.

Además, si su servicio solo expone una interfaz remota para el cliente conectado (el servicio de Android es una arquitectura C/S) para llamar de forma remota al método de ejecución, en este momento no puede dejar que el servicio se ejecute al principio, sino solo enlazar el servicio, de modo que solo cuando se une a Service por primera vez, se creará una instancia del servicio para ejecutarlo, lo que ahorrará una gran cantidad de recursos del sistema, especialmente si su servicio es un servicio remoto, entonces el efecto será más obvio (por supuesto , tomará una cierta cantidad de tiempo crearlo en Tiempo de servicio, esto requiere atención).

  1. Servicio local y servicio remoto

Los servicios locales están adjuntos al proceso principal, lo que ahorra recursos hasta cierto punto. Debido a que el servicio local está en el mismo proceso, no necesita IPC ni AIDL. El bindService correspondiente será mucho más conveniente. La desventaja es que después de que se elimine el proceso principal, el servicio finalizará.

El servicio remoto es un proceso independiente y el formato del nombre del proceso correspondiente es el nombre del paquete más la cadena android:process que especificó. Dado que es un proceso independiente, incluso si se elimina el proceso donde se encuentra la Actividad, el servicio aún se está ejecutando. La desventaja es que el servicio es un proceso independiente, que ocupará ciertos recursos, y usar AIDL para realizar IPC es un poco problemático.

Para startService, ya sea un servicio local o un servicio remoto, el trabajo que debemos hacer es igualmente simple.

9. ¿Cómo se mantiene vivo el Servicio?

Utilice la difusión del sistema para extraer la actividad.
Utilice el servicio del sistema
para extraer la actividad. Utilice el proceso nativo
para extraer la actividad.
use el mecanismo de sincronización de cuentas para extraer actividad

10. ¿Cuál es el motivo por el que el subproceso actualizó la interfaz de usuario y provocó el bloqueo?
En el método de dibujo del disparador requestLayout, hay un método checkThread:

void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

Entre ellos, mThread se compara con el hilo actual. Y mThread se asigna cuando se crea una instancia de ViewRootImpl.

Entonces, el motivo del bloqueo es que el subproceso cuando la vista se dibuja en la interfaz (es decir, el subproceso cuando se crea ViewRootImpl) y el subproceso cuando se actualiza la interfaz de usuario no son el mismo subproceso.

11. La relación entre Activity, Dialog, PopupWindow, Toast y Window.
Esta es una pregunta extendida, permítanme hablar brevemente sobre ella desde la perspectiva del método de creación:

Actividad. La PhoneWindow creada durante el proceso de creación de la actividad es la ventana jerárquica más pequeña, llamada ventana de la aplicación, con un rango jerárquico de 1 a 99. (Una ventana con una jerarquía grande puede cubrir una ventana con una jerarquía pequeña)
Diálogo. El proceso de visualización de Diálogo es básicamente el mismo que el de Actividad. También crea PhoneWindow, inicializa DecorView y agrega la vista de Diálogo a DecorView, y finalmente lo muestra a través de addView.
Pero una diferencia es que la ventana de diálogo no es una ventana de aplicación, sino una ventana secundaria, con un rango jerárquico de 1000 a 1999. La visualización de la ventana secundaria debe adjuntarse a la ventana de la aplicación y también cubrir la aplicación. ventana de nivel. Esta es la razón por la que el contexto pasado por el Diálogo debe ser el Contexto de la Actividad.

Ventana emergente. La visualización de PopupWindow es diferente. En lugar de crear PhoneWindow, crea directamente una Vista (PopupDecorView) y luego la muestra a través del método addView de WindowManager.
Si no se crea PhoneWindow, ¿no tiene nada que ver con Window?

No, de hecho, siempre que se llame al método addView de WindowManager, se crea una ventana, independientemente de si ha creado una PhoneWindow o no. View es la manifestación de Window, pero la existencia de PhoneWindow hace que la imagen de Window sea más tridimensional.

Por lo tanto, PopupWindow también se muestra a través de la ventana y su nivel de ventana pertenece a la subventana, que debe adjuntarse a la ventana de la aplicación.

Tostada. Toast es similar a PopupWindow, no hay PhoneWindow nuevo y la Vista se puede mostrar directamente a través del método addView. La diferencia es que pertenece a la Ventana de nivel del sistema y el rango de nivel es 2000-2999, por lo que no necesita adjuntarse a la Actividad.
Al comparar los cuatro, se puede encontrar que siempre que desee mostrar Ver, el método addView de WindowManager estará involucrado, y se usará el concepto de Ventana, y luego se mostrará y se superpondrá en la interfaz de acuerdo con diferentes capas.

La diferencia es que Actividad y Diálogo involucran diseños y elementos más complejos, como temas de diseño, por lo que PhoneWindow se usa para desacoplar para ayudarlos a administrar Ver. La estructura de PopupWindow y Toast es relativamente simple, así que cree directamente una vista similar a DecorView y muéstrela en la interfaz a través de addView.

12. ¿Qué son las funciones de extensión en Kotlin?

La función de extensión en Kotlin en realidad agrega un método final estático a su propia clase, y la clase que debe extenderse se pasa al método como el primer parámetro cuando se usa y luego se usa:

fun String.hello(str: String): String{
    return "hello" + str + this.length
}
 
fun String.hello1(str: String): String{
    return "hello$str"
}

Después de la descompilación, el bytecode generado es el siguiente:

public final class ExtensionTestKt {
   @NotNull
   public static final String hello(@NotNull String $receiver, @NotNull String str) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      Intrinsics.checkParameterIsNotNull(str, "str");
      return "hello" + str + $receiver.length();
   }
 
   @NotNull
   public static final String hello1(@NotNull String $receiver, @NotNull String str) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      Intrinsics.checkParameterIsNotNull(str, "str");
      return "hello" + str;
   }
}

La función de extensión en kotlin en realidad se implementa agregando una función final estática pública a la clase, lo que puede reducir el uso de clases utils.

Las funciones de extensión se resuelven estáticamente y se manejan mediante el proceso de despacho estático. Esto significa que la función de extensión llamada está determinada por el tipo de expresión en la que se llama a la función, no por el resultado de la evaluación del tiempo de ejecución de la expresión. Esto significa que al usar esta función de extensión, si la clase misma y sus subclases extienden la misma función, esta función no se reescribirá. Al usarla, solo se usará según sea necesario. El tipo real del objeto del método se utiliza para determinar cuál se llama, lo que equivale a llamar a un método estático. en lugar de envío dinámico.

Resumir

Estas preguntas diarias organizadas esta vez son algunas preguntas de programación, algunas de las cuales son problemas que podemos encontrar en nuestro desarrollo diario. Al hacer estas preguntas, también puede probar su dominio de estas habilidades prácticas de programación. Una pregunta todos los días, un poco de crecimiento todos los días.

Si sientes que tu eficiencia de aprendizaje es baja y te falta una orientación correcta, puedes consultar las siguientes rutas de aprendizaje que he recopilado y organizado a lo largo de los años de trabajo, como referencia para todos:

  1. Determinar la dirección y ordenar la hoja de ruta de crecimiento

mapas mentales

Después de ordenar el conocimiento, es necesario verificar si hay lagunas.

  1. Mire el video para el aprendizaje sistemático

Debido a que estoy en la industria del desarrollo móvil, estudié a ciegas en los últimos años. Como resultado, cuando el entrevistador me preguntó durante la entrevista, mi mente se quedó en blanco por un momento. Solo entonces descubrí que mi tecnología estaba demasiado fragmentada. , y no fui lo suficientemente profundo o sistemático acerca de Android. En ese momento, tomé una decisión: volver a realizar un estudio sistemático. Lo que me faltaba era conocimiento sistemático, y lo que me faltaba era marco estructural e ideas, entonces decidí seguir el video de un maestro para aprender sistemáticamente, que era más efectivo y más completo. Con respecto al aprendizaje en video, personalmente recomiendo que vaya a estudiar a la estación B. Hay muchos videos de aprendizaje en la estación B. La única desventaja es que, debido a que es gratuito, algunas tecnologías se desactualizan fácilmente y algunas relacionadas con los derechos de autor se eliminarán de los estantes.

  1. Aprende sistemáticamente a través del código fuente

Mientras sea un programador, ya sea Java o Android, si no lee el código fuente y solo mira la documentación de la API, permanecerá en la superficie, lo cual no es bueno para el establecimiento y finalización de nuestro sistema de conocimiento y la mejora de la tecnología de combate actual.

"El lenguaje de programación es la forma en que los programadores se expresan, y la arquitectura es la percepción del mundo por parte del programador". Por lo tanto, si los programadores quieren reconocer y aprender rápidamente la arquitectura, es fundamental leer el código fuente. Leer código fuente es resolver problemas + entender cosas, y más importante: ver la idea detrás del código fuente; los programadores dicen: lee miles de líneas de código fuente, practica miles de prácticas.

Lo que más puede ejercitar su habilidad es leer el código fuente directamente, no limitado a leer el código fuente de los principales sistemas, sino que también incluye varias bibliotecas de código abierto excelentes. Por supuesto, es esencial eliminarlo con sus propias manos, y eventualmente se sentirá superficial cuando lo tenga en el papel, y nunca sabrá que tiene que hacerlo usted mismo.

  1. El currículum está bien preparado.

Puede ir a Zhihu para buscar publicaciones sobre currículos escritos por otros, cómo prepararse, atraer recursos humanos, resaltar sus propias fortalezas y habilidades, o pedirle a un amigo que lo ayude a ver si hay algún problema con su currículo, como ser demasiado simple o exagerada, sin foco, etc.

Trate de resumir sus puntos destacados en una oración concisa, con números para ilustrar su impacto y significado.

En segundo lugar, agregue contenido interactivo y visualizable a su currículum, que pueda mostrar sus habilidades únicas.

  1. Cepilla las preguntas para prepararte para la batalla, directamente a la gran fábrica.

Dentro de una semana antes de la entrevista, puede comenzar a correr para las preguntas. Tenga en cuenta que al escribir preguntas, se da prioridad a la tecnología y los algoritmos son básicos, como la clasificación. Las preguntas intelectuales, a menos que sean reclutadas por la escuela, generalmente no se hacen mucho.

Finalmente, los amigos que lo necesiten pueden agregar el código QR a continuación para responder a JJ y recibirlo gratis, lo prometemos 100%免费
]

Supongo que te gusta

Origin blog.csdn.net/B1ueSocks/article/details/123556452
Recomendado
Clasificación