当前位置: 首页 > 编程日记 > 正文

仅需6步,教你轻易撕掉app开发框架的神秘面纱(6):各种公共方法及工具类的封装

为什么要封装公共方法

封装公共方法有2方面的原因:
一是功能方面的原因:有些方法很多地方都会用,而且它输入输出明确,并且跟业务逻辑无关。比如检查用户是否登录,检查某串数字是否为合法的手机号。像这种方法就应该封装起来,供各个模块调用,避免重复造轮子。

二是防止出错:每一个合格的程序员就是从一个个错误中走出来的,任何一个架构包括android/iOS都有一些容易犯的错,我们可以把这些容易犯错的地方封装一下,每次用统一的,规定好的处理方式,这样就不会出错了。

防止重复:这个词在框架构造中提到的次数最多。这也是编写高可用代码的最重要的因素之一。

封装哪些内容及依据

下面就是一个最基本的应用需要封装的方法:

  1. 时间相关:获取本地时间,获取服务器时间,获取时间格式化等等。
  2. log和toast:log和toast的封装是为了做开关。应用正式版本中大多数调试log都应关闭。
  3. 异常上传:把客户端错误上传至服务器,就可以从服务端查看客户端哪里问题最严重,有的放矢。对于android来说,获取未捕获异常也很重要,请 查看此文 ,向下滚动至第三点:异常类捕获。
  4. 常见错误规避:如类型转换,subString这些容易引发异常的地方。
  5. 一些工具方法:如dp转px,px转dp,md5,判断手机号邮箱是否合法,获取设备信息等等。

代码:
贴这些代码的目的是分辨出哪些代码可以放在utils中,并且要养成往utils中提取代码的习惯。

更充分一点儿说:只要有2个地方使用的类似代码,就需要考虑是否可以提取成公共方法了。

时间相关函数:

//android: TimeUtils.java
public class TimeUtils {private static long timeOffset = Long.MAX_VALUE;//同服务器的时间差public static long getLocalTime(){//获取本地时间,单位:sreturn (long)(getLocalTimeMs() / 1000.0);}public static long getCurrTime(){//获取服务器时间,单位:sreturn getCurrTimeInner(getLocalTime(), 1);}private static long getCurrTimeInner(long base, long factor){if(timeOffset != Long.MAX_VALUE && timeOffset != 0){return base + timeOffset * factor;}return base;}public static long getLocalTimeMs(){//获取本地时间,单位:msreturn System.currentTimeMillis();}public static long getCurrTimeMs(){//获取服务器时间,单位:msreturn getCurrTimeInner(getLocalTimeMs(), 1000);}public static void adjustTimeOff(long serverTimeStamp){//调整时间差,需要在调用服务器接口时获取到服务器时间后调用timeOffset = serverTimeStamp - getLocalTime();}
}
//iOS: TimeUtils.h#import <Foundation/Foundation.h>@interface TimeUtils : NSObject+(double)getLocalTimeWithSec;//获取本地时间,单位:s
+(double)getLocalTimeWithMSec;//获取本地时间,单位:ms
+(double)getCurrentTimeWithSec;//获取服务器时间,单位:s
+(double)getCurrentTimeWithMSec;//获取服务器时间,单位:ms
+(void)setTimeOffsetWithServer:(double) serverTime;调整时间差,需要在调用服务器接口时获取到服务器时间后调用@end
//iOS: TimeUtils.m#import "TimeUtils.h"static double sTimeOffWithServer = 0;@implementation TimeUtils+(double)getLocalTimeWithSec{return [[NSDate date]timeIntervalSince1970];
}+(double)getLocalTimeWithMSec{return [self getCurrentTimeWithSec] * 1000;
}+(void)setTimeOffsetWithServer:(double) serverTime{sTimeOffWithServer = serverTime - [self getLocalTimeWithSec];
}+(double)getCurrentTimeWithSec{return [self getLocalTimeWithSec] + sTimeOffWithServer;
}+(double)getCurrentTimeWithMSec{return [self getLocalTimeWithMSec] + sTimeOffWithServer;
}@end

log及toast封装

关联章节:网络模块封装

