网易首页 > 网易号 > 正文 申请入驻

Android Q 存储新特性适配脑壳疼?指南来了!

0
分享至

原文:

https://mp.weixin.qq.com/s/aiDMyAfAZvaYIHuIMLAlcg

简单回顾下:Android Q 适配 之 存储新特性

接下来看看存储新特性的适配啦~

继续第二章,且看第二回~

2. 存储空间限制

2.3 适配指导

Android Q Scoped Storage 新特性谷歌官方适配文档:

https://developer.android.google.cn/preview/privacy/scoped-storage

OPPO 适配指导如下,分为:访问 APP 自身 App-specific 目录文件、使用 MediaStore 访问公共目录、使用 SAF 访问指定文件和目录、分享 App-specific 目录下文件和其他细节适配。

2.3.1 访问 APP 自身 App-specific 目录文件

无需任何权限,APP 即可直接使用文件路径来读写自身 App-specific 目录下的文件。获取 App-specific 目录路径的接口如下表所示。

如下,以新建并写入文件为例。

// set "Documents" as subDir
final File[] dirs = getExternalFilesDirs("Documents");
File primaryDir = null;
if (dirs != null && dirs.length > 0) {
primaryDir = dirs[0];
}
if (primaryDir == null) {
return;
}
File newFile = new File(primaryDir.getAbsolutePath(), "MyTestDocument");
OutputStream fileOS = null;
try {
fileOS = new FileOutputStream(newFile);
if (fileOS != null) {
fileOS.write("file is created".getBytes(StandardCharsets.UTF_8));
fileOS.flush();
}
} catch (IOException e) {
LogUtil.log("create file fail");
} finally {
try {
if (fileOS != null) {
fileOS.close();
}
} catch (IOException e1) {
LogUtil.log("close stream fail");
}
}

2.3.2 使用 MediaStore 访问公共目录

APP 无法直接访问公共目录下的文件。MediaStore 为 APP 提供了访问公共目录下媒体文件的接口。APP 在有适当权限时,可以通过 MediaStore 查询到公共目录文件的 Uri,然后通过 Uri 读写文件。

MediaStore 相关的 Google 官方文档:

https://developer.android.google.cn/reference/android/provider/MediaStore

2.3.2.1 MediaStore 的 Uri 和路径对照表

MediaStore 提供了下列几种类型的访问 Uri,通过查询对应 Uri 数据(在 MediaProvider 中),达到访问的目的。

下列每种类型又分为三种 Uri:Internal、External、可移动存储。

在 Android Q 上,所有的外部存储设备,包括内置卡、SD 卡等,都会被命名,即设备的 Volume Name。MediaStore 可以通过 Volume Name 获取对应存储设备的 Uri。

for (String volumeName : MediaStore.getExternalVolumeNames(this)){
MediaStore.Images.Media.getContentUri(volumeName);
}

MediaProvider 对于 APP 新建到公共目录的文件,通过 ContentResolver.insert 方法中的 Uri 来确定具体存放目录。其中下表中

content://media//>

2.3.2.2 APP 通过 MediaStore 访问文件所需要的权限

通过 MediaStore 提供的 Uri,使用 ContentResolver 的 insert 接口,将文件保存到公共目录下。不同的 Uri,可以保存到不同的公共目录中,详见 2.3.2.1。

ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DESCRIPTION, "This is an image");
values.put(MediaStore.Images.Media.DISPLAY_NAME, "Image.png");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
values.put(MediaStore.Images.Media.TITLE, "Image.png");
values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/test");
Uri external = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver resolver = context.getContentResolver();
Uri insertUri = resolver.insert(external, values);
LogUtil.log("insertUri: " + insertUri);
OutputStream os = null;
try {
if (insertUri != null) {
os = resolver.openOutputStream(insertUri);
}
if (os != null) {
final Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
bitmap.compress(Bitmap.CompressFormat.PNG, 90, os);
// write what you want
}
} catch (IOException e) {
LogUtil.log("fail: " + e.getCause());
} finally {
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
LogUtil.log("fail in close: " + e.getCause());
}
}

