Android大厂面试通关秘籍

🔥Android大厂面试通关秘籍:深度剖析技术原理与源码

一、Android系统架构大揭秘

1.1 架构分层的奇妙世界

嘿,朋友们!想象一下Android系统就像一座宏伟的大厦,它有着清晰的分层结构,每一层都有着独特的使命。咱们先从最底层的Linux内核层说起,这就像是大厦的地基,稳如泰山地支撑着整个系统。

c 复制代码
// kernel/sched/sched.h
// 定义调度类,这可是进程调度的核心哦
struct sched_class {
    const struct sched_class *next;  // 指向下一个调度类的指针

    // 任务入队操作,就像把任务安排到队列里等待处理
    void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);
    // 任务出队操作,把处理好的任务从队列中移除
    void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags);
    // 选择下一个运行的任务,就像挑选下一个要表演的演员
    struct task_struct * (*pick_next_task) (struct rq *rq);
};

接着是系统运行库层,它就像是大厦里的各种工具间,存放着各种各样的工具(C/C++库和Android运行时),供上层使用。以SQLite这个常用的数据库库为例,它的代码就像一个精密的机器,每一行都有着重要的作用。

c 复制代码
// external/sqlite/sqlite3.c
// 打开数据库函数,就像打开一个神秘的宝箱
int sqlite3_open(
    const char *filename,   /* Database filename (UTF-8) */
    sqlite3 **ppDb          /* OUT: SQLite db handle */
){
    // 检查参数合法性,就像检查宝箱的钥匙是否正确
    if( ppDb==0 ) return SQLITE_ERROR;
    *ppDb = 0;
    // 调用内部打开数据库函数,真正去打开宝箱啦
    return sqlite3_open16((const void*)filename, ppDb);
}

再往上就是应用框架层,这可是开发者的大舞台,提供了丰富的API让我们可以尽情发挥。Activity的启动流程就像是一场精心编排的舞蹈,每一个动作都有它的意义。

java 复制代码
// frameworks/base/core/java/android/app/ActivityThread.java
// 处理Activity启动请求,就像导演安排一场表演的开场
public void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // 获取Activity实例,就像找到表演的主角
    Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        // 将Activity与窗口建立关联,就像给主角安排一个舞台
        r.createdConfig = new Configuration(mConfiguration);
        Bundle oldState = r.state;
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && r.startsNotResumed, r.isForward);
    }
}

最上层就是应用层啦,这就是我们平时在手机上看到的各种丰富多彩的应用程序,就像大厦里的一个个精彩的房间。

1.2 系统启动的惊险之旅

Android系统的启动就像是一场刺激的冒险之旅,从Bootloader阶段开始,就像是探险者踏上了未知的土地。

c 复制代码
// u-boot/arch/arm/cpu/armv7/start.S
.globl _start
_start:
    // 关闭看门狗,就像关掉一个随时可能触发的陷阱
    ldr r0, =0x40048000
    ldr r1, =0x0
    str r1, [r0]
    // 设置CPU模式,就像调整探险者的装备
    mrs r0, cpsr
    bic r0, r0, #0x1f
    orr r0, r0, #0x10
    msr cpsr, r0
    // 跳转到C语言初始化函数,开始正式的探险啦
    bl cpu_init_crit

接着进入Linux内核启动阶段,这就像是探险者在森林里搭建营地,初始化各种资源。

c 复制代码
// kernel/init/main.c
static int __init init_post(void)
{
    // 执行init进程,就像在营地中安排第一个任务
    if (execute_command) {
        run_init_process(execute_command);
        pr_info("Failed to execute %s.  Attempting defaults...\n",
                execute_command);
    }
    // 尝试执行默认的init程序,就像尝试不同的方法来完成任务
    if (!try_to_run_init_process("/sbin/init") ||
        !try_to_run_init_process("/etc/init") ||
        !try_to_run_init_process("/bin/init") ||
        !try_to_run_init_process("/bin/sh"))
        return 0;

    return 0;
}

然后是init进程启动,它就像营地的指挥官,解析init.rc脚本,安排各种任务,启动Zygote进程。

c 复制代码
// system/core/init/init.c
int main(int argc, char** argv) {
    // 解析init.rc脚本,就像指挥官解读作战计划
    if (!LoadBootScripts(uevent_context, &action_context, &service_list)) {
        ERROR("Error loading init.rc.\n");
    }
    // 启动服务,就像指挥官下达作战命令
    ActionForEach("early-init", action_context, NULL);
    ActionForEach("init", action_context, NULL);
    // 进入主循环,就像指挥官时刻关注着战场情况
    EventLoop(uevent_context, &action_context, &service_list);
    return 0;
}

最后是Zygote进程启动,它就像一个神奇的孵化器,预加载系统资源,为应用程序创建进程。

