Android Studio使用gradle实现资源自动拷贝

把下面的代码拷贝到appgradle.build文件内

继续阅读Android Studio使用gradle实现资源自动拷贝

Android开源VPN客户端之ICS OpenVPN

1. 简介

Android API level 14+提供了VPNService服务框架,在不root的情况下,开发者可以创建自己的VPN服务应用。
目前主要有三种,分别为OpenVPN for Android(ICS OpenVPN),OpenVPN Connect,OpenVPN Settings。

OpenVPN for Android和OpenVPN Connect使用官方VPNService API (Android 4.0+),不需要root权限,而OpenVPN Settings需要root权限。
OpenVPN for Android是一个开源的客户端,由Arne Schwabe开发,他定位于更高级的用户,提供更多的设置,它可以在app内导入配置文件。
OpenVPN Connect是一个不开源的客户端,它由OpenVPN Technologies , Inc. 开发,它定位于普通用户,它基于OpenVPN协议的C++实现。
OpenVPN Settings是最老的客户端,需要root权限,它并不使用VPNService API。

2. 编译OpenVPN for Android

2.1 开发环境搭建

Ubuntu16,Android Studio,NDK,SDK
首先在Ubuntu下安装Android studio,安装完毕后,使用sdk manager工具安装sdk。
关于NDK,可以使用sdk manager来下载,不过在编译源码的过程中出现了错误,经过尝试发现sdk manager下载的是14版本的ndk,而14版本的ndk编译OpenVPN for Android会出现错误,建议使用12版本的ndk。

2.2 配置环境变量

安装完Android studio,解压完ndk后,需要配置环境变量。

进入用户目录(cd /home/bleach),打开配置文件(vim .bashrc),添加如下内容:

2.3 获取OpenVPN for Android源码

打开搜索引擎,输入ics openvpn,进行搜索,然后进入ics openvpn的github主页,选择master分支里的一个已经添加标签的版本,本文选择的是0.6.64版本的源码。

进入Ubuntu命令行,创建一个源码目录,然后使用git clone命令将源码下载到本地。

2.4 编译源码

step1:进入ics-openvpn-v0.6.64/ics-openvpn目录,修改.gitmodules文件。

将.gitmodules文件中的url更改掉。

step2:执行指令

step3:然后执行指令

step4:然后执行指令

upate指令会消耗一定的时间。
step5:最后进入ics-openvpn-v0.6.64/ics-openvpn/main目录,执行./misc/build-native.sh,等待编译完成,可能需要修改build-native.sh的执行权限。

3. 编译常见问题

3.1 ndk-build not found

未配置ndk环境变量,请参考2.2。

3.2 各种编译错误,比如unknown directive。

很有可能是ndk版本的问题,请使用12版本的ndk尝试编译。

3.3 cannot find .git directory in openvpn,aborting

不要在github上下载zip包的源码,请使用git clone指令将源码下载到本地。

4. 导入源码

将源码导入到Android studio中,目前OpenVPN for Android不支持导入到eclipse中,请使用Android studio开发环境。
打开Android studio,将源码导入,Android studio会自动更新gradle信息,更新完毕后,编译工程,就会生成对应apk包。

参考链接


Android开源VPN客户端之ICS OpenVPN

oKHttp 3.0忽略/检查https证书

最近公司项目需要,网络协议支持HTTPS,之前接触不多,所以这次想总结一下https在Android开发中的相关内容

一、HTTPS 证书

对于HTTPS和证书的概念,大家可以自行搜索百度。

证书分两种:

1、花钱向认证机构购买的证书。服务器如果使用了此类证书的话,那对于移动端来说,直接可以忽略此证书,直接用https访问。与之不同的是ios内置了很多信任的证书,所以他们不需要做任何操作

2、另一种是自己制作的证书,使用此类证书的话是不受信任的,也不需要花钱,所以需要我们在代码中将此类证书设置为信任证书

二、如何忽略证书

注意,下面的操作在线上环境强烈不建议采纳,只能是在测试环境中解决测试问题的时候才能使用。

1、服务器如果加上了证书的话,那么你们的网络请求的url将从http:xx改成https:xx,如果你直接也将http改成https的话而什么也不做的话,客户端将直接报错,如图:

继续阅读oKHttp 3.0忽略/检查https证书

Android:调整线程调用栈大小

在常规的Android开发过程中,随着业务逻辑越来越复杂,调用栈可能会越来越深,难免会遇到调用栈越界的情况,这种情况下,就需要调整线程栈的大小。

当然,主要还是增大线程栈大小,尤其是存在jni调用的情况下,C++层的栈开销有时候是非常恐怖的,比如说递归调用。