2.3.2.4 使用 MediaStore 查询文件

用 MediaStore 提供的 Uri 指定设备,selection 参数指定过滤条件,通过 ContentResolver.query 接口查询文件 Uri。

Uri external = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver resolver = context.getContentResolver();
String selection = MediaStore.Images.Media.TITLE + "=?";
String[] args = new String[] {"Image"};
String[] projection = new String[] {MediaStore.Images.Media._ID};
Cursor cursor = resolver.query(external, projection, selection, args, null);
Uri imageUri = null;
if (cursor != null && cursor.moveToFirst()) {
imageUri = ContentUris.withAppendedId(external, cursor.getLong(0));
cursor.close();
}

2.3.2.5 使用 MediaStore 读取文件

通过以上查询方式得到 Uri 之后,通过以下方式读取文件:

1)通过 ContentResolver openFileDescriptor 接口,选择对应的打开方式。例如”r” 表示读,”w” 表示写,返回 ParcelFileDescriptor 类型的文件描述符。

ParcelFileDescriptor pfd = null;
if (imageUri != null) {
try {
pfd = context.getContentResolver().openFileDescriptor(imageUri, "r");
if (pfd != null) {
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
// show the bitmap, or do something else.
}
} catch (IOException e) {
LogUtil.log("fail: " + e.getCause());
} finally {
try {
if (pfd != null) {
pfd.close();
}
} catch (IOException e) {
LogUtil.log("fail in close: " + e.getCause());
}
}
}

2)访问 Thumbnail,使用 ContentResolver.loadThumbnail 接口。通过传入 size 参数,MediaProvider 返回指定大小的 Thumbnail。

3)Native 代码访问文件

如果 Native 代码需要访问文件,可以参考下面方式:

  • 通过 openFileDescriptor 返回 ParcelFileDescriptor

  • 通过 ParcelFileDescriptor.detachFd() 读取 FD

  • 将 FD 传递给 Native 层代码

  • 通过 close 接口关闭 FD

String fileOpenMode = "r";
ParcelFileDescriptor parcelFd = resolver.openFileDescriptor(uri, fileOpenMode);
if (parcelFd != null) {
int fd = parcelFd.detachFd();
// Pass the integer value "fd" into your native code. Remember to call
// close(2) on the file descriptor when you're done using it.
}

2.3.2.6 使用 MediaStore 修改文件

根据查询得到的文件 Uri,使用 MediaStore 修改其他 APP 新建的多媒体文件,需要 catch RecoverableSecurityException ,由 MediaProvider 弹出弹框给用户选择是否允许 APP 修改或删除图片 / 视频 / 音频文件。用户操作的结果,将通过 onActivityResult 回调返回到 APP。如果用户允许,APP 将获得该 Uri 的修改权限,直到设备下一次重启。

根据文件 Uri,通过下列接口,获取需要修改文件的 FD 或者 OutputStream:

1)getContentResolver().openOutputStream(contentUri)

获取对应文件的 OutputStream。

2)getContentResolver().openFile 或者 getContentResolver().openFileDescriptor

通过 openFile 或者 openFileDescriptor 打开文件,需要选择 Mode 为”w”,表示写权限。这些接口返回一个 ParcelFileDescriptor。

