日期: 2021 年 7 月 14 日

iOS 使用QLPreviewController预览本地和网络文件

iOS 使用QLPreviewController预览本地和网络文件

*近在项目中要做一个文档预览的功能,做的时候用到了iOS原生的QLPreviewController类,在此做个记录分享

首先引入头文件

#import <QuickLook/QuickLook.h>

遵循代理

 

QLPreviewControllerDataSource

声明一个QLPreviewController变量

 

@property (strong, nonatomic)QLPreviewController *previewController;

 

再声明一个NSUrl对象存放要返回的文件路径

 

@property (copy, nonatomic)NSURL *fileURL;
 

在viewDidLoad中初始化

  1. – (void)viewDidLoad {
  2. [super viewDidLoad];
  3. self.previewController = [[QLPreviewController alloc] init];
  4. self.previewController.dataSource = self;
  5. }

 

 

在预览本地文件的事件里面写下如下代码

  1. //获取本地文件路径
  2. self.fileURL = [NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:@”李碧华佳句赏析.doc” ofType:nil]];
  3. [self presentViewController:self.previewController animated:YES completion:nil];

遵循代理方法

  1. #pragma mark – QLPreviewControllerDataSource
  2. -(id<QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index {
  3. return self.fileURL;
  4. }
  5. – (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)previewController{
  6. return 1;
  7. }

这样本地文件的预览就成了

 

添加文件到项目的时候要注意,将文件放到工程项目之后,还要到Build Phases的Copy Bundle Resources中将文件添加到资源库,不然使用的时候会找不到文件而崩溃

 

接下来是预览网络文件

在网络文件预览的事件里面写下如下代码(需要引入af)

  1. NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
  2. AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
  3. NSString *urlStr = @”https://www.tutorialspoint.com/ios/ios_tutorial.pdf”;
  4. NSString *fileName = [urlStr lastPathComponent]; //获取文件名称
  5. NSURL *URL = [NSURL URLWithString:urlStr];
  6. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  7. //判断是否存在
  8. if([self isFileExist:fileName]) {
  9. NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
  10. NSURL *url = [documentsDirectoryURL URLByAppendingPathComponent:fileName];
  11. self.fileURL = url;
  12. [self presentViewController:self.previewController animated:YES completion:nil];
  13. }else {
  14. [SVProgressHUD showWithStatus:@”下载中”];
  15. NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress){
  16. } destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
  17. NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
  18. NSURL *url = [documentsDirectoryURL URLByAppendingPathComponent:fileName];
  19. return url;
  20. } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
  21. [SVProgressHUD dismiss];
  22. self.fileURL = filePath;
  23. [self presentViewController:self.previewController animated:YES completion:nil];
  24. }];
  25. [downloadTask resume];
  26. }

添加文件是否存在判断方法

  1. //判断文件是否已经在沙盒中存在
  2. -(BOOL) isFileExist:(NSString *)fileName
  3. {
  4. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  5. NSString *path = [paths objectAtIndex:0];
  6. NSString *filePath = [path stringByAppendingPathComponent:fileName];
  7. NSFileManager *fileManager = [NSFileManager defaultManager];
  8. BOOL result = [fileManager fileExistsAtPath:filePath];
  9. return result;
  10. }

 

 

这样网络文件预览就成了

iOS 证书设置指南

创建应用程序 ID
登陆 苹果开发者网站 进入开发者账户。

%title插图%num
从开发者账户页面左侧入口进入 “Certificates, IDs & Profiles” 页面。

%title插图%num
创建 App ID,填写 App ID 的 NAME 和 Bundle ID(如果 ID 已经存在可以直接跳过此步骤)。

%title插图%num
注: 此处需要指定具体的 Bundle ID 不要使用通配符。

%title插图%num
为 App 开启 Push Notification 功能。如果是已经创建的 App ID 也可以通过设置开启 Push Notification 功能。

%title插图%num
填写好以上属性后,点击 “Continue”,确认 AppId 属性的正确性,点击 “Register”,注册 AppId 成功。
两种鉴权方式的配置
*光官网应用的鉴权信息一旦配置,只能用相同 bundleID 的鉴权信息进行更新,无法修改为其他的 bundleID,请在配置前仔细检查 bundleID 是否正确,若因特殊原因需要修改,请联系 support@jpush.cn

方式一:通过 .p12 证书鉴权
如果你之前没有创建过 Push 证书或者是要重新创建一个新的,请在证书列表下面新建。

%title插图%num
新建证书需要注意选择 APNs 证书种类。如图 APNs 证书有开发(Development)和生产(Production)两种。
注:开发证书用于开发调试使用;生产证书既能用于开发调试,也可用于产品发布。此处我们选择生产证书为例。

%title插图%num
点击 “Continue”, 之后选择该证书准备绑定的 AppID。

%title插图%num
再点 “Continue” 会让你上传 CSR 文件。( CSR 文件会在下一步创建)

%title插图%num
打开系统自带的 KeychainAccess 创建 Certificate Signing Request。如下图操作:

%title插图%num
填写“用户邮箱”和“常用名称” ,并选择“存储到磁盘”,证书文件后缀为 .certSigningRequest 。

%title插图%num
回到浏览器中 CSR 上传页面,上传刚刚生成的后缀为 .certSigningRequest 的文件。
生成证书成功后,点击 “Download” 按钮把证书下载下来,是后缀为 .cer 的文件。

%title插图%num
双击证书后,会在 “KeychainAccess” 中打开,选择左侧“钥匙串”列表中“登录”,以及“种类”列表中“我的证书”,找到刚才下载的证书,并导出为 .p12 文件。如下图:

%title插图%num

%title插图%num

在*光控制台上,进入你应用的应用设置中 iOS 的鉴权方式选择 “证书”,上传刚才导出的 .p12 证书。*光会在后台为你的应用进行鉴权。
Apple 的生产推送证书允许用于开发环境的推送,勾选将生产证书用于开发环境,开发者可以仅上传生产证书,即可在官网推送平台处选择开发环境做推送,不用再生成和上传开发证书。

%title插图%num
方式二:通过 APNs Auth Key 鉴权
点击左侧列表 “Keys” 中的 “All”,看账户中是否已有 auth key,没有则点击 “+” 新建。

%title插图%num
填写该 key 的描述并选择服务,如下图。 (注:在开发和生产环境均可使用,且不会过期。)

%title插图%num
点击 “Continue” 让你确认信息,再点击 “confirm”,就可以下载该 key 了。(注意:记下 key id,而且只可以下载一次,请妥善保存。)

%title插图%num
获取你之前创建过的应用的 Bundle ID

%title插图%num
在开发者账户的 “Membership” 页面获取 Team ID