//android LogUtils.java
public class LogUtils {public static boolean isOpen = true;//所有非错误log必须使用此方法打印public static void d(String tag, String msg){if (isOpen){Log.d(tag, msg);}}//所有不带有异常的错误必须使用此方法打印public static void e(String tag, String msg){if (isOpen){Log.e(tag, msg);}else{uploadErrorLog(Utils.getClientInfo(), msg);}}public static String[] exceptionToString(Throwable e){StackTraceElement[] eles = e.getStackTrace();String []ret = new String[eles.length + 1];ret[0] = e.toString();for (int i = 0; i < eles.length; i++) {ret[i + 1] = "  at " + eles[i].toString();}return ret;}//所有带有异常的错误必须使用此方法打印public static void e(String tag, Throwable tr){if(isOpen){String exarr[] = exceptionToString(tr);for (int i = 0; i < exarr.length; i++){e(tag, exarr[i]);}}else{String exarr[] = exceptionToString(tr);StringBuilder sb = new StringBuilder();for (String s: exarr){sb.append(s);}//注:ClientInfo中包含:设备信息,应用信息,设备号等信息。uploadErrorLog(Utils.getClientInfo(), sb.toString());}}//上传错误至服务器private static void uploadErrorLog(String clientInfo, String errorString){//TODO:向特定服务器上传//此处为伪代码,具体代码请联系本系列的第四章,统一编写。Server.post("{\"clientInfo\":" + clientInfo + ",\"errorString\":" + errorString);}//提示用toastpublic static void toastTip(Context context,@NonNull String msg) {showToast(context, msg);}//调试用toastpublic static void toastDebug(Context context,@NonNull String msg) {if (isOpen) {showToast(context, msg);}}private static void showToast(Context context,@NonNull String msg) {Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();}
}
//iOS: LogUtils.h//debug log的宏定义:所有非错误log必须使用此宏打印
#if defined(DEBUG) && DEBUG == 1
#define DebugLog(...) NSLog(__VA_ARGS__)
#else
#define DebugLog(...)
#endif//error log 的宏定义:所有错误log必须使用此宏打印
#if defined(DEBUG) && DEBUG == 1
#define ErrorLog(...) DebugLog(__VA_ARGS__)
#else
#define ErrorLog(...)                                                                         \
do{                                                                                           \NSString *errorString = [NSString stringWithFormat: __VA_ARGS__];                         \NSLog(errorString);                                                                       \[LogUtils uploadErrorLogWithClientInfo:[Utils getClientInfo] andErrorString:errorString]; \
}while(0);
#endif//tip toast宏定义:所有提示用户所用的宏定义必须使用此宏打印
#define TipToast(msg, duration, viewCtl) [LogUtils toastWithMessage: msg andDuration: duration andViewController:viewCtl]//debug toast宏定义:所有debug用的宏定义必须使用此宏打印
#if defined(DEBUG) && DEBUG == 1
#define DebugToast(msg, duration, viewCtl) TipToast(msg, duration, viewCtl)
#else
#define DebugToast(msg, duration, viewCtl)
#endif@interface LogUtils : NSObject
//上传错误数据至服务器。
+(void) uploadErrorLogWithClientInfo:(NSString *)clientInfo andErrorString:(NSString*) errorString;
//为某个页面显示toast,模拟android
+(void) toastWithMessage:(NSString *)msg andDuration:(NSInteger) duration andViewCotroller:(UIViewController *)viewCtl;
@end
//iOS: LogUtils.m
#import "LogUtils.h"
@implements LogUtils
+(void)uploadErrorLogWithClientInfo:(NSString *)clientInfo andErrorString:(NSString*) errorString{//TODO:向特定服务器上传//此处为伪代码,具体代码请联系本系列的第四章:[网络模块封装](http://blog.csdn.net/hard_man/article/details/50699346),统一编写。[Server postWithString: "{\"clientInfo\":" + clientInfo + ",\"errorString\":" + errorString];
}
+(void) toastWithMessage:(NSString *)msg andDuration:(NSInteger) duration andViewCotroller:(UIViewController *)viewCtl{UIView *baseView = [[UIView alloc] init];[viewCtl.view addSubview:baseView];baseView.userInteractionEnabled = NO;//此处使用Masory来指定View的大小和位置[baseView makeConstraints:^(MASConstraintMaker *make) {make.edges.equalTo(viewCtl.view);}];UIView *toastBg = [[UIView alloc] init];//背景色toastBg.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.8];//设置圆角toastBg.layer.cornerRadius = 3;toastBg.layer.masksToBounds = YES;//关闭点击toastBg.userInteractionEnabled = NO;[baseView addSubview:toastBg];[toastBg makeConstraints:^(MASConstraintMaker *make) {make.width.lessThanOrEqualTo(viewCtl.view.bounds.size.width - 30);make.centerX.equalTo(baseView);make.centerY.equalTo(baseView).offset(0);}];//文字UILabel *label = [[UILabel alloc] init];label.text = msg;label.textColor = [Color whiteColor];label.textAlign = NSTextAlignmentLeft;label.font = [UIFont systemFontOfSize: 15];label.numberOfLines = 3;label.userInteractionEnabled = NO;[toastBg addSubview:label];[label makeConstraints:^(MASConstraintMaker *make) {make.edges.equalTo(toastBg).insets(UIEdgeInsetsMake(5, 10, 5, 10));}];[baseView layoutIfNeeded];[UIView animateWithDuration:0.3 delay:duration options:UIViewAnimationOptionCurveLinear animations:^{toastBg.alpha = 0;} completion:^(BOOL finished) {[baseView removeFromSuperview];}];
}
@end
//android: Utils.java
//下面是我项目中使用的,查看更多可[点击此处](http://www.cnblogs.com/cr330326/p/4422507.html)。
public class Utils{//获取app信息public static String getClientInfo(){StringBuilder ret = new StringBuilder();//系统版本ret.append("os:android");//系统版本号:ret.append(",osVersion:" + android.os.Build.VERSION.RELEASE);//手机型号:ret.append(",phoneBrand:" + android.os.Build.BRAND);ret.append(",phoneModel:" + android.os.Build.MODEL);//      ret.append(",phoneDevice:" + android.os.Build.DEVICE);
//      ret.append(",phoneID:" + android.os.Build.ID);
//      ret.append(",phoneBootLoader:" + android.os.Build.BOOTLOADER);
//      ret.append(",phoneBoard:" + android.os.Build.BOARD);
//      ret.append(",phoneCpuAbi:" + android.os.Build.CPU_ABI);
//      ret.append(",phoneCpuAbi2:" + android.os.Build.CPU_ABI2);
//      ret.append(",phoneDisplay:" + android.os.Build.DISPLAY);
//      ret.append(",phoneFingerPrint:" + android.os.Build.FINGERPRINT);
//      ret.append(",phoneHardware:" + android.os.Build.HARDWARE);
//      ret.append(",phoneHost:" + android.os.Build.HOST);
//      ret.append(",phoneManufacturer:" + android.os.Build.MANUFACTURER);
//      ret.append(",phoneProduct:" + android.os.Build.PRODUCT);
//      ret.append(",phoneRadio:" + android.os.Build.RADIO);
//      ret.append(",phoneSerial:" + android.os.Build.SERIAL);
//      ret.append(",phoneTags:" + android.os.Build.TAGS);
//      ret.append(",phoneTime:" + android.os.Build.TIME);
//      ret.append(",phoneType:" + android.os.Build.TYPE);
//      ret.append(",phoneUser:" + android.os.Build.USER);
//      ret.append(",phoneGetRadioVersion:" + android.os.Build.getRadioVersion());//app 版本号try {PackageInfo pkgInfo = Constants.GLOBAL_CONTEXT.getPackageManager().getPackageInfo(Constants.GLOBAL_CONTEXT.getPackageName(), 0);ret.append(",appVersionName:" + pkgInfo.versionName);ret.append(",appVersionCode:" + pkgInfo.versionCode);} catch (NameNotFoundException e) {}//uuidTelephonyManager telephonyManager = (TelephonyManager) Constants.GLOBAL_CONTEXT.getSystemService(Context.TELEPHONY_SERVICE);ret.append(",uuid:" + telephonyManager.getDeviceId());return ret.toString();}//避免出错的substringpublic static String subString(String src, int start, int to){if(src != null){int len = src.length;int wantLen = to - start + 1;if(wantLen < len){to = len - 1;wantLen = to - start + 1;}if(to >= start){return src.substring(start, wantLen);}else{return null;}}return null;}public static int getStatusBarHeight() {Class<?> c = null;Object obj = null;Field field = null;int x = 0, sbar = 0;try {c = Class.forName("com.android.internal.R$dimen");obj = c.newInstance();field = c.getField("status_bar_height");x = Integer.parseInt(field.get(obj).toString());sbar = context.getResources().getDimensionPixelSize(x);} catch (Exception e1) {e1.printStackTrace();}if (sbar == 0) {sbar = Util.dp(20);}return sbar;}//获取屏幕高度public static int getScreenHeight() {return context.getResources().getDisplayMetrics().heightPixels;}public static int getScreenWidth() {return context.getResources().getDisplayMetrics().widthPixels;}//dp sp px 之间转换public static int px2dp(int px) {return (int) (1.0f * px / context.getResources().getDisplayMetrics().density + 0.5f);}public static int dp2px(int dp) {return (int) (1.0f * dp * context.getResources().getDisplayMetrics().density + 0.5f);}public static int sp2px(int sp) {return (int) (1.0f * sp * context.getResources().getDisplayMetrics().scaledDensity + 0.5f);}public static int px2sp(int px) {return (int) (1.0f * px / context.getResources().getDisplayMetrics().scaledDensity + 0.5f);}public static int sp2dp(int sp) {return px2dp(sp2px(sp));}public static int dp2sp(int dp) {return px2sp(dp2px(dp));}public static int dp(int dp) {return dp2px(dp);}public static int sp(int sp) {return sp2px(sp);}//读取asstes图片public static Bitmap getImageFromAssetsFile(String fileName, Context context) {Bitmap image = null;AssetManager am = context.getResources().getAssets();try {InputStream is = am.open(fileName);image = BitmapFactory.decodeStream(is);is.close();} catch (IOException e) {e.printStackTrace();}return image;}//验证邮箱格式public static boolean isEmail(String email) {String str = "^([\\w-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([\\w-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$";Pattern p = Pattern.compile(str);Matcher m = p.matcher(email);return m.matches();}//验证手机号码格式public static boolean isMobileNO(String mobiles) {Pattern p = Pattern.compile("^1(3[0-9]|4[57]|5[0-35-9]|8[025-9]|7[0-9])\\d{8}$");Matcher m = p.matcher(mobiles);return m.matches();}
}
//iOS: Utils.h
@interface Utils:NSObject//下面是一系列回调函数block
typedef void (^VoidIntCallback) (NSUInteger);
typedef void (^VoidCallback) ();
typedef void (^VoidStringCallback) (NSString *);
typedef void (^VoidBoolCallback) (BOOL);
typedef void (^VoidIdCallback) (id);//获取当前设备名称
+ (NSString *)getDeviceName;
//获取设备信息
+(NSString *)getClientInfo;
+(int)getRandomNumber:(int)from to:(int)to;
//获取文件夹的大小
+(float) folderSizeAtPath:(NSString*) folderPath;
+(long long) fileSizeAtPath:(NSString*) filePath;
//清除缓存
+(NSString *)clearCache;
//判断输入是否合法
+(BOOL) isValidPhone:(NSString *)num;
+(BOOL) isValidEmail:(NSString *)email;//一个页面中局部view显示/隐藏时所用的动画。默认采取渐显/渐隐的方式
+(void) createShowAnimForViewTypeChangeWithOneView:(UIView *) view andComplete:(VoidCallback) completeCb;
+(void) createHideAnimForViewTypeChangeWithOneView:(UIView *) view andComplete:(VoidCallback) completeCb;
+(void) createAnimForViewTypeChangeWithFromView:(UIView *) fromView toView:(UIView *)toView andComplete:(VoidCallback) completeCb;
@end
//iOS: Utils.m
#import "Utils.h"
#import <ADSupport/ASIdentifierManager.h>
@implements Utils//这个函数是从网上找的代码,不是很准确,使用的方法也奇怪,这里只是表示一个意思,可令getClientInfo调用。
//想要更正确的代码,请自行查找。
+ (NSString *)getDeviceName{CGRect rect = [[UIScreen mainScreen] bounds];CGFloat width = rect.size.width;CGFloat height = rect.size.height;//get current interface OrientationUIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];//unknownif (UIInterfaceOrientationUnknown == orientation) {return @"unknown";}//    portrait  width * height//    iPhone4:320*480//    iPhone5:320*568//    iPhone6:375*667//    iPhone6Plus:414*736//portraitif (UIInterfaceOrientationPortrait == orientation) {if (width ==  320.0f) {if (height == 480.0f) {return @"iphone4/iPhone4s";//iphone4} else {return @"iPhone5/iPhone5s";}} else if (width == 375.0f) {return @"iPhone6/iPhone6s";} else if (width == 414.0f) {return @"iPhone6plus/iPhone6sPlus";}} else if (UIInterfaceOrientationLandscapeLeft == orientation || UIInterfaceOrientationLandscapeRight == orientation) {//landscapeif (height == 320.0) {if (width == 480.0f) {return @"iphone4/iPhone4s";} else {return @"iPhone5/iPhone5s";}} else if (height == 375.0f) {return @"iPhone6/iPhone6s";} else if (height == 414.0f) {return @"iPhone6plus/iPhone6sPlus";}}return -1;
}
//获取设备信息
+(NSString *)getClientInfo{NSMutableString *ret = [[NSMutableString alloc]init];[ret appendString:@"os:iphone"];[ret appendFormat:@",osVersion:%f", [[[UIDevice currentDevice] systemVersion] floatValue]];[ret appendString:@",phoneBrand:iphone"];[ret appendFormat:@",phoneModel:%@", [self getDeviceName]];[ret appendFormat:@",appVersionName:%@", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]];[ret appendFormat:@",appVersionCode:%@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]];NSString * adId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];NSString * deviceId = [[UIDevice currentDevice].identifierForVendor UUIDString];[ret appendFormat:@",deviceId:%@", deviceId];NSString *identifier = nil;if(!adId){identifier = deviceId;}else{identifier = adId;[ret appendFormat:@",adId:%@", adId];}[ret appendFormat:@",uuid:%@", identifier];return ret;
}+ (long long) fileSizeAtPath:(NSString*) filePath{NSFileManager* manager = [NSFileManager defaultManager];if ([manager fileExistsAtPath:filePath]){return [[manager attributesOfItemAtPath:filePath error:nil] fileSize];}return 0;
}+(int)getRandomNumber:(int)from to:(int)to{return (int)(from + (arc4random() % (to - from + 1)));
}//遍历文件夹获得文件夹大小,返回多少M
+ (float ) folderSizeAtPath:(NSString*) folderPath{NSFileManager* manager = [NSFileManager defaultManager];if (![manager fileExistsAtPath:folderPath]) return 0;NSEnumerator *childFilesEnumerator = [[manager subpathsAtPath:folderPath] objectEnumerator];NSString* fileName;long long folderSize = 0;while ((fileName = [childFilesEnumerator nextObject]) != nil){NSString* fileAbsolutePath = [folderPath stringByAppendingPathComponent:fileName];folderSize += [self fileSizeAtPath:fileAbsolutePath];}return folderSize/(1024.0*1024.0);
}+ (NSString *)clearCache
{//清除缓存目录NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);NSString *searchPath = [searchPaths lastObject];NSString *str = [NSString stringWithFormat:@"缓存已清除%.1fM", [self folderSizeAtPath:searchPath]];NSLog(@"%@",str);NSArray *files = [[NSFileManager defaultManager] subpathsAtPath:searchPath];for (NSString *p in files) {NSError *error;NSString *currPath = [searchPath stringByAppendingPathComponent:p];if ([[NSFileManager defaultManager] fileExistsAtPath:currPath]) {BOOL ret = [[NSFileManager defaultManager] removeItemAtPath:currPath error:&error];YYLog(@"移除文件 %@ ret= %d", currPath, ret);}else{YYLog(@"文件不存在 %@", currPath);}}return str;
}
+(BOOL) isValidNum:(NSString *)num{const char *cvalue = [num UTF8String];int len = (int)strlen(cvalue);for (int i = 0; i < len; i++) {if(cvalue[i] < '0' || cvalue[i] > '9'){return NO;}}return YES;
}
+(BOOL) isValidPhone:(NSString *)num{if (!num) {return NO;}const char *cvalue = [num UTF8String];int len = (int)strlen(cvalue);if (len != 11) {return NO;}if (![Util isValidNum:num]){return NO;}NSString *preString = [[NSString stringWithFormat:@"%@",num] substringToIndex:2];if ([preString isEqualToString:@"13"] ||[preString isEqualToString: @"15"] ||[preString isEqualToString: @"18"] ||[preString isEqualToString: @"17"]){return YES;}else{return NO;}return YES;
}+ (BOOL) isValidEmail:(NSString *)e
{if (!e) {return NO;}NSArray *array = [e componentsSeparatedByString:@"."];if ([array count] >= 4) {return NO;}NSString *emailRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];return [emailTest evaluateWithObject:e];
}@end

