深入探索Android Service:后台服务的终极指南(中)


引言


在深入探索了Service的基本概念和生命周期管理后,本文将聚焦于Android Service的进阶应用,包括前台服务的运用、Android 5.0以上版本中隐式启动Service的问题、确保Service稳定性的策略,以及Service在进程间通信和复杂后台任务处理中的高效利用。我们将通过实际代码示例,展示如何在实际开发中应用这些高级特性。


一、前台服务与通知

在Android中,前台服务(Foreground Service)是一种用户明确感知到的服务,比如音乐播放器。前台服务通过在状态栏中显示一个持久的通知来告知用户服务正在进行中。系统倾向于保留前台服务,使其不易被系统在内存不足时杀死。


以下是如何创建和启动一个前台服务的代码示例:

1、创建通知

首先,你需要创建一个通知,该通知将显示在状态栏中,告知用户服务正在运行。

public class MyForegroundService extends Service {
    private static final int NOTIFICATION_ID = 1;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 创建通知
        Notification notification = new NotificationCompat.Builder(this, "channel_id") // 对于Android 8.0以上需要一个通知渠道
            .setContentTitle("Foreground Service")
            .setContentText("Service is running in the background")
            .setSmallIcon(R.drawable.ic_notification) // 状态栏图标
            .setChannelId("channel_id") // Android 8.0以上需要设置通知渠道的ID
            .setOngoing(true) // 通知是持久的
            .build();

        // 启动前台服务
        startForeground(NOTIFICATION_ID, notification);

        // 服务的其他逻辑...
        return START_STICKY; // 或 START_REDELIVER_INTENT,根据服务需求选择
    }

    @Override
    public void onDestroy() {
        // 停止前台服务
        stopForeground(true);
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // 如果服务不需要绑定,则返回null
        return null;
    }
}

2、在AndroidManifest.xml中声明Service


确保你的AndroidManifest.xml文件中已经声明了服务,并且如果你打算在Android 8.0(API级别26)以上使用通知渠道,还需要在清单中声明一个通知渠道。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">

    <application
        ... >

        <service android:name=".MyForegroundService" />

        <!-- Android 8.0以上版本需要的通知渠道 -->
        <service android:name=".NotificationChannelService">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
            </intent-filter>
        </service>

    </application>
</manifest>

3、启动前台服务

最后,在Activity或其他组件中,使用Intent启动前台服务。

Intent serviceIntent = new Intent(this, MyForegroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(serviceIntent); // Android 8.0及以上使用startForegroundService
} else {
    startService(serviceIntent);
}

4、注意事项

  • 从Android 8.0(API级别26)开始,所有通知必须通过通知渠道发出。确保你已经创建了至少一个通知渠道。
  • 前台服务的通知应该是用户可感知的,避免滥用前台服务。
  • 当服务不再需要运行在前台时,应该调用stopForeground(true)来停止前台状态并移除通知。之后,服务可以继续作为一个普通后台服务运行。

通过上述步骤,你可以在Android应用中创建和管理前台服务。


二、 Android 5.0以上隐式启动问题

在Android 5.0(API级别21)及以上版本中,使用隐式Intent启动Service是一个不安全的做法,因为Google为了提高系统的安全性,限制了隐式Intent启动Service的能力。以下是如何在Android 5.0及以上版本中解决隐式启动Service问题的具体方法和代码示例。


1、隐式启动Service的问题

在Android 5.0之前,你可以使用一个隐式Intent来启动Service,如下所示:

Intent serviceIntent = new Intent();
serviceIntent.setAction("com.example.myapp.SERVICE_ACTION");
startService(serviceIntent);

但是,在Android 5.0及以上版本中,上述代码会抛出SecurityException异常,因为系统不允许通过隐式Intent启动Service。


2、解决方案

为了解决这个问题,你可以采取以下两种方法之一:


方法1:设置Action和PackageName

在Intent中显式地设置Action和当前应用的PackageName,如下所示:

Intent serviceIntent = new Intent();
serviceIntent.setAction("com.example.myapp.SERVICE_ACTION");
serviceIntent.setPackage(getPackageName());
startService(serviceIntent);

这里,getPackageName()是当前应用的包名,而"com.example.myapp.SERVICE_ACTION"是你Service的Action字符串。


方法2:将隐式Intent转换为显式Intent

通过将隐式Intent转换为显式Intent,可以确保Intent安全地启动Service。以下是如何进行转换的示例代码:

public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
    // 检索所有匹配给定Intent的服务
    PackageManager pm = context.getPackageManager();
    List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
    
    // 确保只找到一个匹配的服务
    if (resolveInfo == null || resolveInfo.size() != 1) {
        return null;
    }
    
    // 获取组件信息并创建ComponentName
    ResolveInfo serviceInfo = resolveInfo.get(0);
    String packageName = serviceInfo.serviceInfo.packageName;
    String className = serviceInfo.serviceInfo.name;
    ComponentName component = new ComponentName(packageName, className);
    
    // 创建一个新的Intent,使用旧的Intent来重用extras等
    Intent explicitIntent = new Intent(implicitIntent);
    // 设置组件为显式的
    explicitIntent.setComponent(component);
    return explicitIntent;
}

使用上述方法,你可以这样启动Service:

Intent mIntent = new Intent(); // 辅助Intent
mIntent.setAction("com.example.myapp.SERVICE_ACTION");
final Intent serviceIntent = new Intent(getExplicitIntent(this, mIntent));
if (serviceIntent != null) {
    startService(serviceIntent);
}

在上述代码中,我们首先创建了一个隐式Intent(mIntent),然后调用getExplicitIntent方法将其转换为显式Intent(serviceIntent),最后使用显式Intent安全地启动Service。


3、注意事项

  • 确保Service的Action字符串是唯一的,以避免与其他应用的Service冲突。
  • 使用显式Intent而不是隐式Intent,可以提高应用的安全性和稳定性。
  • 在清单文件中为Service指定正确的权限,如果需要的话。

通过上述方法,你可以确保在Android 5.0及以上版本中安全地启动Service。


三、 保证Service不被杀死的策略

在Android中,保证Service不被系统杀死主要涉及到Service的生命周期管理和一些特定的策略。以下是一些常用的策略和相应的代码示例。

1、使用START_STICKY返回值

onStartCommand方法中返回START_STICKY,这样当Service因为内存不足而被系统杀死后,系统会尝试重新启动Service。

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // 处理服务任务
    return START_STICKY;
}


2、使用START_REDELIVER_INTENT

返回START_REDELIVER_INTENT,这样当Service被杀死时,系统会尝试重新传递最后一个Intent给Service。

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // 处理服务任务
    return START_REDELIVER_INTENT;
}

3、创建前台Service

将Service设置为前台Service,这样可以提高Service的优先级,减少被系统杀死的风险。

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Notification notification = new NotificationCompat.Builder(this, "channel_id")
        .setContentTitle("Service Title")
        .setContentText("Service is running")
        .build();

    startForeground(NOTIFICATION_ID, notification);

    // 处理服务任务
    return START_STICKY;
}

4、监听系统广播重启Service

onDestroy方法中发送一个自定义广播,然后在一个BroadcastReceiver中监听这个广播并重启Service。

@Override
public void onDestroy() {
    // 发送重启Service的广播
    Intent restartIntent = new Intent(this, ServiceRestartReceiver.class);
    sendBroadcast(restartIntent);
    super.onDestroy();
}

public static class ServiceRestartReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
    // 重启Service
    context.startService(new Intent(context, MyService.class));
    }
}

确保在AndroidManifest.xml中注册ServiceRestartReceiver

<receiver android:name=".ServiceRestartReceiver" />

5、监控Service状态并在需要时重启

创建一个辅助的Service或BroadcastReceiver来监控你的主Service状态,并在它被杀死时重启。

public class ServiceWatcher extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (isServiceRunning(context, MyService.class)) {
            // Service is running, no action needed
        } else {
            // Service is not running, restart it
            context.startService(new Intent(context, MyService.class));
        }
    }

    public boolean isServiceRunning(Context context, Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }
}

并在AndroidManifest.xml中注册ServiceWatcher

<receiver android:name=".ServiceWatcher" />

使用这些策略时,需要考虑应用的用户体验和系统资源的合理使用。滥用这些机制可能会导致系统资源紧张,影响设备的整体性能,甚至可能导致应用被系统或用户杀死。因此,合理使用这些策略,并且只在确实需要时使用它们。


四、通过Service进行有效的进程间通信(IPC)

在Android中,Service可以实现进程间通信(IPC),允许不同应用或同一应用的不同组件之间交换信息。IPC通常通过Binder机制实现。以下是通过Service进行进程间通信的步骤和代码示例。


1、定义Binder类

首先,你需要定义一个Binder类,它是Service和客户端之间通信的桥梁。

public class MyBinder extends Binder {
    public MyService getService() {
        return MyService.this;
    }
}

public class MyService extends Service {
    private final IBinder binder = new MyBinder();
    // ...
}

2、实现Service的onBind方法

在Service中,重写onBind方法并返回Binder对象。

@Override
public IBinder onBind(Intent intent) {
    return binder;
}

