最近为了制作 Android 应用打包脚本,学习了一下 gradle。Gradle 构建系统语法简洁、功能强大、配置灵活,笔者只是把它当作一个构建工具来使用,基于它所提供的便利制作可以修改版本号、编译号、id 及导入证书的脚本。
对于一个项目或者一个工程,Gradle 可以定义多个构建任务,debug 和 release 是常见的两个构建任务,用户还可以根据需要自定义自己的构建任务,如测试构建任务和预发布构建任务,甚至是针对不同发布渠道的构建任务。这里只用到 debug 任务。
gradle 命令行支持传入自定义参数,并在编译过程注入这些参数。
修改 appid 及 版本号 修改 build.gradle 文件 android { compileSdkVersion 21 buildToolsVersion '26.0.2' defaultConfig { applicationId project.hasProperty('applicationId') ? applicationId : "com.domain.myApp" minSdkVersion 14 targetSdkVersion 21 versionCode project.hasProperty('versionCode') ? versionCode.toInteger() : 100 versionName project.hasProperty('versionName') ? versionName : "1.0.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } 命令行中传入对应 key 的参数 gradle assembleDebug -PversionCode="200" -PversionName="2.0.0" -PapplicationId="com.domain.myApp.debug" 修改应用 id 的最好同时修改包名,不然会有包名冲突,修改包名需要修改 AndroidManifest.xml 文件,先在 build.gradle 文件中使用 manifestPlaceholders 属性定义一个键:
介绍 关于 Core Bluetooth Core Bluetooth 框架提供 iOS 应用和 Mac 应用与设备(配备了蓝牙低能耗无线技术的设备)通信的类。例如,应用可以发现、探测并与低能耗外围设备(比如心率监听器和数字恒温器)交互。从 macOS 10.9 和 iOS 6 开始,Mac 和 iOS 设备还可以当做蓝牙低能耗外设来使用,为其它设备提供数据,包括其他 Mac 和 iOS 设备。
一览 蓝牙低能耗无线技术基于蓝牙 4.0 规范,规范中除了别的之外,定义了与低能耗设备通信的一套协议。Core Bluetooth 框架是蓝牙低能耗协议栈的一个抽象,也就是说,它为开发者隐藏了许多规范中的底层细节,让开发者更加容易开发应用(与蓝牙低能耗设备交互的应用)。
中央和外围是 Core Bluetooth 的核心成员 在蓝牙低能耗通信中,有两个核心成员:中央(central)和外围(peripheral)。每个成员扮演不同的角色。外围通常拥有其他设备需要的数据,中央通常使用外围提供的信息来完成一些任务。例如,一个配备了蓝牙低能耗技术的数字恒温器可能为一个 iOS 应用提供房间的温度信息,然后该应用采用用户友好的方式来显示温度。
每个成员在扮演它的角色时都会执行一组不同的任务。外围通过在空中广播持有的数据来让自身的存在被感知,中央设备扫描附近的外围设备(可能包含中央设备感兴趣的数据)。当中央设备发现外围设备,中央设备就请求与外围设备连接并开始探测和交互外围设备的数据。外围设备负责以适当的方式来响应中央设备。
相关章节:Core Bluetooth Overview
Core Bluetooth 简化了一般的蓝牙任务 Core Bluetooth 框架抽离了蓝牙 4.0 规范中的底层细节。因此,应用中需要实现的一般蓝牙低能耗任务被简化了。如果开发实现中央角色的应用,Core Bluetooth 使得发现、连接外围设备和探测、交互外围数据变得简单。另外,Core Bluetooth 还让本地设备实现外围角色变得简单。
相关章节:Performing Commmon Central Role Tasks,Performing Common Peripheral Role Tasks
iOS 应用的状态影响蓝牙的表现 当应用处于后台或挂起状态时,蓝牙相关的特性会受到影响。在这两种状态下,默认是应用无法执行蓝牙低能耗任务。也就是说,如果应用需要在后台执行蓝牙低能耗任务,可以声明支持 Core Bluetooth 后台运行模式中的一个或两个(一个属于中央角色,另一个属于外围角色)。即使在你指定了一个后台运行模式或两个都指定,当应用处于后台时,某些蓝牙任务的执行依然会有所不同,设计应用时,需要考虑到这些差异。
即使应用支持后台处理,应用仍然可能在任意时刻被系统终止以清空内存给当前前台应用使用。在 iOS 7之后,Core Bluetooth 支持保存中央和外围管理者对象的状态信息并在应用启动的时候恢复该状态,可以使用这个特性来支持涉及蓝牙设备的长期活动(long-term actions)。
verification, validation, authentication, authorization 这几个术语很常用,也经常被误用,这里做一次对比总结。
identity
A security principal (you or a computer, typically) wants to access a system. Because the system doesn’t know you yet, you need to make a declaration of who you are. Your answer to the question “Who are you” is the first thing you present to a system when you want to use it. Some common examples of identity are user IDs, digital certificates (which include public keys), and ATM cards.
原文地址:本地和远程通知编程指南
应用中的通知 本地和远程通知概览 重要 这篇文档包含开发中有关 API 或技术的初步信息,这些信息可能会改变,并且根据这篇文档来实现的软件应当在最终的操作系统软件中进行测试。
本地通知和远程通知是在应用有新数据可用时通知用户的两种方式,即使此时应用不在前台运行。例如,短信应用可能会让用户知道有新的短信来了,日历应用可能会通知用户即将到来的约会。本地通知和远程通知的区别很简单:
对于本地通知,应用在本地配置通知的细节并把这些细节传给系统,然后由系统来处理通知的传递(当应用不在前台时)。iOS、tvOS、watchOS 都支持本地通知。 对于远程通知,使用公司服务器中的一个通过苹果推送通知服务把数据推送到用户的设备。iOS、tvOS、watchOS、macOS 都支持远程通知。 本地通知和远程通知都需要添加代码来支持应用中的通知的调度和处理。对于远程通知,必须提供一个服务器环境,该环境能够接收来自用户设备的数据和发送通知相关的数据到 苹果推送消息服务 (简称 APNs,由苹果提供的用来处理远程通知传递的服务)。
User Notifications 和 User Notifications UI 框架 从 iOS 10、watchOS 3、tvOS 10 开始,User Notifications 框架提供一致的方式来和处理本地通知。除了管理本地通知,该框架也支持远程通知的处理,然而远程通知的配置仍然需要一些平台特有的 API。因为这是一个独立的框架,所以可以在应用中或者扩展中使用,比如 WatchKit 扩展。
注意 macOS 上远程通知的配置和处理需要使用平台特有的方法(在 AppKit 框架中找)
User Notifications 框架也支持创建 通知服务应用扩展 (notification service app extension),它可以让你在远程通知传递之前修改通知的内容。如果在应用中包含通知服务应用扩展,系统会把收到的通知在传递给用户之前先传递给扩展。可以使用这类扩展来给应用的通知实现端到端的加密、在通知传递前修改其内容,又或者下载与通知相关的额外的图片或媒体文件。
User Notifications UI 框架是 User Notifications 的配套,它可以让你自定义系统的通知界面的外观。使用User Notifications UI 框架来定义 通知内容应用扩展(notification content app extension),它的任务就是提供一个包含自定义内容的视图控制器来显示在通知界面中。系统会显示自定义视图控制器而不是默认的系统界面。可以使用这种扩展在通知界面中加入多媒体或动态内容。
更多有关 User Notifications 框架的类的信息,请看 User Notifications Framework Reference。关于创建通知内容应用扩展的类的信息,请看 User Notifications UI Framework Reference。
最近为了学习 Android,找从事 Android 开发的朋友推荐些书,最后他推荐了《第一行代码》(第 2 版)和《Android 开发艺术探索》两本书。本文是在阅读了《第一行代码》之后所做的笔记,主要记录 Android 平台上的一些比较有趣的特性以及它和 iOS 的不同之处。
Android 全貌 2008 年 9 月,Google 正式发布 Android 1.0 系统 2014 年 Google I/O 大会上发布号称史上版本改动最大的 Android 5.0 系统,这版本使用 ART 运行环境替换 Dalvik 虚拟机,同时推出 Wear、Auto、TV 系统 2016 年 Google I/O 大会推出 Android 7.0,加入多窗口模式 Android 系统架构 Android 系统架构分为四层:Linux 内核层、系统运行库层、应用架构层、应用层。
Linux 内核层:主要包含一些硬件的底层驱动。
系统运行库层:包含 C/C++ 的底层支持库,例如:支持 3D 绘图的 OpenGL|ES 库、浏览器内核 Webkit 库和 SQLite 数据库支持库。另外还包含 Android 运行时库。
应用架构层:包含构建应用程序用到的 API,开发人员主要使用这层提供的 API 来构建应用。
应用层:包含手机上安装的应用,联系人、短信等。
Android 系统为开发人员提供了:
四大组件,活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver) 和内容提供器(Content Provider) 系统控件 SQLite 数据库 多媒体,音乐、视频、图片、拍照、闹铃等 地理位置定位 Android 开发环境及工具 环境:JDK + Android SDK
在代理方法中拦截协议 使用 JavaScriptCore WKWebView 的 WKScriptMessagehandler 使用 NSURLProtocol 拦截请求 使用第三方库 WebViewJavascriptBridge 使用 WebSocket 这里只介绍第 6 种,其它的相关资料网上有很多。
使用 WebSocket 的方式需要在应用内起一个 websocket server 服务(有很多第三方的 websocket server 库),html 页面通过 Websocket 连接到服务,接着就是发送消息了,剩下的就跟代理方法拦截协议类似。
// OC code, 以 PocketSocket 这个库为例 _socketServer = [PSWebSocketServer serverWithHost:nil port:9001]; _socketServer.delegate = self; _socketServer.delegateQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); [_socketServer start]; #pragma mark - PSWebSocketServerDelegate - (void)serverDidStart:(PSWebSocketServer *)server { NSLog(@"Server did start…"); } - (void)serverDidStop:(PSWebSocketServer *)server { NSLog(@"Server did stop…"); } - (BOOL)server:(PSWebSocketServer *)server acceptWebSocketWithRequest:(NSURLRequest *)request { NSLog(@"Server should accept request: %@", request); return YES; } - (void)server:(PSWebSocketServer *)server webSocket:(PSWebSocket *)webSocket didReceiveMessage:(id)message { // 在这里拦截 NSLog(@"Server websocket did receive message: %@", message); NSString *text = message; NSURL *url = [NSURL URLWithString:text]; if ([url.
译自 Three Common Mistakes of the First Time Tech Lead
by Patrick Kua
Tech Principal and generalising Specialist
Don’t miss the author’s earlier post on the 5 Tips for Being an Effective Tech Lead.
别错过作者先前写的关于成为高效技术主管的5个秘诀。
The first time a developer steps into the role of a Tech Lead can be difficult. The skills and experience of a seasoned developer do not automatically translate into the skills necessary for the Tech Lead role.
HTTPS HyperText Transfer Protocol,超文本传输协议,是互联网上使用最广泛的一种协议。HTTP协议传输的数据都是未加密的,也就是明文的,不适合用来传输隐私信息。默认 80 端口。
Hyper Text Transfer Protocol over Secure Socket Layer,安全的超文本传输协议,网景公式设计了 SSL(Secure Sockets Layer) 协议用于对 Http 协议传输的数据进行加密,保证会话过程中的安全性。默认 443 端口。
SSL 包含对称加密和非对称加密,在建立传输链路时,SSL 首先使用非对称加密的方式对对称加密密钥进行加密,建立链路后,使用对称加密的方式对传输内容进行加密。非对称加密有更高的安全性,在这个基础上使用对称加密可以获得更快的速度,提高传输效率。
单向认证 客户端校验服务端证书
client hello 客户端发起一条到服务端的连接,包含客户端支持的 TLS 版本、支持的加密套件(即加密算法)以及客户端随机数。
server hello 服务端回应,包含服务端 SSL 证书、选择的加密套件以及服务端随机数。
authentication 客户端向颁发证书的 CA 验证服务端的 SSL 证书,以确认服务端是它声称的那个身份,从而保证客户端与域名真正的所有者通信。
send premaster key 客户端发送另一个随机数 “premaster key”,并对它使用服务端的公钥(从服务端 SSL 证书中获取得到)进行加密。
decrypt premaster key 服务端解密 premaster key
create session key 客户端和服务端各自使用客户端随机数、服务端随机数以及 premaster key 来计算 “session key”,即对称加密密钥,两边计算得出同样的结果并各自保留。
client send finished 客户端使用 session key 加密一条 finished 消息并发送给服务端。
国内 DCloud 团队推出的 HTML5+ 技术框架可以用来开发 Hybrid 应用。经过调研,我们决定试一试 。框架的核心原理是使用 iOS 系统原生 UIWebView 和 WKWebView 来加载资源并渲染界面,Native 的能力(如拍照、蓝牙)通过自定义插件来提供。
我们的应用有个需求,就是在 webview 加载完页面或者加载页面之前加入一些东西。比如:加载完页面后,根据 HTML 的 title 标签来设置导航栏标题。
原生想要插手页面加载周期,只能靠代理方法。但是因为没法修改源码,所以只能找其它办法。主要思路是:使用 Method Swizzle 找出代理对象然后再换掉代理方法实现。
以 UIWebView 为例,具体操作如下:
第一步,通过交换 setDelegate 的实现,找到目标代理对象所属的类;
UIWebView+Intercepter.m - (void)p_setDelegate:(id<UIWebViewDelegate>)delegate { [self p_setDelegate:delegate]; Class delegateClass = [self.delegate class]; // 进一步交换 delegateClass 的代理方法 [UIWebViewDelegateHook exchangeUIWebViewDelegateMethod:delegateClass]; } #pragma mark - Method Swizzling + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [super class]; // When swizzling a class method, use the following: // Class class = object_getClass((id)self); SEL originalSelector = @selector(setDelegate:); SEL swizzledSelector = @selector(p_setDelegate:); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } }); } 第二步,把目标代理对象所属类的代理方法实现换成我们自己写的方法实现。
iOS 10 开始对隐私权限更加严格, 如需使用隐私权限需要在工程的 info.plist 文件中声明,如果不声明程序在调用隐私权限(如相机)时应用程序会崩溃。
key 可以从下拉列表选择,value 为弹框提示文字(类型 String)
权限名称 Key 值 通讯录 NSContactsUsageDescription 麦克风 NSMicrophoneUsageDescription 相册 NSPhotoLibraryUsageDescription 相机 NSCameraUsageDescription 持续获取地理位置 NSLocationAlwaysUsageDescription 使用时获取地理位置 NSLocationWhenInUseUsageDescription 蓝牙 NSBluetoothPeripheralUsageDescription 语音转文字 NSSpeechRecognitionUsageDescription 日历 NSCalendarsUsageDescription