代码清单:

    //android: LogUtils.javaTimeUtils.javaUtils.java
    //iOS:LogUtils.hLogUtils.mTimeUtils.hTimeUtils.mUtils.hUtils.m

至此框架基本搭建完毕,可以快乐地写页面去啦。
当然在项目进行的过程中,也需要慢慢给这个还瘦弱的框架添枝加叶。
让它慢慢壮大,更加完整。
等经过一个或2个项目的洗礼,就会成为一个完整的,不错的框架了。

相关文章:

MySQL优化配置之query_cache_size

原理MySQL查询缓存保存查询返回的完整结果。当查询命中该缓存&#xff0c;会立刻返回结果&#xff0c;跳过了解析&#xff0c;优化和执行阶段。 查询缓存会跟踪查询中涉及的每个表&#xff0c;如果这写表发生变化&#xff0c;那么和这个表相关的所有缓存都将失效。 但是随着服…

request.getSession()

request.getSession(); 与request.getSession(false);区别 服务器把session信息发送给浏览器 浏览器会将session信息存入本地cookie中 服务器本地内存中也会留一个此session信息 以后用户发送请求时 浏览器都会把session信息发送给服务器 服务器会依照浏览器发送过来的se…

alpine 交互sh_在这个免费的交互式教程中学习Alpine JS