OutputStream os = null;
try {
if (imageUri != null) {
os = resolver.openOutputStream(imageUri);
}
} catch (IOException e) {
LogUtil.log("open image fail");
} catch (RecoverableSecurityException e1) {
LogUtil.log("get RecoverableSecurityException");
try {
((Activity) context).startIntentSenderForResult(
e1.getUserAction().getActionIntent().getIntentSender(),
100, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e2) {
LogUtil.log("startIntentSender fail");
}
}

2.3.2.7 使用 MediaStore 删除文件

删除其他 APP 新建的媒体文件,与修改类似,需要用户授权。删除文件使用 ContentResolver.delete 接口。

getContentResolver().delete(imageUri, null, null);

2.3.3 使用 SAF 访问指定文件和目录

SAF,即 Storage Access Framework。根据当前系统中存在的 DocumentsProvider,让用户选择特定的文件或文件夹,使调用 SAF 的 APP 获取它们的读写权限。APP 通过 SAF 获得文件或目录的读写权限,无需申请任何存储相关的运行时权限。

SAF 相关的 Google 官方文档:

https://developer.android.com/guide/topics/providers/document-provider

使用 SAF 获取文件或目录权限的过程:

APP 通过特定 Intent 调起 DocumentUI -> 用户在 DocumentUI 界面上选择要授权的文件或目录 -> APP 在回调中解析文件或目录的 Uri,最后根据这一 Uri 可进行读写删操作。

2.3.3.1 使用 SAF 选择单个文件

使用 Intent.ACTION_OPEN_DOCUMENT 调起 DocumentUI 的文件选择页面,用户可以选择一个文件,将它的读写权限授予 APP。

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
// you can set type to filter files to show
intent.setType("*/*");
startActivityForResult(intent, REQUEST_CODE_FOR_SINGLE_FILE);

2.3.3.2 使用 SAF 修改文件

通过 2.3.3.1 的方式,用户选择文件授权给 APP 后,在 APP 的 onActivityResult 回调中收到返回结果,解析出对应文件的 Uri。然后使用该 Uri,用户可以获取可写的 ParcelFileDescriptor 或者打开 OutputStream 进行修改。

if (requestCode == REQUEST_CODE_FOR_SINGLE_FILE && resultCode == Activity.RESULT_OK) {
Uri fileUri = null;
if (data != null) {
fileUri = data.getData();
}
if (fileUri != null) {
OutputStream os = null;
try {
os = getContentResolver().openOutputStream(fileUri);
os.write("something".getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
LogUtil.log("modify document fail");
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e1) {
LogUtil.log("close fail");
}
}
}
}
}

2.3.3.3 使用 SAF 删除文件

类似修改文件,在回调中解析出文件 Uri,然后使用 DocumentsContract.deleteDocument 接口进行删除操作。

if (requestCode == REQUEST_CODE_FOR_SINGLE_FILE && resultCode == Activity.RESULT_OK) {
Uri fileUri = null;
if (data != null) {
fileUri = data.getData();
}
if (fileUri != null) {
try {
DocumentsContract.deleteDocument(getContentResolver(), fileUri);
} catch (FileNotFoundException e) {
LogUtil.log("delete document fail");
}
}
}

2.3.3.4 使用 SAF 新建文件

APP 通过 Intent.ACTION_CREATE_DOCUMENT 调起 DocumentUI 界面,由用户决定文件命名,以及存放位置。

Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
// you can set file mimetype
intent.setType("*/*");
// default file name
intent.putExtra(Intent.EXTRA_TITLE, "myFileName");
startActivityForResult(intent, REQUEST_CODE_FOR_CREATE_FILE);

在用户确定后,操作结果将返回到 APP 的 onActivityResult 回调中,APP 解析出文件 Uri,之后就可以利用这一 Uri 对文件进行读写删操作。

if (requestCode == REQUEST_CODE_FOR_CREATE_FILE && resultCode == Activity.RESULT_OK) {
Uri fileUri = null;
if (data != null) {
fileUri = data.getData();
}
// read/update/delete by the uri got here.
LogUtil.log("uri: " + fileUri);
}

2.3.3.5 使用 SAF 选择目录

通过 Intent.ACTION_OPEN_DOCUMENT_TREE 调起 DocumentUI 界面,用户可以选择任意文件夹,将它及其子文件夹的读写权限授予 APP。

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE_FOR_DIR);

在右上角的菜单中选择 show internal storage,可以在左侧菜单中选择内置存储设备,接着用户可以选择内置存储设备中的任意文件夹。

在用户确定后,APP 的 onActivityResult 回调收到操作结果,解析出被选文件夹的 uriTree。根据这一 uriTree ,进一步可以生成表示被选文件夹的 DocumentFile,利用 DocumentFile 提供的 API 可以对目录下的文件进行各种操作。

