Android工程化实践:组件化发布知识汇总

发表于 1年以前  | 总阅读数:1603 次

之前看了本文就收藏了起来,主要文中描述到了一个把本地 aar 上传到 maven 仓库的办法,之前有遇到过这个问题,没有正面面对规避了一下,看来是时候正面解决了,好了,阅读正文吧。

前言

如今,项目开发已经不再是单兵作战的时代,而往往是多团队、多组件协同开发。此时,我们会发布组件 & 管理组件的技巧。

在这篇文章里,我将带你理解组件的基本概念,以及组件发布 & 快照预览 & 依赖切换的实战应用经验。如果能帮上忙,请务必点赞加关注,这对我真的非常重要。

目录

前置知识

这篇文章的内容会涉及以下前置 / 相关知识,贴心的我都帮你准备好了,请享用~

Gradle | 进阶篇(Project & Task & 构建生命周期)

https://juejin.cn/post/6917486983946338318

Gradle | 手把手自定义 Gradle 插件

https://juejin.cn/post/6916687373208125447

概念剖析

1.1 什么是 POM?

POM(Project Object Model)指项目对象模型,用于描述项目构件的基本信息。一个有效的 POM 节点中主要包含以下信息:

配置 描述 举例('com.github.bumptech.glide:glide:4.11.0')
groupId 组织 / 公司的名称 com.github.bumptech.glide
artifactId([ˈɑːtɪfækt]) 组件的名称 glide
version 组件的版本 4.11.0
packaging 打包的格式 aar

1.2 什么是仓库(repository)?

在项目中,我们会需要依赖各种各样的二方库或三方库,这些依赖一定会存放在某个位置(Place),这个 “位置” 就叫做仓库。使用仓库可以帮助我们管理项目构件,例如 jar、aar 等等。

主流的构建工具都有三个层次的仓库概念:

1、本地仓库: 无论使用 Linux 还是 Window,计算机中会有一个目录用来存放从中央仓库或远程仓库下载的依赖文件。

2、中央仓库: 开源社区提供的仓库,是绝大多数开源库的存放位置。比如 Maven 社区的中央仓库 Maven Central。

https://search.maven.org/

3、私有仓库: 公司或组织的自定义仓库,可以理解为二方库的存放位置。

构建时搜索依赖的顺序如下:

1、在本地仓库搜索,如果搜索不到,执行步骤 2。

2、在中央仓库和私有仓库中搜索,搜索顺序按照repositories中声明的顺序依次查找。如果找到,则下载依赖文件到本地仓库,否则执行步骤 3。

3、如果最终找不到依赖项,则抛出错误 “无法找到依赖项”。

如何在项目中声明仓库:

Gradle 默认的本地仓库目录:C:\Users\Administrator\.gradle\caches\modules-2\files-2.1,Gradle 不会默认执行远程仓库和中央仓库,需要在项目级或模块级 build.gradle 文件中声明。例如:

项目级别 build.gradle

buildscript {
    repositories {
        [Gradle 插件的仓库]        
    }
}
allprojects {
    repositories {
        [项目中所有模块依赖的仓库]  
    }
}

模块级别 build.gradle

repositories{
    [当前模块依赖的仓库]
}

Gradle 支持多种类型的仓库,例如 Maven、ivy、flatDir。其中 flatDir 一般用于指定本地 aar 文件的地址。更多分析在 第 4.2 节。

repositories{
    maven { url '...' }
    ivy { url '...' }
    flatDir { dirs '...' }
}

Gradle 内置了一些常用中央仓库的路径,可以直接通过函数获取。例如:

google()        // https://dl.google.com/dl/android/maven2/
mavenCentral()   // https://repo.maven.apache.org/maven2/
jCenter()       // https://jcenter.bintray.com/

有时候,直接访问中央仓库的速度太慢,此时可以尝试替换为国内大厂的中央仓库镜像。例如:

maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' }

1.3 什么是 Gradle 插件?

「Gradle 插件」和「Gradle」这两个概念是比较容易混淆的。Gradle 是构建工具,相当于一个构建环境;而 Gradle 插件本质上就是具体的构建任务,我们将一个构建任务模块化抽离出来,提供给其他项目复用,就是一个 Gradle 插件。例如:

com.android.application:Android 应用插件

com.android.library:Android 模块插件

kotlin-android:Android Kotlin 插件

maven & maven-pulish:Maven 插件

1.4 快照(SNAPSHOT)有什么用?

快照是一种特殊的版本,与常规版本最大的不同是:快照版本每次构建时都会在远程仓库中检查最新的快照。