alpine 交互shAlpine.js is a rugged, minimal framework for composing Javascript behavior in your markup. Thats right, in your markup! Alpine.js是一个坚固的最小框架&#xff0c;用于在标记中构成Javascript行为。 是的&#xff0c;在您的标记中&#xff01; It allo…

浅谈 MVP in Android

一、概述 对于MVP&#xff08;Model View Presenter&#xff09;&#xff0c;大多数人都能说出一二&#xff1a;“MVC的演化版本”&#xff0c;“让Model和View完全解耦”等等。本篇博文仅是为了做下记录&#xff0c;提出一些自己的看法&#xff0c;和帮助大家如何针对一个Acti…

test markdown

test test public void main(String[] args){System.out.println("test"); } 转载于:https://www.cnblogs.com/cozybz/p/5427053.html

java开发工具对比eclipse·myeclipse·idea

eclipse:不说了&#xff0c;习惯了 myeclipse&#xff1a;MyEclipse更适合企业开发者&#xff0c;更团队开发 idea:idea更适合个人开发者,细节优化更好转载于:https://www.cnblogs.com/gjack/p/8136964.html

软件测试质量过程检测文档_如何编写实际上有效的质量检查文档

软件测试质量过程检测文档A software product is like an airplane: it must undergo a technical check before launch.软件产品就像飞机&#xff1a;必须在发射前经过技术检查。 Quality Assurance is a necessary step towards launching a successful software product. I…