if (requestCode == REQUEST_CODE_FOR_DIR && resultCode == Activity.RESULT_OK) {
Uri uriTree = null;
if (data != null) {
uriTree = data.getData();
}
if (uriTree != null) {
// create DocumentFile which represents the selected directory
DocumentFile root = DocumentFile.fromTreeUri(this, uriTree);
// list all sub dirs of root
DocumentFile[] files = root.listFiles();
// do anything you want with APIs provided by DocumentFile
// ...
}
}

2.3.3.6 永久保存获取的目录权限

在 2.3.3.5 中,通过 SAF 获取了用户指定目录的读写权限,直至设备下一次重启。APP 可以通过 takePersistableUriPermission 接口获取该 uriTree 的永久权限,并将 uriTree 以 SharedPreferences 等形式持久化保存,以备之后随时使用。

if (uriTree != null) {
// get persistable uri permission
final int takeFlags = data.getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(uriTree, takeFlags);
// save uriTree to sharedPreference
SharedPreferences sp = getSharedPreferences("DirPermission", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("uriTree", uriTree.toString());
editor.commit();
}

在使用保存的 uriTree 时,首先检查是否顺利从 SharedPreferences 中获取到 uriTree,然后通过 takePersistableUriPermission 接口是否抛异常来判断权限是否仍存在。如果权限不存在,则重新通过 SAF 申请权限。

SharedPreferences sp = getSharedPreferences("DirPermission", Context.MODE_PRIVATE);
String uriTree = sp.getString("uriTree", "");
if (TextUtils.isEmpty(uriTree)) {
startSafForDirPermission();
} else {
try {
Uri uri = Uri.parse(uriTree);
final int takeFlags = getIntent().getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(uri, takeFlags);
// uri tree permission is granted, do what you want with this uri
LogUtil.log("uri is granted");
DocumentFile root = DocumentFile.fromTreeUri(this, uri);
} catch (SecurityException e) {
LogUtil.log("uri is not granted");
startSafForDirPermission();
}
}

APP 申请到目录的永久权限后,用户可以在该 APP 的设置页面取消目录的访问权限,即点击如下图的 “Clear access” 按钮。

2.3.4 分享 App-specific 目录下文件

APP 可以选择以下的方式,将自身 App-specific 目录下的文件分享给其他 APP 读写。

2.3.4.1 使用 FileProvider

APP 可以使用 FileProvider 将私有文件的读写权限赋给其他 APP。这种方式十分适用于 APP 主动发起事件的情况,例如从 APP 将某个私有文件分享给其他 APP。

FileProvider 相关的 Google 官方文档:

  • https://developer.android.google.cn/reference/androidx/core/content/FileProvider

  • https://developer.android.com/training/secure-file-sharing/setup-sharing

自定义 FileProvider 及使用的基本步骤:

1)在 AndroidManifest.xml 中声明 App 的 FileProvider

android:authorities="com.oppo.whoops.fileprovider"
android:
android:grantUriPermissions="true"
android:exported="false">
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths"/>

2)根据 FileProvider 声明中的 meta data,在 res/xml 中新建 filepaths.xml ,用于定义分享的路径。

name represents what other apps see in the shared uri as subdir. -->

3)在 APP 逻辑代码中生成要分享的 uri,设置权限,然后发送 uri。

String filePath = getExternalFilesDir("Documents") + "/MyTestImage.PNG";
Uri uri = FileProvider.getUriForFile(this, "com.oppo.whoops.fileprovider", new File(filePath));
Intent intent = new Intent(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(uri, getContentResolver().getType(uri));
startActivity(Intent.createChooser(intent, "File Provider share"));

4)接收方 APP 的组件设置对应的 intent-filter。

5)接收方 APP 的组件收到 intent,解析获得 uri,通过 uri 获取文件的 FD。