java 复制代码
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
    // 注册Zygote socket,就像打开一个通信的通道
    registerZygoteSocket(socketName);
    // 预加载类和资源,就像提前准备好各种工具
    preloadClasses();
    preloadResources();
    // 启动Zygote服务,就像孵化器开始工作啦
    if (enableLazyPreload) {
        ZygoteServer.registerNewZygoteSocket(socketName);
    }
    // 进入Zygote主循环,时刻准备为新的应用进程服务
    runSelectLoop(abiList);
}

二、四大组件的精彩表演

2.1 Activity的奇幻之旅

Activity就像是Android世界里的明星,有着自己独特的生命周期。它的生命周期方法调用流程就像是一场精彩的舞台剧,每一个环节都扣人心弦。

java 复制代码
// frameworks/base/core/java/android/app/ActivityThread.java
private void performCreate(ActivityClientRecord r, Bundle savedInstanceState) {
    // 调用Activity的attach方法,就像给明星穿上华丽的服装
    r.activity.attach(appContext, this, getInstrumentation(), r.token,
            r.ident, app, r.intent, r.activityInfo, title, r.parent,
            r.embeddedID, r.lastNonConfigurationInstances, config,
            r.referrer, r.voiceInteractor, window, r.configCallback,
            r.assistToken);
    // 调用Activity的 onCreate方法,就像明星闪亮登场
    if (r.isPersistable()) {
        mInstrumentation.callActivityOnCreate(r.activity, r.state, r.persistentState);
    } else {
        mInstrumentation.callActivityOnCreate(r.activity, r.state);
    }
}

当Activity进入onStart方法时,就像是明星开始在舞台上展示自己的风采。

java 复制代码
// frameworks/base/core/java/android/app/ActivityThread.java
private void performStart(ActivityClientRecord r) {
    // 调用Activity的 onStart方法
    mInstrumentation.callActivityOnStart(r.activity);
    // 标记Activity已经开始
    r.activity.mStarted = true;
    r.stopped = false;
}

onResume方法就像是明星达到了表演的高潮,吸引着观众的目光。

java 复制代码
// frameworks/base/core/java/android/app/ActivityThread.java
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
        boolean reallyResume, int seq, String reason) {
    // 获取Activity实例
    ActivityClientRecord r = mActivities.get(token);
    if (r != null) {
        // 调用Activity的 onResume方法
        mInstrumentation.callActivityOnResume(r.activity);
        // 将Activity置于前台
        if (r.window != null) {
            r.window.getDecorView().setVisibility(View.VISIBLE);
        }
    }
}

当Activity进入onPause方法时,就像是明星的表演暂时告一段落。

java 复制代码
// frameworks/base/core/java/android/app/ActivityThread.java
private void performPause(ActivityClientRecord r, boolean saveState,
        PendingTransactionActions pendingActions) {
    // 调用Activity的 onPause方法
    mInstrumentation.callActivityOnPause(r.activity);
    // 标记Activity已经暂停
    r.paused = true;
    if (saveState) {
        callCallActivityOnSaveInstanceState(r);
    }
}

onStop方法就像是明星离开了舞台,暂时消失在观众的视线中。

java 复制代码
// frameworks/base/core/java/android/app/ActivityThread.java
private void performStop(ActivityClientRecord r, boolean saveState,
        PendingTransactionActions pendingActions) {
    // 调用Activity的 onStop方法
    mInstrumentation.callActivityOnStop(r.activity);
    // 标记Activity已经停止
    r.stopped = true;
    if (saveState) {
        callCallActivityOnSaveInstanceState(r);
    }
}

最后,onDestroy方法就像是明星的表演彻底结束,一切都回归平静。

java 复制代码
// frameworks/base/core/java/android/app/ActivityThread.java
private void performDestroyActivity(IBinder token, boolean finishing,
        int configChanges, boolean getNonConfigInstance, String reason) {
    // 获取Activity实例
    ActivityClientRecord r = mActivities.get(token);
    if (r != null) {
        // 调用Activity的 onDestroy方法
        mInstrumentation.callActivityOnDestroy(r.activity);
        // 移除Activity记录
        mActivities.remove(token);
    }
}

2.2 Service的幕后英雄

Service就像是Android世界里的幕后英雄,默默地在后台为我们提供各种服务。它有两种启动方式:startServicebindService

2.2.1 startService启动方式

当我们使用startService启动一个Service时,就像是给幕后英雄下达了一个任务。

java 复制代码
// frameworks/base/core/java/android/app/ContextImpl.java
@Override
public ComponentName startService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, false, mUser);
}

private ComponentName startServiceCommon(Intent service, boolean requireForeground,
        UserHandle user) {
    try {
        // 调用ActivityManager的startService方法
        ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                requireForeground,
                getOpPackageName(), user.getIdentifier());
        if (cn != null) {
            if (cn.getPackageName().equals("!")) {
                throw new SecurityException(
                        "Not allowed to start service " + service + " without permission " +
                        cn.getClassName());
            } else if (cn.getPackageName().equals("!!")) {
                throw new IllegalStateException(
                        "Cannot start service " + service + ": " + cn.getClassName());
            }
        }
        return cn;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}