%title插图%num
在*光控制台上,进入你应用的应用设置中 iOS 的鉴权方式选择 “Token Authentication”,上传 auth key 文件,并填写你的 KEY ID,TeamID,和指定应用的 BundleID。*光会在后台为你的应用进行鉴权。

%title插图%num
Provisioning Profile 的创建
创建 Provisioning Profile 的前提,已在 Apple Developer 网站创建待发布应用所使用的 Bundle ID 的 App ID,且为该 App ID 创建了 iOS Development 证书。

在苹果开发者账号的 Provisioning Profile 页面点击下图按钮,创建 Provisioning Profile

%title插图%num

选择此 Provisioning Profile 的环境后点击 [Continue]:

%title插图%num
选择要创建 Provisioning Profile 的 App ID 后点击 [Continue]:

%title插图%num
选择所属的开发者证书,(这里创建了多个开发者证书,建议只创建一个,方便管理)为了方便,选择了 [Select All],再点击 [Continue] 进入下一步:

%title插图%num
为该 Provisioning Profile 选择将要安装的设备(一般选择 [Select All]),点击 [Continue]:

%title插图%num
给该 Provisioning Profile 填写 Profile Name,点击 [generate] 完成创建。

%title插图%num
填写完 Profile Name 后点击 [generate] 完成创建,之后点击 [DownLoad] 下载 Provisioning Profile

%title插图%num
双击下载下来的 Provisioning Profile,添加到 xcode。
XCode 的证书配置教程
参照 iOS SDK 集成指南集成 JPush SDK 和上传了推送用到的 p12 证书后在编译运行前需要先配置一下证书,步骤如下:

打开 xxx-info.plist 的 Bundle identifier 项把上传到 JPush 控制台的 bundle id 填写进去:

%title插图%num
点击项目,选择目标 TARGETS 后进入 Build Setting 界面,搜索 “Code signing”,按照下图配置

%title插图%num

iOS证书信任设置

  1. 下载证书后去设置=>通用=>描述文件安装描述文件。
  2. 设置=>通用=>关于本机=>证书信任设置=>信任证书
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

iOS HTTPS证书不受信任解决办法

之前开发App的时候服务端使用的是自签名的证书,导致iOS开发过程中调用HTTPS接口时,证书不被信任

– (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{

/*方法一*/
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if(completionHandler)
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
}

/*方法二*/
// SecTrustRef servertrust = challenge.protectionSpace.serverTrust;
// SecCertificateRef certi= SecTrustGetCertificateAtIndex(servertrust, 0);
// NSData *certidata = CFBridgingRelease(CFBridgingRetain(CFBridgingRelease(SecCertificateCopyData(certi))));
// NSString *path = [[NSBundle mainBundle] pathForResource:@”证书名称” ofType:@”cer”];
NSLog(@”证书 : %@”,path);
// NSData *localCertiData = [NSData dataWithContentsOfFile:path];
// if ([certidata isEqualToData:localCertiData]) {
// NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:servertrust];
// [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
// completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
NSLog(@”服务端证书认证通过”);
// }else {
// completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
// NSLog(@”服务端认证失败”);
// }

}

这里有两个方法,*个是信任所有证书,第二个是把服务端自签名的证书放到本地,类似白名单的样子去加载

源码

HttpRequest.h

//
// HttpRequest.h
//
// Created by Michael Zhan on 2017/5/17.
// Copyright © 2017年 Michael Zhan. All rights reserved.
//

#import <Foundation/Foundation.h>

static NSString * const baseUrl = @”http://”;

typedef void (^SuccessBlock)(NSString * data);
typedef void (^FailureBlock)(NSError * error);

@interface HttpRequest : NSObject <NSURLSessionTaskDelegate>

– (void)getWithDict:(NSString *)paramUrl NSDictionary:(NSDictionary *)paramDicet success:(SuccessBlock)successBlock failure:(FailureBlock)failureBlock;

– (void)postWithDict:(NSString *)paramUrl NSDictionary:(NSDictionary *)paramDicet success:(SuccessBlock)successBlock failure:(FailureBlock)failureBlock;

– (void)getWithString:(NSString *)paramUrl NSString:(NSString *)paramString success:(SuccessBlock)successBlock failure:(FailureBlock)failureBlock;

– (void)postWithString:(NSString *)paramUrl NSString:(NSString *)paramString success:(SuccessBlock)successBlock failure:(FailureBlock)failureBlock;

– (void)postWithDict2String:(NSString *)paramUrl NSDictionary:(NSDictionary *)paramDicet success:(SuccessBlock)successBlock failure:(FailureBlock)failureBlock;

@end

HttpRequest.m

//
// HttpRequest.m
//
// Created by Michael Zhan on 2017/5/17.
// Copyright © 2017年 Michael Zhan. All rights reserved.
//

#import “HttpRequest.h”

@implementation HttpRequest

– (void)getWithDict:(NSString *)paramUrl NSDictionary:(NSDictionary *)paramDicet success:(SuccessBlock)successBlock failure:(FailureBlock)failureBlock{

NSMutableString * mutableStringUrl = [[NSMutableString alloc] initWithString:paramUrl];
[mutableStringUrl appendString:[HttpRequest convertToJsonData:paramDicet]];

NSLog(@”url %@”,mutableStringUrl);

NSURL * url = [NSURL URLWithString:[mutableStringUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];

NSURLRequest * request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10];

//2程序自动安装证书的方式
NSURLSession * session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc]init]];

NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
failureBlock(error);
} else {
NSString * result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
successBlock(result);
}
}];
[dataTask resume];

}

– (void)postWithDict:(NSString *)paramUrl NSDictionary:(NSDictionary *)paramDicet success:(SuccessBlock)successBlock failure:(FailureBlock)failureBlock{

NSURL * url = [NSURL URLWithString:paramUrl];

NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:100];

request.HTTPMethod = @”POST”;
NSString * jsonStr = [HttpRequest convertToJsonData:paramDicet];
request.HTTPBody = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];

//2程序自动安装证书的方式
NSURLSession * session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc]init]];

NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
failureBlock(error);
[session finishTasksAndInvalidate];
} else {
NSString * result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
successBlock(result);
[session finishTasksAndInvalidate];
}
}];
[dataTask resume];
}

– (void)postWithDict2String:(NSString *)paramUrl NSDictionary:(NSDictionary *)paramDicet success:(SuccessBlock)successBlock failure:(FailureBlock)failureBlock{

NSURL * url = [NSURL URLWithString:paramUrl];

NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:100];