Android深度探索--HAL与驱动开发----第一章读书笔记

1.1 Android拥有非常完善的系统构架可以分为四层&#xff1a; 第一层&#xff1a;Linux内核。主要包括驱动程序以及管理内存、进程、电源等资源的程序 第二层&#xff1a;C/C代码库。主要包括Linux的.so文件以及嵌入到APK程序中的NDK代码 第三层&#xff1a;android SDK API …

[NOI2011]Noi嘉年华

题解:我们设计状态方程如下: num[i][j]表示从时间i到j中有多少个 pre[i][j]表示时间1~i中,A选了j个时的B能选的数量的最大值. nex[i][j]表示时间i~cnt中,A选了j个时的B能选的数量的最大值. mus[i][j]表示从时间i到j的保证选时,A和B选的数量中的较小值的最大值. ①对于num数组直…

只有20%的iOS程序员能看懂:详解intrinsicContentSize 及 约束优先级/content Hugging/content Compression Resistance

在了解intrinsicContentSize之前&#xff0c;我们需要先了解2个概念&#xff1a; AutoLayout在做什么约束优先级是什么意思。 如果不了解这两个概念&#xff0c;看intinsic content size没有任何意义。 注&#xff1a;由于上面这几个概念都是针对UIView或其子类(UILabel&…