3、在客户端绑定Service

客户端使用bindService方法绑定Service。

private ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // 获取Service的实例
        MyBinder myBinder = (MyBinder) service;
        MyService myService = myBinder.getService();
        // 现在可以调用Service的公共方法
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        // 处理Service连接断开的情况
    }
};

Intent serviceIntent = new Intent(this, MyService.class);
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);

4、在Service中定义公共方法

在Service中,定义客户端可以调用的公共方法。

public class MyService extends Service {
    // ...

    public void doSomething() {
        // 执行一些操作
    }
}

客户端通过Binder调用这些方法:

myService.doSomething();

5、处理跨进程通信

如果Service需要跨进程通信,可以使用Messenger或AIDL(Android接口定义语言)。


(1)、使用Messenger

第一步,在Service中创建一个Handler和一个Messenger

public class MyMessengerService extends Service {
    private final Messenger messenger = new Messenger(new IncomingHandler());

    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }

    private class IncomingHandler extends Handler {
        public void handleMessage(Message msg) {
            // 处理消息
            // 可以选择回复消息
            Message replyMsg = Message.obtain();
            // 设置回复消息并发送
            try {
                messenger.send(replyMsg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
}

第二步、在客户端发送消息给Service。

private ServiceConnection serviceConnection = new ServiceConnection() {
    // ...
};

Intent serviceIntent = new Intent(this, MyMessengerService.class);
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);

// 发送消息
Message message = Message.obtain();
message.what = SOME_MSG;
// 可以设置更多信息,如arg1, arg2, obj等
messenger.send(message);

(2)、使用AIDL

第一步,定义AIDL接口。

// ISomeService.aidl
package com.example.myapp;

interface ISomeService {
    void doSomething();
}

第二步,实现AIDL接口。

public class SomeService extends Service {
    private final ISomeService.Stub binder = new ISomeService.Stub() {
        @Override
        public void doSomething() {
            // 实现方法
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
}

第三步,客户端绑定Service并调用方法。

private ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        ISomeService someService = ISomeService.Stub.asInterface(service);
        // 调用远程方法
        try {
            someService.doSomething();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        // ...
    }
};

Intent serviceIntent = new Intent(this, SomeService.class);
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);

使用这些方法,你可以实现Service的进程间通信。记得在AndroidManifest.xml中声明Service,并为跨进程通信的Service添加android:exported="true"属性。


五、 实战案例:利用Service实现复杂的后台任务处理

在Android中,使用Service执行文件下载任务是常见的应用场景。以下是一个使用Service进行文件下载的示例,包括Service的创建、绑定,以及在客户端启动下载任务和接收下载进度更新。


1、创建下载Service

首先,创建一个Service类来处理下载任务。

public class DownloadService extends Service {
    public static final String ACTION_DOWNLOAD = "com.example.DOWNLOAD";
    public static final String EXTRA_URL = "com.example.EXTRA_URL";

    private DownloadManager downloadManager;
    private Handler handler;

    @Override
    public void onCreate() {
        super.onCreate();
        downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
        handler = new Handler();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String downloadUrl = intent.getStringExtra(EXTRA_URL);
        if (downloadUrl != null && !downloadUrl.isEmpty()) {
            startDownload(downloadUrl);
        }
        return START_NOT_STICKY;
    }

    private void startDownload(String url) {
        // 创建下载请求
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        request.setTitle("Downloading...");
        request.setDescription("Downloading file...");
        request.setDestinationInExternalFilesDir(this, "Download", "example.apk");

        // 将下载请求加入下载队列
        downloadManager.enqueue(request);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null; // 如果不需要绑定,则返回null
    }
}

2、在AndroidManifest.xml中声明Service

在应用的AndroidManifest.xml文件中声明Service。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">

    <application ... >
        <service android:name=".DownloadService" />
    </application>
</manifest>

3、启动下载Service

在应用的其他组件中,如Activity中,使用startService方法启动下载Service。

Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setAction(DownloadService.ACTION_DOWNLOAD);
downloadIntent.putExtra(DownloadService.EXTRA_URL, "http://example.com/file.apk");
startService(downloadIntent);

4、接收下载进度

为了接收下载进度,可以利用广播接收器。

在Service中发送进度更新广播:

private BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
            long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
            // 检查下载是否成功
            boolean success = queryDownloadStatus(downloadId);
            if (success) {
                // 下载成功,可以在这里安装文件或执行其他操作
            }
        }
    }
};