request.HTTPMethod = @”POST”;
NSString * jsonStr = [NSString stringWithFormat:@”%@”,paramDicet];
request.HTTPBody = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
request.timeoutInterval = 10;
request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;

//2程序自动安装证书的方式
NSURLSession * session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc]init]];

NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
failureBlock(error);
[session finishTasksAndInvalidate];
} else {
NSString * result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
successBlock(result);
[session finishTasksAndInvalidate];
}
}];
[dataTask resume];
}

– (void)getWithString:(NSString *)paramUrl NSString:(NSString *)paramString success:(SuccessBlock)successBlock failure:(FailureBlock)failureBlock{

NSMutableString * mutableStringUrl = [[NSMutableString alloc] initWithString:paramUrl];
[mutableStringUrl appendString:paramString];

NSLog(@”url %@”,mutableStringUrl);
NSURL * url =[NSURL URLWithString:[mutableStringUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];

NSURLRequest * request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10];

//2程序自动安装证书的方式
NSURLSession * session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc]init]];

NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
failureBlock(error);
[session finishTasksAndInvalidate];
} else {
NSString * result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
successBlock(result);
[session finishTasksAndInvalidate];
}
}];
[dataTask resume];

}

– (void)postWithString:(NSString *)paramUrl NSString:(NSString *)paramString success:(SuccessBlock)successBlock failure:(FailureBlock)failureBlock{

NSURL * url = [NSURL URLWithString:paramUrl];

NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:100];
request.HTTPMethod = @”POST”;
request.HTTPBody = [paramString dataUsingEncoding:NSUTF8StringEncoding];
request.timeoutInterval = 10;
request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;

//2程序自动安装证书的方式
NSURLSession * session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc]init]];

NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
failureBlock(error);
[session finishTasksAndInvalidate];
} else {
NSString * result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
successBlock(result);
[session finishTasksAndInvalidate];
}
}];
[dataTask resume];
}

+ (NSString *)convertToJsonData:(NSDictionary *)dict{
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonString;
if (!jsonData) {
NSLog(@”%@”,error);
}else{
jsonString = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];
}
NSMutableString *mutStr = [NSMutableString stringWithString:jsonString];
NSRange range = {0,jsonString.length};
//去掉字符串中的空格
[mutStr replaceOccurrencesOfString:@” ” withString:@”” options:NSLiteralSearch range:range];
NSRange range2 = {0,mutStr.length};
//去掉字符串中的换行符
[mutStr replaceOccurrencesOfString:@”\n” withString:@”” options:NSLiteralSearch range:range2];
return mutStr;
}

– (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{

/*方法一*/
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if(completionHandler)
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
}

/*方法二*/
// SecTrustRef servertrust = challenge.protectionSpace.serverTrust;
// SecCertificateRef certi= SecTrustGetCertificateAtIndex(servertrust, 0);
// NSData *certidata = CFBridgingRelease(CFBridgingRetain(CFBridgingRelease(SecCertificateCopyData(certi))));
// NSString *path = [[NSBundle mainBundle] pathForResource:@”zwp” ofType:@”cer”];
NSLog(@”证书 : %@”,path);
// NSData *localCertiData = [NSData dataWithContentsOfFile:path];
// if ([certidata isEqualToData:localCertiData]) {
// NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:servertrust];
// [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
// completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
NSLog(@”服务端证书认证通过”);
// }else {
// completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
// NSLog(@”服务端认证失败”);
// }

}

@end

iOS15可以多开APP?其实是专注模式的功能

昨天我看到一篇文章说iOS15可以多开APP我当时就震惊了,心里窃喜真的这样就完美了,可是当我点开文章的时候发现,原来不是,只不过是iOS15中的专注模式导致的,这个功能*对犀利!

%title插图%num

看到了没这么多微信,这么多王者,甚至可以克隆出来更多的图标,没错,就是图标,只能说图标,因为点开任意一个图标就只跳转同一个APP,一个能打的都没有吗?也不知道这个功能是不是为了孩子学习而开发的。操作起来也是很简单,只需要去资源库拖拽这个图标就行了!

%title插图%num

我只想说:别影响我学习了。其实这个专注模式就是让你更快的找到你需要的APP,因为现在APP图标花里胡哨的,很难在很快的时间找到我们需的APP,我记得有一次我要找一个翻译软件,我记得之前我下载好就没删除,但是我翻遍了我的APP都没找到,我直接去APPStore里面搜索一下,结果我早就下载过得只是没找到而已。

所以这个功能还是有点用处,我们可以把常用的APP多放几个页面,这样随时都能很快的找到此APP,这个功能也能让大家装一把,不过我要在这里说一下,关于这个iOS15*个测试版,Bug确实很多,但是聪明的大家都找到对应解决办法,但是我还是不建议大家更新这个版本,我这里说一下关于iOS15beta1中微信发不了朋友圈视频的解决办法,我们可以在朋友圈发一张图片,接着删除这条朋友圈,在点击从新编辑,进入后删除图片。再次添加,我们添加一个视频就行了!至于其他Bug大家可以翻看昨天的推文下方的留言。