这就需要分三种情况,主线程,自定义线程池,AsyncTask

主线程的线程栈是没有办法进行修改的,这个没办法处理。

针对线程池的情况,需要在创建线程的时候,调用构造函数

通过设置stackSize参数来解决问题。

参考代码如下:

针对AsyncTask的情况,一般是通过调用

指定线程池来运行,在特定的线程池中调整线程栈的大小。

参考代码如下:

参考链接


com.android.tools.r8.ApiLevelException: Invoke-customs are only supported starting with Android O

原因是某些第三方库使用了Java 1.8API,导致整个项目必须使用Java 1.8

解决方法就是增加如下编译命令

完整的例子如下:

参考链接


Android 6.0/10.0扫描不到Ble设备需开启位置权限

蓝牙设备扫描需要定位权限的原因在于蓝牙定位的存在,蓝牙定位属于精确定位,因此需要开启精确定位权限,而精确定位跟GPS又是直接相关的,因此才会导致GPS权限需要获取。

项目中需要用到Android Ble蓝牙4.0开发技术,于是开启了蓝牙填坑之旅,说实话,蓝牙开发坑真多,跳出一个又进入下一个,每次遇到 问题,就觉得不可能解决了,还好在自己的摸索中,都一一的化解了,以此来记录安卓蓝牙开发的心得。

     接手的蓝牙开发项目,原来的同事已经写好,不用再去写,开始也就大概看了看Android蓝牙开发相关资料,对比项目中的蓝牙开发代码,发现发现搜索、连接蓝牙有一样的处理也有不一样的处理,想着项目不一样,代码处理肯定不一样,于是就没去深究,毕竟项目运行起来使用正常,然而,没过几天,客户提出蓝牙搜索太慢,然后就去调试,先看代码是搜索蓝牙8秒钟才显示搜索到的蓝牙设备列表,查看日志,其实不到8秒就搜索到外围设备,只是搜索8秒后停止扫描才显示蓝牙设备,那就把显示成3、4秒呗。
        正这样想,同事拿来一部荣耀8,装的APP怎么就是搜不到蓝牙设备,然后调试其他手机,都能搜索到,于是就纳闷了,荣耀8怎么就是搜索不到蓝牙设备呢,其他手机都能,再看一遍代码,也没有专门针对荣耀8处理的地方,再想,荣耀8是系统7.0的手机,是不是跟权限没开有关呢,对比其他华为手机,申请的蓝牙权限也都开着,荣耀8就是搜索不到,是不是荣耀8手机有问题,同事告知下载一个第三方蓝牙调试工具查查看,令人疑惑的是荣耀8用第三方蓝牙调试工具立刻就能扫描显示出来,试了几次都是立刻扫描出来,当时心想,我靠,什么鬼,这么迅速就能把蓝牙设备扫描显示出来,领导也说,别人的调试工具都能做这么快,你也得做这么快,想着目前的代码都是几秒后才能搜索到,目前的荣耀8还搜索不出来,客户那边还一个劲儿的给老板吐槽太慢,于是压力剧增,真正开始了蓝牙开发不归之旅。
     但是再考虑索慢时,领导说,得把荣耀8搜索不到的问题先解决,因为荣耀8是项目实施人员给客户安装硬件调试数据要用的,那就先把搜索慢暂时放一放,想着一边搜索慢没还没着落,荣耀8手机就是搜索不到,想想就头大,查找荣耀8为什么搜索不到蓝牙设备的资料,也没发现有效的办法,这时,用荣耀手机的人说,另一个公司的项目app(简称B吧,荣耀8搜索不到蓝牙设备的项目简称A吧)就能正常搜索到蓝牙设备,一打开调试还真是每次都能搜索到,看了一下两个项目的代码:

      两个项目中的蓝牙适配器的获取,去发现,动态注册广播、广播接收蓝牙搜索结果、蓝牙连接都一样,至于手机是否支持 ble 4.0,蓝牙是否开启的逻辑,连接上对数据的处理、断开mBluetoothGatt.disconnect(),关闭mBluetoothGatt.close() 的逻辑网上有很多,我这里就不在详说;
       经过2个项目对比,发现就蓝牙搜索多久停止不一样,荣耀8能搜索到蓝牙设备的时间项目代码设置是10秒,而搜索不到的项目是设置8秒,带着半信半疑把项目A的蓝牙搜索时间由8秒改成10秒,运行,奇迹还真是出现了,荣耀8可以正常搜索到蓝牙设备了,反复测了好多次都能正常搜索到蓝牙设备,我是不信邪的人,还改成8秒试试,果然还是搜索不到。
     于是和领导说,你看荣耀8必须10秒才能搜索 到蓝牙设备,搜索到的蓝牙设备列表不晚点展示不行啊,领导自然 是不同意,拿出第三方蓝牙提哦啊是工具说,那第三方就能很快搜索到,你想想其他法吧,想着有的手机快,有的手机慢,提出意见,能不能搜索到一个设备就展示到列表,不必等10秒后再展示,这样体验好些,反正领导说不行,既然领导说不行,那就回去研究吧。
    查了很多蓝牙搜索慢怎么办?看了半天,当然这期间调试过各种其他方法,都没用,功夫不负有心人,其中一个博客说:btAdapt.startDiscovery();搜索返回的结果慢, 现在都用这个 btAdapt.startLeScan(getmLeScanCallback); 其他也没多说什么,喜出望外,这不是正是我要的吗,于是改蓝牙搜索,上代码:

            经过这样修改之后,奇迹又出现了,蓝牙搜索到确实快了很多,一秒左右甚至不到一秒就把蓝牙设备搜索出来了, 真是令人喜出望外,想起华为荣耀8那么奇葩,不知这个方法在华为荣耀8上测试如何,然而,2秒,4秒,8秒,10秒,20秒都在华为荣耀8搜索不出来,心又凉了半截,暗想这个方法看来还不适用,又白整了,于是又查各种资料,有的说Android系统6.0以上的手机需要定位权限,低头一想,还真是,一直测试的手机是Android系统6.0以下的,华为荣耀8是7.0以上的,抱着试试的态度,申请权限,还真是努力出奇迹,定位权限允许后,华为荣耀8确实也在不到一秒将蓝牙设备搜索出来,测试了其他6.0以上的手机,都正常很快搜出来。
        测试没有其他问题,打包发布,然而客户不给定位权限,问公司怎么一点搜索不出来,告知需要允许定位,才能搜索到,客户觉得一个蓝牙搜索你跟我要定位,很是不理解,老板也认为这样的体验也不好,客户不给权限,就无法使用,非常不友好。告知领导6.0以上必须给定位权限,否则搜不到, 这个没法啊,领导说你看第三方的蓝牙调试工具,在6.0以上的手机,不用打开定位,而且搜索的更快,第三方都能做到,你想办法吧, 我当然不苟同,下载了几个第三方蓝牙调试工具,有的确实要定位权限不允许定位就无法搜索到,有的确实禁止定位权限也能快速搜索到蓝牙设备,这么啪啪打脸,只能埋头去想办法了,同时,为了保证程序兼容性,公司又买了几部6.0以上的安卓手机,让我更惊心的是,魅族6和华为畅享7不仅要允许定位权限,GPS定位还要打开,GPS不打开,也是搜索不到蓝牙设备,想起以前开发的项目,由于安卓碎片化,不同的手机厂商对定位,GPS管理不一样,有的打开定位权限GPS权限也给了,有的打开GPS定位权限也给了,有的是他们两个独立,处理起来很麻烦,而蓝牙有的要GPS,有的不要GPS,6.0一下还不需要任何定位权限,想着这处理起来太麻烦,还是安心去找不要定位权限的方法吧,其实当时心里是没底的,虽然第三方已经实现这种不需要定位权限就能搜索到,心想,第三方也不知用的是什么黑科技,于是就查找Android系统6.0一上的手机不需要定位权限就能把蓝牙设备搜索出来等相关资料,其中最有帮助的查到 btAdapt.startLeScan(getmLeScanCallback) 这个方法已过时,Google已经在api里将其划掉,不推荐使用,推荐使用:

喜出过望,难道是用的方法过时了,所以需要定位权限,上代码:

停止扫描的方法是:

        然而,虽然使用了新方法,速度也是一样快,但是还是需要定位权限,魅族6和华为畅享7还得打开GPS,看来这个也不是正解,当然项目中的扫描外围蓝牙设备的方法也都换成了scanner.startScan(scanCallback);
第一天毫无结果,灰头土脸,昏天暗地,焦头烂额地回家。
第二天继续查找相关资料,在一个偶然的机会,看到一篇博客说,把app中的build.gradle中的:

中的  targetSdkVersion 25改成 targetSdkVersion 22,就不需要定位权限也可以搜索到蓝牙设备了,看到网上很少有人这样说的,搜了那么多资料,就一篇这样说的,带着半信半疑将targetSdkVersion改成 22, 编译,也没报任何错,运行,还真是又出奇迹,测试了手上的所有手机,都不需要定位,不要GPS就可以搜索到蓝牙设备。
       至于为什么会这样,可以查找 Android compileSdkVersion、buildToolsVersion,targetSdkVersion之间的区别,网上有很多解释,保证能解答你的疑惑,下面附上一些简单的解释
     targetSDKVersion
    简单来说就代表着你的App能够适配的系统版本,意味着你的App在这个版本的手机上做了充分的 前向 兼容性处理和实际测试。其实我们写代码时都是经常干这么一件事,就是 if(Build.VERSION.SDK_INT >= 23) { ... } ,这就是兼容性处理最典型的一个例子。如果你的target设置得越高,其实调用系统提供的API时,所得到的处理也是不一样的,甚至有些新的API是只有新的系统才有的Android6.0普通权限normal permission 和 危险权限dangerous permission 。
 Normal Permission:写在xml文件里,那么App安装时就会默认获得这些权限,即使是在Android6.0系统的手机上,用户也无法在安装后动态取消这些normal权限,这和以前的权限系统是一样的,不变。