2.2.2 bindService启动方式

bindService启动方式就像是和幕后英雄建立了一种紧密的联系,可以随时和它沟通。

java 复制代码
// frameworks/base/core/java/android/app/ContextImpl.java
@Override
public boolean bindService(Intent service, ServiceConnection conn,
        int flags) {
    warnIfCallingFromSystemProcess();
    return bindServiceCommon(service, conn, flags, mUser);
}

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
        UserHandle user) {
    IServiceConnection sd;
    if (conn == null) {
        throw new IllegalArgumentException("connection is null");
    }
    if (mPackageInfo != null) {
        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                mMainThread.getHandler(), flags);
    } else {
        throw new RuntimeException("Not supported in system context");
    }
    validateServiceIntent(service);
    try {
        // 调用ActivityManager的bindService方法
        int res = ActivityManager.getService().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
        if (res < 0) {
            throw new SecurityException(
                    "Not allowed to bind to service " + service);
        }
        return res != 0;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

Service的生命周期也有它自己的特点,onCreate方法就像是幕后英雄接到任务后开始准备工作。

java 复制代码
// frameworks/base/core/java/android/app/ActivityThread.java
private void handleCreateService(CreateServiceData data) {
    // 获取Service实例
    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = (Service) cl.loadClass(data.info.name).newInstance();
    } catch (Exception e) {
        if (!mInstrumentation.onException(service, e)) {
            throw new RuntimeException(
                    "Unable to instantiate service " + data.info.name
                    + ": " + e.toString(), e);
        }
    }
    // 调用Service的 onCreate方法
    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
    context.setOuterContext(service);
    Application app = packageInfo.makeApplication(false, mInstrumentation);
    service.attach(context, this, data.info.name, data.token, app,
            ActivityManager.getService());
    service.onCreate();
    mServices.put(data.token, service);
    try {
        ActivityManager.getService().serviceDoneExecuting(
                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

onStartCommand方法就像是幕后英雄开始执行任务。

java 复制代码
// frameworks/base/core/java/android/app/ActivityThread.java
private void handleServiceArgs(ServiceArgsData data) {
    // 获取Service实例
    Service s = mServices.get(data.token);
    if (s != null) {
        try {
            int res;
            if (!data.taskRemoved) {
                // 调用Service的 onStartCommand方法
                res = s.onStartCommand(data.args, data.flags, data.startId);
            } else {
                s.onTaskRemoved(data.args);
                res = Service.START_STICKY;
            }
            QueuedWork.waitToFinish();
            try {
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(s, e)) {
                throw new RuntimeException(
                        "Unable to start service " + s
                        + " with " + data.args + ": " + e.toString(), e);
            }
        }
    }
}

onDestroy方法就像是幕后英雄完成任务后休息。

java 复制代码
// frameworks/base/core/java/android/app/ActivityThread.java
private void handleStopService(IBinder token) {
    // 获取Service实例
    Service s = mServices.get(token);
    if (s != null) {
        try {
            // 调用Service的 onDestroy方法
            s.onDestroy();
            mServices.remove(token);
        } catch (Exception e) {
            if (!mInstrumentation.onException(s, e)) {
                throw new RuntimeException(
                        "Unable to destroy service " + s + ": " + e.toString(), e);
            }
        }
        try {
            ActivityManager.getService().serviceDoneExecuting(
                    token, SERVICE_DONE_EXECUTING_STOP, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}

2.3 BroadcastReceiver的消息使者

BroadcastReceiver就像是Android世界里的消息使者,负责传递各种消息。当我们发送一个广播时,就像是给消息使者下达了一个送信的任务。

java 复制代码
// frameworks/base/core/java/android/content/ContextWrapper.java
@Override
public void sendBroadcast(Intent intent) {
    mBase.sendBroadcast(intent);
}

// frameworks/base/core/java/android/app/ContextImpl.java
@Override
public void sendBroadcast(Intent intent) {
    warnIfCallingFromSystemProcess();
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    try {
        // 调用ActivityManager的broadcastIntent方法
        ActivityManager.getService().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                getUserId());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

当广播被接收到时,BroadcastReceiveronReceive方法就会被调用,就像是消息使者把信送到了收件人手中。

java 复制代码
// 自定义BroadcastReceiver类
public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 处理接收到的广播消息
        String action = intent.getAction();
        if (action != null && action.equals("com.example.MY_BROADCAST")) {
            Toast.makeText(context, "Received broadcast: " + action, Toast.LENGTH_SHORT).show();
        }
    }
}

2.4 ContentProvider的数据管家

ContentProvider就像是Android世界里的数据管家,负责管理和提供应用程序之间的数据共享。当我们查询数据时,就像是向数据管家询问一些信息。

java 复制代码
// frameworks/base/core/java/android/content/ContentResolver.java
@Override
public Cursor query(Uri uri, String[] projection, String selection,
        String[] selectionArgs, String sortOrder) {
    return query(uri, projection, selection, selectionArgs, sortOrder, null);
}

public Cursor query(Uri uri, String[] projection, String selection,
        String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return null;
    }
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
        long startTime = SystemClock.uptimeMillis();
        // 调用ContentProvider的query方法
        qCursor = unstableProvider.query(mPackageName, uri, projection, selection,
                selectionArgs, sortOrder, cancellationSignal);
        if (qCursor == null) {
            return null;
        }
        // 确保Cursor的类型正确
        if (!qCursor.getNotificationUri().equals(uri)) {
            qCursor.setNotificationUri(getContentResolver(), uri);
        }
        stableProvider = acquireProvider(uri);
        if (stableProvider != null) {
            if (qCursor instanceof CrossProcessCursor) {
                ((CrossProcessCursor) qCursor).setContentProvider(stableProvider);
            }
        }
    } catch (RemoteException e) {
        if (stableProvider != null) {
            releaseProvider(stableProvider);
        }
        releaseUnstableProvider(unstableProvider);
        throw e.rethrowFromSystemServer();
    }
    releaseUnstableProvider(unstableProvider);
    if (stableProvider != null) {
        qCursor.setExtras(stableProvider.call(mPackageName,
                ContentProvider.METHOD_GET_EXTRAS, null, null));
        releaseProvider(stableProvider);
    }
    return qCursor;
}

当我们插入数据时,就像是向数据管家存入一些物品。

java 复制代码
// frameworks/base/core/java/android/content/ContentResolver.java
@Override
public Uri insert(Uri uri, ContentValues values) {
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return null;
    }
    Uri result = null;
    try {
        // 调用ContentProvider的insert方法
        result = unstableProvider.insert(mPackageName, uri, values);
    } catch (RemoteException e) {
        releaseUnstableProvider(unstableProvider);
        throw e.rethrowFromSystemServer();
    }
    releaseUnstableProvider(unstableProvider);
    return result;
}

当我们更新数据时,就像是对数据管家保管的物品进行修改。

java 复制代码
// frameworks/base/core/java/android/content/ContentResolver.java
@Override
public int update(Uri uri, ContentValues values, String selection,
        String[] selectionArgs) {
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return 0;
    }
    int count = 0;
    try {
        // 调用ContentProvider的update方法
        count = unstableProvider.update(mPackageName, uri, values, selection, selectionArgs);
    } catch (RemoteException e) {
        releaseUnstableProvider(unstableProvider);
        throw e.rethrowFromSystemServer();
    }
    releaseUnstableProvider(unstableProvider);
    return count;
}

当我们删除数据时,就像是从数据管家那里拿走一些物品。

java 复制代码
// frameworks/base/core/java/android/content/ContentResolver.java
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return 0;
    }
    int count = 0;
    try {
        // 调用ContentProvider的delete方法
        count = unstableProvider.delete(mPackageName, uri, selection, selectionArgs);
    } catch (RemoteException e) {
        releaseUnstableProvider(unstableProvider);
        throw e.rethrowFromSystemServer();
    }
    releaseUnstableProvider(unstableProvider);
    return count;
}