redux rxjs_可观察的RxJS和Redux入门指南

redux rxjsRedux-Observable is an RxJS-based middleware for Redux that allows developers to work with async actions. Its an alternative to redux-thunk and redux-saga.Redux-Observable是Redux的基于RxJS的中间件&#xff0c;允许开发人员使用异步操作。 它是redux-t…

javascript数组排序和prototype详解

原型的概念:&#xff1a;原型对象里的所有属性和方法 被所有构造函数实例化出来的对象所共享&#xff0c;类似于java中的 static 正因为共享所以单一的操作 就会影响了全局&#xff0c;因此使用时需注意 基于prototype&#xff1a;为数组扩展方法 //获取数组最大值function get…

Qt 在Label上面绘制罗盘

自己写的一个小小的电子罗盘的一个小程序&#xff0c;不过是项目的一部分&#xff0c;只可以贴绘制部分代码 效果如下图 首先开始自己写的时候&#xff0c;虽然知道Qt 的坐标系是从左上角开始的&#xff0c;所以&#xff0c;使用了算法&#xff0c;在绘制后&#xff0c;在移动回…

终极方案!解决正确设置LaunchImage后仍然不显示的问题

对于如何设置LaunchImage&#xff0c;网络上有各种各样的教程。 主要分2点&#xff1a; 1. 正确设置图片尺寸 2. 取消LaunchScreen.xib 但是经过上述步骤之后&#xff0c;你觉得完全没有问题了&#xff0c;但是仍然无法显示LaunchImage。 或者&#xff0c;你在多个模拟器上…