快照版本:1.0.0-SNAPSHOT

常规版本:1.0.0

为什么会有这种设计呢(牺牲编译时间)?因为在大型软件项目中,往往是多个团队(或多个同学)协同开发不同模块,例如 A 模块依赖 B 模块,两个模块并行开发。如果模块 B 不使用快照版本(例如版本为 1.0.0),那么当 B 模块在开发阶段需要更新,A 模块就无法接收到更新。因为 A 模块本地仓库中已经下载了 B 模块的 1.0.0 版本,所以构建时不会重复去下载远程仓库中更新的版本。

直接的解决办法可以清除 A 模块的本地仓库缓存,或者每次 B 模块更新都升级版本,很显然两个办法都不灵活,频繁升级版本也是对版本号的滥用,不利于版本管理。而如果模块 B 使用快照版本(1.0.0-SNAPSHOT),A 模块每次构建都会去检查远程仓库是否有 B 模块的新快照,就可以保证一直依赖 B 模块的最新版本。

总的来说,SNAPSHOT 适合快节奏协同开发阶段,代表着不稳定 & 开发中的版本。常规版本适合于正式发布版本,如果正式版本使用 SNAPSHOT,会导致重复构建正式版本不稳定。

Maven 构建生命周期

我们需要使用 Maven 插件来发布类库,简单理解下 Maven 构建的生命周期,主要分为以下个步骤:

任务 阶段 描述
compile 编译 编译源代码
test 测试 执行单元测试
package 打包 创建发布组件,如 jar、aar
install 安装 安装组件包到本地仓库
deploy / upload 部署 上传组件包到远程仓库

—— 图片引用自网络

如何发布组件(artifacts)?

在 Gradle 中发布组件,可以使用以下两个 Maven 插件:

Maven Plugin(旧版)

https://docs.gradle.org/4.8/userguide/maven_plugin.html

Maven Publish Plugin

https://docs.gradle.org/current/userguide/publishing_maven.html

3.1 发布到本地仓库

我们需要使用 Maven 插件的uploadArchives任务,并且需要指定组件的信息。例如:

模块级 build.gradle

plugins {
    id 'com.android.library'
    id 'kotlin-android'
    id 'maven'
}
...
uploadArchives {
    repositories {
        mavenDeployer {
            // 发布地址:直接发布到项目本地路径
            repository(url: uri('../repository'))
            // 组件信息:com.pengxr.demo:maven:v1.0.0
            pom.groupId = "com.pengxr.demo"
            pom.artifactId = "maven"
            pom.version = "v1.0.0"
        }
    }
}

执行 Gradle Sync 之后,就可以在 Gradle 窗口该模块的 Tasks 列表中找到名为uploadArchives的任务。执行任务,完成后项目中会新增一个repository目录,里面就是新发布的组件。

注意事项:

1、升级到 Android Stidio 4.2 之后,如果在 Gradle 栏目中找不到 Task 列表,在设置里取消勾选此项即可。

2、无法发布应用模块。

plugins {
    id 'com.android.application' // 无法发布应用模块
    id 'kotlin-android'
    id 'maven'
}

3.2 使用 nexus 搭建私有仓库

发布组件到本地仓库只能单机使用,在实际工作中,我们往往需要将组件发布给其他团队成员使用。此时,可以将组件发布到 局域网私有仓库。最常见的私有仓库管理工具是 Nexus [ˈneksəs]。按照以下步骤搭建环境:

1、下载 Nexus 安装包: 这里以 Mac 环境为例:下载地址:

https://help.sonatype.com/repomanager3/download

2、启动 Nexus 服务进程: 进入安装路径/nexus-3.30.1-01/bin,在终端运行命令:

./nexus start
./nexus status

输出:nexus is running. 表示启动成功

需要停止服务时,可以执行命令:
./nexus stop

3、浏览器打开 http://127.0.0.1:8081/,进入 Nexus 管理页面。

4、点击右上角 Sign in 登录: 默认账号名是 admin,首次登录会弹窗提示密码的存储位置(根据指示到相应路径下的文件中找到密码复制粘贴过来),登录成功后界面如下:

这个列表包含了所有的 Nexus 仓库,点击 “Copy” 按钮,可以复制仓库的 URL 地址。其中两个仓库比较常用:

maven-release:策略为 Release 的宿主类型仓库,用于部署内部组件的发布版本;

maven-snapshots:策略为 Shapshot 的宿主类型仓库,用于部署内部组件的快照版本。

