Glide填坑指南

一、前言:再优秀的开源库都有坑要填 手上的项目使用的图片加载框架是:Universal-Image-Loader+业务需要定制化的一些代码。Universal-Image-Loader 这个框架是一个非常经典好用的框架,唯一的问题是是作者很久之前就不再更新了。所以综合考虑下,确定使用Glide+封装代替当前的图片加载框架。 二、困惑: 在没有真正使用 Glide 之前,我所看到的文章基本都是赞美这个库的功能强大,加载流畅。然而,当我用上了以后,才发现并不完美。遇到了不少的坑,需要自己填。 2.1 Glide 配合 OKHttp 使用的坑: 需要在Gradle中引入: compile “com.github.bumptech.glide:glide:3.7.0” compile “com.github.bumptech.glide:okhttp3-integration:1.4.0@aar” 这里就有一个坑,如果你用到自定义的 GlideModule,这里的可能会失效,被com.github.bumptech.glide:okhttp3-integration:1.4.0@aar默认的替换 解决方法是升级版本号: compile “com.github.bumptech.glide:okhttp3-integration:1.4.0@aar” -》 compile “com.github.bumptech.glide:okhttp3-integration:1.5.0” 注意,没有@arr 2.2 OKHttpClient 超时设置导致图片无法加载坑: 因为Glide本身只负责图片加载,网络请求图片数据由网络框架决定。网络请求一般会有超时的问题,坑的是OKHttp默认的超时时间太短了,如果不修改,网络状态比较差 就很容易请求超时,图片自然就加载不出来。我设置的参数是60,60,30这个可以自己根据实际情况确定。 `//这个是源码里面的,默认超时时间,都是10s,10000ms connectTimeout = 10_000; readTimeout = 10_000; writeTimeout = 10_000; //手动设置超时时间 OkHttpClient client=new OkHttpClient.Builder() .connectTimeout(HTTP_CONNECT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(HTTP_READ_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(HTTP_WRITE_TIMEOUT, TimeUnit.SECONDS) .build;` 2.3 Glide 查看 log 的坑: 如果你使用 Glide 经常出现图片加载不出来或者加载有问题,你需要查看 Glide 本身的 log,不过这个必须通过 adb 命令开启,详情百度,需要注意如果是请求图片问题,关注请求的 log,图片加载||转换的问题,关注图片加载||转换的log。 ...

2018年9月14日 · 1 分钟 · 天边的星星

Android hdpi ldpi mdpi xhdpi xxhdpi适配详解