三、性能优化的魔法秘籍

3.1 内存优化的神奇之旅

在Android开发中,内存优化就像是一场神奇的魔法之旅,我们要让应用程序在有限的内存空间里翩翩起舞。首先,我们要避免内存泄漏,这就像是要堵住魔法城堡里的漏洞,不让魔力(内存)泄漏出去。

java 复制代码
// 错误示例:可能导致内存泄漏的单例模式
public class MySingleton {
    private static MySingleton instance;
    private Context context;

    private MySingleton(Context context) {
        this.context = context;  // 这里如果传入的是Activity的Context,可能会导致内存泄漏
    }

    public static MySingleton getInstance(Context context) {
        if (instance == null) {
            instance = new MySingleton(context);
        }
        return instance;
    }
}

// 正确示例:使用Application的Context
public class MySingleton {
    private static MySingleton instance;
    private Context context;

    private MySingleton(Context context) {
        this.context = context.getApplicationContext();  // 使用Application的Context,避免内存泄漏
    }

    public static MySingleton getInstance(Context context) {
        if (instance == null) {
            instance = new MySingleton(context);
        }
        return instance;
    }
}

另外,我们还要合理使用数据结构,就像是选择合适的魔法道具一样。例如,在需要存储键值对的地方,我们可以根据具体情况选择HashMap或者SparseArray

java 复制代码
// 使用HashMap存储键值对
HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(1, "Apple");
hashMap.put(2, "Banana");
String value = hashMap.get(1);

// 使用SparseArray存储键值对,对于整数键的情况,SparseArray更节省内存
SparseArray<String> sparseArray = new SparseArray<>();
sparseArray.put(1, "Apple");
sparseArray.put(2, "Banana");
String value2 = sparseArray.get(1);

3.2 布局优化的艺术之美

布局优化就像是一场艺术创作,我们要让应用程序的界面既美观又高效。首先,我们要减少布局嵌套,就像是减少艺术品的层次,让它更加简洁。