Dangerous Permission:还是得写在xml文件里,但是App安装时具体如果执行授权分以下几种情况:
1、targetSDKVersion < 23 & API(手机系统) < 6.0 :安装时默认获得权限,且用户无法在安装App之后取消权限。
 2、targetSDKVersion < 23 & API(手机系统) >= 6.0 :安装时默认获得权限,但是用户可以在安装App完成后动态取消授权( 取消时手机会弹出提醒,告诉用户这个是为旧版手机打造的应用,让用户谨慎操作 )。
3、targetSDKVersion >= 23 & API(手机系统) < 6.0 :安装时默认获得权限,且用户无法在安装App之后取消权限。
 4、targetSDKVersion >= 23 & API(手机系统) >= 6.0 :安装时不会获得权限,可以在运行时向用户申请权限。用户授权以后仍然可以在设置界面中取消授权,用户主动在设置界面取消后,在app运行过程中可能会出现crash。
其他:
        1.虽然解决了大问题,但是目前项目中发现,连续扫描-断开-扫描四五次,就无法再搜索到外围蓝牙设备,尤其是在每次搜索1-2秒内扫描断开,4、5次无论如何也是搜索不到,需要重新进入界面才能扫描到,每次扫描花费在4、5秒以上的,出现4、5次扫不到的几率就很低了,当然,4、5秒是老板和客户无法忍受的。
2.蓝牙连接成功自动断开;

参考链接


Android dp 和 CSS px

最近在给WebView前端传递标题栏高度的时候,传递的是实际像素Pixel,结果前端直接设置这个数值的时候,发现太高了。搜索了一下,发现前端的CSS样式设置的像素值是逻辑像素,这个逻辑像素跟实际像素是不同的,对于Android来说,就是设备的dp数值。

参考文章如下:

前情提要:设计师给的设计稿是1242分辨率的(iPhone6p),标注的字体大小是54px,前端工程师写的H5页面是18px,效果正常且能自适应iPhone6。来调查一下 为什么是标注数值除以3?为什么自动适应iPhone6?css里的px和Android的dp有怎样的关系?

在进行具体的分析之前,首先得知道下面这些关键性基本概念(术语)。

物理像素(physical pixel)

一个物理像素是显示器(手机屏幕)上最小的物理显示单元,在操作系统的调度下,每一个设备像素都有自己的颜色值和亮度值。

设备独立像素(density-independent pixel)

设备独立像素(也叫密度无关像素),可以认为是计算机坐标系统中得一个点,这个点代表一个可以由程序使用的虚拟像素(比如: css像素),然后由相关系统转换为物理像素。

所以说,物理像素和设备独立像素之间存在着一定的对应关系,这就是接下来要说的设备像素比

设备像素比(device pixel ratio)

设备像素比(简称dpr)定义了物理像素和设备独立像素的对应关系,它的值可以按如下的公式的得到:

在javascript中,可以通过window.devicePixelRatio获取到当前设备的dpr。

在css中,可以通过-webkit-device-pixel-ratio-webkit-min-device-pixel-ratio-webkit-max-device-pixel-ratio进行媒体查询,对不同dpr的设备,做一些样式适配(这里只针对webkit内核的浏览器和webview)。


iPhone6的物理像素是750 x 1334,设备像素比(devicePixelRatio)是2,设备独立像素是375 x 667 (750/2 x 1334/2)

iPhone6p的物理像素是1080 x 1920,但logic pixel是1242 x 2208, 设备像素比(devicePixelRatio)是3,设备独立像素是414 x 736 (1242/3 x 2208/3)