c# 持续集成 单元测试_如何在不进行单元测试的情况下设置持续集成

c# 持续集成 单元测试Do you think continuous integration is not for you because you have no automated tests? Or no unit tests at all? Not true. Tests are important. But there are many more aspects to continuous integration than just testing. Lets see what…

Handlebars模板引擎

介绍 Handlebars 是 JavaScript 一个语义模板库&#xff0c;通过对view和data的分离来快速构建Web模板。它采用"Logic-less template"&#xff08;无逻辑模版&#xff09;的思路&#xff0c;在加载时被预编译&#xff0c;而不是到了客户端执行到代码时再去编译&#…

字符集图标制作

字符集图标&#xff1a; 将网页上常见的icon做成font&#xff08;字符集&#xff09;&#xff0c;以字体的方式插入到网页上&#xff0c;作用是减轻服务器负担&#xff0c;减少宽带。 我最常在这两个网站上下载字体图标&#xff1a; https://icomoon.io/app/#/select https://w…

Adobe源码泄漏?3行代码搞定,Flash动画无缝导入Android/iOS/cocos2dx(一)

[注] iOS代码已重构&#xff0c;效率提升90%&#xff0c;200层动画不卡。[2016.10.27] 项目介绍 项目名称&#xff1a;FlashAnimationToMobile 源码。 使用方法点这里。 这是一个把flash中的关键帧动画(不是序列帧)导出&#xff0c;然后在iOS&#xff0f;Android原生应用中解…

背景图像位置css_CSS背景图像大小教程–如何对整页背景图像进行编码

背景图像位置cssThis tutorial will show you a simple way to code a full page background image using CSS. And youll also learn how to make that image responsive to your users screen size.本教程将向您展示一种使用CSS编写整页背景图像的简单方法。 您还将学习如何使…

复习es6-解构赋值+字符串的扩展