xml 复制代码
<!-- 错误示例:过多的布局嵌套 -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="World" />
    </LinearLayout>
</LinearLayout>

<!-- 正确示例:减少布局嵌套 -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="World" />
</LinearLayout>

我们还可以使用include标签来复用布局,就像是复制艺术品的一部分,提高创作效率。

xml 复制代码
<!-- main_layout.xml -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include layout="@layout/header_layout" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Main content" />
</LinearLayout>

<!-- header_layout.xml -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Header" />
</LinearLayout>

3.3 电量优化的节能之道

电量优化就像是一场节能行动,我们要让应用程序在消耗最少电量的情况下完成任务。首先,我们要合理使用唤醒锁,就像是合理使用能源开关,避免不必要的电量消耗。

java 复制代码
// 获取唤醒锁
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
        "MyApp:MyWakeLock");
wakeLock.acquire();  // 申请唤醒锁

// 在不需要时释放唤醒锁
if (wakeLock.isHeld()) {
    wakeLock.release();
}

我们还可以使用JobScheduler来安排后台任务,就像是合理安排工作时间,让应用程序在合适的时间执行任务,减少电量消耗。

java 复制代码
// 创建JobInfo对象
JobInfo jobInfo = new JobInfo.Builder(JOB_ID, new ComponentName(this, MyJobService.class))
       .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)  // 设置网络条件
       .setPeriodic(15 * 60 * 1000)  // 设置任务执行周期
       .build();

// 获取JobScheduler实例
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
// 调度任务
jobScheduler.schedule(jobInfo);

四、多线程与异步处理的精彩对决

4.1 Thread的冲锋陷阵

Thread就像是战场上的冲锋兵,勇往直前地执行任务。我们可以通过继承Thread类来创建一个新的线程。

java 复制代码
// 继承Thread类创建线程
class MyThread extends Thread {
    @Override
    public void run() {
        // 线程要执行的任务
        for (int i = 0; i < 10; i++) {
            System.out.println("MyThread: " + i);
            try {
                Thread.sleep(1000);  // 线程休眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 在主线程中启动新线程
public class Main {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();  // 启动线程
    }
}

我们也可以通过实现Runnable接口来创建线程,这样可以更好地实现代码的复用。

java 复制代码
// 实现Runnable接口创建线程
class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 线程要执行的任务
        for (int i = 0; i < 10; i++) {
            System.out.println("MyRunnable: " + i);
            try {
                Thread.sleep(1000);  // 线程休眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 在主线程中启动新线程
public class Main {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();  // 启动线程
    }
}

4.2 Handler的消息传递

Handler就像是战场上的传令兵,负责在不同线程之间传递消息。我们可以在主线程中创建一个Handler,然后在子线程中通过Handler发送消息。

java 复制代码
// 在主线程中创建Handler
Handler handler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        // 处理接收到的消息
        if (msg.what == 1) {
            String data = (String) msg.obj;
            System.out.println("Received message: " + data);
        }
    }
};

// 在子线程中发送消息
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(2000);  // 线程休眠2秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Message message = Message.obtain();
        message.what = 1;
        message.obj = "Hello from child thread";
        handler.sendMessage(message);  // 发送消息
    }
}).start();

4.3 AsyncTask的便捷助手

AsyncTask就像是战场上的便捷助手,它简化了异步任务的处理。我们可以通过继承AsyncTask类来创建一个异步任务。

java 复制代码
// 继承AsyncTask类创建异步任务
class MyAsyncTask extends AsyncTask<Void, Integer, String> {
    @Override
    protected void onPreExecute() {
        // 在任务执行前的准备工作
        System.out.println("Task started");
    }

    @Override
    protected String doInBackground(Void... voids) {
        // 后台执行的任务
        for (int i = 0; i < 10; i++) {
            publishProgress(i);  // 发布进度
            try {
                Thread.sleep(1000);  // 线程休眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return "Task completed";
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        // 处理进度更新
        System.out.println("Progress: " + values[0]);
    }

    @Override
    protected void onPostExecute(String result) {
        // 任务执行完成后的处理
        System.out.println(result);
    }
}

// 在主线程中执行异步任务
MyAsyncTask myAsyncTask = new MyAsyncTask();
myAsyncTask.execute();

4.4 ExecutorService的高效指挥官

ExecutorService就像是战场上的高效指挥官,它可以管理线程池,提高线程的执行效率。我们可以通过Executors类来创建不同类型的线程池。

java 复制代码
// 创建固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);

// 提交任务到线程池
for (int i = 0; i < 10; i++) {
    final int taskId = i;
    executorService.submit(new Runnable() {
        @Override
        public void run() {
            System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
            try {
                Thread.sleep(2000);  // 线程休眠2秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Task " + taskId + " is completed");
        }
    });
}

// 关闭线程池
executorService.shutdown();

五、Android安全的坚固防线

5.1 数据加密的秘密武器

在Android开发中,数据加密就像是为我们的数据穿上了一层坚固的铠甲,保护它不被窃取。我们可以使用Cipher类来进行数据加密和解密。

java 复制代码
        byte[] decodedBytes = Base64.getDecoder().decode(encryptedText);
        byte[] decryptedBytes = cipher.doFinal(decodedBytes);
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }

    // 生成密钥
    public static SecretKey generateKey() throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
        keyGenerator.init(128); // 密钥长度为128位
        return keyGenerator.generateKey();
    }