国家统计局关于2021年夏粮产量数据的公告

  根据对全国25个夏粮生产省(区、市)的调查,2021年全国夏粮播种面积、单位面积产量、总产量如下:   一、全国夏粮播种面积26438千公顷(39657万亩),比2020年增加265.5千公顷(398.2万亩),增长1.0%。其中小麦播种面积22911千公顷(34367万亩),比2020年增加200.2千公顷(300.4万亩),增长0.9%。   二、全国夏粮单位面积产量5515.7公斤/公顷(367.7公斤/亩),比2020年增加57.4公斤/公顷(3.8公斤/亩),增长1.1%。其中小麦单位面积产量5863.4公斤/公顷(390.9公斤/亩),比2020年增加62.3公斤/公顷(4.2公斤/亩),增长1.1%。   三、全国夏粮总产量14582万吨(2916亿斤),比2020年增加296.7万吨(59.3亿斤),增长2.1%。其中小麦产量13434万吨(2687亿斤),比2020年增加258.9万吨(51.8亿斤),增长2.0%。 国家统计局   2021年7月14日 2021年各地区夏粮产量情况    总产量  (万吨) 播种面积 (千公顷) 单位面积产量 (公斤/公顷) 全国总计 14582.3 26437.9 5515.7         北  京 6.9 13.1 5235.3 天  津 72.2 118.5 6089.0 河  北 1482.7 2270.8 6529.3 山  西 243.4 536.8 4533.4 内 蒙 古       辽  宁       吉  林       黑 龙 江       上  海 7.9 11.8 6665.1 江  苏 1380.7 2461.6 5609.1 浙  江 65.3 166.2 3931.4 安  徽 1699.9 2846.6 5971.5 福  建 24.4 55.2 4418.0 江  西 22.6 70.6 3203.7 山  东 2637.2 3995.5 6600.5 河  南 3803.2 5692.1 6681.5 湖  北 473.4 1305.8 3625.6 湖  南 45.2 113.9 3965.9 广  东 64.6 137.9 4683.5 广  西 26.6 116.0 2293.1 海  南 10.1 23.7 4264.6 重  庆 121.1 371.8 3255.8 四  川 429.2 1090.4 3936.6 贵  州 255.9 883.6 2896.2 云  南 261.8 971.0 2696.2 西  藏       陕  西 470.6 1104.7 4260.0 甘  肃 328.8 867.2 3791.9 青  海       宁  夏 20.7 73.7 2808.8   新  疆 628.1 1139.3 5512.4 注: 1.甘肃、宁夏、新疆部分地区小麦收获尚未完成,3省区的数据为预产数,实产数以日后出版的《中国农村统计年鉴-2021》为准。 2.此表中部分数据因四舍五入,存在总计与分省合计数不等的情况。    关于夏粮产量调查的说明   国家统计局公布的夏粮产量数据,由夏粮主产区开展以省为总体抽样调查和非主产区重点调查两种调查方式得出。   (一)统计口径   夏粮包括夏收的谷物、豆类和薯类。其中夏收谷物主要包括小麦、大麦、燕麦、荞麦等;夏收豆类主要包括蚕豆、豌豆等;夏收薯类包括马铃薯和甘薯。   (二)调查范围   北京、天津、河北、山西、江苏、安徽、山东、河南、湖北、重庆、四川、贵州、云南、陕西、甘肃、宁夏、新疆17个省(区、市)的夏粮产量数据,由国家统计局各调查总队组织在抽中的400多个国家调查县(市)开展抽样调查和实割实测调查,得出夏粮产量。   上海、浙江、福建、江西、湖南、广东、广西、海南8个省(区、市)的夏粮产量数据由各有关调查总队根据重点调查测算得出。   (三)调查样本   夏粮产量抽样调查由播种面积和单位面积产量抽样调查组成。   夏粮播种面积抽样调查在国家调查县(市)进行。全国共抽取6000多个样本村,每个样本村抽取3个面积约60亩的样方地块。在调查时节,对样方地块内及其压盖的所有自然地块开展调查,各有关省级调查总队根据调查基础数据推算得出省级夏粮播种面积。   夏粮单位面积产量抽样调查在国家调查县(市)抽取的面积调查地块中进行,全国共抽取近4000个测产样本点、10000多个测产地块,每个测产地块中再按照要求抽选3-5个10平方尺的小样方,通过对样方内小麦进行实割实测,推算得出全省小麦平均单位面积产量。   (四)测产方法   小麦产量实割实测是指基层调查员在小麦收获前,按照《农林牧渔业统计报表制度》对小麦种植地块进行逐块踏田估产、排队,抽取一定数量调查地块做出标记,在收割期时由各县级调查队员或者辅助调查员对抽中地块进行放样、实际割取样本,再通过脱粒、晾晒、测水杂和称重等环节测算地块单位面积产量。省级调查总队根据各抽中地块数据推算全省平均单位面积产量。   夏粮产量数据是以抽样调查的播种面积与单位面积产量相乘得出。   (五)其他   甘肃、宁夏、新疆部分地区由于春小麦成熟较晚,目前夏粮数据为初步预计数。 

2021年7月上旬流通领域重要生产资料市场价格变动情况