Uri uri = getIntent().getData();
ParcelFileDescriptor pdf = null;
try {
if (uri != null) {
LogUtil.log("Uri: " + uri);
pdf = getContentResolver().openFileDescriptor(uri, "r ");
LogUtil.log("Pdf: " + pdf);
}
} catch (FileNotFoundException e) {
LogUtil.log("open file fail
");
} finally {
try {
if (pdf != null) {
pdf.close();
}
} catch (IOException e1) {
LogUtil.log("close fd fail ");
}
}

2.3.4.2 使用 ContentProvider

APP 可以实现自定义 ContentProvider 来向外提供 APP 私有文件。这种方式十分适用于内部文件分享,不希望有 UI 交互的情况。

ContentProvider 相关的 Google 官方文档:

https://developer.android.google.cn/guide/topics/providers/content-providers

2.3.4.3 使用 DocumentsProvider

Android 默认提供的 ExternalStorageProvider、DownloadStorageProivder 和 MediaDocumentsProvider 会显示在 SAF 调起的 DocumentUI 界面中。ExternalStorageProvider 展示了所有外部存储设备的所有目录及文件,包括 App-specific 目录,所以 App-specific 目录下的文件也可以通过 SAF 授权给其他 APP。

APP 也可以自定义 DocumentsProvider 来提供向外授权。自定义的 DocumentsProivder 将作为第三方 DocumentsProvider 展示在 SAF 调起的界面中。DocumentsProvider 的使用方法请参考官方文档。

DocumentsProvider 相关的 Google 官方文档:

https://developer.android.google.cn/reference/kotlin/android/provider/DocumentsProvider

2.3.5 细节适配

2.3.5.1 图片的地理位置信息

Android Q 上,默认情况下 APP 不能获取图片的地理位置信息。如果 APP 需要访问图片上的 Exif Metadata,需要完成以下步骤:

1)申请 ACCESS_MEDIA_LOCATION 权限。

2)通过 MediaStore.setRequireOriginal 返回新 Uri。

Uri photoUri = Uri.withAppendedPath(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
cursor.getString(idColumnIndex));
final double[] latLong;
// Get location data from the ExifInterface class.
photoUri = MediaStore.setRequireOriginal(photoUri);
InputStream stream = getContentResolver().openInputStream(photoUri);
if (stream != null) {
ExifInterface exifInterface = new ExifInterface(stream);
double[] returnedLatLong = exifInterface.getLatLong();
// If lat/long is null, fall back to the coordinates (0, 0).
latLong = returnedLatLong != null ? returnedLatLong : new double[2];
// Don't reuse the stream associated with the instance of "ExifInterface".
stream.close();
} else {
// Failed to load the stream, so return the coordinates (0, 0).
latLong = new double[2];
}

2.3.5.2 DATA 字段数据不再可靠

MediaStore 中,DATA(即_data)字段,在 Android Q 中开始废弃,不再表示文件的真实路径。读写文件或判断文件是否存在,不应该使用 DATA 字段,而要使用 openFileDescriptor。

同时也无法直接使用路径访问公共目录的文件。

2.3.5.3 MediaStore.Files 接口自过滤

通过 MediaStore.Files 接口访问文件时,只展示多媒体文件(图片、视频、音频)。其他文件,例如 PDF 文件,无法访问到。

2.3.5.4 文件的 Pending 状态

Android Q 上,MediaStore 中添加了一个 IS_PENDING Flag,用于标记当前文件是 Pending 状态。

其他 APP 通过 MediaStore 查询文件,如果没有设置 setIncludePending 接口,就查询不到设置为 Pending 状态的文件,这就能使 APP 专享此文件。

这个 flag 在一些应用场景下可以使用,例如在下载文件的时候:下载中,文件设置为 Pending 状态;下载完成,把文件 Pending 状态置为 0。

ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "myImage.PNG");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
values.put(MediaStore.Images.Media.IS_PENDING, 1);
ContentResolver resolver = context.getContentResolver();
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Uri item = resolver.insert(uri, values);
try {
ParcelFileDescriptor pfd = resolver.openFileDescriptor(item, "w", null);
// write data into the pending image.
} catch (IOException e) {
LogUtil.log("write image fail");
}
// clear IS_PENDING flag after writing finished.
values.clear();
values.put(MediaStore.Images.Media.IS_PENDING, 0);
resolver.update(item, values, null, null);