1、了解几个概念 (1)分辨率。分辨率就是手机屏幕的像素点数,一般描述成屏幕的“宽×高”,安卓手机屏幕常见的分辨率有480×800、720×1280、1080×1920等。720×1280表示此屏幕在宽度方向有720个像素,在高度方向有1280个像素。 (2)屏幕大小。屏幕大小是手机对角线的物理尺寸,以英寸(inch)为单位。比如某某手机为“5寸大屏手机”,就是指对角线的尺寸,5寸×2.54厘米/寸=12.7厘米。 (3)密度(dpi,dots per inch;或PPI,pixels per inch)。从英文顾名思义,就是每英寸的像素点数,数值越高当然显示越细腻。假如我们知道一部手机的分辨率是1080×1920,屏幕大小是5英寸,你 能否算出此屏幕的密度呢?哈哈,中学的勾股定理派上用场啦!通过宽1080和高1920,根据勾股定理,我们得出对角线的像素数大约是2203,那么用 2203除以5就是此屏幕的密度了,计算结果是440。440dpi的屏幕已经相当细腻了。 2、实际密度与系统密度 尚未发现他处使用“实际密度”和“系统密度”这两个词汇,暂时由我如此定义吧。 “实际密度”就是我们自己算出来的密度,这个密度代表了屏幕真实的细腻程度,如上述例子中的440dpi就是实际密度,说明这块屏幕每寸有440个 像素。5英寸1080×1920的屏幕密度是440,而相同分辨率的4.5英寸屏幕密度是490。如此看来,屏幕密度将会出现很多数值,呈现严重的碎片 化。而密度又是安卓屏幕将界面进行缩放显示的依据,那么安卓是如何适配这么多屏幕的呢? 其实,每部安卓手机屏幕都有一个初始的固定密度,这些数值是120、160、240、320、480,我们权且称为“系统密度”。大家发现规律没 有?相隔数值之间是2倍的关系。一般情况下,240×320的屏幕是低密度120dpi,即ldpi;320×480的屏幕是中密度160dpi,即 mdpi;480×800的屏幕是高密度240dpi,即hdpi;720×1280的屏幕是超高密度320dpi,即xhdpi;1080×1920的 屏幕是超超高密度480dpi,即xxhdpi。 安卓对界面元素进行缩放的比例依据正是系统密度,而不是实际密度。 3、一个重要的单位dp dp也可写为dip,即density-independent pixel。你可以想象dp更类似一个物理尺寸,比如一张宽和高均为100dp的图片在320×480和480×800的手机上“看起来”一样大。而实际 上,它们的像素值并不一样。dp正是这样一个尺寸,不管这个屏幕的密度是多少,屏幕上相同dp大小的元素看起来始终差不多大。 另外,文字尺寸使用sp,即scale-independentpixel的缩写,这样,当你在系统设置里调节字号大小时,应用中的文字也会随之变大变小。 4、dp与px的转换 在安卓中,系统密度为160dpi的中密度手机屏幕为基准屏幕,即320×480的手机屏幕。在这个屏幕中,1dp=1px。 100dp在320×480(mdpi,160dpi)中是100px。那么100dp在480×800(hdpi,240dpi)的手机上是多少 px呢?我们知道100dp在两个手机上看起来差不多大,根据160与240的比例关系,我们可以知道,在480×800中,100dp实际覆盖了 150px。因此,如果你为mdpi手机提供了一张100px的图片,这张图片在hdpi手机上就会拉伸至150px,但是他们都是100dp。 中密度和高密度的缩放比例似乎可以不通过160dpi和240dpi计算,而通过320px和480px也可以算出。但是按照宽度计算缩放比例不适 用于超高密度xhdpi和超超高密度xxhdpi了。即720×1280中1dp是多少px呢?如果用720/320,你会得出1dp=2.25px,实 际这样算出来是不对的。dp与px的换算要以系统密度为准,720×1280的系统密度为320,320×480的系统密度为 160,320/160=2,那么在720×1280中,1dp=2px。同理,在1080×1920中,1dp=3px。 大家可以记住下面这个比例,dp与px的换算就十分easy啦! ldpi:mdpi:hdpi:xhdpi:xxhdpi=3:4:6:8:12,我们发现,相隔数字之间还是2倍的关系。计算的时候,以mdpi 为基准。比如在720×1280(xhdpi)中,1dp等于多少px呢?mdpi是4,xhdpi是8,2倍的关系,即1dp=2px。反着计算更重 要,比如你用PhotoShop在720×1280的画布中制作了界面效果图,两个元素的间距是20px,那要标注多少dp呢?2倍的关系,那就是 10dp! 当安卓系统字号设为“普通”时,sp与px的尺寸换算和dp与px是一样的。比如某个文字大小在720×1280的PS画布中是24px,那么告诉工程师,这个文字大小是12sp。 5、建议在xdhpi中作图 安卓手机有这么多屏幕,我到底依据哪种屏幕作图呢?没有必要为不同密度的手机都提供一套素材,大部分情况下,一套就够了。 现在手机比较高的分辨率是1080×1920,你可以选择这个尺寸作图,但是图片素材将会增大应用安装包的大小。并且尺寸越大的图片占用的内存也就 越高。如果你不是设计ROM,而是做一款应用,我建议大家用PS在720×1280的画布中作图。这个尺寸兼顾了美观性、经济性和计算的简单。美观性是 指,以这个尺寸做出来的应用,在720×1280中显示完美,在1080×1920中看起来也比较清晰;经济性是指,这个分辨率下导出的图片尺寸适中,内 存消耗不会过高,并且图片文件大小适中,安装包也不会过大;计算的简单,就是1dp=2px啊,多好计算啊! 做出来的图片,记着让界面工程师放进drawable-xhdpi的资源文件夹中。 6、屏幕的宽高差异 在720×1280中作图,要考虑向下兼容不同的屏幕。通过计算我们可以知道,320×480和480×800的屏幕宽度都是320dp,而 720×1280和1080×1920的屏幕宽度都是360dp。它们之间有40dp的差距,这40dp在设计中影响还是很大的。如下图蝴蝶图片距离屏幕 的左右边距在320dp宽的屏幕和360dp宽的屏幕中就不一样。 不仅宽度上有差异,高度上的差异更加明显。对于天气等工具类应用,由于界面一般是独占式的,更要考虑屏幕之间的比例差异 如果想消除这些比例差异,可以通过添加布局文件来实现。一般情况下,布局文件放在layout文件夹中,如果要单独对360dp的屏幕进行调整,你 可以单做做一个布局文件放在layout-w360dp中;如果你想对某个特殊的分辨率进行调整,那么你可以将布局文件放在标有分辨率的文件夹中,如 layout-854×480。 7、几个资源的文件夹 在720×1280中做了图片,要让开发人员放到drawable-xhdpi的资源文件夹中,这样才可以显示正确。个人认为仅提供一套素材就可以 了,可以测试一下应用在低端手机上运行是否流畅,如果比较卡顿,可以根据需要提供部分mdpi的图片素材,因为xhdpi中的图片运行在mdpi的手机上 会比较占内存。 以应用图标为例,xhdpi中的图标大小是96px,如果要单独给mdpi提供图标,那么这个图标大小是48px,放到drawable-mdpi 的资源文件夹中。各个资源文件夹中的图片尺寸同样符合ldpi:mdpi:hdpi:xhdpi:xxhdpi=3:4:6:8:12的规律。 如果你把一个高2px的分割线素材做成了9.png图片,你想让细线在不同密度中都是2px,而不被安卓根据密度进行缩放,怎么办?你可以把这个分 割线素材放到drawable-nodpi中,这个资源文件夹中的图片,将按照实际像素大小进行显示,而不会被安卓根据密度进行缩放。即在mdpi中细线 是2px(2dp),在xhdpi中细线是2px(1dp)。 ...