    public static void main(String[] args) {
        try {
            // 生成密钥
            SecretKey secretKey = generateKey();
            String plainText = "Hello, Android Security!";

            // 加密数据
            String encryptedText = encrypt(plainText, secretKey);
            System.out.println("Encrypted Text: " + encryptedText);

            // 解密数据
            String decryptedText = decrypt(encryptedText, secretKey);
            System.out.println("Decrypted Text: " + decryptedText);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,generateKey 方法用于生成一个 AES 加密所需的密钥。encrypt 方法接收明文和密钥,使用 Cipher 类以加密模式初始化,将明文转换为字节数组进行加密,最后使用 Base64 编码将加密后的字节数组转换为字符串。decrypt 方法则接收加密后的字符串和密钥,先将 Base64 编码的字符串解码为字节数组,再使用 Cipher 类以解密模式初始化,对字节数组进行解密,最终将解密后的字节数组转换为字符串。

5.2 权限管理的严格把关

Android 的权限管理就像是一扇大门的守卫,严格控制着应用对敏感信息和功能的访问。我们在 AndroidManifest.xml 中声明所需的权限。

xml 复制代码
<!-- AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">

    <!-- 声明网络访问权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 声明读取外部存储权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

从 Android 6.0(API 级别 23)开始,部分危险权限需要在运行时动态请求。以下是一个动态请求读取外部存储权限的示例代码。

java 复制代码
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

public class MainActivity extends AppCompatActivity {
    private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 检查是否已经有读取外部存储的权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            // 如果没有权限,请求权限
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                    PERMISSION_REQUEST_READ_EXTERNAL_STORAGE);
        } else {
            // 已经有权限,执行相应操作
            Toast.makeText(this, "Already have read external storage permission", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSION_REQUEST_READ_EXTERNAL_STORAGE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 用户授予了权限,执行相应操作
                Toast.makeText(this, "Read external storage permission granted", Toast.LENGTH_SHORT).show();
            } else {
                // 用户拒绝了权限
                Toast.makeText(this, "Read external storage permission denied", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

在上述代码中,ContextCompat.checkSelfPermission 方法用于检查应用是否已经拥有指定的权限。如果没有权限,使用 ActivityCompat.requestPermissions 方法请求权限。onRequestPermissionsResult 方法用于处理权限请求的结果,根据用户的选择执行相应的操作。

5.3 代码混淆的隐身衣

代码混淆就像是给我们的代码穿上了一件隐身衣,让反编译者难以理解代码的逻辑。在 Android 开发中,我们可以使用 ProGuard 或 R8 进行代码混淆。

首先,在 build.gradle 文件中开启代码混淆。

groovy 复制代码
android {
    buildTypes {
        release {
            minifyEnabled true // 开启代码混淆
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

minifyEnabled true 表示开启代码混淆,proguardFiles 指定了混淆规则文件。proguard-android-optimize.txt 是 Android 提供的默认混淆规则文件,proguard-rules.pro 是我们自定义的混淆规则文件。

以下是一个简单的 proguard-rules.pro 文件示例。

scala 复制代码
# 保留 AndroidManifest.xml 中声明的类
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService

# 保留实现了特定接口的类
-keepclassmembers class * implements android.os.Parcelable {
    static ** CREATOR;
}

# 保留注解
-keepattributes *Annotation*

# 保留 native 方法
-keepclasseswithmembernames class * {
    native <methods>;
}

在上述规则中,-keep 指令用于保留指定的类和成员,避免被混淆。例如,保留了 Android 四大组件、实现了 Parcelable 接口的类、注解和 native 方法等。

六、新特性与架构模式的前沿探索

6.1 Android Jetpack的强大助力

Android Jetpack 是一系列库、工具和指南的集合,旨在帮助开发者更轻松地构建高质量的 Android 应用。下面我们来看看几个常用的 Jetpack 组件。

6.1.1 ViewModel

ViewModel 用于存储和管理与 UI 相关的数据,并且在配置更改(如屏幕旋转)时保持数据的存活。以下是一个简单的 ViewModel 示例。

java 复制代码
import androidx.lifecycle.ViewModel;

public class MyViewModel extends ViewModel {
    private int count = 0;

    // 获取计数器的值
    public int getCount() {
        return count;
    }

    // 增加计数器的值
    public void incrementCount() {
        count++;
    }
}

在 Activity 中使用 ViewModel。

java 复制代码
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private MyViewModel viewModel;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取 ViewModel 实例
        viewModel = new ViewModelProvider(this).get(MyViewModel.class);

        textView = findViewById(R.id.textView);
        updateUI();

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 增加计数器的值
                viewModel.incrementCount();
                updateUI();
            }
        });
    }

    // 更新 UI
    private void updateUI() {
        int count = viewModel.getCount();
        textView.setText("Count: " + count);
    }
}

在上述代码中,MyViewModel 类继承自 ViewModel,用于存储和管理计数器的值。在 MainActivity 中,使用 ViewModelProvider 获取 MyViewModel 的实例,并且在点击按钮时更新计数器的值并刷新 UI。由于 ViewModel 在配置更改时保持存活,所以即使屏幕旋转,计数器的值也不会丢失。

6.1.2 LiveData

LiveData 是一种可观察的数据持有者类,它可以感知 Activity、Fragment 等组件的生命周期,确保数据的更新只在组件处于活跃状态时才会通知观察者。以下是一个使用 LiveData 的示例。

java 复制代码
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class MyViewModel extends ViewModel {
    private MutableLiveData<Integer> countLiveData = new MutableLiveData<>();

    public MyViewModel() {
        // 初始化计数器的值
        countLiveData.setValue(0);
    }

    // 获取 LiveData 对象
    public LiveData<Integer> getCountLiveData() {
        return countLiveData;
    }

    // 增加计数器的值
    public void incrementCount() {
        int currentCount = countLiveData.getValue() == null ? 0 : countLiveData.getValue();
        countLiveData.setValue(currentCount + 1);
    }
}

在 Activity 中观察 LiveData。

java 复制代码
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private MyViewModel viewModel;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取 ViewModel 实例
        viewModel = new ViewModelProvider(this).get(MyViewModel.class);

        textView = findViewById(R.id.textView);

        // 观察 LiveData 的变化
        viewModel.getCountLiveData().observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer count) {
                // 当 LiveData 的值发生变化时,更新 UI
                textView.setText("Count: " + count);
            }
        });

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 增加计数器的值
                viewModel.incrementCount();
            }
        });
    }
}