中国统计信息服务中心 卓创资讯   据对全国流通领域9大类50种重要生产资料市场价格的监测显示,2021年7月上旬与6月下旬相比,39种产品价格上涨,9种下降,2种持平。 2021年7月上旬流通领域重要生产资料市场价格变动情况  产品名称 单位 本期价格(元) 比上期 价格涨跌(元) 涨跌幅 (%) 一、黑色金属         螺纹钢(Φ16-25mm,HRB400E) 吨 4929.6 45.7 0.9 线材(Φ6.5mm,HPB300) 吨 5234.5 1.5 0.0 普通中板(20mm,Q235) 吨 5391.4 18.9 0.4 热轧普通薄板(3mm,Q235) 吨 5606.0 106.1 1.9 无缝钢管(219*6,20#) 吨 5979.8 -68.0 -1.1 角钢(5#) 吨 5323.6 34.3 0.6 二、有色金属         电解铜(1#) 吨 68668.9 636.6 0.9 铝锭(A00) 吨 18900.2 164.8 0.9 铅锭(1#) 吨 15530.7 219.1 1.4 锌锭(0#) 吨 22357.1 442.1 2.0 三、化工产品         硫酸(98%) 吨 666.4 17.0 2.6 烧碱(液碱,32%) 吨 555.8 21.6 4.0 甲醇(优等品) 吨 2397.3 0.6 0.0 纯苯(石油苯,工业级) 吨 8593.6 412.2 5.0 苯乙烯(一级品) 吨 9521.9 394.3 4.3 聚乙烯(LLDPE,7042) 吨 8515.6 139.9 1.7 聚丙烯(T30S) 吨 8713.7 56.8 0.7 聚氯乙烯(SG5) 吨 9071.7 103.4 1.2 顺丁胶(BR9000) 吨 12520.7 271.3 2.2 涤纶长丝(FDY150D/96F) 吨 7914.3 242.4 3.2 四、石油天然气         液化天然气(LNG) 吨 4050.3 254.9 6.7 液化石油气(LPG) 吨 4323.8 202.7 4.9 汽油(95#国VI) 吨 8359.1 29.5 0.4 汽油(92#国VI) 吨 8121.4 31.5 0.4 柴油(0#国VI) 吨 6556.3 9.5 0.1 石蜡(58#半) 吨 7303.1 23.9 0.3 五、煤炭         无烟煤(洗中块) 吨 1385.7 41.9 3.1 普通混煤(4500大卡) 吨 725.0 3.1 0.4 山西大混(5000大卡) 吨 815.0 3.1 0.4 山西优混(5500大卡) 吨 905.0 3.1 0.3 大同混煤(5800大卡) 吨 930.0 3.1 0.3 焦煤(主焦煤) 吨 2050.0 13.7 0.7 焦炭(二级冶金焦) 吨 2677.8 37.2 1.4 六、非金属建材         普通硅酸盐水泥(P.O 42.5袋装) 吨 450.3 -4.3 -0.9 普通硅酸盐水泥(P.O 42.5散装) 吨 399.6 -13.7 -3.3 浮法平板玻璃(4.8/5mm) 吨 2889.2 21.8 0.8 七、农产品(主要用于加工)         稻米(粳稻米) 吨 3943.2 -21.1 -0.5 小麦(国标三等) 吨 2536.2 -10.2 -0.4 玉米(黄玉米二等) 吨 2735.5 -17.5 -0.6 棉花(皮棉,白棉三级) 吨 16793.8 437.6 2.7 生猪(外三元) 千克 16.0 2.1 15.1 大豆(黄豆) 吨 5221.0 -18.2 -0.3 豆粕(粗蛋白含量≥43%) 吨 3590.8 172.7 5.1 花生(油料花生米) 吨 8166.7 -66.6 -0.8 八、农业生产资料         尿素(小颗料) 吨 2814.3 18.7 0.7 复合肥(硫酸钾复合肥,氮磷钾含量45%) 吨 2955.4 186.6 6.7 农药(草甘膦,95%原药) 吨 49750.0 1500.0 3.1 九、林产品         天然橡胶(标准胶SCRWF) 吨 12767.0 175.7 1.4 纸浆(漂白化学浆) 吨 5361.4 -5.5 -0.1 瓦楞纸(高强) 吨 4131.8 7.1 0.2 注:上期为2021年6月下旬。    附注   1.指标解释   流通领域重要生产资料市场价格,是指重要生产资料经营企业的批发和销售价格。与出厂价格不同,生产资料市场价格既包含出厂价格,也包含有经营企业的流通费用、利润和税费等。出厂价格与市场价格互相影响,存在时滞,两者的变动趋势在某一时间段内有可能会出现不完全一致的情况。   2.监测内容   流通领域重要生产资料市场价格监测内容包括9大类50种产品的价格。类别与产品规格说明详见附表。   3.监测范围   监测范围涵盖全国31个省(区、市)300多个交易市场的近2000家批发商、代理商、经销商等经营企业。   4.监测方法   价格监测方法包括信息员现场采价,电话、即时通讯工具和电子邮件询价等。   5.涨跌个数的统计   产品价格上涨、下降、持平个数按照涨跌幅(%)进行统计。   6.发布日期   每月4日、14日、24日发布上一旬数据,节假日顺延。 附表:流通领域重要生产资料市场价格监测产品规格说明表  序号 监测产品 规格型号 说明   一、黑色金属      1   螺纹钢 Φ16-25mm,HRB400E 屈服强度≥400MPa  2 线材 Φ6.5mm,HPB300 屈服强度≥300MPa  3 普通中板 20mm,Q235 屈服强度≥235MPa  4 热轧普通薄板 3mm,Q235 屈服强度≥235MPa  5 无缝钢管 219*6,20# 20#钢材,屈服强度≥245MPa  6 角钢 5# 屈服强度≥235MPa   二、有色金属      7 电解铜 1# 铜与银质量分数≥99.95%  8 铝锭 A00 铝质量分数≥99.7%  9 铅锭 1# 铅质量分数≥99.994% 10 锌锭 0# 锌质量分数≥99.995%   三、化工产品     11  硫酸 98% H2SO4质量分数≥98% 12 烧碱(液碱) 32% NaOH质量分数≥32%的离子膜碱 13 甲醇 优等品 水质量含量≤0.10% 14 纯苯(石油苯) 工业级 苯纯度≥99.8% 15 苯乙烯 一级品 纯度≥99.5% 16 聚乙烯(LLDPE) 7042 熔指:2.0±0.5g/10min 17 聚丙烯 T30S 熔指:3.0±0.9g/10min 18 聚氯乙烯 SG5 K值:66-68 19 顺丁胶 BR9000 块状、乳白色,灰分≤0.20% 20 涤纶长丝 FDY150D/96F 150旦,AA级   四、石油天然气     21 液化天然气 LNG 甲烷含量≥75%,密度≥430kg/m3 22 液化石油气 LPG 饱和蒸汽压1380-1430kPa 23 汽油 95#国VI 国VI标准 24 汽油 92#国VI 国VI标准 25 柴油 0#国VI 国VI标准 26 石蜡 58#半 熔点不低于58℃   五、煤炭     27 无烟煤 洗中块 挥发分≤8% 28 普通混煤 4500大卡 山西粉煤与块煤的混合煤,热值4500大卡 29 山西大混 5000大卡 质量较好的混煤,热值5000大卡 30 山西优混 5500大卡 优质的混煤,热值5500大卡 31 大同混煤 5800大卡 大同产混煤,热值5800大卡 32 焦煤  主焦煤 含硫量<1% 33 焦炭 二级冶金焦 12.01%≤灰分≤13.50%   六、非金属建材     34 普通硅酸盐水泥 P.O 42.5袋装 抗压强度42.5MPa 35 普通硅酸盐水泥 P.O 42.5散装 抗压强度42.5MPa 36 浮法平板玻璃 4.8/5mm 厚度为4.8/5mm的无色透明玻璃   七、农产品(主要用于加工)     37 稻米 粳稻米 杂质≤0.25%,水分≤15.5% 38 小麦 国标三等 杂质≤1.0%,水分≤12.5% 39 玉米 黄玉米二等 杂质≤1.0%,水分≤14.0% 40 棉花(皮棉) 白棉三级 纤维长度≥28mm,白或乳白色 41 生猪 外三元 三种外国猪杂交的肉食猪 42 大豆 黄豆 杂质≤1.0%,水分≤13.0% 43 豆粕 粗蛋白含量≥43% 粗蛋白≥43%,水分≤13.0% 44 花生 油料花生米 杂质≤1.0%,水分≤9.0%   八、农业生产资料     45 尿素 小颗料 总氮≥46%,水分≤1.0% 46 复合肥 硫酸钾复合肥 氮磷钾含量45% 47 农药(草甘膦) 95%原药 草甘膦质量分数≥95%   九、林产品     48 天然橡胶 标准胶SCRWF 杂质含量≤0.05%,灰分≤0.5% 49 纸浆 漂白化学浆 亮度≥80%,黏度≥600cm³/g 50 瓦楞纸 高强 80-160g/m2  

国家统计局农村司司长李锁强解读夏粮生产情况

2021年我国夏粮丰收  产量增加297万吨 ——国家统计局农村司司长李锁强解读夏粮生产情况   国家统计局公布的全国夏粮生产数据显示,2021年全国夏粮总产量14582万吨(2916亿斤),比2020年增加296.7万吨(59.3亿斤),增长2.1%。其中小麦产量13434万吨(2687亿斤),比2020年增加258.9万吨(51.8亿斤),增长2.0%。2021年夏粮播种面积恢复性增长,单产稳步提高,夏粮喜获丰收。   一、夏粮播种面积恢复性增长   2021年全国夏粮播种面积26438千公顷(39657万亩),比2020年增加265.5千公顷(398.2万亩),增长1.0%,扭转了连续5年下滑势头。其中小麦播种面积22911千公顷(34367万亩),比2020年增加200.2千公顷(300.4万亩),增长0.9%。   夏粮播种面积增加的主要原因:一是责任压实。各地实行粮食安全党政同责,层层压实粮食生产责任,落实*严格的耕地保护制度,坚决遏制耕地“非农化”、防止“非粮化”。二是政策扶持。进一步加大了对粮食生产的支持力度,提高小麦*低收购价,稳定农民种粮补贴,支持复垦撂荒地、开发冬闲田,挖掘面积潜力。三是适期播种。2020年秋冬播期间,土壤墒情较好,基本实现适期播种,山东、安徽等地区2019年因旱未播种的面积实现恢复性增长。四是效益带动。去年以来,粮食价格上涨,农民种粮收入增加,稳定了市场预期,农户种植意愿增强。   二、夏粮单产水平稳步提高   2021年全国夏粮单产5515.7公斤/公顷(367.7公斤/亩),比2020年增加57.4公斤/公顷(3.8公斤/亩),增长1.1%,连续3年增长。其中小麦单产5863.4公斤/公顷(390.9公斤/亩),比2020年增加62.3公斤/公顷(4.2公斤/亩),增长1.1%。   夏粮单产提高的主要原因:一是气候条件总体有利。秋冬播以来,主产区大部降水充沛、热量充足、墒情适宜,气象条件总体有利于夏粮生长发育和产量形成。越冬期间,部分地区遭遇低温寒潮天气,但未造成明显冻害。初春气温回升较快,高于常年同期,小麦返青拔节期提前,发育进程加快。4月以来北方麦区气温接近常年同期或略低,小麦幼穗分化时间延长,穗粒数增加。抽穗开花期,各地光温水条件较为适宜。灌浆成熟期,光照充足,昼夜温差大,有利于小麦干物质积累和品质提高。二是田间管理得到加强。针对小麦条锈病、赤霉病发生风险高的情况,各地及早制定防控预案,加强田间管理,加大病虫害防控力度,推进统防统治、联防联控,落实“一喷三防”等措施,后期病虫害得到有效控制。三是农业生产条件不断改善。建设旱涝保收、高产稳产的高标准农田;发展农业社会化服务,因地制宜开展多种形式的生产托管,积*推广优良品种及规范化播种、节水稳产等农业生产技术,促进单产水平稳步提高。   2021年夏粮丰收,为全年粮食产量保持在1.3万亿斤以上奠定了坚实基础,为“十四五”开好局、起好步,推动经济社会高质量发展,构建新发展格局提供了有力支撑。 

Android代码内存优化建议-OnTrimMemory优化

OnTrimMemory
OnTrimMemory 回调是 Android 4.0 之后提供的一个API,这个 API 是提供给开发者的,它的主要作用是提示开发者在系统内存不足的时候,通过处理部分资源来释放内存,从而避免被 Android 系统杀死。这样应用在下一次启动的时候,速度就会比较快。
本文通过问答的方式,从各个方面来讲解 OnTrimMemory 回调的使用过程和效果。想要开发高性能且用户体验良好的 Android 应用,那么这篇文章你不应该错过。

OnTrimMemory回调的作用?
OnTrimMemory是Android在4.0之后加入的一个回调,任何实现了ComponentCallbacks2接口的类都可以重写实现这个回调方法.OnTrimMemory的主要作用就是指导应用程序在不同的情况下进行自身的内存释放,以避免被系统直接杀掉,提高应用程序的用户体验.
Android系统会根据不同等级的内存使用情况,调用这个函数,并传入对应的等级:
TRIM_MEMORY_UI_HIDDEN 表示应用程序的所有UI界面被隐藏了,即用户点击了Home键或者Back键导致应用的UI界面不可见.这时候应该释放一些资源.
TRIM_MEMORY_UI_HIDDEN 这个等级比较常用,和下面六个的关系不是很强,所以单独说.

下面3个等级是当我们的应用程序真正运行时的回调:
TRIM_MEMORY_RUNNING_MODERATE 表示应用程序正常运行,并且不会被杀掉。但是目前手机的内存已经有点低了,系统可能会开始根据LRU缓存规则来去杀死进程了。

TRIM_MEMORY_RUNNING_LOW 表示应用程序正常运行,并且不会被杀掉。但是目前手机的内存已经非常低了,我们应该去释放掉一些不必要的资源以提升系统的性能,同时这也会直接影响到我们应用程序的性能。

TRIM_MEMORY_RUNNING_CRITICAL 表示应用程序仍然正常运行,但是系统已经根据LRU缓存规则杀掉了大部分缓存的进程了。这个时候我们应当尽可能地去释放任何不必要的资源,不然的话系统可能会继续杀掉所有缓存中的进程,并且开始杀掉一些本来应当保持运行的进程,比如说后台运行的服务。
当应用程序是缓存的,则会收到以下几种类型的回调:

TRIM_MEMORY_BACKGROUND 表示手机目前内存已经很低了,系统准备开始根据LRU缓存来清理进程。这个时候我们的程序在LRU缓存列表的*近位置,是不太可能被清理掉的,但这时去释放掉一些比较容易恢复的资源能够让手机的内存变得比较充足,从而让我们的程序更长时间地保留在缓存当中,这样当用户返回我们的程序时会感觉非常顺畅,而不是经历了一次重新启动的过程。
TRIM_MEMORY_MODERATE 表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的中间位置,如果手机内存还得不到进一步释放的话,那么我们的程序就有被系统杀掉的风险了。
TRIM_MEMORY_COMPLETE 表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的*边缘位置,系统会*优先考虑杀掉我们的应用程序,在这个时候应当尽可能地把一切可以释放的东西都进行释放。
哪些组件可以实现OnTrimMemory回调?
Application.onTrimMemory()
Activity.onTrimMemory()
Fragement.OnTrimMemory()
Service.onTrimMemory()
ContentProvider.OnTrimMemory()

OnTrimMemory回调中可以释放哪些资源?
通常在架构阶段就要考虑清楚,我们有哪些东西是要常驻内存的,有哪些是伴随界面存在的.一般情况下,有下面几种资源需要进行释放:
缓存 缓存包括一些文件缓存,图片缓存等,在用户正常使用的时候这些缓存很有作用,但当你的应用程序UI不可见的时候,这些缓存就可以被清除以减少内存的使用.比如第三方图片库的缓存.
一些动态生成动态添加的View. 这些动态生成和添加的View且少数情况下才使用到的View,这时候可以被释放,下次使用的时候再进行动态生成即可.比如原生桌面中,会在OnTrimMemory的TRIM_MEMORY_MODERATE等级中,释放所有AppsCustomizePagedView的资源,来保证在低内存的时候,桌面不会轻易被杀掉.
2.1 例子:释放不常用到的View.
代码出处:Launcher

Launcher.java:
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
mAppsCustomizeTabHost.onTrimMemory();
}
}

