Retrofit2 使用FastJson作为Converter

Retortfit2 Retrofit是由Square公司出品的针对于Android和Java的类型安全的Http客户端,网络服务基于OkHttp 。 个人觉得更为准确的说法是,Retrofit是OkHttp的一个包装工具类,可以更加方便的调用Restful API。 Retrofit2 默认提供的Converter Gson: com.squareup.retrofit2:converter-gson Jackson: com.squareup.retrofit2:converter-jackson Moshi: com.squareup.retrofit2:converter-moshi Protobuf: com.squareup.retrofit2:converter-protobuf Wire: com.squareup.retrofit2:converter-wire Simple XML: com.squareup.retrofit2:converter-simplexml Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars 下面就来讲解如何扩展Converter 首先创建FastJsonConverterFactory 类,并继承Converter.Factory,重写其中的responseBodyConverter方法与requestBodyConverter方法。 import java.lang.annotation.Annotation; import java.lang.reflect.Type; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Converter; import retrofit2.Retrofit; public class FastJsonConverterFactory extends Converter.Factory{ public static FastJsonConverterFactory create() { return new FastJsonConverterFactory(); } /** 需要重写父类中responseBodyConverter,该方法用来转换服务器返回数据 */ @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { return new FastJsonResponseBodyConverter<>(type); } /** ...

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

Mac安装mysql 8.0.13步骤

下载Mysql 下载地址 <span class="md-link" spellcheck="false">[https://dev.mysql.com/downloads/mysql/](https://dev.mysql.com/downloads/mysql/)</span> 选择tar压缩文件 配置环境 解压文件放到你希望的目录中 配置环境变量 例如: vi ~/.bash_profile export PATH=$PATH:/usr/local/mysql/bin 使其生效:source ~/.bash_profile 初始化mysql mysql 8.0 执行 scripts 目录下的 mysql_install_db 脚本完成一些默认的初始化(创建默认配置文件、授权表等) cd /usr/local/mysql sudo scripts/mysql_install_db <span class="md-image md-img-loaded" contenteditable="false" data-src="https://ws2.sinaimg.cn/large/006tNbRwly1fx9xmzyphej31m6074jti.jpg">![](https://ws2.sinaimg.cn/large/006tNbRwly1fx9xmzyphej31m6074jti.jpg)</span> <span class="">为了快速启动和停止mysql 我配置了别名</span> <span class="md-image md-img-loaded" contenteditable="false" data-src="https://ws4.sinaimg.cn/large/006tNbRwly1fx9xkpfh6oj30x603wwfa.jpg">![](https://ws4.sinaimg.cn/large/006tNbRwly1fx9xkpfh6oj30x603wwfa.jpg)</span> <span class="">检测mysql的status 执行mysqlstatus</span> <span class="">启动mysql 执行 </span> 出现这个错误 ...

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

使用BottomNavigationView底部导航栏、添加数量角标提醒

度娘了一圈发现基本上都是TabLayout或者其他的导航栏添加角标,所以写这篇博客记录下来。 先来看下实现的效果图: 代码也是很简单的 BottomNavigationMenuView中的每一个Tab就是一个FrameLayout,所以我们可以在上面随意添加View、这样也就可以实现我们的角标了。 //获取整个的NavigationView BottomNavigationMenuView menuView = (BottomNavigationMenuView) navigationView.getChildAt(0); //这里就是获取所添加的每一个Tab(或者叫menu), View tab = menuView.getChildAt(3); BottomNavigationItemView itemView = (BottomNavigationItemView) tab; //加载我们的角标View,新创建的一个布局 View badge = LayoutInflater.from(this).inflate(R.layout.menu_badge, menuView, false); //添加到Tab上 itemView.addView(badge); menu_badge.xml file: 这里这个布局的大小,其实也就是每一个Tab的大小了。把显示数量的TextView水平居中,这样也就刚好在Tab的中间了(剩下就看你自己想怎么放了….) <?xml version=”1.0″ encoding=”utf-8″?> <LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android” android:layout_width=”match_parent” android:layout_height=”match_parent” android:orientation=”vertical”> <TextView android:id=”@+id/tv_msg_count” android:layout_width=”15dp” android:layout_height=”15dp” android:layout_gravity=”center” android:layout_marginLeft=”@dimen/dp_10″ android:layout_marginTop=”@dimen/dp_3″ android:background=”@drawable/bg_red_circle_10″ android:gravity=”center” android:textColor=”@color/white” android:textSize=”@dimen/sp_12″ android:visibility=”gone” /> 设置角标的数量 TextView count = (TextView) badge.findViewById(R.id.tv_msg_count); count.setText(String.valueOf(1)); //如果没有消息,不需要显示的时候那只需要将它隐藏即可 count.setVisibility(View.GONE); - 在点击事件中完成各个menu的点击事件,实现Fragment的替换,另外三个点击事件内容类似 <div class="top-box hide"> <div class="alert-info"> </div> </div> ``` `<span class=“hljs-comment”>// 去掉menu大于3个后的动画</span> BottomNavigationViewHelper.disableShiftMode(bottomNavigationView); ...

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

新时期的Node.js入门总结

基础汇总 require引用的文件中不要有内部调用,否则可能有未知隐患(内存泄漏、或者直接崩溃) Buffer 是Node特有的数据类型(固有属性、不需要require),主要用来处理二进制数据(Buffer通常表现为十六进制的字符串),新Node API Buffer()方法为Deprecated,推荐使用Buffer.form来初始化一个Buffer对象 <blockquote> buffer.toString([encoding],[start],[end]) buffer 支持编码类型 ASCII Base64 Binary Hex UTF-8 UTF-16LE/UCS-2 Buffer一个常用的场景就是HTTP的POST请求,例如 var body = &#8221; req.setEncoding(&#8216;utf-8&#8217;); req.on(&#8216;data&#8217;,function(chumk){ body += chunk; }) req.on(&#8216;end&#8217;,function(){ }) </blockquote> <span class="">Fill System 是Node中使用最为频繁的模块之一,该模块提供了读写文件的能力,是借助于底层的linuv的C++ API实现的。</span> <blockquote contenteditable="true"> 常用API readFile writeFile stat <span class="">fs.stat获取文件的状态(可以用来判断文件还是文件夹)</span> </blockquote> HTTP服务 是Node的核心模块。 <blockquote contenteditable="true"> var http = require(&#8220;http&#8221;) var server = http.createServer(function(req,res){ // req.url 获取访问的路径 //req.method 请求方法 req.on(&#8220;data&#8221;,function(chunk){ }).on(&#8220;end&#8221;,function(){ }); res.writeHead(200,{&#8216;Content-Type&#8217;:&#8217;text/plain&#8217;,&#8221;Content-Length&#8221;:Buffer.byteLength(body)}); res.end(&#8216;Hello Node!!!&#8217;);//每个HTTP请求的最后都会被调用,当客户端的请求完成后,开发者应该调用该方法结束HTTP请求 }); server.on(&#8220;connection&#8221;,function(req,res){ }); server.on(&#8220;request&#8221;,function(req,res){ }) server.listen(3000) //处理异常 process.on(&#8220;uncaughtException&#8221;,function(){ }) // req.headesr 表示head信息 POST上传文件 表单类型设置为: enctype=&#8221;multipart/form-data&#8221; 服务器处理上传文件通常基于stream来实现,这里比较流行的第三方库formidable function dealUpload(req,res){ var form = new formidable.IncomingForm(); <span class="">form.eekpExtension = true;</span> form.uploadDir =__dirname; from.parse(req,function(err,fields.files){ if(err)throw err; console.log(fields); console.log(files); res.writeHead(200,{&#8220;Content-type&#8221;:&#8217;text/plain&#8217;}); <span class="">res.end(&#8216;upload finished&#8217;);</span> <span class="">})</span> } HTTP 客户端服务 var http=require(&#8220;http&#8221;); http.get(&#8220;<span class="md-link" spellcheck="false">[http://www.baidu.com](http://www.baidu.com)</span>&#8220;,function(res){ var statusCode = res.statusCode; if(statusCode==200){ res.on(&#8220;data&#8221;,function(chunk){ }); res.on(&#8220;end&#8221;,function(){ }); res.on(&#8220;error&#8221;,function(e){ <span class="">})</span> <span class="">}</span> }); //代理服务器 var http =require(&#8220;http&#8221;); var url = require(&#8220;url&#8221;); http.createServer(function(req,res){ var url = req.url.substring(1,req.url.length);//去掉最前面的/ var proxyRequest = http.request(url,function(pres){ res.writhHead(pres.statusCode,pres.headers); pres.on(&#8220;data&#8221;,function(data){ <span class="">res.write(data);</span> }); pres.on(&#8220;end&#8221;,function(){ <span class="">res.end();</span> <span class="">})</span> }); req.on(&#8220;data&#8221;,function(data){ <span class="">proxyRequest.write(data);</span> }); req.on(&#8220;end&#8221;,function(){ <span class="">proxyReques.end();</span> <span class="">});</span> <span class="">}).listen(8080);</span> </blockquote> <span class="">WebSocekt (比较出名的WebSocket模块还有Socket.IO)</span> <blockquote contenteditable="true"> Node 实现WebSocket var WebSocketServer = require(&#8220;ws&#8221;).Server; var wss = new WebScoketServer({port:3304}); wss.on(&#8220;connection&#8221;,function(ws){ ws.on(&#8220;message&#8221;,function(message){ <span class="">console.log(message);</span> }); <span class="">ws.send(&#8220;Node Hello WebSocket&#8221;);</span> <span class="">})</span> </blockquote> Events 在Node中只定义了一个类EventEmitter <blockquote contenteditable="true"> var eventEmitter = require(&#8220;events&#8221;); var myEmitter = new eventEmitter(); <span class="">myEmitter.on(&#8220;begin&#8221;,function(){//注册一个begin事件</span> <span class="">console.log(&#8220;begin&#8221;);</span> <span class="">});</span> <span class="">myEmitter.emit(&#8220;begin&#8221;);//触发begin事件</span> </blockquote> 多进程服务 <blockquote contenteditable="true"> child_process模块中包括很多创建子进程的方法,包括fork、spawn、exec、execFile <span class="">Cluster是Node 0.6之后新增模块(Cluster可以看做是做了封装的child_process模块)</span> </blockquote> <span class="">Proces对象是一个全局的对象,每个Node进程都有独立的process对象,该对象中存储了当前的环境变量,也定义了一些事件</span> <blockquote contenteditable="true"> process.getuid();//用户id process.argv;//Node的命令行参数列表 process.pid;//进程id process.cwd();//当前目录 process.versoin;//Node版本 <span class="">process.env;//</span> </blockquote> Timer setTimeout setInterval nvm <blockquote contenteditable="true"> nvm install version 安装某个版本的node nvm use veresion 切换到某个版本 nvm ls 列出当前安装的所有的Node版本 let关键字 会创建一个块级作用域 const变量不可以再被修改 </blockquote> 函数 <blockquote contenteditable="true"> 参数可以设置默认值 function gred(x=&#8221;a&#8221;,y=&#8221;b&#8221;){ } Spread运算符(&#8230;)展开运算符 var ab=[&#8220;ab&#8221;,&#8221;cd&#8221;] gred(..ab); 箭头函数(ES6) var func= a=>a;等价于 var func = function(a){ <span class="">return a;</span> } 多个参数 var func=(a,b)=>{ <span class="">console.log(a,b);</span> <span class="">}</span> </blockquote> Promise 异步处理 <blockquote contenteditable="true"> var promis = new Promise(function(resolve.reject){ //执行相关异步操作 //resolve(data) // reject(err) }),then(res=>{}).catch(err=>{}); <span class="">promise.all 多个promise需要执行封装为一个</span> </blockquote> 回调的终点&#8211;async/await <blockquote contenteditable="true"> node 7.6.0之后原生支持 var asyncReadFile = async function(){ var result1 = await readFile(&#8216;a.txt&#8217;); var result 2 = await readFile(&#8216;b.txt&#8217;); console.log(result1); <span class="">console.log(result2);</span> <span class="">}</span> </blockquote> Koa2 构建web站点 <blockquote contenteditable="true"> koa-static 静态文件服务 koa-router 路由服务 koa-bodyparse </blockquote> MongoDB (Mongoose) <blockquote contenteditable="true"> npm install mongoose var mongoose = require(&#8220;mongoose&#8221;) mongoose.connect(&#8220;mongodb://xxx/test&#8221;); var db = mongose.connection; db.on(&#8220;error&#8221;,console.error.bind(console.&#8217;conection error:&#8217;)) db.on(&#8216;open&#8217;.function(callback){ }) var loginSchema = new mongoose.Schema({ username:String, <span class="">password:String</span> }) <span class="">var login = db.model(&#8220;login&#8221;,loginSchema,&#8221;login&#8221;)//第一个名称是创建实例使用的名称,第二个是表结构参数,第三个是数据库显示的结合的名称不填的话默认是实例名称的复数s</span> var user1 = new login({username:&#8221;zhang&#8221;,password:&#8217;test&#8217;}) user1.save(function(err){ <span class="">if(err) return handleError(err)</span> <span class="">})</span> </blockquote> Redis <blockquote> npm install redis var redis = require(&#8220;redis&#8221;) var client = redis.createClient(&#8216;6379&#8242;,&#8217;127.0.0.1&#8217;) client.on(&#8220;error&#8221;,function(error){ }) client.on(&#8220;ready&#8221;,funciont(){ }) <span class="">client.set(&#8220;name&#8221;,&#8221;zhang&#8221;,redis.print)</span> client.get(&#8220;name&#8221;,function(err,reply){ }) client.publish(&#8216;test&#8217;,&#8221;hello,Node&#8221;) client.subscribe(&#8216;test&#8217;) client.on(&#8220;message&#8221;,function(channel,message){ <span class="">})</span> </blockquote> Localtunnel <blockquote contenteditable="true"> localtunnel是一个有名的第三方模块 localtunnel.me </blockquote> 爬虫 <blockquote contenteditable="true"> robot.txt是爬虫默认规则 PHelper cheerio request.js //node第三方的HTTP请求 <span class="md-link" spellcheck="false">[https://github.com/request/request](https://github.com/request/request)</span> cheerio 网页解析 <span class="md-link" spellcheck="false">[https://github.com/cheeriojs/cheerio](https://github.com/cheeriojs/cheerio)</span> <span class="">selenium</span> MongoDB存储数据 Redis消息队列 </blockquote> 测试与调试 <blockquote contenteditable="true"> 使用Assert模块 Jasmine Ava.js nyc代码覆盖率 Travis <span class="">node-inspector v8-inspector</span> </blockquote> package.json <blockquote contenteditable="true"> package.json常用字段 name项目名称 verion项目版本号 scripts项目不同阶段的命令 version字段说明 version:完全匹配 <span spellcheck="false">`&gt;`</span>version 大于这个版本 <span spellcheck="false">`&gt;=`</span>version 大于登录这个版本 ~version 非常接近这个版本 ^version与这个版本不兼容 1.2.x 这个符号1.2.x的版本 x是任意数字 *或者“” 任何版本都可以 <span class="md-expand">version1-version2 版本在version1和version2之间(包括version1和version2)</span> </blockquote>

2018年11月2日 · 3 分钟 · 天边的星星

Android studio项目java文件过大导致的问题记录

最近在做一个项目的时候,出现了一个很奇怪的问题,我的java文件前面出现了一个奇怪的蓝色j, 这使得我的代码其他地方无法对它进行调用,所以程序一运行,调用到它的地方就会报错,(因为我的这个代码是用protobuffer协议自动生成的java文件,比较大,大概有5M多)。 后来上网查了一下,发现是IDEA对能关联的文件大小做了限制,主要是为了保护内存,默认值为2500kb或者5000kb,对于一般的java文件也够用了,只是这里我用protocbuf生成的java文件过大,所以要对它这个默认的值进行修改。 找到Android studio 的安装目录下bin 下的idea.properties文件(默认安装路径:C:\Program Files\Android\Android Studio\bin)。 打开这个文件,对里面的idea.max.intellisense.filesizej进行设置,保存( 单位是kb) 然后重启Android studio即可。

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

node.js之热更新重启nodemon

安装nodemon: 修改package.json “scripts”: { “start”: “node ./bin/www”, “run”:”./node_modules/.bin/nodemon ./bin/www” }, 启动命令 npm run run nodemon最大的作用就是项目重启,触发重启的事件就是系统文件改变了。因为我们开发过程中经常要对系统文件进行修改,我们每次修改后的通常做法是先退出程序然后再开启程序,这样很劳神费力改一个字都要做这两个步骤,现在好了,我们文件随便改,一改他就重启,这两个步骤就被省略了会大大提高我们的编码效率和减少debug时间。 转自:https://blog.csdn.net/lbPro0412/article/details/82454163

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

解决 Mac OSX 无法识别 Android 设备

在终端输入命令,进入用户目录 ``` $ cd ~ ``` 测试adb,开启终端,输入命令,显示出”Android Debug Bridge version 1.0.39″ 为配置成功 ``` $ adb version ``` 第二步工作是:创建、修改 adb_usb.ini 文件,这里也分为2小步 打开终端,输入命令,查看设备信息 ``` $ system_profiler SPUSBDataType ``` 1.2 得到自己对应的设备信息,其中Vendor ID 中的信息,需要保留,等下使用 ``` SAMSUNG_Android: Product ID: 0x6753 Vendor ID: 0x05e4 (Samsung Electronics Co., Ltd.) Version: 6.00 Serial Number: 7d0076027c174055 Speed: Up to 420 Mb/sec Manufacturer: SAMSUNG Location ID: 0x13200000 / 10 Current Available (mA): 400 Current Required (mA): 86 Extra Operating Current (mA): 0 </div> 2.创建、修改adb_usb.ini文件,可以在终端输入命令,也可以查找文件 2.1 查找adb_usb.ini文件 2.1.1输入命令 <div class="cnblogs_code"> vi ~/.android/adb_usb.ini ...

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

Android动态化框架App Bundles

摘要: Android App Bundles 在今年的Google I/O大会上,Google向 Android 引入了新 App 动态化框架(即Android App Bundle,缩写为AAB),与Instant App不同,AAB是借助Split Apk完成动态加载,使用AAB动态下发方式,可以大幅度减少应用体积。 ## Android App Bundles 在今年的Google I/O大会上,Google向 Android 引入了新 App 动态化框架(即Android App Bundle,缩写为AAB),与Instant App不同,AAB是借助Split Apk完成动态加载,使用AAB动态下发方式,可以大幅度减少应用体积。现在只须在 Android Studio 中构建一个应用束 (app bundle),就可以将应用所需的全部内容 (适用于所有设备) 都涵盖在内:所有语言、所有设备屏幕大小、所有硬件架构。 下面是Dynamic Delivery示意效果图: 不过要想体验Dynamic Delivery,需要先下载 Android Studio 3.2 学习Android App Bundles可以将它和Split Apks来对比学习。 Split Apks split apks是Android 5.0开始提供多apk构建机制,借助split apks可以将一个apk基于ABI和屏幕密度两个维度拆分城多个apk,这样可以有效减少apk体积。当用户下载应用程序安装包时,只会包含对应平台的so和资源。因为需要google play支持,所以国内就没戏了。针对不同cpu架构问题,国内应用开发商大部分都会将so文件只放在armabi目录下,如此做虽然可以有效减少包体积,但可能带来性能问题。split apks详细的内容可以访问下面的链接:[https://link.zhihu.com/?target=https%3A//developer.android.com/studio/build/configure-apk-splits%3Fauthuser%3D2](https://link.zhihu.com/?target=https%3A//developer.android.com/studio/build/configure-apk-splits%3Fauthuser%3D2) Split Apks的运作原理有点类似于Android的组件化,安装应用程序时,首先安装base apk,然后安装split apks。为了说明splite apks运作原理,来看一下Android 5.0关于splite apks的源码。 打开[ApplicationInfo](http://androidxref.com/5.0.0_r2/xref/frameworks/base/core/java/android/content/pm/ApplicationInfo.java)类中,可以看到如下信息: `/** * Full paths to zero &lt;span class="hljs-keyword">or&lt;/span> more &lt;span class="hljs-keyword">split&lt;/span> APKs that, &lt;span class="hljs-keyword">when&lt;/span> combined with the base * APK &lt;span class="hljs-keyword">defined&lt;/span> in {@link &lt;span class="hljs-comment">#sourceDir}, form a complete application.&lt;/span> *&lt;span class="hljs-regexp">/ public String[] splitSourceDirs; /&lt;/span>** * Full path to the publicly available parts of {@link &lt;span class="hljs-comment">#splitSourceDirs},&lt;/span> * including resources &lt;span class="hljs-keyword">and&lt;/span> manifest. This may be different from * {@link &lt;span class="hljs-comment">#splitSourceDirs} if an application is forward locked.&lt;/span> *&lt;span class="hljs-regexp">/ public String[] splitPublicSourceDirs;&lt;/span>` [LoadeApk](http://androidxref.com/5.0.0_r2/xref/frameworks/base/core/java/android/app/LoadedApk.java)中有PathClassLoader和Resources创建过程。LoadedApk#mClassLoader是PathClassLoader实例引用,接着看PathClassLoader的创建过程。 zipPaths = new ArrayList<>(); final ArrayList<String> libPaths = new ArrayList<>(); ....... zipPaths.add(mAppDir); //将split apk路径追加到zipPaths中 if (mSplitAppDirs != null) { Collections.addAll(zipPaths, mSplitAppDirs); } libPaths.add(mLibDir); ...... final String zip = TextUtils.join(File.pathSeparator, zipPaths); final String lib = TextUtils.join(File.pathSeparator, libPaths); ...... //如果mSplitAppDirs不为空,则zip将包含split apps所有路径。 mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib, mBaseClassLoader); StrictMode.setThreadPolicy(oldPolicy); } else { if (mBaseClassLoader == null) { mClassLoader = ClassLoader.getSystemClassLoader(); } else { mClassLoader = mBaseClassLoader; } } return mClassLoader; } }" data-snippet-id="ext.b0abcb7003a3d76aeb1ae260fba2afe7" data-snippet-saved="false" data-codota-status="done">`&lt;span class="hljs-function">&lt;span class="hljs-keyword">public&lt;/span> ClassLoader &lt;span class="hljs-title">getClassLoader&lt;/span>&lt;span class="hljs-params">()&lt;/span> &lt;/span>{ &lt;span class="hljs-keyword">synchronized&lt;/span> (&lt;span class="hljs-keyword">this&lt;/span>) { &lt;span class="hljs-keyword">if&lt;/span> (mClassLoader != &lt;span class="hljs-keyword">null&lt;/span>) { &lt;span class="hljs-keyword">return&lt;/span> mClassLoader; } &lt;span class="hljs-keyword">if&lt;/span> (mIncludeCode && !mPackageName.equals(&lt;span class="hljs-string">"android"&lt;/span>)) { ...... &lt;span class="hljs-keyword">final&lt;/span> ArrayList&lt;String&gt; zipPaths = &lt;span class="hljs-keyword">new&lt;/span> ArrayList&lt;&gt;(); &lt;span class="hljs-keyword">final&lt;/span> ArrayList&lt;String&gt; libPaths = &lt;span class="hljs-keyword">new&lt;/span> ArrayList&lt;&gt;(); ....... zipPaths.add(mAppDir); &lt;span class="hljs-comment">//将split apk路径追加到zipPaths中&lt;/span> &lt;span class="hljs-keyword">if&lt;/span> (mSplitAppDirs != &lt;span class="hljs-keyword">null&lt;/span>) { Collections.addAll(zipPaths, mSplitAppDirs); } libPaths.add(mLibDir); ...... &lt;span class="hljs-keyword">final&lt;/span> String zip = TextUtils.join(File.pathSeparator, zipPaths); &lt;span class="hljs-keyword">final&lt;/span> String lib = TextUtils.join(File.pathSeparator, libPaths); ...... &lt;span class="hljs-comment">//如果mSplitAppDirs不为空,则zip将包含split apps所有路径。&lt;/span> mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib, mBaseClassLoader); StrictMode.setThreadPolicy(oldPolicy); } &lt;span class="hljs-keyword">else&lt;/span> { &lt;span class="hljs-keyword">if&lt;/span> (mBaseClassLoader == &lt;span class="hljs-keyword">null&lt;/span>) { mClassLoader = ClassLoader.getSystemClassLoader(); } &lt;span class="hljs-keyword">else&lt;/span> { mClassLoader = mBaseClassLoader; } } &lt;span class="hljs-keyword">return&lt;/span> mClassLoader; } }` 在创建PathClassLoader时,dex文件路径包含base app和split apps路径,LoadedApk#mResources是Resources实例引用,Resources的源码如下: `&lt;span class="hljs-function">&lt;span class="hljs-keyword">public&lt;/span> Resources &lt;span class="hljs-title">getResources&lt;/span>(&lt;span class="hljs-params">ActivityThread mainThread&lt;/span>) &lt;/span>{ &lt;span class="hljs-keyword">if&lt;/span> (mResources == &lt;span class="hljs-literal">null&lt;/span>) { mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, &lt;span class="hljs-literal">null&lt;/span>, &lt;span class="hljs-keyword">this&lt;/span>); } &lt;span class="hljs-keyword">return&lt;/span> mResources; }` 可以发现:split apks资源路径(LoadedApk#mSplitResDirs)也会被增加至Resources中。 Android App Bundles 下面再来看Android App Bundles,Android App Bundle 支持模块化,通过Dynamic Delivery with split APKs,将一个apk拆分成多个apk,按需加载(包括加载C/C++ libraries),这样开发者可以随时按需交付功能,而不是仅限在安装过程中。 Android App Bundle 通常会包括以下几个文件: - Base Apk:首次安装的apk,公共代码和资源,所以其他的模块都基于Base Apk; - Configuration APKs:native libraries 和适配当前手机屏幕分辨率的资源; - Dynamic feature APKs:不需要在首次安装就加载的模块。 ![这里写图片描述](https://img-blog.csdn.net/20180516221843565) AAB并不是一个插件化框架,它利用的是Android Framework提供的split apks技术来完成的,而所有安装split apk工作均是通过IPC交由google play完成。 具体使用时,在Android Studio新增一项module——Dynamic Feature Module。 在创建dynamic_feature时,有两个选项是默认勾选的,当然我们也可以更改其状态。 ...

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

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 分钟 · 天边的星星

typora使用

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