css里面有物理像素逻辑像素的概念,在这里,逻辑像素可以理解为设备独立像素

其实,logic point更适合翻译成逻辑点。无论是逻辑像素还是逻辑点,只需要理解一个逻辑点(逻辑像素)需要一个或一个以上的物理像素来展示就可以了。

那么,为什么iPhone6p是这样奇葩的逻辑点呢?

  • 如果逻辑点分辨率用 360x640, 360x640@3x 正好是 1080x1920。但是逻辑pt分辨率 360x640 就会比 iPhone 6的 375x667 还低,也就是说相同字号的情况下,iPhone 6如果一行显示了25个字,而 iPhone 6 Plus按这个逻辑pt方案,一行就会只能显示24 个字了。
  • 如果逻辑点分辨率用 540x960,540x960@2x 正好是 1080x1920。但是iOS UI 元素尺寸在屏幕上的实际物理面积一下子就变小了,比如标签栏或导航栏按钮的物理高度只有原来的 81.5% ,点击面积就只有iPhone 6 的0.815*0.815=66.4%,用户点击就困难了。
  • 如果物理分辨率做到1242x2208就减少了一个从1242压缩到1080的过程,但是1242的物理分辨率是在1080p和2k屏之前的尺寸,功耗和成本都会提升,而且这样非1440的2k分辨率的屏幕采购也是问题。

至于为什么一定是 414x736,有人估计应该是在 5.5inch 和 ppi=461 这两个前提限定的情况下,按这个 414x736 pt 分辨率,屏幕上 UI 元素操作物理大小最接近 iPhone 6上的表现。


Android世界中更加复杂,这里列举了几款手机的基本数据

  Nexus 4 Nexus 6 Nexus 6p LG L24 Mi 4c
物理像素 768 x 1280 1440 x 2560 1440 x 2560 1440 x 2560 1080 x 1920
设备像素比(dpr) 2 3.5 3.3 4 3
设备独立像素(dip/dp) 384 x 640 412 x 732 435 x 773 360 x 640 360 x 640

Android提供了获取density的方法:

density的官方定义是The logical density of the display,翻译过来是屏幕的逻辑密度,其实就是之前提到的设备像素比(dpr)


在浏览器中,获取设备像素比就比较简单:

比如,我写了一个网页,用手机浏览器打开:

结果显示与Android的density的结果是一致的。

注:<meta name="viewport" content="width=device-width, initial-scale=1">
By specifying width=”device-width”, you are asking the browser to apply a scaling factor to its screen pixels. One css pixel occupies one or more screen pixels. How many more? This value is called the css pixel ratio.


回顾一下

同一台设备,它的设备像素比(dpr)是确定的,无论通过Android api 获取还是浏览器 js/css 获取。
The css px is actually Device Independent Pixel(dip), and it is a common pattern to use px as dp in css.
css中的px具有与Android中的dp等同的效果

参考链接


Centos 7配置Jenkins构建Android持续集成(离线内网环境)

安装配置Jenkins

为了安全考虑,首先需要解锁Jenkins

继续阅读Centos 7配置Jenkins构建Android持续集成(离线内网环境)

Gradle传递System Property

最近在搭建Jenkins环境,实现Android自动化编译的过程中,由于内网服务器不能访问外网,因此只能配置Robolectric访问内网的服务器。

根据官方文档 Configuring Robolectric,发现如果要更改Robolectric默认的下载服务器链接地址,需要在项目的每个lib库中都配置如下参数,才能实现:

但是这样配置有一个问题,就是没办法动态调整链接地址。

而我们使用

手工指定的参数,并没有在编译的时候生效。

参数丢失的原因是因为测试用例在新的JVM中执行,传入的参数不会自动带给新创建的JVM

这时需要在Gradle脚本中将读到的值重新设到系统属性里面,才可以被Java程序读到。

每个项目的build.gradle中都增加上面的配置之后,就可以保证在外部编译的时候动态指定参数了。

Android Studio Chipmunk | 2021.2.1 Patch 1 以及以上的版本,可能会发生如下报错:

该报错的原因是 Android Studio 没有正确读取我们配置的 robolectric.dependency.repo.url 环境变量,导致无法成功下载,当然也可能是由于高版本不支持 HTTP ,链接地址必须是 HTTPS 导致此问题,这个没有深究。

解决方法是,在命令行执行

确保在当前用户目录下的 .m2 目录下,成功完成 robolectric 需要的全部文件的下载。这样,执行测试用例的时候,就可以从本地获取依赖文件,而不需要再去服务器上获取。

参考链接