AppsCustomizeTabHost.java:
public void onTrimMemory() {
mContent.setVisibility(GONE);
// Clear the widget pages of all their subviews – this will trigger the widget previews
// to delete their bitmaps
mPagedView.clearAllWidgetPages();
}

AppsCustomizePagedView.java:
public void clearAllWidgetPages() {
cancelAllTasks();
int count = getChildCount();
for (int i = 0; i < count; i++) {
View v = getPageAt(i);
if (v instanceof PagedViewGridLayout) {
((PagedViewGridLayout) v).removeAllViewsOnPage();
mDirtyPageContent.set(i, true);
}
}
}

PagedViewGridLayout.java
@Override
public void removeAllViewsOnPage() {
removeAllViews();
mOnLayoutListener = null;
setLayerType(LAYER_TYPE_NONE, null);
}

OnTrimMemory和onStop的关系?
onTrimMemory()方法中的TRIM_MEMORY_UI_HIDDEN回调只有当我们程序中的所有UI组件全部不可见的时候才会触发,这和onStop()方法还是有很大区别的,因为onStop()方法只是当一个Activity完全不可见的时候就会调用,比如说用户打开了我们程序中的另一个Activity。
因此,我们可以在onStop()方法中去释放一些Activity相关的资源,比如说取消网络连接或者注销广播接收器等,但是像UI相关的资源应该一直要等到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)这个回调之后才去释放,这样可以保证如果用户只是从我们程序的一个Activity回到了另外一个Activity,界面相关的资源都不需要重新加载,从而提升响应速度。
需要注意的是,onTrimMemory的TRIM_MEMORY_UI_HIDDEN 等级是在onStop方法之前调用的.