在上述代码中,MyViewModel 类中的 countLiveData 是一个 MutableLiveData 对象,用于存储计数器的值。在 MainActivity 中,使用 observe 方法观察 countLiveData 的变化,当值发生变化时,会自动调用 onChanged 方法更新 UI。

6.1.3 Room

Room 是一个 SQLite 对象映射库,它提供了一种更简单、更安全的方式来操作 SQLite 数据库。以下是一个使用 Room 的示例。

首先,定义实体类。

java 复制代码
import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity(tableName = "users")
public class User {
    @PrimaryKey(autoGenerate = true)
    private int id;
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

然后,定义 DAO(数据访问对象)接口。

java 复制代码
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import java.util.List;

@Dao
public interface UserDao {
    // 插入用户数据
    @Insert
    void insert(User user);

    // 查询所有用户数据
    @Query("SELECT * FROM users")
    List<User> getAllUsers();
}

接着,定义数据库类。

java 复制代码
import androidx.room.Database;
import androidx.room.RoomDatabase;

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

最后,在 Activity 中使用 Room 数据库。

java 复制代码
import androidx.appcompat.app.AppCompatActivity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.TextView;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private AppDatabase database;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取数据库实例
        database = AppDatabase.getInstance(this);
        textView = findViewById(R.id.textView);

        // 插入用户数据
        User user = new User("John", 25);
        new InsertUserTask().execute(user);

        // 查询所有用户数据
        new GetAllUsersTask().execute();
    }

    // 插入用户数据的异步任务
    private class InsertUserTask extends AsyncTask<User, Void, Void> {
        @Override
        protected Void doInBackground(User... users) {
            database.userDao().insert(users[0]);
            return null;
        }
    }

    // 查询所有用户数据的异步任务
    private class GetAllUsersTask extends AsyncTask<Void, Void, List<User>> {
        @Override
        protected List<User> doInBackground(Void... voids) {
            return database.userDao().getAllUsers();
        }

        @Override
        protected void onPostExecute(List<User> users) {
            StringBuilder stringBuilder = new StringBuilder();
            for (User user : users) {
                stringBuilder.append("Name: ").append(user.getName()).append(", Age: ").append(user.getAge()).append("\n");
            }
            textView.setText(stringBuilder.toString());
        }
    }
}

在上述代码中,User 类是一个实体类,使用 @Entity 注解标记为数据库表。UserDao 接口是数据访问对象,使用 @Dao 注解标记,定义了插入和查询数据的方法。AppDatabase 类是数据库类,使用 @Database 注解标记,指定了实体类和数据库版本。在 MainActivity 中,使用 AppDatabase.getInstance 方法获取数据库实例,通过异步任务插入和查询数据。

6.2 MVVM架构模式的魅力展现