2.3.5.5 使用 MediaStore 接口定义好的 Columns

在使用 MediaStore 接口时,如果用到 Projection,Column Name 要使用在 MediaStore 中定义好的。如果 APP 引用的库使用了其他 Column Name,需要由 APP 做好 Column Name 映射。

2.3.5.6 设置相对路径

Android Q 上,通过 MediaStore 存储到公共目录的文件,除了 Uri 跟公共目录关系中规定的每一个存储空间的一级目录外,可以通过 MediaColumns.RELATIVE_PATH 来指定存储的次级目录,这个目录可以使多级,具体方法如下:

1)ContentResolver insert 方法

通过 values.put(Media.RELATIVE_PATH, "Pictures/album/family") 指定存储目录。其中,Pictures 是一级目录,album/family 是子目录。

2)ContentResolver update 方法

通过 values.put(Media.RELATIVE_PATH, "Pictures/album/family") 指定存储目录。通过 update 方法,可以移动文件的存储地方。

2.3.5.7 卸载应用

如果 APP 在 AndroidManifest.xml 中声明:android:hasFragileUserData="true",卸载应用会有提示是否保留 APP 数据。默认应用卸载时 App-specific 目录下的数据被删除,但用户可以选择保留。

2.3.5.8 新建虚拟可移动存储

APP 适配时,如果一个设备没有可移动存储,可以使用下面的方法新建虚拟存储设备:

1)命令行

adb shell sm set-virtual-disk true

2)在设置 -> 存储 -> Virtual SD,进行初始化

另外,关于存储权限的(如何启用)影响范围

  • 模拟器在Android Q Beat1中,谷歌暂未开放存储权限的改动。我们需要使用adb命令

    adb shell sm set-isolated-storage on

来开启模拟器对于存储权限的变更来进行适配。

  • 真机当满足以下每个条件时,将开启兼容模式,即不开启Q设备中的存储权限改动:
应用targetSDK<=P。
应用安装在从 Android P 升级到 Android Q 的设备上。

但是当应用重新安装(更新)时,不会重新开启兼容模式,存储权限改动将生效。

所以按官方文档所说,无论targetSDK是否为Q,必须对应用进行存储权限改动的适配。

日问题:

除了适配,还有什么问题脑壳疼?

专属升级社区:《这件事情,我终于想明白了》

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
丰田兰德酷路泽FJ日本上市 约合19万人民币

丰田兰德酷路泽FJ日本上市 约合19万人民币

车质网
2026-05-15 09:09:13
卖不动了,日系车集体退守中国

卖不动了,日系车集体退守中国

21世纪经济报道
2026-05-13 23:28:25
穆帅:我已经收到本菲卡续约报价;目前和皇马没有任何接触

穆帅:我已经收到本菲卡续约报价;目前和皇马没有任何接触

懂球帝
2026-05-15 17:50:14
马斯克长子现状:生活很低调,跟着母亲姓,和马斯克关系很紧张

马斯克长子现状:生活很低调,跟着母亲姓,和马斯克关系很紧张

青橘罐头
2026-05-15 22:03:20
5位北舞走出的女星,北舞老师:我培养的人才,都被拐去当演员了

5位北舞走出的女星,北舞老师:我培养的人才,都被拐去当演员了

阿纂看事
2026-05-15 19:27:53
12小时航程1万公里,全程不加油,特朗普的空军一号,我国能造吗

12小时航程1万公里,全程不加油,特朗普的空军一号,我国能造吗

泠泠说史
2026-05-14 21:50:29
葡媒:穆里尼奥和皇马已经完成签约,下周亮相伯纳乌

葡媒:穆里尼奥和皇马已经完成签约,下周亮相伯纳乌

懂球帝
2026-05-15 14:15:07
啥都和爱国扯到一起,就是一种病

啥都和爱国扯到一起,就是一种病