类型(Type):group(仓库组)、hosted(宿主)、proxy(代理)和 virtual(虚拟);

格式(Format):maven1、maven2、nuget

5、发布到指定仓库: 在模块级 build.gradle 中增加配置。

模块级 build.gradle

apply plugin: 'maven' // Maven 插件
...
uploadArchives {
    repositories {
        mavenDeployer {
            // url:仓库路径
            // userName:账号名
            // password:密码
            repository(url: "http://127.0.0.1:8081/repository/maven-releases/"){
                authentication(userName: "admin", password: "pengxurui123")
            }

            pom.groupId = "com.pengxr.demo"
            pom.artifactId = "maven"
            pom.version = "v1.0.0"
        }
    }
}

执行任务,发布成功后可以在 nexus 管理平台上看到新发布的类库:

6、依赖类库: 在项目级 build.gradle 声明远程仓库,在模块级 build.gradle 中依赖类库。

项目级 build.gradle

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url "http://127.0.0.1:8081/repository/maven-releases/" }
    }
}

模块级 build.gradle

dependencies {
    ...
    implementation 'com.github.pengxurui:MavenPuhlish:v1.0.4'
}

提示: 当然了,实际项目中 nexus 不可能配置在本机上,而是会配置在局域网服务器中。

3.3 发布到 Github 仓库

如果你需要将开源,那么就需要发布到公共仓库,这一节介绍发布到 Github 的步骤:

1、依赖 Github Maven 插件: 在项目级 build.gradle 中添加插件依赖。

项目级 build.gradle

dependencies {
    ...
    classpath "com.github.dcendents:android-maven-gradle-plugin:1.5" // // GitHub Maven 插件
}

2、应用 Github Maven 插件: 在发布模块的 build.gradle 中应用插件。

模块级 build.gradle

apply plugin: 'com.github.dcendents.android-maven' // GitHub Maven 插件

3、声明 group: 同时在发布模块的 build.gradle 中声明组件的 groupId。

模块级 build.gradle

apply plugin: 'com.github.dcendents.android-maven' // GitHub Maven 插件
group = 'com.github.pengxurui' // github 的用户名

4、将项目 push 到 Github。

5、在 Github 上创建 Release Tag(在本地创建 Tag 再推到 Gtihub 也一样)。

6、将项目上传到 JitPack: 打开 jitpack.io/,将项目链接复制到输入… Look up,等待编译完成。到这里就完成发布了。

https://jitpack.io/

7、依赖类库: 在项目级 build.gradle 声明远程仓库,在模块级 build.gradle 中依赖类库。

项目级 build.gradle

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url "https://jitpack.io" }
    }
}

模块级 build.gradle

dependencies {
    ...
    implementation 'com.github.pengxurui:MavenPuhlish:v1.0.4'
}

踩坑记录:

模块 build.gradle 中应用了 Github Maven 插件后,要去掉 uploadArchives 任务,否则构建会报错。

Exception is:
java.lang.IllegalAccessError: tried to access method org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.<init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V from class org.gradle.api.plugins.AndroidMavenPlugin$8

3.4 指定发布二进制文件

使用新版 Maven 插件,可以直接以指定二进制文件的方式发布组件。例如:

apply plugin: 'maven-publish'

publishing {
    publications {
        [任务名](MavenPublication) {
            groupId MAVEN_GROUP_ID
            artifactId MAVEN_ARTIFACTID
            version MAVEN_VERSION
            artifact([文件路径])
        }
    }
    repositories {
        maven {
            // 发布仓库路径
            url MAVEN_RELEASE_URL
        }
    }
}

实战应用

4.1 封装通用发布脚本

随着项目组件化程度加深,越来越多组件需要发布到 Maven 仓库,此时就很有必要将 Maven 发布能力封装为一个通用脚本,步骤如下:

步骤 1:封装发布脚本:

apply plugin: 'maven'

uploadArchives {
    repositories {
        mavenDeployer {
            // 是否快照版本
            def isSnapShot = Boolean.valueOf(MAVEN_IS_SNAPSHOT)
            def versionName = MAVEN_VERSION
            if (isSnapShot) {
                versionName += "-SNAPSHOT"
            }
            // 组件信息
            pom.groupId = MAVEN_GROUP_ID
            pom.artifactId = MAVEN_ARTIFACTID
            pom.version = versionName

            // 快照仓库路径
            snapshotRepository(url: uri(MAVEN_SNAPSHOT_URL)) {
                authentication(userName: MAVEN_USERNAME, password: MAVEN_USERNAME)
            }
            // 发布仓库路径
            repository(url: uri(MAVEN_RELEASE_URL)) {
                authentication(userName: MAVEN_USERNAME, password: MAVEN_USERNAME)
            }

            println("###################################"
                    + "\nuploadArchives = " + pom.groupId + ":" + pom.artifactId + ":" + pom.version + "." + pom.packaging
                    + "\nrepository =" + (isSnapshot ? MAVEN_SNAPSHOT_URL : MAVEN_RELEASE_URL)
                    + "\n###################################"
            )
        }
    }
}