MVVM(Model - View - ViewModel)架构模式是一种流行的 Android 应用架构模式,它将视图(View)和业务逻辑(ViewModel)分离,提高了代码的可维护性和可测试性。以下是一个简单的 MVVM 示例。

首先,定义 Model 层。

java 复制代码
// 定义用户数据模型
public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

然后,定义 ViewModel 层。

java 复制代码
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class UserViewModel extends ViewModel {
    private MutableLiveData<User> userLiveData = new MutableLiveData<>();

    // 初始化用户数据
    public void initUser() {
        User user = new User("Alice", 30);
        userLiveData.setValue(user);
    }

    // 获取用户数据的 LiveData 对象
    public LiveData<User> getUserLiveData() {
        return userLiveData;
    }
}

接着,定义 View 层(Activity)。

java 复制代码
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private UserViewModel viewModel;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取 ViewModel 实例
        viewModel = new ViewModelProvider(this).get(UserViewModel.class);

        textView = findViewById(R.id.textView);

        // 初始化用户数据
        viewModel.initUser();

        // 观察用户数据的变化
        viewModel.getUserLiveData().observe(this, new Observer<User>() {
            @Override
            public void onChanged(User user) {
                // 当用户数据发生变化时,更新 UI
                textView.setText("Name: " + user.getName() + ", Age: " + user.getAge());
            }
        });
    }
}

在上述代码中,User 类是 Model 层,用于存储用户数据。UserViewModel 类是 ViewModel 层,使用 LiveData 来存储和管理用户数据,并且提供了初始化用户数据和获取用户数据的方法。MainActivity 是 View 层,通过 ViewModelProvider 获取 UserViewModel 的实例,观察 UserLiveData 的变化,当数据发生变化时更新 UI。通过这种方式,实现了视图和业务逻辑的分离。

七、总结与展望

7.1 总结

通过对 Android 大厂面试相关技术的深入剖析,我们全面了解了 Android 开发的各个方面。从 Android 系统的基础架构和启动流程,我们明白了系统的底层原理和运行机制,这为我们开发出更稳定、高效的应用奠定了基础。四大组件作为 Android 应用的核心,它们各自有着独特的生命周期和使用场景,合理运用它们可以构建出功能丰富、交互流畅的应用。

在性能优化方面,我们学习了内存优化、布局优化和电量优化的方法和技巧。内存优化可以避免应用出现内存泄漏和内存溢出的问题,提高应用的稳定性;布局优化可以减少布局嵌套,提高界面的加载速度;电量优化可以降低应用的电量消耗,提升用户体验。

多线程与异步处理让我们能够在应用中高效地处理耗时任务,避免阻塞主线程,保证应用的流畅性。安全方面,数据加密、权限管理和代码混淆等技术为我们的数据和应用提供了可靠的保护。

新特性和架构模式的探索,如 Android Jetpack 和 MVVM 架构模式,让我们能够跟上技术发展的步伐,使用更先进的工具和方法来开发应用,提高开发效率和代码质量。

7.2 展望

随着技术的不断发展,Android 开发领域也在不断演进。未来,我们可以期待更多创新的技术和特性出现在 Android 系统中。例如,人工智能和机器学习技术在 Android 应用中的应用将越来越广泛,我们可以利用这些技术实现图像识别、语音交互、智能推荐等功能,为用户带来更加智能、便捷的体验。

同时,随着 5G 技术的普及,Android 应用的网络性能将得到极大提升,我们可以开发出更加流畅、实时的在线应用,如高清视频直播、多人在线游戏等。

在架构模式方面,可能会出现更加先进、灵活的架构模式,进一步提高代码的可维护性和可扩展性。而且,随着跨平台开发技术的发展,Android 开发可能会与其他平台的开发更加融合,开发者可以使用一套代码同时开发 Android 和 iOS 等多个平台的应用。

作为 Android 开发者,我们需要不断学习和掌握新的技术和知识,紧跟技术发展的潮流,才能在激烈的竞争中脱颖而出,开发出更加优秀的 Android 应用,为用户带来更好的体验。

相关推荐
二J1 小时前
管理100个小程序-很难吗
android·小程序
s11show_1631 小时前
hz修改后台新增keyword功能
android·java·前端
quququ_21381 小时前
Java求职面试:从Spring Boot到微服务的全面考核
java·spring boot·微服务·面试·技术栈
慕仲卿1 小时前
交叉熵损失函数介绍
面试
慕仲卿2 小时前
Torch 分布式训练关键参数
面试
egghead263162 小时前
Promise、Generator、async/await、axios、Ajax、Fetch 啥是啥?
面试
IT技术图谱2 小时前
【绝非标题党】网络监听新姿势:APT编译时注解实现高扩展框架
android·面试·架构
云之兕2 小时前
在面试中被问到spring是什么?
spring·面试·职场和发展
江城开朗的豌豆2 小时前
JavaScript篇:遍历数组:for循环与forEach的本质区别与实战选择
前端·javascript·面试
RichardLai883 小时前
[Flutter 基础] - Flutter基础组件 - Text
android·flutter