2018年8月22日 · 2 分钟 · 天边的星星

虚拟导航栏适配

在写界面的时候 然后发现在界面最底下的几行文字 正好被虚拟导航栏遮挡住了,不滑动还看不到底下的文字,所以想隐去这些导航栏。 采用下面的代码将DecorView中的属性设置为隐藏 navigation,我这里注销掉了全屏的属性 ,按需添加or删除属性吧 11 && Build.VERSION.SDK_INT < 19) { // lower api View v = this.getWindow().getDecorView(); if(v!=null){ v.setSystemUiVisibility(View.GONE); } } else if (Build.VERSION.SDK_INT >= 19) { //for new api versions. View decorView = getWindow().getDecorView(); int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY/* | View.SYSTEM_UI_FLAG_FULLSCREEN*/; if (decorView != null) { decorView.setSystemUiVisibility(uiOptions); } } }1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 " data-snippet-id="ext.c635ae4a92490f41cf801e12d12759e7" data-snippet-saved="false" data-codota-status="done">` &lt;span class="hljs-keyword">protected&lt;/span> &lt;span class="hljs-keyword">void&lt;/span> &lt;span class="hljs-title">hideBottomMenu&lt;/span>() { &lt;span class="hljs-comment">//隐藏虚拟按键&lt;/span> &lt;span class="hljs-keyword">if&lt;/span> (Build.VERSION.SDK_INT &gt; &lt;span class="hljs-number">11&lt;/span> && Build.VERSION.SDK_INT &lt; &lt;span class="hljs-number">19&lt;/span>) { &lt;span class="hljs-comment">// lower api&lt;/span> View v = &lt;span class="hljs-keyword">this&lt;/span>.getWindow().getDecorView(); &lt;span class="hljs-keyword">if&lt;/span>(v!=&lt;span class="hljs-keyword">null&lt;/span>){ v.setSystemUiVisibility(View.GONE); } } &lt;span class="hljs-keyword">else&lt;/span> &lt;span class="hljs-keyword">if&lt;/span> (Build.VERSION.SDK_INT &gt;= &lt;span class="hljs-number">19&lt;/span>) { &lt;span class="hljs-comment">//for new api versions.&lt;/span> View decorView = getWindow().getDecorView(); &lt;span class="hljs-keyword">int&lt;/span> uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY&lt;span class="hljs-comment">/* | View.SYSTEM_UI_FLAG_FULLSCREEN*/&lt;/span>; &lt;span class="hljs-keyword">if&lt;/span> (decorView != &lt;span class="hljs-keyword">null&lt;/span>) { decorView.setSystemUiVisibility(uiOptions); } } }` ...

2018年8月3日 · 1 分钟 · 天边的星星

Android 优化工具收集

在开发Android的过程中,我们会遇到各种问题,有时候为了满足某一个需求(或者快速的上线完成一个版本), 我们会使用各种框架,代码编写可能随意一些,造成一些冗余的代码,整个工程越跑越慢,APP越跑越慢, 当达到一定阶段的时候,就不得不需要我们进行工程的优化,今天就来说说优化过程中使用到的工具。 Android的应用性能的指标主要有: 布局复杂度:布局复杂会导致布局需要更长的时间,从而导致进入应用慢、页面切换慢; 耗电量:耗电量大会导致机器发热、缩短机器的有效使用时长; 内存:内存消耗大会导致频繁GC,GC时会暂停其它工作,导致页面卡顿;内存泄露会导致剩余可用内存越来越小;内存不足会导致应用异常; 网络:频繁的网络访问会导致耗电和影响应用的性能;网络交互数据大小会影响网络传输的效率; 程序执行效率:糟糕的代码会严重影响程序的运行效率,UI线程过多的任务会阻塞应用的正常运行,长时间持有某个对象会导致潜在的内存泄露,频繁的IO操作、网络操作而不用缓存会严重影响程序的运行效率。 工具 Memory Monitor:查看整个app所占用的内存,以及发生GC的时刻 Allocation Tracker:使用此工具来追踪内存的分配。 Heap Tool:查看当前内存快照,便于对比分析哪些对象有可能是泄漏了的。 Battery History Tool 电量审查工具(Android 5.0,不属于编译器的工具) traceview 工具(计算每个方法占用CPU时间) Networking Traffic Tool (android studio)网络请求发生的时间,每次请求的数据量等信息 Hierarchy viewer 层级显示工具(检测布局复杂度,各视图的布局耗时情况) Lint 代码审查工具 给出代码优化建议 耗电量:Android开发者模式中的电量统计 leakcanary:square/leakcanary · GitHub,通过集成到程序中的方式,在程序运行时检测应用中存在的内存泄露,并在页面中显示,在应用中集成leancanry后,程序运行时会存在卡顿的情况,这个是正常的,因为leancanry就是通过gc操作来检测内存泄露的,gc会知道应用卡顿,说明文档:LeakCanary 中文使用说明、LeakCanary: 让内存泄露无所遁形。 GT:GT Home,GT是腾讯开发的一款APP的随身调测平台,利用GT,可以对CPU、内存、流量、点亮、帧率/流畅度进行测试,还可以查看开发日志、crash日志、抓取网络数据包、APP内部参数调试、真机代码耗时统计等等,需要说明的是,应用需要集成GT的sdk后,GT这个apk才能在应用运行时对各个性能进行检测。 iTest:iTest,业内首创的Android自动化性能监控工具,它能够记录特定应用的性能消耗情况,包括cpu、内存、流量、电量等信息,支持浮窗实时查看应用的具体信息,iTest不需要集成sdk到应用中,在itest中选中需要测试的应用即可进行测试; Emmagee:Emmagee下载、NetEase/Emmagee · GitHub,网易开发的性能检测工具,Emmage和iTest一样,不需要在应用中集成sdk,能够对应用的常用性能指标进行检测,并以csv的格式保存方便查看应用的各项参数 APT:Tencent/apt | CODE,腾讯出的。 FPSService:百度一位开发者写的帧率测试工具,需要集成到应用中才可查看 AndroidGodEye:AndroidGodEye是一个可以在PC浏览器中实时监控Android数据指标(比如性能指标,但是不局限于性能)的工具,你可以通过wifi/usb连接手机和pc,通过pc浏览器实时监控手机性能。 系统分为三部分: Core 核心部分,提供所有模块; Debug Monitor部分,提供Debug阶段开发者面板; Toolbox 快速接入工具集,给开发者提供各种便捷接入的工具。 AndroidGodEye提供了多种监控模块,比如cpu、内存、卡顿、内存泄漏等等,并且提供了Debug阶段的Monitor看板实时展示这 些数据。而且提供了api供开发者在release阶段进行数据上报。 慢慢一个个工具使用吧 参考引用 https://blog.csdn.net/u010255127/article/details/49135551 https://segmentfault.com/a/1190000012413613

2018年5月23日 · 1 分钟 · 天边的星星

为TabLayout添加角标的最简单方法

一、前言 在开发中,我们常常需要ViewPager结合Fragment一起使用,来实现多页签的切换效果。在以前,我们有以下一系列第三方库来帮我们实现: PagerSlidingTabStrip SmartTabLayout FlycoTabLayout ViewPagerIndicator … 而现在,我们可以使用Design support library库的TabLayout来实现了。 二、最终效果图 三、TabLayout的使用 1. 添加依赖 由于TabLayout在design包内,所以首先需要在app目录下的build.gradle中添加以下依赖: ``` 1 2 3 4 ``` <td class="code hljs bash"> <div class="top-box hide"> <div class="alert-info"> </div> </div> ``` dependencies { … compile ‘com.android.support:design:23.4.0’ } </td> </tr> </table></figure> ## 2. 创建布局 {#2-创建布局} 布局相当简单,只要添加TabLayout和ViewPager的布局即可: ### layout/activity_main.xml {#layout-activity-main-xml}<figure class="highlight xml"> <table> <tr> <td class="gutter"> <div class="top-box hide"> <div class="alert-info"> </div> </div> ``` <span class="line">1</span> <span class="line">2</span> <span class="line">3</span> <span class="line">4</span> <span class="line">5</span> <span class="line">6</span> <span class="line">7</span> <span class="line">8</span> <span class="line">9</span> <span class="line">10</span> <span class="line">11</span> <span class="line">12</span> <span class="line">13</span> <span class="line">14</span> <span class="line">15</span> <span class="line">16</span> <span class="line">17</span> <span class="line">18</span> <span class="line">19</span> <span class="line">20</span> <span class="line">21</span> </td> <td class="code hljs xml"> <div class="top-box hide"> <div class="alert-info"> </div> </div> ``` <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" ...

2018年4月4日 · 8 分钟 · 天边的星星

Android 分包(多dex)处理

Android 分包导致低于5.0版本报错ClassNotFoundException 项目在4.4系统运行就报错,在5.0以上没问题。原因就是 报错ClassNotFoundException 方法数超64K(1K=1024,65536刚好是64K);需要分包操作 解决方案 1、在module下的build.gradle文件 ,添加multiDexEnabled true `defaultConfig {` ` ``applicationId ``"com.haxi.mh"` ` ``minSdkVersion ``15` ` ``targetSdkVersion ``26` ` ``versionCode ``1` ` ``versionName ``"1.0.0"` ` ``testInstrumentationRunner ``"android.support.test.runner.AndroidJUnitRunner"` ` ``// dex突破65535的限制` ` ``multiDexEnabled ``true` ` ``ndk {` ` ``// 设置支持的SO库架构` ` ``abiFilters ``'armeabi'` `//, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'` ` ``}` ` ``}` 2.兼容低版本(在build.gradle中 android代码库中,和defaultConfig同级) `//分包兼容低版本` `dexOptions {` ` ``javaMaxHeapSize ``"4g"` ` ``jumboMode = ``true` `}` 3.添加依赖包 `/* multidex兼容包,配合AndroidStudio实现了一个APK包含多个dex的功能 */` ` ``compile ``'com.android.support:multidex:1.0.1'` `4.让自己的Application继承MultiDexApplication类,或者在Application下重新attachBaseContext(Context base)方法,初始化 MultiDex.install(this); ` `@Override` `protected``void``attachBaseContext(Context base) {` ` ``super``.attachBaseContext(base);` ` ``MultiDex.install(base);` `}` 其他: 此外,有没有办法指定某些类被分包到主dex呢?有,在app目录下创建一个maindexlist.txt,我们在这个txt里将我们想要放在主dex中的类写进去即可,为了方便减少错误的出现,可以在在\app\build\intermediates\multi-dex\debug目录下找到了一个maindexlist.txt,注意,这个你直接在改了没用,一运行又恢复了,你要的做的是将这个maindexlist.txt复制到app目录下,在进行添加添加指定类即可, 参考: [https://www.2cto.com/kf/201712/707967.html](https://www.2cto.com/kf/201712/707967.html) [https://stackoverflow.com/questions/35225939/android-multidex-classnotfoundexception](https://stackoverflow.com/questions/35225939/android-multidex-classnotfoundexception) [http://blog.csdn.net/daitu_liang/article/details/72987378](http://blog.csdn.net/daitu_liang/article/details/72987378)

2018年2月28日 · 1 分钟 · 天边的星星

cocospod 版本更新与更新到指定版本

一、升级本地cocopod到最新版本 1、先切换gem源 gem source -a https://gems.ruby-china.org 查看是否切换成功 gem source -l 打印出*\* CURRENT SOURCES ** https://gems.ruby-china.org 就说明切换成功,如果还是官方的源, 请手动重启电脑尝试 2、接下来就可以开始升级了cocoapods了 3、然后敲下 4、剩下的就是设置pod仓库了 pod setup 重新设置下 pod仓库 二、cocopod升级到指定的指定版本 1、在终端输入:sudo gem uninstall cocoapods 显示所有的版本,输入相应的数字,删除不想要的版本 2、在终端输入:sudo gem install cocoapods -v指定版本号 输出:Fetching: cocoapods-1.1.1.gem (100%) ERROR: While executing gem … (Errno::EPERM) 3、上面的方法不行,那咱们就换一种输入:sudo gem install -n /usr/local/bin cocoapods -v 指定版本

2018年1月22日 · 1 分钟 · 天边的星星

android 使用命令查找apk

第一步:查找apk的包名 adb shell pm list package 查找所有安装的包名 adb shell pm list package -3 查找所有安装的第三方的包名 adb shell ps 查找正在执行的进程 adb shell dumpsys activity activities ,它会列出当前手机中所有正在运行的应用的详细信息,按打开顺序排列,最后打开的APK信息会放在输出的最前面。 第二步根据包名或者到安装的路径 adb shell path 包名 (例如adb shell pm path phxDroid.phxDroid) 第三步导出 adb pull apk的路径 apk导出路径 (例如 adb pull /data/app/phxDroid.phxDroid-1.apk ~/Desktop/ 导出apk到桌面)

2017年12月18日 · 1 分钟 · 天边的星星

Mac 下制作icns 图标

使用mac中自带的工具iconutil iconutil -c icns iconset文件夹 生成icns文件 例如:iconutil -c icns abc.iconset iconutil -c iconset icns文件 生成iconset文件夹,里面包含多种png图片 制作iconset文件时文件中文件名称必须是下面的命名,否则可能转换不成功 Filename Image Size (in pixels) icon_512x512@2x.png 1024 x 1024 icon_512x512.png 512 x 512 icon_256x256@2x.png 512 x 512 icon_256x256.png 256 x 256 icon_128x128@2x.png 256 x 256 icon_128x128.png 128 x 128 icon_32x32@2x.png 64 x 64 icon_32x32.png 32 x 32 icon_16x16@2x.png 32 x 32 icon_16x16.png 16 x 16 这样就可以生成icns文件,我们就可以替换mac下的app的图标了 步骤为; 1.选择应用 2.右键-点击显示简介 3.拖动生成的icns文件到简介的左上角的图标处 4.看看应用的图标是否已经替换了,~~ 记录下,防止大家走弯路~~

2017年12月5日 · 1 分钟 · 天边的星星

Android屏幕投影及反向控制原理

这一周过的是够有意思的,先停两天电,然后感冒了,然后项目出Bug了,然后发烧了,呵呵哒,赶紧只能过来写点东西压压惊。鉴于最近正好在研究Android投屏及反像控制和Android双开的技术原理,本周就先写写Android投影以及反向控制的原理了。 1 目标 Android投影屏幕到电脑 电脑端反向控制Android手机(如QQ,微信,淘宝…) 2 背景 最近在项目小组中遇到一件事,小组有时候需要演示demo供大家参考,当演示Android手机投屏时,就需要借助第三方软件进行投屏,比如说360手机管家的演示功能还有一个神器Vysor(通过Google浏览器投屏并控制手机),但是随之也会带来问题,通过反编译Vysor的Apk可以看到它是使用adb命令截屏然后通过Async网络库传输屏幕投影给后台,既然有网络操作,如果是公司比较重要的东西,万一第三方在后面偷偷保留了演示录屏(我相信这些应用应该都不会,有职业操守),然后可能就会有自己去做投屏的需求。 3 预览图 今天写的原理都是经过本人实现过的,目前PC端已经正常工作,并且可以投屏多台Android。Web端通过node.js websocket webrtc HTML实现的目前还在开发中,鉴于之前没怎么用过前端,所以写的比较慢。 目前测试实时投影在真实机上还可以。 PC端的动态截图如下。 ![](http://upload-images.jianshu.io/upload_images/2778947-8a87e728b4a3a098.gif?imageMogr2/auto-orient/strip) 时间.gif ![](http://upload-images.jianshu.io/upload_images/2778947-7599f0e5882c029e.gif?imageMogr2/auto-orient/strip) p2 ![](http://upload-images.jianshu.io/upload_images/2778947-81c7dde223025610.gif?imageMogr2/auto-orient/strip) 地图.gif 4 原理图 ![](http://upload-images.jianshu.io/upload_images/2778947-b9c17077a52e9b6a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 原理.png 5 投屏 投影屏幕,可以去传输图像也可以去传输视频,具体使用哪一种就去看你的需求。而投影图像又分为通过ADB命令去截取图像以及通过Android的ImageReader获取图像然后通过网络传输两种方式,所以投屏的实现是有很多种的,你想使用哪一种都是可以的。 图像流 现在的产品看到他们都是借助手机连线到电脑端的,通过adb直接去截取图片,这样的话就会很快,如果你只是在公司内部用,使用公司的局域网进行通信我觉得也已经够用了,因此也可以实现通过网络Socket直接去传输图像的字节码。但是现在手机分辨率可高了,因此如果你不对图像进行处理直接通过Socket传输的话那么会让PC端投屏变得很卡,因此AndroidClient可以先对图像进行压缩裁剪之后再去传输。 Android端这块我是开启了一个Service,然后通过ImageReader获取屏幕的图像,之后对图片进行裁剪压缩之后再利用Socket传输图像数据信息。其中的基本代码流程如下: `virtualDisplay = mediaProjection.createVirtualDisplay("MainScreen",width,height,dpi ,DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,imageReader.getSurface() ,null,screenHandler); imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader imageReader) { Log.i(TAG, "call onImageAvailable"); try { //如果有图片那么就获取 img = imageReader.acquireLatestImage(); if (img != null) { //图像处理 //send 数据 } } }` 视频流 起初我采用的就是图片传输,后来想想其实还是有其他方案的,其实可以通过获取Android手机的屏幕视频流通过H264进行编码进行传输给后台Server,这样可以让画面显示的更加流畅。Android Client里面有一个MediaCodec的类以及VirtualDisplay类可以去读取Android的屏幕流,然后转化为H264视频流。 ...

2017年11月29日 · 1 分钟 · 天边的星星