这段脚本会读取 MAVEN_IS_SNAPSHOT 配置参数,如果为 true,会在版本号后追加 -SNAPSHOT 后缀,表示快照版本。随后声明了两个仓库:repository(...) 声明的是 Release 仓库地址,而snapshotRepository(...) 声明的是快照仓库地址。Maven 会自动将版本号带 -SNAPSHOT 后缀的组件发布到 snapshotRepository(...) 仓库中,这样就 自动将正式版本和快照版本分发的不同仓库中。

当然了,不用 snapshotRepository(...) 也有办法实现:

def url = isSnapShot ? MAVEN_SNAPSHOT_URL : MAVEN_RELEASE_URL
repository(url: url) {
    authentication(userName: MAVEN_USERNAME, password: MAVEN_USERNAME)
}步骤 2:声明项目级配置参数:

项目级 gradle.properties

MAVEN_SNAPSHOT_URL = /Users/pengxurui/workspace/public/DemoHall/snapshotRepository
MAVEN_RELEASE_URL = /Users/pengxurui/workspace/public/DemoHall/releaseRepository
MAVEN_USERNAME = 
MAVEN_PASSWORD = 
MAVEN_IS_SNAPSHOT = true
MAVEN_GROUP_ID = com.pengxr.demo
...
参数 描述
MAVEN_SNAPSHOT_URL 快照仓库地址
MAVEN_RELEASE_URL 发布仓库地址
MAVEN_USERNAME 仓库账号
MAVEN_PASSWORD 仓库密码
MAVEN_IS_SNAPSHOT 是否快照版本
MAVEN_GROUP_ID 组织 / 公司的名称
MAVEN_ARTIFACTID 组件的名称(在发布模块配置)
MAVEN_VERSION 组件的版本(在发布模块配置)

步骤 3:在发布模块应用脚本

模块级 build.gradle

apply from: '../maven.gradle'
...

步骤 4:在发布模块配置参数 (模块级配置参数会覆盖项目级配置参数)

模块级 gradle.properties

MAVEN_ARTIFACTID = maven
MAVEN_VERSION = v1.0.0
MAVEN_IS_SNAPSHOT = true
...

完成以上步骤并 Sync 后,就可以在 Gradle 窗口中该模块下找到 uploadArchives 任务,执行发布:

输出:

Executing tasks: [uploadArchives] in project /Users/pengxurui/workspace/public/DemoHall/MavenPublish/lib

> Configure project :lib
###################################
uploadArchives = com.pengxr.demo:maven:v1.0.0-SNAPSHOT.jar
repository =/Users/pengxurui/workspace/public/DemoHall/snapshotRepository
###################################

> Task :lib:preBuild UP-TO-DATE

...

步骤 5:依赖组件: 在项目级 build.gradle 中声明依赖仓库,在模块级 build.gradle 中声明依赖:

项目级 build.gradle

allprojects {
    repositories {
        maven { url MAVEN_RELEASE_URL }
        maven { url MAVEN_SNAPSHOT_URL }
        ...
    }
}

模块级 build.gradle

dependencies {
    implementation "com.pengxr.demo:maven:v1.0.0+"
}

其中,版本号 v1.0.0+ 中的 “+” 号表示依赖最大的版本号,优先正式版本。比如远程仓库中存在 v1.0.0,v1.0.0.1,v1.0.0.1-SNAPSHOT 三个类库,那么 v1.0.0+ 依赖的是其 v1.0.0.1。

+ 号和 -SNAPSHOT 的区别?

+ 号影响类库版本的选择,而 -SNAPSHOT 影响是否向远程仓库更新最新版本。

完整代码和演示工程你可以直接下载查看:MavenPublish 下载路径。Demo 里配置的仓库都为本地仓库,在实际项目中,你需要替换为你公司内的私有仓库。

https://github.com/pengxurui/DemoHall

4.2 引用本地 aar 包

有时候,我们直接依赖第三方或第二方提供的 aar 文件。例如:

- aarlib
  \ libs
      - lib-debug-aar
  - build.gradle // api(name: 'lib-debug', ext: 'aar')

输出:Unable to resolve dependency for ':aarlib@debugUnitTest/compileClasspath': Could not resolve :lib-debug.

但是,这样并不能成功依赖。你需要 build.gradle 文件中声明 aar 的 Flat Directory 仓库地址。你可以放在 android{} 节点内,或者直接放在根节点,效果是一样的。例如:

aarlib 模块 build.gradle

dependencies {
    ...
    api(name: 'lib-debug', ext: 'aar')
}

repositories {
    flatDir {
        dirs "libs"
    }
}

现在你就可以成功依赖了。但如果存在另一个依赖 aarlib 的模块,而这个模块又需要依赖 lib-debug.aar,还是会出依赖不到的问题:

- app
  - build.gradle // implementation project(':aarlib')
|
- aarlib
  \ libs
      - lib-debug-aar
  - build.gradle // api(name: 'lib-debug', ext: 'aar')

此时,你同样需要在 app 模块里声明 aar 的 Flat Directory 仓库地址。

app 模块 build.gradle

dependencies {
    ...
    implementation project(':aarlib')
}

repositories {
    flatDir {
        dirs project(':aarlib').file('libs')
    }
}

4.3 引用本地 aar 包(优化)

如果项目组件结构比较简单,第 4.2 节的方法就足够应对本地引用 aar 的问题。否则还是会遇到一些麻烦的,你需要在每个模块的 build.gradle 中都声明 repositories.flatDir{},有办法优化吗?

方法 1:直接依赖改为间接依赖: 新建模块封装 aar,对外部提供外观 API。

方法 2:统一将 aar 文件放置在一个文件夹,并在项目级 build.gradle 中声明仓库地址。

项目级 build.gradle

allprojects {
    repositories {
        google()
        mavenCentral()
        flatDir { dirs project(':aarlib').file('libs') } // 文件夹要放在某个 module 内
    }
}

模块级 build.gradle

api(name: 'lib-debug', ext: 'aar') // 允许间接依赖 aar
implementation(name: 'lib-debug', ext: 'aar') // 不允许间接依赖 aar

方法 3:二次打包 aar: 以上方法在单工程项目下表现良好,但在如果你们的项目包括多个工程,那还是有点麻烦的,有办法优化吗?你可以对 aar 文件二次打包,并发布到 Maven 仓库,这样你就不需要声明 Flat 本地仓库。

- aarpacker
  \ libs
      - lib.aar
      - lib2.aar
  - build.gradle

aarpacker build.gradle

apply plugin: 'maven-publish'

def libPath = project.getProjectDir().getAbsolutePath()

publishing {
    publications {
        lib1(MavenPublication) {
            groupId MAVEN_GROUP_ID
            artifactId "lib"
            version "v1.0.0"
            artifact(libPath + "/libs/lib.aar")
        }

        lib2(MavenPublication) {
            groupId MAVEN_GROUP_ID
            artifactId "lib2"
            version "v1.0.0"
            artifact(libPath + "/libs/lib2.aar")
        }
    }
    repositories {
        maven {
            // 发布仓库路径
            url MAVEN_RELEASE_URL

            // 本地仓库地址不适用账号密码
            // > Failed to publish publication 'maven' to repository 'maven'
            // > Authentication scheme 'all'(Authentication) is not supported by protocol 'file'
            // credentials(PasswordCredentials) {
            //     username = MAVEN_USERNAME
            //     password = MAVEN_PASSWORD
            // }
        }
    }
}

4.4 宿主工程调试类库

增加开关字段:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    if (useLocalLib.toBoolean())
        implementation project(":lib")
    else
        implementation 'com.pengxr.demo:maven:v1.0.0+'
}

本文由哈喽比特于1年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/wFnfV-fKkWlrZMfrhb6dyQ

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:6月以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:6月以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:6月以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:6月以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:6月以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:6月以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:6月以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:6月以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:6月以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:6月以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:6月以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:6月以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:6月以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:6月以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:6月以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:6月以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:6月以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:6月以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:6月以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:6月以前  |  398次阅读  |  详细内容 »
 相关文章
简化Android的UI开发 4年以前  |  520651次阅读
Android 深色模式适配原理分析 3年以前  |  28546次阅读
Android阴影实现的几种方案 1年以前  |  10653次阅读
Android 样式系统 | 主题背景覆盖 3年以前  |  9519次阅读
 目录