OnTrimMemory和OnLowMemory的关系?
在引入OnTrimMemory之前都是使用OnLowMemory回调,需要知道的是,OnLowMemory大概和OnTrimMemory中的TRIM_MEMORY_COMPLETE级别相同,如果你想兼容api<14的机器,那么可以用OnLowMemory来实现,否则你可以忽略OnLowMemory,直接使用OnTrimMemory即可.

为什么要调用OnTrimMemory?
尽管系统在内存不足的时候杀进程的顺序是按照LRU Cache中从低到高来的,但是它同时也会考虑杀掉那些占用内存较高的应用来让系统更快地获得更多的内存。
所以如果你的应用占用内存较小,就可以增加不被杀掉的几率,从而快速地恢复(如果不被杀掉,启动的时候就是热启动,否则就是冷启动,其速度差在2~3倍)。
所以说在几个不同的OnTrimMemory回调中释放自己的UI资源,可以有效地提高用户体验。

有哪些典型的使用场景?
常驻内存的应用
一些常驻内存的应用,比如Launcher、安全中心、电话等,在用户使用过要退出的时候,需要调用OnTrimMemory来及时释放用户使用的时候所产生的多余的内存资源:比如动态生成的View、图片缓存、Fragment等。
有后台Service运行的应用 这些应用不是常驻内存的,意味着可以被任务管理器杀掉,但是在某些场景下用户不会去杀。
这类应用包括:音乐、下载等。用户退出UI界面后,音乐还在继续播放,下载程序还在运行。这时候音乐应该释放部分UI资源和Cache。

关于Android内存优化你应该知道的一切

介绍

在Android系统中,内存分配与释放分配在一定程度上会影响App性能的—鉴于其使用的是类似于Java的GC回收机制,因此系统会以消耗一定的效率为代价,进行垃圾回收。
在中国有句老话:”由俭入奢易,由奢返俭难”。而此谚语也似乎正适应于Android的内存使用。GC回收机制给程序员省去了像C语言程序员那样手动释放内存的工作,但是也带来了一系列的”雷”—动辄内存泄漏,再甚者稍微不慎就会OOM。
这篇文章将会介绍Android的内存管理机制并解释几种在此机制下对内存有影响的几个比较关键的因素。另外,还会介绍如何提高内存管理、检测并避免内存泄漏,以及如何分析内存分配情况

Here we go!!

Android内存管理机制

Android内存模型并没有交换空间(swap space)的概念,而是使用分页(paging)和内存映射(memory-mapping)管理内存,这意味着不管是分配新的对象还是使用已有的映射页这些内存仍然被占据在RAM里而不能被扇出。因此完全释放你app内存的唯一方式是释放对象引用以便于能被垃圾回收器回收。
Dalvik虚拟机为每一个App分配相应大小的可用内存空间,从2M开始到32M(此*大值根据不同的厂商一般会有不同),不可否认,在当前国内各大手机厂商疯狂的拼硬件的时代,这个每个App的可用内存甚至被提高到了256M,这有效的避免了很多OOM的情况,但是如果程序员因此就不管内存管理任意而为,会为此付出严重代价的(App高卸载率).
Android系统会将在后台运行的App进程保存在一个LRU cache中(不懂的自行百度)。当系统内存紧张时,它会根据LRU的策略kill掉一些优先级比较低的进程。当然,究竟哪一个App是当前占用内存*大的程序也是它kill进程时所考虑的一个因素。如果你希望自己的App在后台运行时能尽可能长的”活着”,不被系统kill掉,就要好好的思考如何避免被kill。比如在App转到后台运行之前,尽可能的将没有用的内存给释放掉,这样会减少Android系统打印错误日志甚至终止App的可能性。

如何提高Android内存使用

Android系统是世界上使用率*高的手机系统。每年都有成千上万的年轻人转入到开发Android系统的行列中,但是这些人中,能真正写出稳定、可扩展性强的代码的还是少数。