1. 数组的解构赋值 从数组中获得变量的值&#xff0c;给对应的声明变量赋值,&#xff0c;有次序和对应位置赋值 解构赋值的时候右边必须可以遍历 解构赋值可以使用默认值 惰性求值&#xff0c;当赋值时候为undefined时候&#xff0c;默认是个函数就会执行函数 2.对象解构赋值 与…

Adobe源码泄漏?3行代码搞定,Flash动画无缝导入Android/iOS/cocos2dx(二)

[注] iOS代码已重构&#xff0c;效率提升90%&#xff0c;200层动画不卡。[2016.10.27] 上一篇 点此阅读 简要介绍了FlashToAnimation的功能&#xff0c;也就是将flash动画无缝导入到Android/iOS及cocos2dx中运行, 这一篇介绍这个库的使用方法。点此查看源码。 准备工作 首先…

the user operation is waiting for building workspace to complete解决办法

如果你在开发android应用程序中总是出现一个提示&#xff0c;显示“the user operation is waiting for "building workspace" to complete”&#xff0c;解决办法如下&#xff1a; 1.选择菜单栏的“Project”,然后把菜单栏中“Build Automatically”前面的对钩去掉。…

ios开发趋势_2020年将成为iOS应用开发的主要趋势

ios开发趋势Technology has always brought something new with time. And with these ever-changing technologies, you need to stay updated to get all the benefits from whats new. 随着时间的流逝&#xff0c;技术总是带来新的东西。 借助这些不断变化的技术&#xff0c…

http 权威指南 目录

第一部分 HTTP&#xff1a;Web的基础 第1章 HTTP概述 1.1 HTTP——因特网的多媒体信使 1.2 Web客户端和服务器 1.3 资源 1.3.1 媒体类型 1.3.2 URI 1.3.3 URL 1.3.4 URN 1.4 事务 1.4.1 方法 1.4.2 状态码 1.4.3 Web页面中可以包含多个对象 1.5 报文 1.6 连接 1.6.1 TCP/IP 1.6…

java初学者笔记总结day9

异常的概念throwable&#xff1a;异常&#xff0c;程序非正常执行的情况error&#xff1a;错误&#xff0c;程序非正常执行的情况&#xff0c;这种问题不能处理&#xff0c;或不应该处理exception&#xff1a;例外&#xff0c;程序非正常执行的情况&#xff0c;这种问题可以通过…

1小时学会:最简单的iOS直播推流(一)介绍

最简单的iOS 推流代码&#xff0c;视频捕获&#xff0c;软编码(faac&#xff0c;x264)&#xff0c;硬编码&#xff08;aac&#xff0c;h264&#xff09;&#xff0c;美颜&#xff0c;flv编码&#xff0c;rtmp协议&#xff0c;陆续更新代码解析&#xff0c;你想学的知识这里都有…

leetcode dfs_深度优先搜索:具有6个Leetcode示例的DFS图遍历指南

leetcode dfsHave you ever solved a real-life maze? The approach that most of us take while solving a maze is that we follow a path until we reach a dead end, and then backtrack and retrace our steps to find another possible path. 您是否解决了现实生活中的迷…

MySQL排序原理与MySQL5.6案例分析【转】

本文来自&#xff1a;http://www.cnblogs.com/cchust/p/5304594.html&#xff0c;其中对于自己觉得是重点的加了标记&#xff0c;方便自己查阅。更多详细的说明可以看沃趣科技的文章说明。 前言 排序是数据库中的一个基本功能&#xff0c;MySQL也不例外。用户通过Order by…

7.RabbitMQ RFC同步调用

RabbitMQ RFC同步调用是使用了两个异步调用完成的&#xff0c;生产者调用消费者的同时&#xff0c;自己也作为消费者等待某一队列的返回消息&#xff0c;消费者接受到生产者的消息同时&#xff0c;也作为消息发送者发送一消息给生产者。参考下图&#xff1a; 调用流程如下&…

1小时学会:最简单的iOS直播推流(二)代码架构概述

最简单的iOS 推流代码&#xff0c;视频捕获&#xff0c;软编码(faac&#xff0c;x264)&#xff0c;硬编码&#xff08;aac&#xff0c;h264&#xff09;&#xff0c;美颜&#xff0c;flv编码&#xff0c;rtmp协议&#xff0c;陆续更新代码解析&#xff0c;你想学的知识这里都有…