[{"content":"","date":"2026-02-22","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"2026-02-22","externalUrl":null,"permalink":"/p/","section":"Ps","summary":"","title":"Ps","type":"p"},{"content":"","date":"2026-02-22","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"尾灯在暗夜的公路上拉出红晕，\n引擎的低鸣震颤着发酸的掌心。\n逃离的庆幸只维持了短短一瞬，\n拥堵的车流便将时间无限拉长，\n熟悉的失落感随着夜风倒灌进来。\n曾经让人想要逃避的关切与追问，\n如今都在后视镜里渐渐远去，\n化作一层温热且不舍的雾气。\n推开房间冷冰冰的门，\n行李箱的滚轮碾过安静的走廊，\n我按下了墙角的开关，\n灯光有点过于刺眼了。\n","date":"2026-02-22","externalUrl":null,"permalink":"/p/fog-in-the-rearview-mirror/","section":"Ps","summary":"尾灯在暗夜的公路上拉出红晕，\n引擎的低鸣震颤着发酸的掌心。\n逃离的庆幸只维持了短短一瞬，\n拥堵的车流便将时间无限拉长，\n熟悉的失落感随着夜风倒灌进来。\n","title":"后视镜里的雾气","type":"p"},{"content":"👻 你是怎么找到这里的... ","date":"2026-02-22","externalUrl":null,"permalink":"/","section":"欢迎来到 Code Knight! 🎉","summary":"👻 你是怎么找到这里的...","title":"欢迎来到 Code Knight! 🎉","type":"page"},{"content":"","date":"2026-02-22","externalUrl":null,"permalink":"/tags/%E9%9A%8F%E7%AC%94/","section":"Tags","summary":"","title":"随笔","type":"tags"},{"content":"","date":"2026-02-22","externalUrl":null,"permalink":"/categories/%E6%9D%82%E8%B0%88/","section":"Categories","summary":"","title":"杂谈","type":"categories"},{"content":"窗外的夜风带着初春的料峭，\n机箱风扇的细碎嗡鸣重新成为房间里唯一的背景音。\n日历上那些惹眼的红，\n不知不觉又褪回了平淡的黑白。\n喧嚣与鼎沸仿佛被突然按下了静音键。\n我们总以为节日的仪式感能让时间走得慢一些，\n好让疲惫的灵魂稍作停歇。\n但时间只是狡黠地换了副面孔，\n在推杯换盏与寒暄中悄然流逝，\n什么也没留下。\n敲下终端里的最后一行命令，\n回车。\n桌角杯子里的残茶，\n已经凉透了。\n","date":"2026-02-20","externalUrl":null,"permalink":"/p/fade-noise/","section":"Ps","summary":"窗外的夜风带着初春的料峭，\n机箱风扇的细碎嗡鸣重新成为房间里唯一的背景音。\n日历上那些惹眼的红，\n不知不觉又褪回了平淡的黑白。\n","title":"褪色的喧嚣","type":"p"},{"content":"窗台上的灰尘，\n在冷掉的日光里缓慢起伏。\n空气里有种干燥的焦灼感，\n像一张即将燃尽的废纸。\n如果那个红色的按键真实存在，\n那种诱惑大概源于一种极度的疲惫。\n不是因为仇恨，\n而是因为所有的逻辑与构建，\n都已在这漫长的静默中失去了重量。\n毁灭不是惩罚，\n而是一场彻底的格式化。\n将那些无法修补的错误，\n和沉重的肉身一起，\n归还给虚无的寂静。\n我盯着自己的倒影，\n看它在黑暗中一点点模糊。\n指尖微微下落，\n最终，\n只触碰到了冷硬的桌面。 # ","date":"2025-05-23","externalUrl":null,"permalink":"/p/cracks-on-fingertips/","section":"Ps","summary":"窗台上的灰尘，\n在冷掉的日光里缓慢起伏。\n空气里有种干燥的焦灼感，\n像一张即将燃尽的废纸。\n","title":"毁灭世界的红色按键","type":"p"},{"content":"","date":"2025-05-23","externalUrl":null,"permalink":"/tags/%E8%99%9A%E6%97%A0/","section":"Tags","summary":"","title":"虚无","type":"tags"},{"content":"最近把博客迁移到 hugo 了，VPS 里的所有服务也全部迁移到了 docker 容器里，包括 hugo，静态页托管在 docker 的 nginx 里，源文件放在 github 上。正好可以通过 webhook 来实现源文件 push 后自动部署，于是让 ChatGPT 帮忙写了个监听代码，非常强大非常科学~\n","date":"2023-03-10","externalUrl":null,"permalink":"/p/using-chatgpt/","section":"Ps","summary":"最近把博客迁移到 hugo 了，VPS 里的所有服务也全部迁移到了 docker 容器里，包括 hugo，静态页托管在 docker 的 nginx 里，源文件放在 github 上。正好可以通过 webhook 来实现源文件 push 后自动部署，于是让 ChatGPT 帮忙写了个监听代码，非常强大非常科学~\n","title":"用 ChatGPT 写了个脚本","type":"p"},{"content":"","date":"2020-04-25","externalUrl":null,"permalink":"/categories/ios/","section":"Categories","summary":"","title":"IOS","type":"categories"},{"content":"","date":"2020-04-25","externalUrl":null,"permalink":"/tags/ios/","section":"Tags","summary":"","title":"IOS","type":"tags"},{"content":"在项目的开发过程中免不了需要集成第三方 SDK ，而这个过程中最头疼的事就是在 AppDelegate 中重写各种相关的系统回调方法，比如 application:openURL:options: 和 application:didReceiveRemoteNotification:fetchCompletionHandler: 等等，而每个 SDK 对这些方法中拿到的数据处理方式又各不相同，并且随着集成的第三方 SDK 越来越多，AppDelegate 也变得越来越臃肿，每一个回调方法中充斥着各种第三方 SDK 的处理代码，AppDelegate 亟待瘦身。\n另外，当一个公司内部有多个项目都需要集成一系列 SDK（如分享、推送、支付等） 的时候，将这个重复的集成步骤在公共组件中统一处理是一个非常有必要的事情，这样既能够减少很大一部分重复开发工作，也能够将冗余的代码从 AppDelegate 中剥离出去。而对于各个项目的开发同学来说，使用公共组件中的这些组件能力的时候，更关心的应该是如何在业务中使用这些能力，而不是关心第三方 SDK 需要从哪些系统回调中监听数据，或者第三方 SDK 的生命周期管理，例如：使用支付功能的时候，业务需要使用的能力只有发起支付动作和支付成功或失败的回调，而具体发起支付前需要的参数配置和支付结果的回调应该在哪里监听（通过 url scheme 还是 SDK 内置的 H5 回调），这些是业务方不需要考虑的。\n于是我们集成第三方 SDK 时有了这几个痛点：\n自动完成 SDK 参数配置 监听需要的系统回调 简单来说就是我们需要一个机制来达到\u0026quot;即插即用\u0026quot;的目的，所谓\u0026quot;即插即用\u0026quot;，即拖进项目就可以使用，不需要额外的初始化、响应回调等步骤，要做到这些，我们实现一个 iOS 中的\u0026quot;服务\u0026quot;机制，这个机制需要：\n感知 APP 生命周期 响应 APP 关键回调 自动发现、自动初始化 下面来看看如何实现这几个特性。\n获取已注册的类 # 要做到服务类的自动发现，首先要能够获取到项目中所有的类，这一点不难做到，runtime 给我们提供了这样一个方法：\nOBJC_EXPORT int objc_getClassList(Class *buffer, int bufferLen); 这个方法有两个参数，第一个参数 buffer 是分配好内存空间的数组，用来存储这个方法获取到的所有类的指针，当这个参数是 NULL 时，这个方法只会返回所有已注册类的个数；第二个参数 bufferLen 是第一个参数 buffer 的大小，告诉这个方法 buffer 所能装载的指针数量，当 bufferLen 小于所有注册类的数量时，这个方法会随机填充 bufferLen 数量的已注册类到 buffer 中，注意是随机的，意味着每一次调用得到的结果都不一样。\n我们可以在 runtime 源码的使用示例中理解这两个参数的作用：\nint numClasses = 0, newNumClasses = objc_getClassList(NULL, 0); Class *classes = NULL; while (numClasses \u0026lt; newNumClasses) { numClasses = newNumClasses; classes = realloc(classes, sizeof(Class) * numClasses); newNumClasses = objc_getClassList(classes, numClasses); } // now, can use the classes list; if NULL, there are no classes free(classes); 除此以外，还有另外一个方法可以获取到所有已注册类：\nClass * objc_copyClassList(unsigned int *outCount); 这个方法比上面的方法使用起来更加简单，这个方法会直接创建并返回所有已注册类指针的数组，参数 outCount 是已注册类的数量，使用方法：\nunsigned int outCount; Class *classes = objc_copyClassList(\u0026amp;outCount); // now, can use the classes list; if NULL, there are no classes free(classes); 使用这个方法跑起来并把获取到的类名打印出来你可能就震惊了，已注册类居然有上万个之多：\n_CNCombineLatestObservable _CNDistinctObservable _CNScheduledObservable CNBehaviorSubject CNVirtualSchedulerJob CNTracedLog ... 这里面包括了很多完全没有见过的类，其中也包括了非 NSObject 的子类，我们使用 NSObject 方法对这些非 NSObject 子类进行操作的时候是非常危险的，会导致应用 crash，而事实上我们要关心的只有 NSObject 的子类，所以还需要将获取到的列表中的类做一次过滤，runtime 中提供了一个方法来判断类中是否实现了某个特定方法：\nOBJC_EXPORT BOOL class_respondsToSelector(Class _Nullable cls, SEL _Nonnull sel) 而要过滤出 NSObject 的子类，只需要判断类是否包含 NSObject 中一定会存在的 doesNotRecognizeSelector: 方法即可：\nclass_respondsToSelector(class, @selector(doesNotRecognizeSelector:)) 这样我们就得到了所有已注册的 NSObject 子类，于是我们就可以很方便地通过 NSObject 中的 isSubclassOfClass: 方法来从中获取到项目中我们指定类的子类了。\n什么时候触发自动加载？ # 知道了如何获取所有的类，并且能够从中获取到指定类的子类，我们就实现了自动发现服务类的第一步，那么应该什么时候触发这个获取动作呢？加到 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法中？之前提到了服务类很重要的一点——\u0026ldquo;即插即用\u0026rdquo;，对项目有侵入代码的行为就不能称为即插即用了，所以这个方案不行。通过 hook？这是一个好办法，实际上我们的服务类的确在 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法中挂上了 hook ，用于实现 APP 类加载完毕之后服务类进行一些业务配置相关的初始化动作，比如相关 SDK 的初始化。但是假如某个服务需要实现在 didFinishLaunchingWithOptions: 方法之前执行一些操作（就比如 hook didFinishLaunchingWithOptions: 方法）怎么办？\n了解 runtime 的同学肯定很快就想到了 + (void)load 方法，一起来回顾一下这个方法。先看一下苹果对这个方法的解释：\nInvoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.\n在类或者分类被添加到 runtime 时执行，实现这个方法可以在加载的时候执行一些类相关的行为。具体的运行机制可以看下 dyld 的源码，下次再开篇文章一起来梳理一下。\n由于这个方法在所有类被加载时就会调用，显然调用时机在 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法之前。需要注意的是，原则上在这个方法中只能做一些诸如方法交换等类相关的操作，因为 APP 在启动时执行类加载等资源初始化动作是耗费性能巨大的流程，在这个时候执行一些计算量大的操作会严重影响 APP 启动速度。\n我们新建一个类，实现 + (void)load 方法。接下来的事情就很明确了，我们可以获取到所有已注册的类，并且能够从中获取到指定类的子类，现在只需要实现一个服务类的基类，所有服务类都继承于这个基类，然后在 + (void)load 中获取这个基类的所有子类就能够达到 APP 启动时自动加载项目中所有服务类的目的：\n+ (void)load { // 加载 services NSArray *allServices = [MYRuntime allSubClassesOf:[MYBasicService class]]; ... // 调用 allServices 中的生命周期方法 // hook // Notification ... } allSubClassesOf: 方法就是上面提到的获取到指定类子类的实现。\n接着在服务基类的这个方法中，通过 method swizzling 给 AppDelegate 中的下面的方法挂上钩子，以便服务类可以在这些方法调用时提供相应处理能力：\n@selector(application:didFinishLaunchingWithOptions:) @selector(application:handleOpenURL:) @selector(application:openURL:options:) @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:) @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:) @selector(application:continueUserActivity:restorationHandler:) 除此以外，响应下面的系统通知，以便服务类能感知到 APP 的生命周期：\nUIApplicationDidFinishLaunchingNotification UIApplicationDidEnterBackgroundNotification UIApplicationWillEnterForegroundNotification UIApplicationWillTerminateNotification 以上 hook 和通知的响应都调用下列服务基类的模板方法，以实现服务类的生命周期机制：\n- (void)load; // + (void)load 时调用 - (void)unload; // + (void)dealloc 时调用 - (void)launch; // UIApplicationDidFinishLaunchingNotification 时调用 - (void)terminate; // UIApplicationWillTerminateNotification 时调用 - (void)serviceWillActive; // UIApplicationWillEnterForegroundNotification 时调用 - (void)serviceDidDeactived; // UIApplicationDidEnterBackgroundNotification 时调用 实现服务类 # 继承于服务基类的服务类通过重写服务基类中提供的生命周期模板方法，来对 APP 相应的生命周期作出响应，除此以外，由于我们 hook 了 openURL 相关的方法，我们能够在服务类中很方便的对第三方 APP 跳转回来的动作作出响应，这在将分享 SDK 或支付 SDK 封装成服务的时候非常有用，对于第三方 APP 通过 url scheme 跳转回来的动作，直接在服务类中就能完成处理，不需要对 AppDelegate 中的方法作出任何改动。推送类 SDK 的封装也是一样。\n封装好后的服务类，给业务方提供几个很简单的输入/输出方法或者 block，就可以将第三方 SDK 的能力提供给业务方调用，业务方不需要再考虑集成 SDK 所需要在诸如 AppDelegate 中重写的回调方法了，真正实现将服务类\u0026quot;拖入\u0026quot;项目就可以使用第三方 SDK 的能力，达到\u0026quot;即插即用\u0026quot;的目的。\n注意 # 服务类中的 - (void)load 方法与 NSObject 的 + (void)load 方法调用时机一致，所以在实现服务类的时候，这个方法也仅限于做一些类相关的操作，其他耗费资源的操作如 SDK 的初始化一律在 - (void)launch 中调用，避免影响 APP 启动速度。\n","date":"2020-04-25","externalUrl":null,"permalink":"/p/ios-service/","section":"Ps","summary":"在项目的开发过程中免不了需要集成第三方 SDK ，而这个过程中最头疼的事就是在 AppDelegate 中重写各种相关的系统回调方法，比如 application:openURL:options: 和 application:didReceiveRemoteNotification:fetchCompletionHandler: 等等，而每个 SDK 对这些方法中拿到的数据处理方式又各不相同，并且随着集成的第三方 SDK 越来越多，AppDelegate 也变得越来越臃肿，每一个回调方法中充斥着各种第三方 SDK 的处理代码，AppDelegate 亟待瘦身。\n另外，当一个公司内部有多个项目都需要集成一系列 SDK（如分享、推送、支付等） 的时候，将这个重复的集成步骤在公共组件中统一处理是一个非常有必要的事情，这样既能够减少很大一部分重复开发工作，也能够将冗余的代码从 AppDelegate 中剥离出去。而对于各个项目的开发同学来说，使用公共组件中的这些组件能力的时候，更关心的应该是如何在业务中使用这些能力，而不是关心第三方 SDK 需要从哪些系统回调中监听数据，或者第三方 SDK 的生命周期管理，例如：使用支付功能的时候，业务需要使用的能力只有发起支付动作和支付成功或失败的回调，而具体发起支付前需要的参数配置和支付结果的回调应该在哪里监听（通过 url scheme 还是 SDK 内置的 H5 回调），这些是业务方不需要考虑的。\n","title":"iOS \"即插即用\"的服务类","type":"p"},{"content":"","date":"2020-04-18","externalUrl":null,"permalink":"/tags/%E5%9B%BD%E9%99%85%E5%8C%96/","section":"Tags","summary":"","title":"国际化","type":"tags"},{"content":"这一次需要对公司现有项目做国际化改造，由于项目在早期的迭代过程中没有考虑国际化的需求，项目中的字符串都是通过硬编码的形式直接写在业务逻辑里的，我们在做国际化时遇到的第一个问题就是如何将现有硬编码字符串替换为支持多语言的实现方案。经过初步统计，现有两个项目各有约 4000-5000 的字符串，我们需要一个比较好的方案来快速完成多语言方案替换，尽量不将过多的时间花在这类非技术性问题上。\n多语言宏 # 对于已有的非多语言项目来说，使用苹果提供的多语言宏是最佳选择，因为苹果提供的宏可以通过 Xcode 的 Editor-Export for Localization... 功能直接将所有多语言字符串一键导出为 .xliff 文件或 .strings 文件。\n首先梳理一下 Objective-C 提供的多语言宏，苹果一共提供了四个宏供开发者使用：\nNSLocalizedString(key, comment) NSLocalizedStringFromTable(key, tbl, comment) NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) NSLocalizedString(key, comment) 宏只有两个参数，第一个是 key，第二个是给翻译人员看的注释，可以为 nil 或者空字符串。这个宏书写起来比较简洁，只能从 main bundle 中对应语言的 Localizable.strings 文件里取到对应 key 的显示语言。 NSLocalizedStringFromTable(key, tbl, comment) 比上一个宏多一个 table 参数，可以从 main bundle 中指定的 .strings 文件中获取 key-value 值，table 名称就是 .strings 文件的名称。 NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) 比上一个宏多一个 bundle 参数，可以从指定 bundle 而不是默认的 main bundle 中的 .strings 文件获取 key-value 值。 NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) 比上一个宏多一个 val 参数用于指定默认值，当在对应的文件中取不到对应 key-value 将使用 val 的值返回。 对于公司国际化项目的需求来说，产品明确需要支持 APP 内切换语言 ，于是前两个只能跟随系统语言的宏就不在我们的考虑范围了，而后面两个宏可以指定 bundle ，这就给我们在 APP 内设置不同于系统语言时从不同的 bundle 取相应的语言提供了可能。\n最终我们采用的是 NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) 这个宏，因为我们不会有指定默认值的场景。\n对于 NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) 这个宏，我们需要关心的就是 tbl（table） 和 bundle 这两个值，由于公司 iOS 项目全部实现了业务层级的组件化，所以我们的 .strings 文件可以根据组件来划分，以组件名作为 table 的名称，如订单组件 Order.strings，table 值即为 Order。 bundle 的值是实现 APP 内切换语言的关键所在，我们需要用一个专门的类来管理 APP 显示语言。\nAPP 内切换语言 # 为了实现 APP 内切换语言，我们实现了一个 IANLocalization 单例类，用来管理 APP 语言设置。\n首先是提供读取和设置语言的方法：\n读取方法会先判断用户是否在 APP 内设置过语言，我们将用户设置的语言保存在本地，若没有设置过则读取系统语言：\n- (NSString *)currentLanguage { if (!_currentLanguage) { // 优先使用自定义语言，没有则使用系统语言 if (有自定义语言) { _currentLanguage = 自定义语言; } else { _currentLanguage = 系统语言; } } return _currentLanguage; } 设置语言方法：\n- (void)setCurrentLanguage:(NSString *)currentLanguage { _currentLanguage = currentLanguage; // 保存 currentLanguage 到本地; } 有了这两个方法，我们就可以实现根据用户设置的语言对应到正确的 bundle 了：\n- (NSBundle *)languageBundleFromRoot:(NSBundle *)aBundle { NSString *path = [aBundle pathForResource:self.currentLanguage ofType:@\u0026#34;lproj\u0026#34;]; NSBundle *bundle = [NSBundle bundleWithPath:path]?:aBundle; return bundle; } 看了这个方法你可能会有疑问，为什么还是需要传入一个 bundle？\nCocoapods 组件化与 bundle # 上面提到公司的 iOS 项目实现了组件化，组件通过 Cocoapods 的方式来管理，这使得许多 UIKit 和 Foundation 库里提供的访问资源类的方法不能直接使用了，例如 [UIImage imageNamed:@\u0026quot;\u0026quot;] 方法，这个方法只能从 main bundle 中读取图片资源，而通过 Cocoapods 的 subspec 来管理子组件的项目，图片资源是存在于 subspec 的 resource_bundles 中的，关于图片资源的解决方案会再单独写一篇文章来详细说明。\n而本篇需要讨论的 NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) 宏同样需要用到 bundle，由于 Cocoapods 的原因，也不能直接从 main bundle 中直接访问，需要先获取到访问资源的类所处的 framework，进而获取到多语言文件 .strings 所在的 bundle（.lproj）。\n这里有一点需要特殊说明，与图片资源的处理方式略有不同的是，为了应对前期开发阶段频繁新增/改动多语言文件的情况，多语言文件并没有单独放到每一个 subspec 中，而是放在 pod 最外层的 .resource 中，也就是说，打包后语言文件没有像图片一样存放在 .../*.app/Frameworks/POD_NAME.framework/SUBSPEC_NAME.bundle/Assets.car 中，而是存放在 .../*.app/Frameworks/POD_NAME.framework/*.lproj/*.strings 中，所有子组件同一种语言的 .strings 文件都在一个 .lproj 中，方便统一替换。\n最后可以通过一个宏来实现获取相应 POD_NAME 的 bundle：\n#define IAN_LANGUAGE_BUNDLE [IANLocalization languageBundleFromRoot:[NSBundle bundleForClass:[self class]]] // IANLocalization 需提供相应的类方法来访问单例中的实例方法 体力活 # 方案决定之后就是替换工作了，这就是一个要求细致的体力活，我们的字符串的最终写法变成了：\nNSLocalizedStringFromTableInBundle(@\u0026#34;字符串\u0026#34;, @\u0026#34;组件名\u0026#34;, IAN_LANGUAGE_BUNDLE, @\u0026#34;\u0026#34;) 当然项目里几千个字符串我们是不可能一个个手动去替换的，Xcode 支持正则替换，我们可以使用正则来完成大部分字符串的替换：\n搜索： @\u0026#34;[^\u0026#34;]*[\\u4E00-\\u9FA5]+[^\u0026#34;\\n]*?\u0026#34; 替换为： NSLocalizedStringFromTableInBundle($0, @\u0026#34;组件名\u0026#34;, IAN_LANGUAGE_BUNDLE, @\u0026#34;\u0026#34;) 这样项目中所有的 @\u0026quot;\u0026quot; 里面包含中文的都会被替换掉，其中还包含了大量 NSLog 中的内容，如果不需要翻译这部分内容，可以用一些小技巧，比如先将 NSLog(@\u0026quot; 替换为其他内容让 NSLog 的字符串不是 @\u0026quot;\u0026quot; 这种格式，在正则替换完成后再将 NSLog(@\u0026quot; 替换回来。另外还有 @\u0026quot;\u0026quot; 中包含反斜杠转义的字符串会被正则替换错乱，这个需要手动处理一下，需要体力活的地方就在这里了。\n最后全部都替换完成保证项目可以跑起来之后，就可以通过 Xcode 的 Editor-Export for Localization... 功能将多语言文件导出提供给翻译人员或者上传到诸如 Crowdin 之类的翻译平台上进行翻译了。\n","date":"2020-04-18","externalUrl":null,"permalink":"/p/ios-localization/","section":"Ps","summary":"这一次需要对公司现有项目做国际化改造，由于项目在早期的迭代过程中没有考虑国际化的需求，项目中的字符串都是通过硬编码的形式直接写在业务逻辑里的，我们在做国际化时遇到的第一个问题就是如何将现有硬编码字符串替换为支持多语言的实现方案。经过初步统计，现有两个项目各有约 4000-5000 的字符串，我们需要一个比较好的方案来快速完成多语言方案替换，尽量不将过多的时间花在这类非技术性问题上。\n多语言宏 # 对于已有的非多语言项目来说，使用苹果提供的多语言宏是最佳选择，因为苹果提供的宏可以通过 Xcode 的 Editor-Export for Localization... 功能直接将所有多语言字符串一键导出为 .xliff 文件或 .strings 文件。\n首先梳理一下 Objective-C 提供的多语言宏，苹果一共提供了四个宏供开发者使用：\nNSLocalizedString(key, comment) NSLocalizedStringFromTable(key, tbl, comment) NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment)","title":"组件化下的多语言方案","type":"p"},{"content":"最近在优化项目中的定位功能，总结一下和后台持续定位有关的各方面细节，会涉及到后台运行、延迟定位、无感知唤醒等内容。目前测试的效果是后台挂机（也就是手机关闭屏幕待机）状态下超过 24 小时不间断获取位置并上报服务器。\n权限 # 首先来看一下权限问题，要使用后台定位首先需要保证 Target -\u0026gt; Capabilities -\u0026gt; Background Modes 的开关打开，并勾选 Location updates ，然后在 info.plist 中添加必要的 NSLocationWhenInUseUsageDescription 和 NSLocationAlwaysAndWhenInUseUsageDescription 描述字段，兼容 iOS 10 以及之前版本的还需要 NSLocationAlwaysUsageDescription ，具体描述内容要求简洁清晰地解释 APP 需要定位的理由以及用途。\n这里说一下 iOS 中关于定位的两种权限 使用应用期间 以及 始终 的区别。首先并不是说选择了 使用应用期间 的 APP 就不能够使用后台定位权限了，选择了 使用应用期间 的 APP 依然能够实现后台定位，只是与选择 始终 的情况下在 APP 被杀或者被系统回收之后有一些区别，具体的情况是这样的：\n在项目配置正确且代码中 开启了后台定位功能 （具体下文会讲到）的前提下\n选择 使用应用期间 的 APP 进入后台之后，状态栏会出现蓝色提示横条，显示 “APP 名称”正在活跃使用您的位置信息 并闪烁，刘海屏设备在左上角定位图标处显示蓝框，直到 APP 回到前台或者被杀掉（包括手动或被动）才消失，代码中无法设置蓝框是否显示；\n选择 始终 的 APP 进入后台之后状态栏无蓝色提示横条，但代码中可以设置是否显示。\n两种情况下 APP 均能在后台持续定位不被挂起，除非 APP 被杀死。而被杀死之后选择 始终 的 APP 会在手机移动至少500米之后被唤醒重新定位，而选择 使用应用期间 的 APP 被杀就不再上报了。\n这是关于权限需要在项目中做的配置，除此以外，要使用后台定位还需要在提审的时候注意以下几点：\n在提审的备注中，向审核人员清晰阐述 APP 需要使用后台定位的原因，并且详细描述 APP 从界面中的什么入口触发开启后台定位，从什么入口能停止后台定位，必要的时候苹果可能还会要求提供这一过程的操作录像，可以事先准备好，上传到国内视频网站提供链接给苹果就行。\n在 APP 的上架页面的描述信息中，向用户清晰阐述 APP 的哪些特性和功能必须要使用后台定位权限，并且说明这个操作会影响电池电量。下面是大厂地图应用的描述，可以参考：\n百度地图：\n【温馨提示】\n语音导航和电子狗会持续使用GPS定位服务，切换至后台播报时，仍会保持GPS连接，相比其他操作会消耗更多的电量。\n高德地图：\n【温馨提示】\n—高德地图的驾车语音导航、公交换乘提醒和步行语音导航会持续使用GPS定位服务，切换至后台播放时仍会继续，相比其他操作会消耗更多的电量，并影响电池续航时间；\n腾讯地图：\n注意事项：\n-在后台持续使用GPS会急剧减少电池续航时长\n代码 # 创建一个 CLLocationManager 并配置属性：\n#import \u0026lt;CoreLocation/CoreLocation.h\u0026gt; // ... _locationManager = [CLLocationManager new]; _locationManager.delegate = self; _locationManager.desiredAccuracy = kCLLocationAccuracyBest; _locationManager.distanceFilter = 5.0f; // 申请始终定位权限 [_locationManager requestAlwaysAuthorization]; // 或者申请使用应用期间权限 // [_locationManager requestWhenInUseAuthorization]; // 最后需要在确定权限获取完成之后调用开启定位方法 // [_locationManager startUpdatingLocation]; 注意 requestWhenInUseAuthorization 和 requestAlwaysAuthorization 的区别：\nrequestWhenInUseAuthorization 只会向用户申请 使用应用期间 权限，申请权限的弹窗只会有允许和不允许的选项，用户点击允许只能获得 使用应用期间 权限，若 info.plist 里面正确填写了 always 相关描述，用户还可以手动到隐私设置中选择 始终 权限。 requestAlwaysAuthorization 这个方法在 iOS 10 以及之前版本只会弹窗申请 始终 权限，只有允许和不允许两个选项；从 iOS 11 开始，调用这个方法会出现 3 个选项：『不允许』、『使用应用期间』、『始终允许』，也就是说，即使你调用了 requestAlwaysAuthorization 方法申请 始终 权限，用户依然可以选择 使用应用期间 。 注意：没有按需要在 info.plist 里面写描述字段的，调用相应方法申请权限会导致 crash\n至此已经完成了 CLLocationManager 的初始化和权限申请，要使用后台定位功能，接下来还需要设置下面两个属性：\n_locationManager.pausesLocationUpdatesAutomatically = NO; if (@available(iOS 9.0, *)) { _locationManager.allowsBackgroundLocationUpdates = YES; } 下面解释一下这两个属性。\npausesLocationUpdatesAutomatically # 首先解释一下 pausesLocationUpdatesAutomatically ，这是 iOS 6.0 之后新增的属性，这个属性默认是 YES，允许在不牺牲位置信息准确性的前提下暂停位置更新以节省电池消耗。那么是如何做到不牺牲准确性的呢？首先系统会在它认为位置不大可能发生变化的时候，关闭 GPS 相关硬件来节省电量，例如，用户在使用导航应用的时候停下来吃饭，系统就认为这个时候位置不会变化了。那么系统又是如何识别出这个场景的呢？这取决于另一个属性： activityType ，这个属性是个枚举值：\ntypedef NS_ENUM(NSInteger, CLActivityType) { CLActivityTypeOther = 1,\t// 默认场景，没有特殊操作，只在一段时间位置没有发生改变的时候，暂停更新位置 CLActivityTypeAutomotiveNavigation,\t// 汽车导航场景，当位置长时间都没有发生改变的时候，会关闭部分GPS硬件，直到位置发生改变 CLActivityTypeFitness,\t// 健身使用场景，包括徒步、跑步、自行车等，这个场景下室内定位会被关闭，当位置在一段时间内没有明显变化时，会暂停更新位置，注意是\u0026#34;明显变化\u0026#34;，与上一条程度不一样 CLActivityTypeOtherNavigation, // 其他导航场景，包括轮船、火车、飞机等，不包括步行导航，当位置在一段时间内没有明显变化时，会暂停更新位置 CLActivityTypeAirborne API_AVAILABLE(ios(12.0), macos(10.14), tvos(12.0), watchos(5.0)),\t// 这个是空中使用场景，这个场景很少用到。。。 }; 当定位被暂停时，系统会调用 locationManagerDidPauseLocationUpdates: 回调，可以自行在里面进行一些操作，比如设置触发器在用户离开某个区域的时候，发送本地通知提醒用户重新打开 APP 以恢复定位。经测试在 CLActivityTypeOther 场景下，APP 处于后台，位置 180S 左右不发生变化，APP 就被挂起了。所以如果需要实现后台持续定位，这个属性需要设置为 NO。\nallowsBackgroundLocationUpdates # allowsBackgroundLocationUpdates 这个属性是 iOS 9.0 新增的属性，默认为 NO ，设置为 YES 需要保证 Target -\u0026gt; Capabilities -\u0026gt; Background Modes 的开关打开，并勾选 Location updates ，否则会导致 crash。\n这个属性是使后台持续定位生效的关键，如果不设置这个属性为 YES ，且 APP 中没有其它机制（如手动续命\u0026lt;最多续10分钟\u0026gt;、播放音乐、VOIP 等）保持 APP 后台运行，那 APP 会在位置信息不再更新之后的 180S 时被挂起。而将这个属性设置为 YES 后，APP 将一直接收定位事件，并且不需要续命也能保持一直在后台运行不被挂起，除非被手动杀死或者被系统回收。这里再提一下上面讲权限时讲到的一点，这个属性被设置为YES 时，即使 APP 选择的是 使用应用期间 权限，在进入后台之后只要不被杀死或回收，APP 同样能一直保持后台持续定位且不被挂起，与选择 始终 的区别仅仅是状态栏会出现蓝色提示横条，以及被杀死之后是否能重新唤醒。\n被杀死后无感知自动唤醒 # 开启后台定位之后 APP 处于后台的时候虽然不会被挂起了，但不可避免的会出现被手动杀死，或者前台应用占用过多内存导致后台应用被系统回收的情况，这个时候为了继续获取位置，需要再次唤醒 APP 继续接收定位事件。所幸苹果还是提供了这样的方法，供开发者做到通过定位唤醒 APP。\n首先这个方法需要 始终 权限，然后在开启定位调用 startUpdatingLocation 的同时调用 startMonitoringSignificantLocationChanges 方法。\n[_locationManager startUpdatingLocation]; // 开启位置明显变化监控 [_locationManager startMonitoringSignificantLocationChanges]; 开启了这个方法之后，当位置发生较明显的变化时同样会触发一个位置更新事件，这个变化距离不依据 distanceFilter 属性值变化，而是固定的 500 米（指与上一次触发此事件时的距离），且每 5 分钟最多触发一次，触发的时候如果 APP 被杀死了，这个事件还会唤醒 APP ，APP 会在用户无感知的情况下重新走正常的启动流程，只是界面上没有任何显示，此时会在 application:didFinishLaunchingWithOptions: 方法中携带 UIApplicationLaunchOptionsLocationKey 这个 key 来表明当前启动是被位置事件唤醒的，请注意判断这个 key 来绕过一些不需要在后台启动时执行的逻辑，或者执行一些你想要在后台启动后执行的逻辑，比如启动位置上报服务。在 APP 重新启动之后需要重新调用 startMonitoringSignificantLocationChanges 以保证 APP 再次被杀之后能被重新唤醒。\n由此方法触发的位置更新事件同样会调用正常位置更新服务 startUpdatingLocation 的locationManager:didUpdateLocations: 回调方法，这个更新事件可能是一个最近缓存的位置，也可能是最新位置，而更精确的位置会在几秒钟之后再次回调，所以如果对细微位置差别比较敏感的话，可以通过回调的 CLLocation 对象中的时间戳来过滤。\n最后，调用 stopMonitoringSignificantLocationChanges 可以停止监控不再唤醒 APP，这个方法的调用也非常重要，当业务逻辑不需要后台持续定位的时候一定要调用停止方法，否则 APP 将无止境地在位置变化超过 500 米之后被重新唤醒，对用户的设备无疑是一个负担，这是一个流氓行为。\n省电 # 开启了后台持续定位功能之后对于设备的电量毫无疑问会造成巨大的消耗，这个时候可以做一些事情来减少电量的消耗，苹果提供了一个方法：allowDeferredLocationUpdatesUntilTraveled:timeout: ，延迟位置更新，这个方法在 iOS 6.0 新增，可以设定一个移动半径或者时间间隔，当设备处于低功耗模式时，在这个移动半径或者时间间隔内的位置变化将不会触发位置更新事件。\n方法有两个参数，第一个参数 (CLLocationDistance)distance 是距离半径，单位是米，也可以设置为 CLLocationDistanceMax 来不限制半径；第二个参数 (NSTimeInterval)timeout 是时间间隔，单位是秒，也可以设置为 CLTimeIntervalMax 来不限制时间间隔。这两个参数任一条件达到都会立即触发一次位置更新事件，同样是调用locationManager:didUpdateLocations: 回调，并且触发完成一次之后还将触发 locationManager:didFinishDeferredUpdatesWithError: 方法告知本次延迟更新结束，需要再次使用延迟更新需要重新调用 allowDeferredLocationUpdatesUntilTraveled:timeout: 方法。苹果的建议是直接在locationManager:didUpdateLocations: 回调中调用此方法，在你处理完最新位置之后即可根据需求发起延迟位置更新，这样当 APP 处于后台的时候延迟更新方法就有机会在这个回调中被执行。\n需要注意的是，只有在设备处于低功耗模式的时候延迟更新位置才会生效，低功耗模式是系统控制进入的一个模式（注意不是常说的省电模式），只可能发生在屏幕关闭时由电源管理策略自动进入，并且只有 iPhone 5s 之后具备 M 系列协处理器的设备才能进入低功耗模式，使用延迟更新方法之前可以通过 [CLLocationManager deferredLocationUpdatesAvailable] 方法来判断设备是否支持，另外 连接 Xcode 调试的设备不会进入低功耗模式 。\n除此以外，使用延迟更新方法还需要同时满足以下条件：\n设备 GPS 模块可用，所以模拟器、 iPad 、 iPod 就不支持了\ndesiredAccuracy 需要设置为 kCLLocationAccuracyBest 或者 kCLLocationAccuracyBestForNavigation\ndistanceFilter 需要设置为 kCLDistanceFilterNone\n以上条件缺一不可。\n","date":"2018-12-25","externalUrl":null,"permalink":"/p/ios-background-location/","section":"Ps","summary":"最近在优化项目中的定位功能，总结一下和后台持续定位有关的各方面细节，会涉及到后台运行、延迟定位、无感知唤醒等内容。目前测试的效果是后台挂机（也就是手机关闭屏幕待机）状态下超过 24 小时不间断获取位置并上报服务器。\n权限 # 首先来看一下权限问题，要使用后台定位首先需要保证 Target -\u003e Capabilities -\u003e Background Modes 的开关打开，并勾选 Location updates ，然后在 info.plist 中添加必要的 NSLocationWhenInUseUsageDescription 和 NSLocationAlwaysAndWhenInUseUsageDescription 描述字段，兼容 iOS 10 以及之前版本的还需要 NSLocationAlwaysUsageDescription ，具体描述内容要求简洁清晰地解释 APP 需要定位的理由以及用途。\n","title":"iOS 后台持续定位","type":"p"},{"content":"","date":"2018-12-25","externalUrl":null,"permalink":"/tags/%E5%AE%9A%E4%BD%8D/","section":"Tags","summary":"","title":"定位","type":"tags"},{"content":"NSLog(@\u0026#34;Hello Knight!\u0026#34;); NSLog(@\u0026#34;I am Ian..\u0026#34;); NSLog(@\u0026#34;换机房了，豪华直连。\u0026#34;); NSLog(@\u0026#34;全站 HTTPS 了。\u0026#34;); NSLog(@\u0026#34;启用 CDN 了。。\u0026#34;); ","date":"2018-12-07","externalUrl":null,"permalink":"/p/hello-world/","section":"Ps","summary":"NSLog(@\"Hello Knight!\"); NSLog(@\"I am Ian..\"); NSLog(@\"换机房了，豪华直连。\"); NSLog(@\"全站 HTTPS 了。\"); NSLog(@\"启用 CDN 了。。\");","title":"Hello Knight","type":"p"},{"content":"博客又迁移了，从最早的基于 asp 的 PJBlog，到基于世界上最好的语言 PHP 的 Wordpress，再到现在回归本质基于 HTML 的 Hexo，折腾了一路，现在不折腾了，Hexo 算是很轻量级的博客框架了。\n用 PJBlog 的时候租的是虚拟主机，配置极低，不能安装系统，只能在主机提供的 ASP 服务下部署网站，通过一个 FTP 服务上传文件，这个阶段服务器没啥可玩的，那时候个人网站备案还是很容易的，一个月就能批下来，.cn 域名也只要几块钱一年。后来世界上最好的语言火起来了，果断跟风转到 Wordpress 了，还是买的虚拟主机，PHP的虚拟主机比 ASP 的就要便宜些了，因为 ASP 的主机只能跑在 Windows 下，配置要求更高了不说，光是 Windows 的授权费就高了不少。PHP 的主机可以跑 Linux，虚拟机的价格也低了些。Wordpress 的可玩性就非常大了，各种魔改、插件，各种折腾。\nHexo 是通过 Node.js 直接生成静态页的，没有管理后台，没有动态代码，对服务器毫无要求，甚至可以部署在 Github 上，省下了买主机的钱，也省去了备案的步骤，让博客的本质回归到内容，这才是博客存在的意义呀，不然折腾的再酷炫，没有内容支撑，谁看啊。\n接下来就慢慢把文章迁移过来吧，然后有空就多想想能写些什么，不折腾这玩意了~\n今年开始在深圳敲代码了，加油吧！\n","date":"2015-12-03","externalUrl":null,"permalink":"/p/blog-transfer-to-hexo/","section":"Ps","summary":"博客又迁移了，从最早的基于 asp 的 PJBlog，到基于世界上最好的语言 PHP 的 Wordpress，再到现在回归本质基于 HTML 的 Hexo，折腾了一路，现在不折腾了，Hexo 算是很轻量级的博客框架了。\n","title":"博客系统迁移了","type":"p"},{"content":"","date":"2013-03-14","externalUrl":null,"permalink":"/categories/java/","section":"Categories","summary":"","title":"Java","type":"categories"},{"content":"写于2011年7月,一直在草稿箱里:\n实习的时候无聊，把高三那年在文曲星上用类c的LavaX写的小游戏用java重写了，写了2天，第一天思路错了，监听器怎么都搞不定，方向控制也偷懒想要取巧，结果搞得自己都混乱了，各种bug无从下手。于是第二天全部删掉重写，一天下来总算是能运行了，虽然bug一大堆！然后又用了一天时间测试和bug fix，一休息就拉企鹅过来试玩，期间各种棍子乱飞，箱子乱飞，改代码改了一上午，终于是能玩完一局不出错了，感觉上和高三那年写的一样一样啊，下午又小修修补补的，已知的bug全部搞定，又加了些新功能，玩起来已经非常欢乐了。。。\n游戏说明：\n由于是简单的使用线程睡眠来控制速度的，所以不同电脑速度表现差别会很大，速度不合理的可以修改 SPEED 的值。\n二人游戏，通过Ctrl和Enter键推动上下移动的推杆，推动中间的箱子掉下去砸碎对方的蛋。。。\n游戏过程中会出现随机物品，捡到会发现以下事件：（这个是高三那年写的LavaX版没有的哦）\n1.所有箱子移动一次\n2.对方的杆子变粗\n3.自己的杆子变成最细\n4.自己的速度加快\n5.在对方区域出现障碍物\n另外中间有两个区域，碰到可以使对方变大或者自己变小。\n转载或者再次编译请保留作者信息就行了。\n多指教！\n/**** 代码开始 ****/ /****作者：小乜 ****/ /****2011.7.8 ****/ /*转载或二次编译请保留作者信息！*/ import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.Random; import javax.swing.*; public class BoxFight { public static void main(String[] args){ JFrame f=new JFrame(\u0026#34;箱子大战--BoxFight! V1.0--小乜作品\u0026#34;); f.setSize(400,400); f.setLocation(100,100); f.setResizable(false); MyPanel mp=new MyPanel(); f.add(mp); //创建进程 Thread t=new Thread(mp); f.addKeyListener(mp); f.setDefaultCloseOperation(f.EXIT_ON_CLOSE); mp.addKeyListener(mp); f.show(); JOptionPane.showMessageDialog(f,\u0026#34;游戏说明:\\n二人游戏,使用Ctrl键(1P)和小键盘Enter键(2P)操作\\n把中间红色的箱子推到对手一边掉下即获胜\\n游戏随机出现的超级物品可获得极大的帮助\\n碰到变大区域可将对手推杆变大\\n碰到变小区域可将自己的推杆缩小\u0026#34;); t.start(); } } class MyPanel extends Panel implements Runnable,KeyListener{ public static final int PUSHHEIGHT=5; //推杆的初始厚度 public static final int PUSHWEIGHT=40; //推杆的初始宽度 public static final int BOXX=30;//箱子高 public static final int BOXY=40;//箱子宽 public static final int SPEED=5;//游戏延时 单位毫秒 public static final int ITEMCOUNT=800;//物品出现间隔 单位为移动的像素点 public static final int ITEM=15;//物品长宽 int right_x=0,right_y=2,left_x=2,left_y=2;//左右推杆的坐标 int right_w=PUSHHEIGHT,left_w=PUSHHEIGHT;//推杆厚度 boolean justpushL=false,justpushR=false;//刚推完一次，为true时不允许再推 boolean rightgo=true,leftgo=true;//推杆是否上下移动 boolean rightpush=false,leftpush=false;//推杆是否左右移动 boolean isrightpush=false,isleftpush=false;//推杆是否正在移动 boolean rightUD=true,leftUD=true;//左右推杆的上下 true为下 false为上 boolean rightLR=true,leftLR=true;//左右推杆的左右 true为推 false为返回 boolean isitem=false;//是否出现物品 int[] box_x={180,180,180};//箱子坐标 int[] box_y={0,0,0}; int[] boxlevel={120,140,160,180,200,220,240};//箱子移动边界 int[] boxnow={3,3,3};//箱子当前位置 int itemR=-ITEM,itemL=-ITEM;//物品纵坐标 int itemcount=0;//物品出现时机计数 int isWin; boolean[] ismove={false,false,false};//箱子是否移动 boolean[] moveright={false,false,false};//箱子被右边推 boolean[] moveleft={false,false,false};//箱子被左边推 int whowin=-150,wholose=-150;//谁赢了,也作为画胜利文字的横坐标 int movestepR=1,movestepL=1;//上下移动步长 boolean movestopR=false,movestopL=false;//左右障碍 int stopcount=0;//障碍出现时长计数 //判断输赢 返回被退下的箱子编号 否则返回-1 public int isWin(){ for(int i=0;i\u0026lt;=2;i++){ if(box_x[i]\u0026lt;=120||box_x[i]\u0026gt;=240){ return i; } } return -1; } //延时函数 //\tpublic void Delay(int t){ //\tfor(long i=0;i\u0026lt;t*10;i++){ //\tfor(long j=0;j\u0026lt;t;j++); //\t} //\t} public void paint(Graphics g){ //画背景 g.setColor(Color.black); g.fillRect(160, 0, 80, 70); g.fillRect(160, 100, 80, 60); g.fillRect(160, 190, 80, 60); g.fillRect(160, 280, 80, 90); //画变大变小区域 g.setColor(Color.yellow); g.fillRect(160, 20, 80, 20); g.setColor(Color.gray); g.fillRect(160, 300, 80, 20); g.setColor(Color.black); g.drawString(\u0026#34;变大\u0026#34;, 190, 35); g.drawString(\u0026#34;变小\u0026#34;, 190, 315); //画推杆 g.setColor(Color.green); g.fillRect(left_x, left_y, PUSHWEIGHT, left_w); g.setColor(Color.blue); g.fillRect(350+right_x,right_y, PUSHWEIGHT, right_w); //画蛋 g.setColor(Color.red); g.fillOval(140, 340, 20, 30); g.fillOval(239, 340, 20, 30); g.setColor(Color.yellow); g.drawString(\u0026#34;蛋\u0026#34;, 145, 360); g.drawString(\u0026#34;蛋\u0026#34;, 243,360); //画箱子 g.setColor(Color.red); g.fillRect(box_x[0],70+box_y[0],BOXY,BOXX); g.fillRect(box_x[1],160+box_y[1],BOXY,BOXX); g.fillRect(box_x[2],250+box_y[2],BOXY,BOXX); //画物品 if(isitem){ g.setColor(Color.cyan); g.fillRect(140-ITEM,itemL,ITEM,ITEM); g.fillRect(260,itemR,ITEM,ITEM); } //画障碍 g.setColor(Color.black); if(movestopR){ g.fillRect(100,0,5,350); } if(movestopL){ g.fillRect(300,0,5,350); } //画胜利提示 if(isWin!=-1){ g.setColor(Color.red); g.fillRect(whowin,150,80,20); g.setColor(Color.yellow); g.drawString(\u0026#34;恭喜,你赢了!!\u0026#34;, whowin, 165); g.setColor(Color.black); g.fillRect(wholose,150,80,20); g.setColor(Color.white); g.drawString(\u0026#34;恭喜,你蛋碎了!\u0026#34;, wholose, 165); } } //从右向左移动第i个箱子 public void MoveRight(int i){ rightLR=false; ismove[i]=true; moveright[i]=true; moveleft[i]=false; boxnow[i]--; justpushR=true; } //\t从左向右移动第i个箱子 public void MoveLeft(int i){ leftLR=false; ismove[i]=true; moveleft[i]=true; moveright[i]=false; boxnow[i]++; justpushL=true; } //随机获得物品 参数-1为左边 1为右边 public void getitem(int lr){ Random r=new Random(); int itemnum=r.nextInt(6); //itemnum=4; if(itemnum==0){ //所有箱子移动 if(lr==-1){ for(int i=0;i\u0026lt;=2;i++){ if(boxnow[i]\u0026lt;5){ MoveLeft(i); } } isleftpush=true;\t} else if(lr==1){ for(int i=0;i\u0026lt;=2;i++){ if(boxnow[i]\u0026gt;1){ MoveRight(i); } } isrightpush=true; }\t}else if(itemnum==1){//对方变大 if(lr==-1){ right_w+=30; }else if(lr==1){ left_w+=30; } }else if(itemnum==2){//自己变小为1 if(lr==1){ right_w=1; }else if(lr==-1){ left_w=1; } }else if(itemnum==3){//自己速度变快 if(lr==-1){ movestepL=3; }else if(lr==1){ movestepR=3; } }else if(itemnum\u0026gt;=4){//对方出现障碍 if(lr==1){ movestopR=true; }else if(lr==-1){ movestopL=true; } } } public void run(){ while(true){ //碰到箱子的移动边界 for(int i=0;i\u0026lt;=2;i++){ //右边推箱子 if(!justpushR\u0026amp;\u0026amp;right_x+350==box_x[i]+BOXY\u0026amp;\u0026amp;right_y\u0026gt;=70+i*90-right_w\u0026amp;\u0026amp;right_y\u0026lt;=70+i*90+BOXX){ MoveRight(i); } } for(int i=0;i\u0026lt;=2;i++){ //左边推箱子 if(!justpushL\u0026amp;\u0026amp;left_x+PUSHWEIGHT==box_x[i]\u0026amp;\u0026amp;left_y\u0026gt;=70+i*90-left_w\u0026amp;\u0026amp;left_y\u0026lt;=70+i*90+BOXX){ MoveLeft(i); } } //碰到物品判断 //左 if(left_x+PUSHWEIGHT\u0026gt;=140-ITEM\u0026amp;\u0026amp;left_y\u0026lt;=itemR+ITEM\u0026amp;\u0026amp;left_y+PUSHWEIGHT\u0026gt;=itemR){ isitem=false; itemcount=0; itemR=-ITEM; itemL=-ITEM; leftLR=false; getitem(-1); } //右 if(right_x\u0026lt;=-90\u0026amp;\u0026amp;right_y\u0026lt;=itemL+ITEM\u0026amp;\u0026amp;right_y+PUSHWEIGHT\u0026gt;=itemL){ isitem=false; itemcount=0; itemR=-ITEM; itemL=-ITEM; rightLR=false; getitem(1); } //推杆移动边界 //上下 if(right_y\u0026gt;=320-right_w){ rightUD=false; } if(right_y\u0026lt;=1){ rightUD=true; } if(left_y\u0026gt;=320-left_w){ leftUD=false; } if(left_y\u0026lt;=1){ leftUD=true; } //左右 if(right_x==-112\u0026amp;\u0026amp;((right_y\u0026gt;0\u0026amp;\u0026amp;right_y\u0026lt;=70)||(right_y\u0026gt;100-right_w\u0026amp;\u0026amp;right_y\u0026lt;=160)||(right_y\u0026gt;190-right_w\u0026amp;\u0026amp;right_y\u0026lt;=250)||(right_y\u0026gt;280-right_w\u0026amp;\u0026amp;right_y\u0026lt;=370))){ rightLR=false; if(right_y\u0026gt;=20-right_w\u0026amp;\u0026amp;right_y\u0026lt;=40)left_w+=10; if(right_y\u0026gt;=300-right_w\u0026amp;\u0026amp;right_y\u0026lt;=320)right_w-=10; if(right_w\u0026lt;=0)right_w=1; } if(left_x==159-PUSHWEIGHT\u0026amp;\u0026amp;((left_y\u0026gt;0\u0026amp;\u0026amp;left_y\u0026lt;=70)||(left_y\u0026gt;100-left_w\u0026amp;\u0026amp;left_y\u0026lt;=160)||(left_y\u0026gt;190-left_w\u0026amp;\u0026amp;left_y\u0026lt;=250)||(left_y\u0026gt;280-left_w\u0026amp;\u0026amp;left_y\u0026lt;=370))){ leftLR=false; if(left_y\u0026gt;=20-left_w\u0026amp;\u0026amp;left_y\u0026lt;=40)right_w+=10; if(left_y\u0026gt;=300-left_w\u0026amp;\u0026amp;left_y\u0026lt;=320)left_w-=10; if(left_w\u0026lt;=0)left_w=1; } //障碍判断 if(movestopR){ if(left_x==100-PUSHWEIGHT)leftLR=false; } if(movestopL){ if(right_x==-50)rightLR=false; }//障碍结束 if(right_x==1){ rightpush=false; rightgo=true; right_x=0; rightLR=true; isrightpush=false; justpushR=false; } if(left_x==1){ leftpush=false; leftgo=true; left_x=2; leftLR=true; isleftpush=false; justpushL=false; } isWin=isWin(); if(isWin==-1){ //推杆移动 //右推杆上下移动 if(rightgo){ if(rightUD){ right_y+=movestepR; }else{ right_y-=movestepR; } } //左推杆上下移动 if(leftgo){ if(leftUD){ left_y+=movestepL; }else{ left_y-=movestepL; } } //右推杆左右移动 if(rightpush){ if(rightLR){ right_x--; }else{ right_x++; } } //左推杆左右移动 if(leftpush){ if(leftLR){ left_x++; }else{ left_x--; } } //障碍计数 if(movestopR||movestopL){ stopcount++; if(stopcount==500){ stopcount=0; movestopR=false; movestopL=false; } } //\t物品出现控制 itemcount++; if(itemcount==ITEMCOUNT){ itemcount=0; isitem=true;\tmovestepR=1;//将移动步长恢复为1 movestepL=1; } if(isitem){ itemR++; itemL++; if(itemR==400){ itemR=-ITEM; isitem=false; } if(itemL==400){ itemL=-ITEM; isitem=false; } } }else{ box_y[isWin]+=5; if(box_y[isWin]\u0026gt;=350-(isWin*90+70)-30){ whowin=boxnow[isWin]==0?280:40; wholose=boxnow[isWin]==0?40:280; //JOptionPane.showMessageDialog(this,isWin==6?\u0026#34;右边\u0026#34;:\u0026#34;左边\u0026#34;+\u0026#34;的家伙赢了!\u0026#34;); //break; box_y[isWin]=350-(isWin*90+70)-10; } } //箱子移动 for(int i=0;i\u0026lt;=2;i++){ if(ismove[i]){ if(moveright[i]){ box_x[i]--; } if(moveleft[i]){ box_x[i]++; } if(box_x[i]==boxlevel[boxnow[i]])ismove[i]=false; } } //推杆超出范围的重置 迫不得已这样的 if(left_x==401-PUSHWEIGHT)left_x=-PUSHWEIGHT; if(right_x==-405)right_x=0; repaint(); //线程睡眠 改变速度 try{ Thread.sleep(SPEED);//毫秒居然也延迟那么久 纠结 }catch(InterruptedException e){ e.printStackTrace(); } //Delay(SPEED); } } //按键响应 public void keyPressed(KeyEvent e) { //Enter键 右推杆 if((e.getKeyCode()==10)\u0026amp;\u0026amp;!isrightpush){\trightpush=true;\trightgo=false; isrightpush=true; } //Ctrl键 左推杆 else if((e.getKeyCode()==17)\u0026amp;\u0026amp;!isleftpush){ leftpush=true; leftgo=false; isleftpush=true; }\t//System.out.print(e.getKeyCode()); } public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub\t} public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub }\t} /**** 代码结束 ****/ /****作者：小乜 ****/ /****2011.7.8 ****/ /*转载或二次编译请保留作者信息！*/ ","date":"2013-03-14","externalUrl":null,"permalink":"/p/boxfight-java-source/","section":"Ps","summary":"写于2011年7月,一直在草稿箱里:\n实习的时候无聊，把高三那年在文曲星上用类c的LavaX写的小游戏用java重写了，写了2天，第一天思路错了，监听器怎么都搞不定，方向控制也偷懒想要取巧，结果搞得自己都混乱了，各种bug无从下手。于是第二天全部删掉重写，一天下来总算是能运行了，虽然bug一大堆！然后又用了一天时间测试和bug fix，一休息就拉企鹅过来试玩，期间各种棍子乱飞，箱子乱飞，改代码改了一上午，终于是能玩完一局不出错了，感觉上和高三那年写的一样一样啊，下午又小修修补补的，已知的bug全部搞定，又加了些新功能，玩起来已经非常欢乐了。。。\n游戏说明：\n由于是简单的使用线程睡眠来控制速度的，所以不同电脑速度表现差别会很大，速度不合理的可以修改 SPEED 的值。\n二人游戏，通过Ctrl和Enter键推动上下移动的推杆，推动中间的箱子掉下去砸碎对方的蛋。。。\n","title":"小游戏 箱子大战java版源代码","type":"p"},{"content":"这里是发在 MIUI 论坛原帖的备份，就当做个记录吧。\n原帖地址：\nhttp://www.miui.com/thread-590024-1-1.html\n本软件由小乜汉化,版权归原作者Chainfire所有,转载请保留此信息!\n【 简单介绍】 # ​ 通过手机的OTG功能识别U盘等USB存储设备必备的软件，能将U盘挂载到sdcard/usbstorage下，使手机能够直接读取U盘的内容。目前软件支持的文件格式有：NTFS(中文依然有问题),FAT32,FAT16,EXT2,EXT3,EXT4。\n【 使用方法】 # ​ 非常方便,安装本软件后,不需要打开软件,只需要通过OTG线将USB存储设备与手机连接,系统会弹出是否用stickmount打开USB设备,勾选默认用于该设备,点击确定,若弹出root权限验证,请允许请求root权限,稍等1~2秒,软件就会提示将USB存储设备挂载在了哪个目录下,使用文件管理工具打开该目录就可以浏览存储设备上的文件了!\n​ 汉化版已经修复了原版显示中文文件夹与文件乱码的问题，现在可以正常显示中文文件夹与文件了。\n​ 其实吧，这个软件不汉化也没有太大影响，整个界面没有多少英文。只不过解决中文显示问题的时候顺手把它汉化了，看上去比字母顺眼多了，嘿嘿，有需要的同学就拿去吧~~\n【汉化日志】 # [2012.12.09更新]\n基于V2.10汉化,支持android 4.2,pro付费直装版,无需安装解锁器,直接安装即可使用!\n[2012.07.26更新]\n基于V1.5汉化，偷个懒，就不截图了\n【原版更新日志】 # - More Android Jelly Bean 4.2 related fixes\n- Only works for primary user\n- Twitter/G+ spam now included\n[汉化下载] # 请到论坛原帖下载：\nhttp://www.miui.com/thread-590024-1-1.html\n2012年12月09日更新\nV2.10 汉化PRO直装版\n基于V2.10汉化,支持android 4.2,pro付费直装版,无需安装解锁器,直接安装即可使用,中文不乱码\n[中文直装版]StickMount Pro 2.10.apk (48.79 KB, 下载次数: 6640)\nV1.5 MIUI白色风格：\n[白]StickMount1.5.apk (29.52 KB, 下载次数: 1829)\nV1.5 原版黑色风格：\n[黑]StickMount1.5.apk (29.51 KB, 下载次数: 2070)\n【历史版本】\nV1.3 新增-MIUI白色风格：\n[汉化版_MIUI风格]StickMount.apk (28.08 KB, 下载次数: 698)\nV1.3 原版黑色风格：\n[汉化版]StickMount.apk (28.08 KB, 下载次数: 854)\n","date":"2012-12-11","externalUrl":null,"permalink":"/p/otg-stickmount-pro-zh-cn/","section":"Ps","summary":"这里是发在 MIUI 论坛原帖的备份，就当做个记录吧。\n原帖地址：\nhttp://www.miui.com/thread-590024-1-1.html\n本软件由小乜汉化,版权归原作者Chainfire所有,转载请保留此信息!\n","title":"【小乜汉化】U盘OTG StickMount Pro V2.10中文(解决原版中文乱码)","type":"p"},{"content":"","date":"2012-12-11","externalUrl":null,"permalink":"/categories/android/","section":"Categories","summary":"","title":"Android","type":"categories"},{"content":"","date":"2012-12-11","externalUrl":null,"permalink":"/tags/%E6%B1%89%E5%8C%96/","section":"Tags","summary":"","title":"汉化","type":"tags"},{"content":"这里是发在 MIUI 论坛原帖的备份，就当做个记录吧。\n原帖地址：\nhttp://www.miui.com/thread-836337-1-1.html\n本软件由小乜汉化,版权归软件原作者所有,转载请保留此信息!\n是不是觉得手机有个NFC功能就是个摆设?从买来到现在几乎没有用过NFC功能,也几乎灭有能够用到NFC的地方,现在小乜就给你介绍一个把NFC功能利用起来的方法!【这个小乜可是花了大把时间汉化呀~】\nNFC任务启动器是干什么的? # 简单来说,NFC任务启动器就是通过把手机靠近一个NFC标签来激活手机上的一系列动作的程序.下图就是NFC标签\u0026mdash;-只要几块钱一个,自己到淘宝搜\u0026quot;NFC标签\u0026quot;就有一大把了.要提醒一下,程序内的购买链接只适用于国外用户,国内用户是购买不了的,大家自行到淘宝购买就是了~\n具体怎么使用呢? # 比如我把一个NFC标签挂在床头,并且给它设置的动作是:打开飞行模式,开启闹钟,调到最低亮度,那么当我要碎叫的时候,只需要把手机背面靠近一下这个标签,手机就执行了上面设置的动作,早上醒来之后再靠近一下这个标签,又恢复到之前的状态.\n像这样,还可以挂一个NFC标签在家门口鞋柜边上,设置为:关闭wifi,铃声最大,打开震动,这样出门的时候只需靠近一下\u0026hellip;\u0026hellip;然后回家了,再靠近一下:打开wifi,铃声中等,关闭震动\u0026hellip;.\n车上挂一个,设置为:打开蓝牙,打开地图导航\u0026hellip;.\n\u0026hellip;..\n\u0026hellip;..\n\u0026hellip;..\n是不是很方便呀,嘿嘿,还有各种玩法,比如把自动填写wifi密码功能写进NFC标签里面,以后有朋友来了你家,只要他的手机有NFC功能,碰一下这个标签就能连上你家的wifi了~\n还有别的玩法各位基友们去尝试然后发上来分享呗~~\n【软件信息】 # 软件名称：NFC 任务启动器\n版本信息：基于 4.0.1 正式版汉化\n适用机型/系统：Android 4.0 或更高版本\n测试机型：GN/MIUI V4\n软件截图:\n【更新日志】 # 4.0.1:Fixes for reported issues with new UI and layouts\n【下载地址】 # [汉化]NFC任务启动器4.0.1.apk (2.17 MB, 下载次数: 988)\n","date":"2012-12-11","externalUrl":null,"permalink":"/p/nfc-task-launcher-zh-cn/","section":"Ps","summary":"这里是发在 MIUI 论坛原帖的备份，就当做个记录吧。\n原帖地址：\nhttp://www.miui.com/thread-836337-1-1.html\n本软件由小乜汉化,版权归软件原作者所有,转载请保留此信息!\n","title":"【小乜汉化】NFC任务启动器 4.0.1 汉化版 [11/06更新]","type":"p"},{"content":"这里是发在 MIUI 论坛原帖的备份，就当做个记录吧。下载记录还是蛮多的~~~\n原帖地址：\nhttp://www.miui.com/thread-530785-1-1.html 10W+次查看 1800+跟帖\n本软件在MIUI论坛首发,小乜汉化,版权归原作者franco所有,转载请保留此信息!\n【franco.Kernel Updater的作用】 # 在线更新franco内核\n设置franco内核的高级功能(如调节色温,cpu频率等等)\n设置电压,v4版可以设置gpu电压了\n关屏时关闭一个cpu核心,开屏后自动激活\n设置单核/双核模式\n等等\u0026hellip;.\n注意,本软件需要配套franco内核使用,franco内核下载传送门:http://www.miui.com/thread-530756-1-1.html\n【汉化更新说明】 # V8.0.1 版（2012.11.11）\n1.更新到franco.Kernel Updater v8.0.1\n2.完善下载页与电源模式页汉化\nV8.0 版（2012.11.10）\n1.更新到franco.Kernel Updater v8.0\n第二十版（2012.10.18）\n1.更新到franco.Kernel Updater v7.0\n第十九版（2012.10.11）\n1.更新到franco.Kernel Updater v6.9.3\n第十八版（2012.09.20）\n1.更新到franco.Kernel Updater v6.7.2\n第十七版（2012.09.14）\n1.更新到franco.Kernel Updater v6.6\n第十六版（2012.09.07）\n1.更新到franco.Kernel Updater v6.5\n第十五版（2012.09.03）\n1.更新到franco.Kernel Updater v6.3.2\n2.此版本重新制作了颜色调节界面\n第十四版（2012.8.27）\n1.更新到franco.Kernel Updater v6.1\n第十三版（2012.8.17）\n1.更新到franco.Kernel Updater v6.0\n第十二版（2012.7.13）\n1.更新到franco.Kernel Updater v5.8\n2.作者不再对4.0的franco kernel nightly进行更新了，所以4.0的nightly版一直停留在R196\n第十一版（2012.7.10）\n1.更新到franco.Kernel Updater v5.7\n2.完善上一版本中不能汉化的内容，目前程序已完整汉化！\n3.不再区分黑色与白色版，可以在程序中直接切换了（menu键\u0026mdash;更换风格）\n第十版（2012.7.05）\n1.更新到franco.Kernel Updater v5.6.1\n2.由于作者开始对dex文件进行保护，程序部分内容无法汉化（如tab页标题，内核下载时的按钮，颜色调节器里的推荐文字，桌面小工具等），整体汉化度[98%]\n第九版（2012.7.02）\n1.更新到franco.Kernel Updater v5.4\n第八版（2012.6.08）\n1.更新到franco.Kernel Updater v5.0\n2.修复调速器微调选项错位的问题\n3.此工具现在支持one x以及note，但是这两个机型的界面我没有汉化，抱歉\n第七版（2012.5.31）\n1.更新到franco.Kernel Updater v4.9\n2.桌面小工具已汉化\n（嘿嘿，偷偷说一声，今天小乜生日哦，看到的同学祝福一下呗~~）\n第六版(2012.5.20)\n1.更新到franco.Kernel Updater v4.7.3\n2.修正了电源模式设置后状态显示错误的问题\n3.新增MIUI白色风格界面（5/28新增）\n第五版(2012.5.16)\n1.更新到franco.Kernel Updater v4.7\n2.在OC/UV界面加入简单的调速器解释,方便新手选择(原版木有的哦)\n3.几处翻译润色\n4.调整几处菜单顺序\n第四版(2012.4.24)\n1.更新到franco.Kernel Updater v4.2\n2.几处翻译润色\n第三版 (2012.4.16)\n1.更新到franco.Kernel Updater v4.0\n2.修正几处细节错误\n第二版 (2012.4.15)\n1.修正几处翻译错误\n2.修正几个会造成FC的错误\n请下载了第一版的同学更新\n第一版 (2012.4.14)\n第一版发布\n注意,本软件需要配套franco内核使用,franco内核下载传送门:http://www.miui.com/thread-530756-1-1.html\n【软件截图】 # 【软件下载】 # 请到论坛原帖下载：\nhttp://www.miui.com/thread-530785-1-1.html\n已经安装英文原版的同学,因为更改了软件签名,请卸载原版本之后,再安装汉化版!\n(如果原来安装的是本帖的汉化版可以直接覆盖安装)\n[汉化V8.0.1版]franco.kernel.updater V8.0.1.apk (278.11 KB, 下载次数: 1722)\n[历史版本]\n[汉化V8.0版]franco.kernel.updater V8.0.apk .apk (275.66 KB, 下载次数: 558)\n[第二十版]franco.kernel.updater V7.0.apk (264.22 KB, 下载次数: 655)\n[第十九版]franco.kernel.updater V6.9.3.apk (263.43 KB, 下载次数: 423)\n[第十八版]franco.kernel.updater V6.7.2.apk (255.83 KB, 下载次数: 543)\n[第十七版]franco.kernel.updater V6.6.apk (250.56 KB, 下载次数: 354)\n[第十六版]franco.kernel.updater V6.5.apk (248.93 KB, 下载次数: 354)\n[第十五版]franco.kernel.updater V6.3.2.apk (245.51 KB, 下载次数: 312)\n[第十四版]franco.kernel.updater V6.1.apk (241.4 KB, 下载次数: 381)\n[第十三版]franco.kernel.updater V6.0.apk (240.57 KB, 下载次数: 834)\n[第十二版]franco.kernel.updater V5.8.apk (1.29 MB, 下载次数: 1911)\n[第十一版]franco.kernel.updater V5.7 .apk (1.28 MB, 下载次数: 413)\n[第十版-黑]franco.kernel.updater V5.6.1 .apk (1.29 MB, 下载次数: 276)\n[第十版-MIUI白]franco.kernel.updater V5.6.1.apk (1.29 MB, 下载次数: 219)\n[第九版-MIUI白]franco.kernel.updater V5.4.apk (221.43 KB, 下载次数: 121)\n[第九版-黑]franco.kernel.updater V5.4.apk (221.43 KB, 下载次数: 208)\n[第八版-黑]franco.kernel.updater V5.0.apk (200.99 KB, 下载次数: 566)\n[第八版-MIUI白]franco.kernel.updater V5.0.apk (200.99 KB, 下载次数: 270)\n[第七版-黑]franco.kernel.updater V4.9.apk (196.69 KB, 下载次数: 266)\n[第七版-MIUI白]franco.kernel.updater V4.9.apk (196.69 KB, 下载次数: 141)\n[第六版-MIUI风格]franco.Kernel updater4.7.3.apk (186.3 KB, 下载次数: 98)\n[第六版]franco.Kernel Updater4.7.3.apk (186.33 KB, 下载次数: 430)\n[第五版]franco.Kernel Updater4.7.apk (185.86 KB, 下载次数: 285)\n[第四版]franco.Kernel Updater4.2.apk (181.09 KB, 下载次数: 312)\n[第四版-重签名]com.franco.kernel V4.2.apk (181.09 KB, 下载次数: 132)\n[第三版]franco.Kernel Updater4.0.apk (175.03 KB, 下载次数: 432)\n【原版更新日志】 # 8.0.1\nAdd boot animation download to the background thread Fix share settings force closes when the device couldn\u0026rsquo;t find the files to read from Speed up Governor Control activity start Fix LeanKernel activity occasional force closes when fetching the kernel versions could return null (hope its fixed for good) Fix Galaxy S3 force close when accessing Frequency option (email me if this issue persist of any kind) Add disposable dialog when entering Power Mode activity for the first time 8.0 [这个版本完全重新code了,界面有了很大的变化!]\nApplication 100% coded from scratch New design and organisation All interfaces have been polished and improved from their initial debut Voltages are finally able to be set on boot Built to be able to be translated in other languages in the future Better code, better superuser calls, better usage of threading, more secure 7.0\nCommon:\nSlight update to the app icon courtesy of lab75\nGalaxy Nexus: Calibrated Voltages panel re-designed and its now called Device Monitor. In the Device Monitor from r290 onwards you\u0026rsquo;ll be able to see the current GPU clock live General fixes\nNexus 7: Add GPU overclock preference (not set on boot) Add preference to choose maximum frequency of the companion core (not set on boot)\nOne X: Add GPU overclock preference (not set on boot) 6.9.3\nTo break the ice from the latest 2 Force Close fix builds I thought it would be fun to give a more social aspect into the app. So now we have a Share settings option in the Menu button and lets you share some of the most common, but yet useful, settings with your friends and family via Google+ and other apps. This is just an early introduction of this new feature that I hope to see grow with your feedback and suggestions.\n【就是说支持把配置分享到微博功能了！】\nLet the world know what beastly settings you use! 6.9\nCommon:\nFaster app opening time Add detection for SuperSu and give a one time warning if you\u0026rsquo;re using Superuser Add TCP Congestion Avoidance algorithm swapping interface (One X support on this one is disabled because it needs an update in the kernel) Bug fixes Galaxy Nexus:\nChange Interactive Governor Control way of handling input_boost_freq and hispeed_freq Galaxy SIII:\nAdd JellyBean kernels detection/download Add input_touch_booster interface Email me if a problem arises.\n6.8:\nCommon:\nRe-factor download functions. Now downloads faster, its more robust, more secure and more reliable. Should fix that -1B display bug on some downloads. If you encounter any problem regarding kernel downloadings please email me Some buf fixing as well Galaxy Nexus and Nexus 7:\nAdd input_boost_freq entry to Governor Control - only with interactive governor 6.7.2\nFix FC when clicking Set CPU frequencies on boot\n6.7.1\nFix FC when setting Power Modes set on boot Fix random FC when backing up kernels Fix FC if no Color Profile was created before Change license check from the UI thread to the Background thread, making use of our multi-threading like its used on the root commands and now app launching is much improved! 6.7\nGalaxy Nexus:\nAdd Color Profiles interface - you\u0026rsquo;ll be able to save your current color settings unlimitedly and swap between your favorite values according to your mood and swing at the time. You can also delete and see when they were created. It can contain bugs, so before going mad at me take some time to email me or post in the XDA thread. 6.6\nMove all the root operations to the background thread. This change has a few advantages: if a superuser call is made and for some reason gets blocked it won\u0026rsquo;t block the UI thread anymore. The application will also opens much faster than before. All in all the app its much smoother to use, specially swipping the tabs.\nPost in the respective XDA threads of send me an email if any problem occurs from this update - the unforeseeable can happen 6.5\nCommon:\nRe-code all the download kernel functions - clean and improved code Re-implement the kernel download dialog information - now it shows real percentage of the file plus file sizes Touch events won\u0026rsquo;t cancel kernel download dialogs anymore Some more bug fixings 6.3.2\nMore bug fixing\nInstead of rating the app 1 star because \u0026ldquo;OMG X SETTING DUN STICKZ ON REBUUTS\u0026rdquo; send me an email or post in the respective XDA thread. It\u0026rsquo;s discouraging reading such posts that would be simply avoided by a single email or XDA post. 6.3\nGalaxy SIII:\nAdd initial support for Power Modes - 4 presets and the ability to create your custom modes\nCommon: The cores panel is dynamic and if the kernel is onlining/offlining your cores you\u0026rsquo;ll be able to see it watch live Fix two Force Closes when swipping the tabs Reworked the superuser root command function with better code and using threads when several commands are made Now will ask for root permission on app first start to prevent problems Several under the hood fixes 6.2\nColor Control interfaces completely redesigned from scratch - now color multipliers and RGB gamma are much more easy to use. This changes are in preparation for the soon to be available color profiles system (which will much look alike the kernel backup system but for color stuff). You need to re-set the colors after this update or they won\u0026rsquo;t persist on reboot what-so-ever. If you find any bug please post in the XDA kernel thread or email me before leaving 1 star ratings. ","date":"2012-12-11","externalUrl":null,"permalink":"/p/franco-kernel-updater-zh-cn/","section":"Ps","summary":"这里是发在 MIUI 论坛原帖的备份，就当做个记录吧。下载记录还是蛮多的~~~\n","title":"【小乜汉化】franco内核工具中文版v8.0.1","type":"p"},{"content":" 这里是发在 MIUI 论坛原帖的备份，就当做个记录吧。\n原帖地址：\nhttp://www.miui.com/thread-476404-1-1.html 7W+次查看\n声明 # 本教程由小乜原创,转载请保留此信息!\n刷机有风险,操作需谨慎!任何由本教程带来的损失责任自负\n操作之前请做好备份工作,按照本教程执行完毕之后会清空手机所有数据(包括手机内存储空间内的数据)!\n前言 # \u0026mdash;看完本教程后,你将学到什么?\n本教程以win7为例教你把一部刚拿到手的Galaxy Nexus(仅限GSM版,也就是能用联通3G,移动GSM的版本;电信版,LTE版,4G版不适用本教程),进行解锁,root,recovery操作,为以后的刷机作准备.以后要进行刷机操作,这些步骤是必须的,并且,一台手机只需要做一次本教程下的操作,以后即可任意刷机了.\n步骤简介: # 安装驱动; OEM解锁; root权限获取; 刷入recovery(5.5.0.2). 第一步,安装驱动 # 这一步教你安装fastboot驱动,首先将教程最底下的附件[GN-Tools.ZIP]下载并解压到你能找到的地方,里面包含了驱动以及之后操作所需要的文件.\n一 . 手机关机,同时按住【音量+,音量-,开机键】不放,直到屏幕出现一个肚子被打开的机器人,这就进入了Bootloader界面; 图 二 . 将USB线连接电脑与手机,此时电脑提示发现新硬件,打开设备管理器(桌面 计算机-右键-属性-设备管理器),找到Android 1.0项,点右键,选择\u0026quot;更新驱动程序软件\u0026quot;,如图 图 三 . 点击\u0026quot;浏览计算机以查找驱动程序软件(R)\u0026quot; 图 四 . 点击浏览找到你下载的GN-Tools.rar解压的位置,然后选中drivers文件夹(不要再选中drivers文件夹内的子文件夹) 图 五 . 安装过程中出现兼容性提示,选择第二项\u0026quot;始终安装次驱动程序软件\u0026quot; 图 六 . 安装完成后设备管理器中出现如图项表示安装成功! 至此驱动程序安装完毕! 图 第二步,解锁 # 保持手机在bootloader界面,并连接好USB线,运行GN-Tools文件夹下的*\u0026ldquo;第1步-解锁.bat\u0026rdquo;*文件,电脑上是一个cmd窗口一闪而过,这时手机屏幕上出现警告,提示手机数据将全部清空,选择YES,按电源键确认,之后回到bootloader界面,最下面显示unlocked,表示已经成功解锁!\n第三步,取得root权限 # (如果不想root，或者等下打算刷MIUI或者别的rom，可以跳过这一步)\n保持手机在bootloader界面,并连接好USB线,运行GN-Tools文件夹下的*\u0026ldquo;第2步-root.bat\u0026rdquo;文件,手机屏幕上最下面一行显示Downloadinig\u0026hellip;.文字,这时请等待(如果卡死在这里请扣电池关机,重新同时按住【音量+,音量-,开机键】开机进入bootloader,运行\u0026ldquo;第2步-root.bat\u0026rdquo;*文件),Downloadinig\u0026hellip;.文字消失后表示root权限获取成功!\n第四步,刷入recovery # 保持手机在bootloader界面,并连接好USB线,运行GN-Tools文件夹下的*\u0026ldquo;第3步-recovery.bat\u0026rdquo;*文件,等待手机屏幕上Downloadinig\u0026hellip;.文字消失后,手机会自动进入recovery模式(如果一直卡在google开机画面,请抠电池重新执行第四步).至此本教程的内容就完了,你的手机已经解锁,root,刷入recovery了,如果你现在还不想刷机,请选择recovery界面的第一项reboot system now重启手机(音量键选择,电源键确认)正常进入系统!\n第五步,刷入MIUI # 执行完上一步操作之后，手机开机正常进入系统，在http://www.miui.com/extra.php?mod=download/rom\u0026fid=126这里下载最新的MIUI ROM，将手机连接电脑，把下载的rom放进手机任意位置，然后手机关机,同时按住【音量+,音量-,开机键】不放,进入bootloader，用音量键选择到recovery mode，按电源键确认进入\n在蓝色recovery界面依次执行，音量键选择，电源键确认。\nwipe data/factory reset——Yes——delete all user data. wipe cache partition——Yes-Wipe Cache. 然后\ninstall zip from sdcard. choose zip from sdcard. miui_GalaxyNexus_2.X.X_xxxxxxxxxx_4.0.zip.（这个是刚刚放到手机里的rom） Yes-install miui XXXXX. 等待完成后选择Reboot system now手机会自动重启。\n最后 # 本教程如果有遗漏或者错误的地方,欢迎指正!\n本教程仅供新手参考,高手欢迎提出意见!\n如果本教程对你有所帮助,你可以回帖支持一下哟~~~~\n附件 # GN_Tools.rar\u0026mdash;\u0026mdash;\u0026ndash; GN-Tools.zip (18.9 MB, 下载次数: 30570) （之前提供的版本有问题，现在修复了！）\n(包含:驱动,解锁,root,recovery所需要的全部文件)\n","date":"2012-08-18","externalUrl":null,"permalink":"/p/galaxy-nexus-unlock/","section":"Ps","summary":" 这里是发在 MIUI 论坛原帖的备份，就当做个记录吧。\n原帖地址：\nhttp://www.miui.com/thread-476404-1-1.html 7W+次查看\n","title":"【刷机教程】Galaxy Nexus(GSM)解锁/root/recovery(附驱动下载)","type":"p"},{"content":"下午整理硬盘的时候发现了这些视频,看上去很是怀念,于是就把它们合起来了..\n不要问我这样全屏打码一般的渣画质是怎么回事啊!!!\n那个时候的手机有个100W像素的摄像头已经很不容易了!!!\n而且又经过了优酷的再次压制\u0026hellip;\n你伤不起啊!!!\n你很好奇我是怎么拍的老师上课的照片的不?\n下面手把手教你偷拍老师上课的照片\u0026hellip;\n首先,准备一张试卷,中间挖个手机摄像头大小的洞\u0026hellip;\n然后把摄像头对准那个洞\n然后手同时捏着手机和试卷\n举起来\n假装认真的思考问题\u0026hellip;\nover\u0026hellip;.\n","date":"2011-04-18","externalUrl":null,"permalink":"/p/senior-three-b1-memory/","section":"Ps","summary":"下午整理硬盘的时候发现了这些视频,看上去很是怀念,于是就把它们合起来了..\n","title":"定南中学2008届高三B1班剪影","type":"p"},{"content":"大学就是一个养老院，\n你迫不及待地从高中转进来，\n意图享受你想要的悠闲的生活——未曾想过，\n这是麻醉，\n这是慢性自杀。\n我看了看身边的兄弟们，\n我知道很多人都已经意识到了这一层。\n但是我们都不愿意醒来。\n因为现实总有我们需要逃避的东西，\n比如感情，\n比如考试，\n比如未来。\n游戏里我们无所不能。\n一帮兄弟，\n动不动就拯救世界，\n时不时的毁灭种族。\n不过，\n当点卡用完时，\n我们还是被迫的回来了。\n","date":"2010-09-17","externalUrl":null,"permalink":"/p/some-boring/","section":"Ps","summary":"大学就是一个养老院，\n你迫不及待地从高中转进来，\n意图享受你想要的悠闲的生活——未曾想过，\n这是麻醉，\n这是慢性自杀。\n","title":"牢骚","type":"p"},{"content":"​\t花了一个下午的时间构思和ps\u0026hellip;.终于做出来了一个比较满意的作品,诶,ps还需要加强\u0026hellip;.练习一万遍啊一万遍\u0026hellip;.\n看吧.看吧\u0026hellip;.羞涩\u0026hellip;.\n","date":"2010-04-27","externalUrl":null,"permalink":"/p/t-shirt-design/","section":"Ps","summary":"​\t花了一个下午的时间构思和ps….终于做出来了一个比较满意的作品,诶,ps还需要加强….练习一万遍啊一万遍….\n","title":"给技术团队设计的T-Shirt","type":"p"},{"content":"爱情在生活中是一种调剂\n像是意大利菜\n加了“spicy”之类的东西\n便一下子光鲜夺目起来\n把仲夏夜和爱情联系起来\n仲夏夜就关乎爱情了\n一样的美\n我走在仲夏夜的街上\n刚洗过澡\n换了一套短袖衫\n穿着拖鞋\n塔拉着走\n感觉从未有过的美好\n爱情是一种让人愉悦的东西\n忽觉生活之美\n在其爱之芬芳\n我们都是缘分的信徒吧\n相信这一种神秘的力量\n相信人海中遇见某人\n于某年某月某日\n是一种注定与巧合\n生活是多么的神奇\n我们遇见一批人\n分开一批人\n爱一些人\n恨一些人\n无数的人组成我们的回忆与生活­\n","date":"2009-09-21","externalUrl":null,"permalink":"/p/spicy/","section":"Ps","summary":"爱情在生活中是一种调剂\n像是意大利菜\n加了“spicy”之类的东西\n便一下子光鲜夺目起来\n","title":"Spicy","type":"p"},{"content":"莎翁有一本书，叫做《仲夏夜之梦》\n很喜欢这个名字\n你知道吗\n我几乎喜欢一切和夏天相关的词汇\n夏天的时光是燥热的\n不安分\n带着一点点的肆无忌惮\n日光强烈的时候我喜欢找一片树荫\n看着身边的阳光\n感觉它像水\n流动地经过\n","date":"2009-09-09","externalUrl":null,"permalink":"/p/midsummer/","section":"Ps","summary":"莎翁有一本书，叫做《仲夏夜之梦》\n很喜欢这个名字\n你知道吗\n我几乎喜欢一切和夏天相关的词汇\n","title":"仲夏","type":"p"},{"content":"这个世界上绝对不存在什么“不求回报的付出”\n信誓旦旦讲这句话的人，要么是在骗人，要么是在骗己\n很多人都在恋爱当中一边付出，一边自我催眠\n有的甚至于享受这种悲情角色的扮演\n但是其实内心深处一直在渴求着回报\n而且付出越多，渴求越强烈，内心越是不平衡\n这并不可耻，人本性如此\n之所以还没有表达出来\n是因为时候没到，或者忍耐的界限没到\n人的心就像个天平\n倾斜的幅度越来越大的最终结果就是崩塌\n","date":"2009-08-28","externalUrl":null,"permalink":"/p/give-and-return/","section":"Ps","summary":"这个世界上绝对不存在什么“不求回报的付出”\n信誓旦旦讲这句话的人，要么是在骗人，要么是在骗己\n","title":"付出与回报","type":"p"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}]