以下是提高内存使用的几条建议:
  1. 慎用桥接模式,虽然从程序的设计角度来看,抽象能够帮助我们创建更加灵活的软件架构。但是在手机系统中,这种设计模式有可能会造成很多副作用。除非大有必要,否则尽量不要用桥接模式
  2. 避免使用枚举Enum,一个Enum分配的空间是一个普通常量的两倍,因此尽量少使用枚举
  3. 试着使用Android框架优化后的数据容器,譬如:SparseArray, SparseBooleanArray, 以及 LongSparseArray containers. 使用这些类来替代HashMap的使用。原因是传统的 HashMap 在内存上的实现十分的低效,因为它需要为 HashMap 中每一项在内存中建立映射关系. 另外, SparseArray类非常高效因为它避免了对key和value的自动封箱. 万事都有两面性,这些个被优化过的容器也不例外,千万记住SparseArray等容器并不适应于内部元素很多的集合,当集合的长度超过1000条时,使用SparseArray进行增删改查的效率远比HashMap低
  4. 避免创建不需要的对象。对于生命周期较短的临时变量,尽量想办法规避掉每次都要去创建它,这样GC回收被强制调用机会就会更少,留给Android系统进行UI渲染或者音频加载的时间就会更多,从而避免了卡顿现象
  5. 检测App内存中的可用堆的大小,在代码中可以通过动态的调用ActivityManager::getMemoryClass()方法来查询你的App中的可用内存堆大小。如果系统检测到需要分配的内存大小超过了此值,则会抛出OOM错误
  6. **可以适当适应onTrimMemory回调方法。OnTrimMemory 回调是 Android 4.0 之后提供的一个API,这个 API 是提供给开发者的,它的主要作用是提示开发者在系统内存不足的时候,通过处理部分资源来释放内存,从而避免被 Android 系统杀死。这样应用在下一次启动的时候,速度就会比较快。
  7. 当使用Service应当小心小心再小心!当你需要启动一个服务在后台执行一项任务时,应当在其完成工作之后尽快的停止此服务。可以考虑使用IntentService—当在子线程完成耗时操作之后,IntentService会自动停止并结束自身。然而在实际开发中经常会碰到需要服务去执行一项耗时比较长的任务,比如:音乐播放器,下载APP等等。像这样的应用可以分隔为两个进程:一个进程负责 UI 工作, 另外一个则在后台服务中运行其它的工作. 在AndroidManifest 文件中为各个组件申明 android:process 属性就可以分隔为不同的进程。注意一点:在后台运行的Service*对不能处理或者持有任何UI,否则系统可能会分配双倍甚至三倍的空间来维护UI资源!!
  8. 当你加载 bitmap 时, 需要根据当前设备的分辨率加载相应分辨率的bitmap进入内存,如果下载下来的原图分辨率比设备分辨率高则要压缩它. 要小心bitmap的分辨率增加后所占用的内存也要进行相应的增加(平方级increase2的增长), 因为它是根据x和y的大小来增加内存占用的
  9. 使用代码混淆工具 ProGuard 通过去除没有用的代码和通过语义模糊来重命名类, 字段和方法来缩小, 优化和混淆你的代码. 使用它能使你的代码更简洁, 更少量的RAM映射页.如果构建apk后你没有做后续的任何处理(包括根据你的证书进行签名), 你必须运行 zipalign 工具为你的apk进行优化, 如果不这样做会导致你的应用使用更多的内存,zipalign之后像资源这样的东西不会再从apk中映射(mmap)入内存.注意:goole play store 不接受没有进行zipalign的apk

针对以上几条,后续会单独再post几篇blog单独讲解。

如何避免内存泄漏

程序员在分配内存时如果考虑到了上述9条建议,或许会给App在效率上带来不小的收益,并且可以在后台时依然坚挺(更持久!)。 但是这一切的努力都会因为一个叫做内存泄漏的东东而萎了! 这玩意就如同可乐的存在一样,少喝一点还能扛得住,但是多了的话。。你懂得! 以下是几个常见的造成内存泄漏的情况:

  • 当查询完数据库之后,及时关闭Cursor对象。
  • 记得在Activity的onPause方法中调用unregisterReceiver()方法,解注册广播
  • 避免Content内存泄漏,比如在4.0.1之前的版本上不要讲Drawer对象置为static。当一个Drawable绑定到了View上,实际上这个View对象就会成为这个Drawable的一个callback成员变量,上面的例子中静态的sBackground持有TextView对象lable的引用,而lable只有Activity的引用,而Activity会持有其他更多对象的引用。sBackground生命周期要长于Activity。当屏幕旋转时,Activity无法被销毁,这样就产生了内存泄露问题。
  • 尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,当非静态内部类的引用的声明周期长于Activity的声明周期时,会导致Activity无法被GC正常回收掉。
  • 谨慎使用线程Thread!!这条是很多人会犯的错误: Java中的Thread有一个特点就是她们都是直接被GC Root所引用,也就是说Dalvik虚拟机对所有被激活状态的线程都是持有强引用,导致GC永远都无法回收掉这些线程对象,除非线程被手动停止并置为null或者用户直接kill进程操作。所以当使用线程时,一定要考虑在Activity退出时,及时将线程也停止并释放掉
  • 使用Handler时,要么是放在单独的类文件中,要么就是使用静态内部类。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露–详情请参阅Android中Handler引起的内存泄露

如何分析内存的使用情况

在Mac终端(windows的cmd)中,可以使用adb logcat命令来查看或者统计内存的具体使用情况,另外还可以指定包名来查看相应App的内存使用情况。除此之外,还可以使用三方的工具来分析Android内存的使用情况,比如:DDMS、MAT(Memory Analyzer tool).

在adb logcat中,通常能看到GC相关的log如下图所示

Dalvik GC log

GC_Reason 触发GC回收的原因,可能包含以下几种情况:

  • GC_FOR_ALLOC, 这个是说我们的应用尝试去分配内存而这时候和heap已经快满了(不够用了),这个时候系统会把我们的应用停下来然后进行内存回收,通常heap size会增大
  • GC_CONCURRENT,这个应该的当我们的Heap size 快要被填满的时候触发的一个并发的内存回收
  • GC_EXPLICIT,这个是主动调用系统gc方法触发的GC(在DDMS 点击GC就可以看到)
  • GC_HPROF_DUMP_HEAP 我们在做内存分析创建HPROF(MAT可以分析该文件)的时候会打印

Amount feed 表示本次垃圾收集释放了多少内存

Heap_stats 当前空闲内存占总内存的百分比

External memory stats 表示API 10及以下的外部分配内存,已分配内存/导致垃圾回收的阈值

Pause_time 应用暂停的时间

通常情况下,生成的GC log越大,表示内存的分配与释放发生的频率越高,这种情况下往往会非常影响用户体验!

使用DDMS查看并追踪堆内存的分配情况

通过DDMS,程序员可以很轻松的检测指定进程的内存分配情况。你可以通过“Heap”标签查看*新的实时的堆内存信息,这样可以帮助你辨别出究竟是哪一个操作*有可能造成大量的内存分配。 “Allocation Tracker” 标签显示的是*近所有的内存分配—包含分配对象的类型,是在哪个线程中分配等信息。一下图片演示的是使用DDMS展示进程信息—包含了当前进程、对内存分配统计信息。

heap_stat

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速