老唐有话说
2026-05-14 16:18:17
孩子一生最大的福报,就是遇到一个觉醒后的母亲

孩子一生最大的福报,就是遇到一个觉醒后的母亲

户外阿毽
2026-05-14 09:59:29
河南“00后”新人将6.8万元彩礼捐慈善机构,双方家长得知后又各出一半资金凑够20万元,全力支持爱心捐赠

河南“00后”新人将6.8万元彩礼捐慈善机构,双方家长得知后又各出一半资金凑够20万元,全力支持爱心捐赠

大风新闻
2026-05-15 13:53:39
一个人最大的本事,就是解决问题的能力!(深度好文)

一个人最大的本事,就是解决问题的能力!(深度好文)

辛东方
2026-02-12 08:00:03
官方:中国国家队将于6月9日在黄龙体育场迎战泰国国家队

官方:中国国家队将于6月9日在黄龙体育场迎战泰国国家队

懂球帝
2026-05-15 18:10:39
4-2!维拉锁定前5!英超坏消息:想6队获欧冠资格,需满足3大条件

4-2!维拉锁定前5!英超坏消息:想6队获欧冠资格,需满足3大条件

阿晞体育
2026-05-16 05:57:45
时运不济!骑士全队传噩耗,刚赢天王山,或将被淘汰,哈登命太惨

时运不济!骑士全队传噩耗,刚赢天王山,或将被淘汰,哈登命太惨

你的篮球频道
2026-05-15 10:48:05
中国人民的存款大搬家,开始了

中国人民的存款大搬家,开始了

说财猫
2026-05-15 15:35:00
我和妻子AA制30年,月收入3万全给我姐,直到我心梗进了ICU才明白

我和妻子AA制30年,月收入3万全给我姐,直到我心梗进了ICU才明白

千秋历史
2026-05-13 19:55:29
特朗普43岁小儿媳穿旗袍赴宴,华裔设计师作品,前私教自认普通

特朗普43岁小儿媳穿旗袍赴宴,华裔设计师作品,前私教自认普通

译言
2026-05-15 12:26:05
黄仁勋在深圳吃牛肉火锅,7人消费800,接地气

黄仁勋在深圳吃牛肉火锅,7人消费800,接地气

天光破云来
2026-05-16 01:06:32
加速心梗恶化的原因:饮酒排第8,排第1的,很多朋友天天做

加速心梗恶化的原因:饮酒排第8,排第1的,很多朋友天天做

芹姐说生活
2026-05-15 15:08:06
马筱梅晒儿子接新代言!小杨阿姨替小玥儿姐弟发言,张兰回应

马筱梅晒儿子接新代言!小杨阿姨替小玥儿姐弟发言,张兰回应

阿纂看事
2026-05-15 17:33:40
2026-05-16 06:55:00
码个蛋
码个蛋
码个蛋
808文章数 298关注度
往期回顾 全部

科技要闻

直降千元起步!苹果华为率先开启618让利

头条要闻

黄仁勋在北京喝豆汁痛苦皱眉 问“这是什么东西”

头条要闻

黄仁勋在北京喝豆汁痛苦皱眉 问“这是什么东西”

体育要闻

德约科维奇买的球队,从第6级联赛升入法甲

娱乐要闻

方媛为何要来《桃花坞6》没苦硬吃?

财经要闻

腾讯掉队,马化腾戳破真相

汽车要闻

高尔夫GTI刷新纽北纪录 ID. Polo GTI迎全球首秀

态度原创

本地
家居
旅游
手机
军事航空

本地新闻

用苏绣的方式,打开江西婺源

家居要闻

110㎡淡而有致的生活表达

旅游要闻

2026年“5·19中国旅游日”山东分会场活动即将启动,山东聊城送上文旅惠民大礼包,邀您乐享品质旅游,共赴美好山河!

手机要闻

苹果40W干翻安卓100W!CNET充电实测出炉,这排名你敢信?

军事要闻

乌克兰首都基辅遭空袭 死亡人数增至12人

无障碍浏览 进入关怀版