private boolean queryDownloadStatus(long downloadId) {
    DownloadManager.Query query = new DownloadManager.Query();
    Cursor cursor = downloadManager.query(query);
    boolean success = false;
    if (cursor.moveToFirst()) {
        int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
        success = DownloadManager.STATUS_SUCCESSFUL == status;
    }
    cursor.close();
    return success;
}

@Override
public void onDestroy() {
    // 注销广播接收器
    unregisterReceiver(downloadReceiver);
    super.onDestroy();
}

// 在startDownload方法中注册广播接收器
Intent filter = new Intent(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
filter.setPackage(getPackageName());
registerReceiver(downloadReceiver, new IntentFilter(filter));

AndroidManifest.xml中为下载完成的广播添加一个动态权限:

<manifest ... >
    <uses-permission android:name="com.android.providers.downloads.permission.DOWNLOAD_COMPLETED"/>
    ...
</manifest>

请注意,从Android 8.0(API级别26)开始,对于通过DownloadManager下载的文件,系统会自动进行验证和扫描。确保你的应用遵守了相关的权限和行为准则。

通过这种方式,你可以利用Service实现文件下载任务,并在下载完成后接收通知。这种方法可以确保下载任务在后台独立运行,即使用户切换到其他应用或锁屏也不会中断。


六、结语

Service作为Android平台的基石之一,其强大的后台处理能力和灵活的进程间通信机制,为开发者提供了广阔的应用空间。然而,Service的稳定性和效率仍然是开发者面临的挑战。在未来的技术探索中,我们将进一步讨论Service的最佳实践,包括如何优化Service的性能,以及如何通过Service实现更高效的系统级操作。敬请期待我们的下一篇深度解析文章,带你进入Service的高效能开发世界。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/568752.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

jmeter之连接MySQL数据库

jmeter连接mysql数据库 mysql官网下载地址&#xff1a;MySQL :: Download Connector/J 步骤如下&#xff1a; 1、下载mysql的jar包放入到jmeter的lib/ext下&#xff0c;然后重启jmeter 链接: https://pan.baidu.com/s/1rRrMQKnEuKz8zOUfMdMHFg?pwdawfc 提取码: awfc 2、配置…

构建NodeJS库--前端项目的打包发布

1. 前言 学习如何打包发布前端项目&#xff0c;需要学习以下相关知识&#xff1a; package.json 如何初始化配置&#xff0c;以及学习npm配置项&#xff1b; 模块类型type配置&#xff0c; 这是nodejs的package.json的配置main 入口文件的配置 webpack 是一个用于现代 JavaSc…

ElasticSearch总结二

正向索引和倒排索引&#xff1a; 正向索引&#xff1a; 比方说我这里有一张数据库表&#xff0c;那我们知道对于数据库它一般情况下都会基于i d去创建一个索引&#xff0c;然后形成一个b树。 那么你根据i d进行检索的速度&#xff0c;就会非常的快&#xff0c;那么这种方式的…

Cesium之加载GeoServer或geowebcache的WMTS服务

文章目录 Cesium加载GeoServer的WMTS关键代码WMTS服务地址获取核心参数获取 Cesium加载GeoServer的WMTS关键代码 Cesium之加载GeoServer或geowebcache的WMTS服务关键代码如下 var url2"http://localhost:8090/geowebcache/service/wmts/rest/arcgis_com/{style}/{TileMat…

在excel中,如何在一个表中删除和另一个表中相同的数据?

现在有A表&#xff0c;是活动全部人员的姓名和学号&#xff0c;B表是该活动中获得优秀人员的姓名和学号&#xff0c; 怎么提取没有获得优秀人员的名单&#xff1f; 这里提供两个使用excel基础功能的操作方法。 1.条件格式自动筛选 1.1按住Ctrl键&#xff0c;选中全表中的姓…

的记忆:pandas(实在会忘记,就看作是一个 Excel 表格,或者是 SQL 表,或者是字典的字典。)

pandas 是一个开源的 Python 数据分析库&#xff0c;它提供了快速、灵活和富有表现力的数据结构&#xff0c;旨在使“关系”或“标记”数据的“快速分析、清洗和转换”变得既简单又直观。pandas 非常适合于数据清洗和转换、数据分析和建模等任务。以下是 pandas 的基本概念和主…

用 LM Studio 1 分钟搭建可在本地运行大型语言模型平台替代 ChatGPT

&#x1f4cc; 简介 LM Studio是一个允许用户在本地离线运行大型语言模型&#xff08;LLMs&#xff09;的平台&#xff0c;它提供了一种便捷的方式来使用和测试这些先进的机器学习模型&#xff0c;而无需依赖于互联网连接。以下是LM Studio的一些关键特性&#xff1a; 脱机&am…

C++笔记:C++中的重载

重载的概念 一.函数重载 代码演示例子&#xff1a; #include<iostream> using namespace std;//函数名相同&#xff0c;在是每个函数的参数不相同 void output(int x) {printf("output int : %d\n", x);return ; }void output(long long x) {printf("outp…

RTU遥测终端为城市排水安全保驾护航!

近年来&#xff0c;全球气候变迁与城市化进程不断加速&#xff0c;导致强降雨事件频发&#xff0c;道路低洼地带、下穿式立交桥和隧道等区域在暴雨中常易积水&#xff0c;严重阻碍了人民的出行&#xff0c;甚至危及生命与财产安全。而传统的排水管网管理方式已难以适应现代城市…

mybatis的使用技巧8——联合查询union和union all的区别和用法

在实际项目开发中&#xff0c;会经常联合查询结构相似的多张数据表&#xff0c;使用union关键字就只需要一次sql操作&#xff0c;而无需执行多次查询并通过代码逻辑合并处理&#xff0c;减少了大量繁琐的操作&#xff0c;最重要的是还能通过可选的all关键字筛选重复的数据。 1…

数据结构基础:链表操作入门

数据结构基础&#xff1a;链表操作入门 数据结构基础&#xff1a;链表操作入门链表的基本概念链表的基本操作输出链表插入节点删除节点查找值 完整的链表操作示例结语 数据结构基础&#xff1a;链表操作入门 在计算机科学中&#xff0c;数据结构是组织和存储数据的方式&#x…

海康Visionmaster-常见问题排查方法-启动失数

问题2&#xff1a;VM无法启动&#xff0c;报错&#xff1a;参数错误&#xff1b;  问题原因&#xff1a;客户电脑环境异常导致代理启动失败。  解决方法&#xff1a;安装运行时库&#xff0c;并测试代理能否正常启动,步骤如下&#xff1a; ① 尝试双击代理进程&#xff…

WPF 6 命令

命令 创建一个按钮&#xff0c;新建一个事件&#xff0c;按住F12 就可以添加业务代码 运行代码 此时希望UI与后台代码分离&#xff0c;互不影响 此时新建一个MainViewModel类&#xff0c;来保存业务代码 Icommand 是所有command的父类接口 新建一个command来实现这个接口…

OceanBase诊断调优 】—— 如何快速定位SQL问题

作者简介&#xff1a; 花名&#xff1a;洪波&#xff0c;OceanBase 数据库解决方案架构师&#xff0c;目前负责 OceanBase 数据库在各大型互联网公司及企事业单位的落地与技术指导&#xff0c;曾就职于互联网大厂和金融科技公司&#xff0c;主导过多项数据库升级、迁移、国产化…

蓝桥杯:日期问题(我的绝望题)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;每日一练 &#x1f337;追光的人&#xff0c;终会万丈光芒 目录 前言&#xff1a; &#x1f337;1.问题描述&#xff1a; 1.问题描述&#xff1a; 2.输入格式&#xff1a; 3.输出格式&#…

HarmonyOS开发案例:【相机开发】

基本概念 相机是OpenHarmony多媒体进程提供的服务之一&#xff0c;提供了相机的录像、预览、拍照功能&#xff0c;支持多用户并发取流。 在进行应用的开发前&#xff0c;开发者应了解以下基本概念&#xff1a; 视频帧 视频流指的是将一系列图片数据按照固定时间间隔排列形成的…

探索设计模式的魅力:主从模式与AI大模型的结合-开启机器学习新纪元

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 ✨欢迎加入探索主从模式与AI大模型之旅✨ &#x1f31f;Hey, tech enthusiasts! 你是否还在追…

AI论文速读 |2024[TPAMI]【综述】自监督学习在时间序列分析的分类、进展与展望

题目&#xff1a; Self-Supervised Learning for Time Series Analysis: Taxonomy, Progress, and Prospects 作者&#xff1a;Kexin Zhang, Qingsong Wen(文青松), Chaoli Zhang, Rongyao Cai, Ming Jin(金明), Yong Liu(刘勇), James Zhang, Yuxuan Liang(梁宇轩), Guansong…

运维 kubernetes(k8s)基础学习

一、容器相关 1、发展历程&#xff1a;主机–虚拟机–容器 主机类似别墅的概念&#xff0c;一个地基上盖的房子只属于一个人家&#xff0c;很多房子会空出来&#xff0c;资源比较空闲浪费。 虚拟机类似楼房&#xff0c;一个地基上盖的楼房住着很多人家&#xff0c;相对主机模式…

【python程序打包教程】PyInstaller一键打包Python程序为独立可执行exe文件

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…