ElasticSearch安装与配置

ElasticSearch安装与配置 centos 7 安装 ElasticSearch 安装elasticsearch之前 先要安装java jdk 8 这里提供下载地址:链接: https://pan.baidu.com/s/1ZIJc0JWKMyLl4Iivh-YlBw 密码: gr08 `# 进入opt目录 cd /opt/ # 创建 soft文件夹 用来存储所有的软件 如果你有自己的目录,可以不创建这个目录 mkdir soft cd soft # 执行下载 ElasticSearch 这里使用目前最新的 7.10.1 如果没有wget命令 执行 yum install wget 进行安装 wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.10.1-linux-x86_64.tar.gz # 解压文件 tar -zxvf elasticsearch-7.10.1-linux-x86_64.tar.gz # 修改文件夹名称和移动目录 mv elasticsearch-7.10.1 /opt/elasticsearch # 进入elasticsearch目录 cd /opt/elasticsearch # 默认为了安全,不要使用root进行启动,这里创建一个 elasticsearch 用户和组 # 添加 elasticsearch 组 groupadd elasticsearch # 添加 elasticsearch 用户 并添加到elasticsearch组中 -g 后表示组 useradd elasticsearch -g elasticsearch # 修改文件夹拥有者 表示把elasticsearch文件夹 给elasticsearch用户和elasticsearch组 # 修改这个组之前 先退到elasticsearch的目标文件夹上一级 chown -R elasticsearch:elasticsearch elasticsearch # 修改elasticsearch配置文件 # 进入elasticsearch 的config目录 cd elasticsearch/config/ # 编辑配置文件 参考下面配置文件 vi elasticsearch.yml ## 启动 # 进入 elasticsearch/bin目录 cd bin/ # 使用elasticsearch用户进行启动 sudo -u elasticsearch ./elasticsearch -d # 查询是否启动成功 ps -ef|grep elasticsearch # 查看启动日志 tail -f /opt/elasticsearch/logs/yqzl-service.log # 验证是否成功 curl http://localhost:9200 #出现这个表示成功 :` { "name" : "yqzl-node-1", "cluster_name" : "yqzl-service", "cluster_uuid" : "3wrpP4wwTFCBHdCboBqQfQ", "version" : { "number" : "7.10.1", "build_flavor" : "default", "build_type" : "tar", "build_hash" : "1c34507e66d7db1211f66f3513706fdf548736aa", "build_date" : "2020-12-05T01:00:33.671820Z", "build_snapshot" : false, "lucene_version" : "8.7.0", "minimum_wire_compatibility_version" : "6.8.0", "minimum_index_compatibility_version" : "6.0.0-beta1" }, "tagline" : "You Know, for Search" } ` ` elasticsearch.yml `# ======================== Elasticsearch Configuration ========================= # # NOTE: Elasticsearch comes with reasonable defaults for most settings. # Before you set out to tweak and tune the configuration, make sure you # understand what are you trying to accomplish and the consequences. # # The primary way of configuring a node is via this file. This template lists # the most important settings you may want to configure for a production cluster. # # Please consult the documentation for further information on configuration options: # https://www.elastic.co/guide/en/elasticsearch/reference/index.html # # ---------------------------------- Cluster ----------------------------------- # # Use a descriptive name for your cluster: 集群名称 # cluster.name: yqzl-service # # ------------------------------------ Node ------------------------------------ # # Use a descriptive name for the node: 节点名称 # node.name: yqzl-node-1 # # Add custom attributes to the node: # #node.attr.rack: r1 # # ----------------------------------- Paths ------------------------------------ # # Path to directory where to store the data (separate multiple locations by comma): # #path.data: /path/to/data # # Path to log files: # #path.logs: /path/to/logs # # ----------------------------------- Memory ----------------------------------- # # Lock the memory on startup: # #bootstrap.memory_lock: true # # Make sure that the heap size is set to about half the memory available # on the system and that the owner of the process is allowed to use this # limit. # # Elasticsearch performs poorly when the system is swapping the memory. # # ---------------------------------- Network ----------------------------------- # # Set the bind address to a specific IP (IPv4 or IPv6): 运行访问的ip 0.0.0.0 表示都可以访问 # network.host: 0.0.0.0 # # Set a custom port for HTTP: # #http.port: 9200 # # For more information, consult the network module documentation. # # --------------------------------- Discovery ---------------------------------- # # Pass an initial list of hosts to perform discovery when this node is started: # The default list of hosts is ["127.0.0.1", "[::1]"] # #discovery.seed_hosts: ["host1", "host2"] # # Bootstrap the cluster using an initial set of master-eligible nodes: 初始化主节点 # cluster.initial_master_nodes: ["yqzl-node-1"] # # For more information, consult the discovery and cluster formation module documentation. # # ---------------------------------- Gateway ----------------------------------- # # Block initial recovery after a full cluster restart until N nodes are started: # #gateway.recover_after_nodes: 3 # # For more information, consult the gateway module documentation. # # ---------------------------------- Various ----------------------------------- # # Require explicit names when deleting indices: # #action.destructive_requires_name: true ` 安装遇到问题 `elasticsearch启动时遇到的错误 elasticsearch用户拥有的内存权限太小,至少需要262144; ...

2020年12月18日 · 3 分钟 · 天边的星星

Spring boot 集成ElasticSearch

Spring boot 集成ElasticSearch * [第一步Spring boot集成ELasticSearch][2] * [第二步Spring boot中配置ElasticSearch][3] * [第三步 创建操作的实体Bean(我创建的是UserItemBean)][4] * [第四步 使用ElasticSearch的API进行测试][5] Spring boot 集成ElasticSearch 第一步Spring boot集成ELasticSearch `<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt; &lt;scope&gt;test&lt;/scope&gt; &lt;exclusions&gt; &lt;exclusion&gt; &lt;groupId&gt;org.junit.vintage&lt;/groupId&gt; &lt;artifactId&gt;junit-vintage-engine&lt;/artifactId&gt; &lt;/exclusion&gt; &lt;/exclusions&gt; &lt;/dependency&gt; &lt;dependency&gt; &lt;groupId&gt;com.alibaba&lt;/groupId&gt; &lt;artifactId&gt;fastjson&lt;/artifactId&gt; &lt;version&gt;1.2.68&lt;/version&gt; &lt;/dependency&gt; </dependencies> ` > > Spring boot集成ElasticSearch主要依赖 > > ``` `spring-boot-starter-data-elasticsearch ` 这里需要大家特别注意自己使用的ElasticSearch的版本 是否和自己的版本一致 我这里是使用的最新的ElasticSearch 7.8.0 需要配置版本 &lt;properties&gt; &lt;java.version&gt;1.8&lt;/java.version&gt; &lt;elasticsearch.version&gt;7.8.0&lt;/elasticsearch.version&gt; &lt;/properties&gt; ### 第二步Spring boot中配置ElasticSearch {#toc_2} > 创建一个<mark>ElasticSearchConfig</mark>的类,配置在下ElasticSearch的高级客户端api > > ``` `import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Elasticsearch 配置 */ @Configuration public class ElasticSearchConfig { @Bean public RestHighLevelClient restHighLevelClient(){//配置rest客户端 RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("192.168.1.187", 9200, "http"))); return client; } } ` 注意:这里可以配置多个ElasticSearch 只需要创建多个HttpPost即可 ...

2020年6月28日 · 4 分钟 · 天边的星星

window postMessage使用Demo

使用window.open进行postmessage处理 打开新页面和监听界面的界面 `&lt;!DOCTYPE html> &lt;html lang="en"> &lt;head> &lt;meta charset="UTF-8"> &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"> &lt;title>Document&lt;/title> &lt;/head> 我是监听界面 &lt;body> &lt;script> window.onload = function(){ console.log("onload finish"); if(window.addEventListener){ console.log("onload addEventListener"); window.addEventListener('message', function (e) { console.log("addEventListener我监听到了"); // if (e.data.act == 'response') { // alert(e.data.msg.answer); // } else { // alert('未定义的消息: ' + e.data.act); // } console.log(e.data); }); }else{ window.attachEvent('onmessage', function (e) { console.log("attachEvent我监听到了"); // if (e.data.act == 'response') { // alert(e.data.msg.answer); // } else { // alert('未定义的消息: ' + e.data.act); // } console.log(e.data); }); } } var popup = window.open("indexopen.html"); popup.onload = function(){ // popup.postMessage("hello there!", "*"); console.log("popup 设置监听"); popup.addEventListener('message', function (e) { console.log("popup addEventListener我监听到了"); console.log(e.data); },false); popup.mydataFunction = function(){ console.log("自定义的函数!"); } } &lt;/script> &lt;/body> &lt;/html>` 子界面及发送postmessage界面 ...

2020年6月21日 · 2 分钟 · 天边的星星

node.js中express框架的基本使用

`express是一个基于node.js平台的,快速,开放,极简的web开发框架。 一、安装 express npm install express --save 二、简单使用 express //引入express const express = require('express'); //创建一个应用 let app = express(); //匹配GET请求路径设置回调函数 app.get('/hello', function (req, res) { res.end('hello'); }); //监听端口 app.listen(8888, function () { console.log('port : 8888'); }); 通过访问 localhost:8888/hello 我们就可以看到内容输出了。 当然 express 还支持其他的一些请求方法,比如 app.post(),app.put(),app.delete(),app.head() 等。 //引入express const express = require('express'); //创建一个应用 let app = express(); //匹配POST请求 app.post('/hello', function (req, res) { res.end('post hello'); }); //监听端口 app.listen(8888, function () { console.log('port : 8888'); }); 如果我们想要匹配所有的请求路径,可以使用通配符 * 号。 //引入express const express = require('express'); //创建一个应用 let app = express(); app.get('/hello', function (req, res) { res.end('hello'); }); //*号匹配所有路径 app.get('*', function (req, res) { res.end('not found'); }); //监听端口 app.listen(8888, function () { console.log('port : 8888'); }); express 还提供了 all() 方法,可以匹配所有请求方法。 //引入express const express = require('express'); //创建一个应用 let app = express(); //匹配所有请求方法 app.all('/hello', function (req, res) { res.end('all hello'); }); //监听端口 app.listen(8888, function () { console.log('port : 8888'); }); 三、express 中间件的概念 express中间件就是处理http请求的函数,用来完成一些特定的操作,比如登陆检查,权限控制等等。 1、一个中间件处理完请求和响应,可以把数据传递给下一个中间件。 2、在回调函数中使用 next(),就可以让请求继续向下传递。 3、通过不同路径,分别执行不同的中间件。 我们可以使用 use() ,在路由数组中添加一个中间件。注意我们设置的路由路径最终会存放在一个数组里,由上到下的把路径加入这个数组中。 //引入express const express = require('express'); //创建一个应用 let app = express(); //如果没有设置路径,则会匹配全部 app.use(function (req, res, next) { console.log('匹配全部路径'); //注意这里如果不调用next(),则请求并不会向下传递。 next(); }); app.use('/hello', function (req, res, next) { console.log('use hello'); next(); }); app.get('/hello', function (req, res, next) { console.log('get hello'); next(); }); //监听端口 app.listen(8888, function () { console.log('port : 8888'); }); next() 函数可以传入一个参数,表示错误信息,默认将执行错误中间件。 //引入express const express = require('express'); //创建一个应用 let app = express(); app.get('/hello', function (req, res, next) { console.log('get hello'); next('error!!!'); }); //注意错误处理中间件的参数是4个 app.use(function (err, req, res, next) { console.log(err); res.end(err); }); //监听端口 app.listen(8888, function () { console.log('port : 8888'); }); 四、express中的request对象 在express中对原生的req请求对象进行了扩展,添加了一些属性和方法。 //引入express const express = require('express'); //创建一个应用 let app = express(); app.get('/hello', function (req, res, next) { //主机名 res.write('req.hostname : ' + req.hostname + '\r\n'); //请求URL的路径 res.write('req.path : ' + req.path + '\r\n'); //查询字符串对象 res.write('req.query : ' + JSON.stringify(req.query) + '\r\n'); //请求的远程IP地址 res.write('req.ip : ' + req.ip + '\r\n'); //请求方法 res.write('req.method : ' + req.method + '\r\n'); res.end(); }); //监听端口 app.listen(8888, function () { console.log('port : 8888'); }); 通过 req.params 获取路径里的参数 //引入express const express = require('express'); //创建一个应用 let app = express(); app.get('/list/:key/:page/:size', function (req, res, next) { //注意,设置了多少参数,地址就需要传入多少参数 res.end(JSON.stringify(req.params)); }); //监听端口 app.listen(8888, function () { console.log('port : 8888'); }); 五、express中的response对象 express中也对原生的res对象进行了扩展,添加了一些属性和方法。 const path = require('path'); //引入express const express = require('express'); //创建一个应用 let app = express(); app.get('/send/str', function (req, res, next) { //send方法会自动判断数据类型,并进行相应的head信息设置 //如果参数是字符串,则Content-Type为 text/html res.send('hello, world'); }); app.get('/send/arr', function (req, res, next) { //如果参数是一个数组,则返回json res.send(&#91;1, 2, 3]); }); app.get('/send/obj', function (req, res, next) { //如果参数是一个对象,则返回json res.send({name: 'xiaoxu', age: 24}); }); app.get('/send/number', function (req, res, next) { //如果是一个数字,则返回相应状态码短语 res.send(404); }); app.get('/download', function (req, res, next) { //提示下载文件 res.download('./1.txt'); }); app.get('/json', function (req, res, next) { //响应json对象 res.json({name: 'xiaoxu'}); }); app.get('/jsonp', function (req, res, next) { //客户端请求时,需要带上callback=test res.jsonp('hello'); }); app.get('/redirect', function (req, res, next) { //重定向到一个地址 res.redirect('http://www.baidu.com'); }); app.get('/sendfile', function (req, res, next) { //发送一个文件 res.sendFile(path.resolve('./1.txt')); }); app.get('/sendstatus', function (req, res, next) { //发送一个状态码 res.sendStatus(302); }); //监听端口 app.listen(8888, function () { console.log('port : 8888'); }); 六、ejs模板的使用 支持express的模板有很多种,这里我们使用ejs作为模板引擎。 安装ejs: npm install ejs 使用ejs模板 const path = require('path'); const express = require('express'); //创建一个应用 let app = express(); //设置模板引擎 app.set('view engine', 'ejs'); //设置模板目录 app.set('views', path.join(__dirname, 'views')); //监听 app.listen(8888); 如果我们希望,ejs能够渲染html页面,可以使用如下 const path = require('path'); const express = require('express'); let app = express(); //设置视图引擎为html引擎 app.set('view engine', 'html'); //设置视图的路径 app.set('views', path.join(__dirname, 'views')); //配置html引擎 app.engine('html', require('ejs').__express); app.listen(8888); 渲染视图,输出内容。 const path = require('path'); const express = require('express'); let app = express(); app.set('view engine', 'html'); app.set('views', path.join(__dirname, 'views')); app.engine('html', require('ejs').__express); app.get('/hello', function (req, res, next) { //参数一表示模板的名称,会在当前项目目录下的views目录查找 //参数二表示传入模板中的数据 res.render('hello', { name: 'xiaoxu', age: 24 }); }); app.listen(8888); hello.html的代码: &lt;!doctype html> &lt;html lang="zh-CN"> &lt;head> &lt;meta charset="UTF-8"> &lt;title>Document&lt;/title> &lt;/head> &lt;body> &lt;%= name %> &lt;%= age %> &lt;/body> &lt;/html> 七、静态文件服务器 有些时候我们需要在页面上加载css,js,img等静态资源,指定存放静态资源的目录,浏览器发出非html文件请求时,服务器会到这个目录下找对应的资源。 const path = require('path'); const express = require('express'); let app = express(); app.set('view engine', 'html'); app.set('views', path.join(__dirname, 'views')); app.engine('html', require('ejs').__express); //注意express.static这个中间件是express内置的 app.use(express.static(path.join(__dirname, 'public'))); app.get('/hello', function (req, res, next) { //参数一表示模板的名称,会在当前项目目录下的views目录查找 //参数二表示传入模板中的数据 res.render('hello', { name: 'xiaoxu', age: 24 }); }); app.listen(8888); 八、使用body-parser中间件解析post过来的数据 安装body-parser npm install body-parser 使用body-parser const path = require('path'); const express = require('express'); const bodyParser = require('body-parser'); let app = express(); app.set('view engine', 'html'); app.set('views', path.join(__dirname, 'views')); app.engine('html', require('ejs').__express); //解析 application/json app.use(bodyParser.json()); //解析 application/x-www-form-urlencoded app.use(bodyParser.urlencoded({extended:false})); app.post('/form', function (req, res) { console.log(req.body); }); app.listen(8888);`

2020年4月14日 · 4 分钟 · 天边的星星

Koa2 和 Express 中间件对比

koa2 中间件 koa2的中间件是通过 async await 实现的,中间件执行顺序是“洋葱圈”模型。 中间件之间通过next函数联系,当一个中间件调用 next() 后,会将控制权交给下一个中间件, 直到下一个中间件不再执行 next() 后, 将会沿路折返,将控制权依次交换给前一个中间件。 如图: koa2 中间件实例 app.js: `const Koa = require('koa'); const app = new Koa(); // logger app.use(async (ctx, next) => { console.log('第一层 - 开始') await next(); const rt = ctx.response.get('X-Response-Time'); console.log(`&lt;span class="katex math inline">{ctx.method} -----------&lt;/span>{ctx.url} ----------- &lt;span class="katex math inline">{rt}`); console.log('第一层 - 结束') }); // x-response-time app.use(async (ctx, next) => { console.log('第二层 - 开始') const start = Date.now(); await next(); const ms = Date.now() - start; ctx.set('X-Response-Time', `&lt;/span>{ms}ms`); console.log('第二层 - 结束') }); // response app.use(async ctx => { console.log('第三层 - 开始') ctx.body = 'Hello World'; console.log('第三层 - 结束') }); app.listen(3000); ` 执行app.js后,浏览器访问 http://localhost:3000/text , 控制台输出结果: ...

2020年3月26日 · 3 分钟 · 天边的星星

Spring boot 读取properties

## Spring boot 读取properties 在开发中我们需要通过属性文件配置常用属性,例如数据库相关、日志相关、测试相关等。 自定义properties文件获取属性 application.properties获取属性 ### 自定义properties文件获取属性 使用@configurationProperties((prefix=”xxx.yyy”)) 和 @PropertySource(“classpath:xxxconfig.properties”) xxx.yyy表示的是属性文件中的前缀(有时候我们希望可以分的更清楚) 使用自定义的时候需要给类添加@Component 让Spring管理类的生命周期 在使用的地方使用@Autowired 让系统进行初始化 // PropertySource默认取application.properties // @PropertySource(value = “xxxconfig.properties”) 例如 “` @Component @ConfigurationProperties(prefix = “com.xxx”) @PropertySource(“classpath:myconfig.properties”) public class TestBean { private String host; public String getHost() { return host; } public void setHost(String host) { this.host = host; } } //使用地方 @Autowired private TestBean testBean “` ### application.properties获取属性 共有三种 参考上面自定义 只是不用设置PropertySource PropertySource默认取application.properties 使用@Value注解 使用Environment #### 使用@Value ...

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

Java AES256加密解密实现

import it.sauronsoftware.base64.Base64; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.Security; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; /** java实现AES256加密解密 依赖说明: bcprov-jdk15-133.jar:PKCS7Padding javabase64-1.3.1.jar:base64 local_policy.jar 和 US_export_policy.jar需添加到%JAVE_HOME%\jre\lib\security中(lib中版本适合jdk1.7) */ public class AES256 { public static byte[] encrypt(String content, String password) { try { //”AES”:请求的密钥算法的标准名称 KeyGenerator kgen = KeyGenerator.getInstance(“AES”); //256:密钥生成参数;securerandom:密钥生成器的随机源 SecureRandom securerandom = new SecureRandom(tohash256Deal(password)); kgen.init(256, securerandom); //生成秘密(对称)密钥 SecretKey secretKey = kgen.generateKey(); //返回基本编码格式的密钥 byte[] enCodeFormat = secretKey.getEncoded(); //根据给定的字节数组构造一个密钥。enCodeFormat:密钥内容;”AES”:与给定的密钥内容相关联的密钥算法的名称 SecretKeySpec key = new SecretKeySpec(enCodeFormat, “AES”); //将提供程序添加到下一个可用位置 Security.addProvider(new BouncyCastleProvider()); //创建一个实现指定转换的 Cipher对象,该转换由指定的提供程序提供。 //”AES/ECB/PKCS7Padding”:转换的名称;”BC”:提供程序的名称 Cipher cipher = Cipher.getInstance(“AES/ECB/PKCS7Padding”, “BC”); ...

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

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

Android WebView 与登录状态保持一致,建立SESSION会话

在登陆界面获取验证码的时候: ``` `new Thread(){ @Override public void run() { try { SharedPreferences spf = getSharedPreferences(<span class=“hljs-string”>“Cookie”</span>, Context<span class=“hljs-preprocessor”>.MODE</span>_PRIVATE)<span class=“hljs-comment”>;</span> HttpClient client = new DefaultHttpClient()&lt;span class="hljs-comment">;&lt;/span> HttpGet get = new HttpGet(Gloable&lt;span class="hljs-preprocessor">.DOLOAD&lt;/span>+&lt;span class="hljs-string">"code.gif"&lt;/span>)&lt;span class="hljs-comment">;&lt;/span> HttpResponse response = client&lt;span class="hljs-preprocessor">.execute&lt;/span>(get)&lt;span class="hljs-comment">;&lt;/span> Cookie cookie = ((AbstractHttpClient) client)&lt;span class="hljs-preprocessor">.getCookieStore&lt;/span>()&lt;span class="hljs-preprocessor">.getCookies&lt;/span>()&lt;span class="hljs-preprocessor">.get&lt;/span>(&lt;span class="hljs-number">0&lt;/span>)&lt;span class="hljs-comment">;&lt;/span> String sessionId = cookie&lt;span class="hljs-preprocessor">.getValue&lt;/span>()&lt;span class="hljs-comment">;&lt;/span> SharedPreferences&lt;span class="hljs-preprocessor">.Editor&lt;/span> editor = spf&lt;span class="hljs-preprocessor">.edit&lt;/span>()&lt;span class="hljs-comment">;&lt;/span> editor&lt;span class="hljs-preprocessor">.putString&lt;/span>(&lt;span class="hljs-string">"sessionId"&lt;/span>, sessionId)&lt;span class="hljs-comment">;&lt;/span> String cookieString = cookie&lt;span class="hljs-preprocessor">.getName&lt;/span>()+&lt;span class="hljs-string">"="&lt;/span>+cookie&lt;span class="hljs-preprocessor">.getValue&lt;/span>()+ &lt;span class="hljs-string">";domain="&lt;/span>+cookie&lt;span class="hljs-preprocessor">.getDomain&lt;/span>()&lt;span class="hljs-comment">;&lt;/span> Log&lt;span class="hljs-preprocessor">.e&lt;/span>(&lt;span class="hljs-string">"test"&lt;/span>, &lt;span class="hljs-string">"cookieString:"&lt;/span>+cookieString)&lt;span class="hljs-comment">;&lt;/span> editor&lt;span class="hljs-preprocessor">.putString&lt;/span>(&lt;span class="hljs-string">"cookieString"&lt;/span>, cookieString)&lt;span class="hljs-comment">;&lt;/span> editor&lt;span class="hljs-preprocessor">.commit&lt;/span>()&lt;span class="hljs-comment">;&lt;/span> Log&lt;span class="hljs-preprocessor">.i&lt;/span>(&lt;span class="hljs-string">"info"&lt;/span>, &lt;span class="hljs-string">"b--JSESSIONID="&lt;/span> + sessionId)&lt;span class="hljs-comment">;&lt;/span> if (response&lt;span class="hljs-preprocessor">.getStatusLine&lt;/span>()&lt;span class="hljs-preprocessor">.getStatusCode&lt;/span>() == &lt;span class="hljs-number">200&lt;/span>) { byte[] bytes = EntityUtils&lt;span class="hljs-preprocessor">.toByteArray&lt;/span>(response&lt;span class="hljs-preprocessor">.getEntity&lt;/span>())&lt;span class="hljs-comment">;&lt;/span> final Bitmap bitmap = BitmapFactory&lt;span class="hljs-preprocessor">.decodeByteArray&lt;/span>(bytes, &lt;span class="hljs-number">0&lt;/span>, bytes&lt;span class="hljs-preprocessor">.length&lt;/span>)&lt;span class="hljs-comment">;&lt;/span> runOnUiThread(new Runnable() { public void run() { Drawable drawable = new BitmapDrawable(bitmap)&lt;span class="hljs-comment">;&lt;/span> iv_showCode&lt;span class="hljs-preprocessor">.setBackgroundDrawable&lt;/span>(drawable)&lt;span class="hljs-comment">;&lt;/span> } })&lt;span class="hljs-comment">;&lt;/span> } } catch (ClientProtocolException e) { // TODO Auto-generated catch block e&lt;span class="hljs-preprocessor">.printStackTrace&lt;/span>()&lt;span class="hljs-comment">;&lt;/span> } catch (IOException e) { // TODO Auto-generated catch block e&lt;span class="hljs-preprocessor">.printStackTrace&lt;/span>()&lt;span class="hljs-comment">;&lt;/span> } } }&lt;span class="hljs-preprocessor">.start&lt;/span>()&lt;span class="hljs-comment">;&lt;/span>` 在webview加载url之前: ``` `SharedPreferences spf = getSharedPreferences(&lt;span class="hljs-string">"Cookie"&lt;/span>, Context&lt;span class="hljs-preprocessor">.MODE&lt;/span>_PRIVATE)&lt;span class="hljs-comment">;&lt;/span> CookieSyncManager&lt;span class="hljs-preprocessor">.createInstance&lt;/span>(this)&lt;span class="hljs-comment">;&lt;/span> CookieManager cookieManager = CookieManager&lt;span class="hljs-preprocessor">.getInstance&lt;/span>()&lt;span class="hljs-comment">;&lt;/span> String cookieString = spf&lt;span class="hljs-preprocessor">.getString&lt;/span>(&lt;span class="hljs-string">"cookieString"&lt;/span>, &lt;span class="hljs-string">""&lt;/span>)&lt;span class="hljs-comment">;&lt;/span> cookieManager&lt;span class="hljs-preprocessor">.setCookie&lt;/span>(url, cookieString)&lt;span class="hljs-comment">;&lt;/span> CookieSyncManager&lt;span class="hljs-preprocessor">.getInstance&lt;/span>()&lt;span class="hljs-preprocessor">.sync&lt;/span>()&lt;span class="hljs-comment">;&lt;/span> webview&lt;span class="hljs-preprocessor">.loadUrl&lt;/span>(url)&lt;span class="hljs-comment">;&lt;/span>` android客户端通过httpClient或者httpUrlConnection进行登录后,为了把登录状态同步到webView中,这时需要进行cookie的同步 一.cookie同步方式 下面是登录线程: public class LoginThread extends Thread{ private Handler loginHandler; public LoginThread(Handler loginHandler) { this.loginHandler = loginHandler; } @Override public void run() { List cookieLst = new ArrayList(); HttpParams httpParams = new BasicHttpParams(); ConnManagerParams.setMaxTotalConnections(httpParams, 5); ConnManagerParams.setTimeout(httpParams, 151000); HttpConnectionParams.setSoTimeout(httpParams, 101000); HttpConnectionParams.setTcpNoDelay(httpParams, true); HttpPost httpPost = new HttpPost(“http://192.168.1.107/cookie/login.php”); List nvPairs = new ArrayList(); nvPairs.add(new BasicNameValuePair(“name”, “lisi”)); nvPairs.add(new BasicNameValuePair(“age”,”22″)); nvPairs.add(new BasicNameValuePair(“gender”, “男”)); ...

2016年10月11日 · 4 分钟 · 天边的星星

android获取web服务器端session并验证登陆

传统网页实现用户登陆一般采用session或cookie记录用户基本信息又或者两者结合起来使用。android也可以采用session实现用户登陆验证并记录用户登陆状态时的基本信息,session是在服务器端的;而类似cookie的记录方式,则可以在客户端采用xml文件记录用户基本信息,重要数据则可以加密存放客户端。android实现的session登陆功能与网页请求不同的是,网页形式的一次成功的登陆请求后,再点击其他页面时,session一直是存在的,在一定时间内是有效的;而采用android客户端请求的一次成功登陆后,再次发送新的请求,则会产生新的session,而不是原来的。这就需要记录session的id号,并在整个请求过程中都记录并传递这个id号,才能保证session的一致性。 以获取php session为例,主要思路实现分为客户端与服务器端3个步骤。 附件:源码下载 1.)客户端(ANDROID) 建立一个名为GetWebSession的android项目,编写GetWebSession.java,LoginSuccessActivity.java,GetUserInfoActivity.java三个activity类。 GetWebSession.java主要是实现布局界面以及发送用户名和密码到php服务器端验证,如果验证成功则跳转到LoginSuccessActivity.java类。GetWebSession.java主要涉及到与服务器端连接请求,对从服务器端返回的json数据(如用户id,session等)进行解析,并存入HashMap,传递到LoginSuccessActivity.java 代码如下: [view plain](http://www.yoyong.com/archives/178#)[copy to clipboard](http://www.yoyong.com/archives/178#)[print](http://www.yoyong.com/archives/178#)[?](http://www.yoyong.com/archives/178#) - <span class="keyword">package</span> com.login.main; - <span class="keyword">import</span> java.io.IOException; - <span class="keyword">import</span> java.io.UnsupportedEncodingException; - <span class="keyword">import</span> java.util.ArrayList; - <span class="keyword">import</span> java.util.HashMap; - <span class="keyword">import</span> java.util.List; - <span class="keyword">import</span> org.apache.http.HttpEntity; - <span class="keyword">import</span> org.apache.http.HttpResponse; - <span class="keyword">import</span> org.apache.http.client.ClientProtocolException; - <span class="keyword">import</span> org.apache.http.client.entity.UrlEncodedFormEntity; - <span class="keyword">import</span> org.apache.http.client.methods.HttpPost; - <span class="keyword">import</span> org.apache.http.impl.client.DefaultHttpClient; - <span class="keyword">import</span> org.apache.http.message.BasicNameValuePair; - <span class="keyword">import</span> org.apache.http.protocol.HTTP; - <span class="keyword">import</span> org.apache.http.util.EntityUtils; - <span class="keyword">import</span> org.json.JSONException; - <span class="keyword">import</span> org.json.JSONObject; - <span class="keyword">import</span> android.app.Activity; - <span class="keyword">import</span> android.content.Context; - <span class="keyword">import</span> android.content.Intent; - <span class="keyword">import</span> android.os.Bundle; - <span class="keyword">import</span> android.view.View; - <span class="keyword">import</span> android.view.View.OnClickListener; - <span class="keyword">import</span> android.widget.Button; - <span class="keyword">import</span> android.widget.EditText; - <span class="keyword">import</span> android.widget.Toast; - <span class="keyword">public</span> <span class="keyword">class</span> GetWebSession <span class="keyword">extends</span> Activity { - <span class="comment">/** Called when the activity is first created. */</span> - <span class="keyword">private</span> EditText user; - <span class="keyword">private</span> EditText password; - <span class="keyword">private</span> Button loginBtn; - <span class="keyword">private</span> Button logoutBtn; - <span class="comment">//主要是记录用户会话过程中的一些用户的基本信息</span> - <span class="keyword">private</span> HashMap<String, String> session =<span class="keyword">new</span> HashMap<String, String>(); - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> onCreate(Bundle savedInstanceState) { - <span class="keyword">super</span>.onCreate(savedInstanceState); - setContentView(R.layout.main); - user=(EditText)findViewById(R.id.user); - password=(EditText)findViewById(R.id.password); - loginBtn=(Button)findViewById(R.id.loginBtn); - loginBtn.setOnClickListener(loginClick); - logoutBtn=(Button)findViewById(R.id.logoutBtn); - logoutBtn.setOnClickListener(logoutClick); - } - OnClickListener loginClick=<span class="keyword">new</span> OnClickListener() { - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> onClick(View v) { - <span class="comment">// TODO Auto-generated method stub</span> - <span class="keyword">if</span>(checkUser()){ - Toast.makeText(v.getContext(), <span class="string">&#8220;用户登录成功!&#8221;</span>, Toast.LENGTH_SHORT).show(); - Context context = v.getContext(); - Intent intent = <span class="keyword">new</span> Intent(context, - LoginSuccessActivity.<span class="keyword">class</span>); - <span class="comment">//传递session参数,在用户登录成功后为session初始化赋值,即传递HashMap的值</span> - Bundle map = <span class="keyword">new</span> Bundle(); - map.putSerializable(<span class="string">&#8220;sessionid&#8221;</span>, session); - intent.putExtra(<span class="string">&#8220;session&#8221;</span>, map); - context.startActivity(intent); <span class="comment">// 跳转到成功页面</span> - } - <span class="keyword">else</span> - Toast.makeText(v.getContext(), <span class="string">&#8220;用户验证失败!&#8221;</span>, Toast.LENGTH_SHORT).show(); - } - }; - OnClickListener logoutClick=<span class="keyword">new</span> OnClickListener() { - <span class="annotation">@Override</span> - <span class="keyword">public</span> <span class="keyword">void</span> onClick(View v) { - <span class="comment">// TODO Auto-generated method stub</span> - System.exit(<span class="number"></span>); - } - }; - <span class="keyword">private</span> <span class="keyword">boolean</span> checkUser(){ - String username=user.getText().toString(); - String pass=password.getText().toString(); - DefaultHttpClient mHttpClient = <span class="keyword">new</span> DefaultHttpClient(); - HttpPost mPost = <span class="keyword">new</span> HttpPost(<span class="string">&#8220;http://10.0.2.2/web/php/login.php&#8221;</span>); - <span class="comment">//传递用户名和密码相当于</span> - <span class="comment">//http://10.0.2.2/web/php/login.php?username=&#8221;&password=&#8221;</span> - List<BasicNameValuePair> pairs = <span class="keyword">new</span> ArrayList<BasicNameValuePair>(); - pairs.add(<span class="keyword">new</span> BasicNameValuePair(<span class="string">&#8220;username&#8221;</span>, username)); - pairs.add(<span class="keyword">new</span> BasicNameValuePair(<span class="string">&#8220;password&#8221;</span>, pass)); - <span class="keyword">try</span> { - mPost.setEntity(<span class="keyword">new</span> UrlEncodedFormEntity(pairs, HTTP.UTF_8)); - } <span class="keyword">catch</span> (UnsupportedEncodingException e) { - <span class="comment">// TODO Auto-generated catch block</span> - e.printStackTrace(); - } - <span class="keyword">try</span> { - HttpResponse response = mHttpClient.execute(mPost); - <span class="keyword">int</span> res = response.getStatusLine().getStatusCode(); - <span class="keyword">if</span> (res == <span class="number">200</span>) { - HttpEntity entity = response.getEntity(); - <span class="keyword">if</span> (entity != <span class="keyword">null</span>) { - String info = EntityUtils.toString(entity); - System.out.println(<span class="string">&#8220;info&#8212;&#8212;&#8212;&#8211;&#8220;</span>+info); - <span class="comment">//以下主要是对服务器端返回的数据进行解析</span> - JSONObject jsonObject=<span class="keyword">null</span>; - <span class="comment">//flag为登录成功与否的标记,从服务器端返回的数据</span> - String flag=<span class="string">&#8220;&#8221;</span>; - String name=<span class="string">&#8220;&#8221;</span>; - String userid=<span class="string">&#8220;&#8221;</span>; - String sessionid=<span class="string">&#8220;&#8221;</span>; - <span class="keyword">try</span> { - jsonObject = <span class="keyword">new</span> JSONObject(info); - flag = jsonObject.getString(<span class="string">&#8220;flag&#8221;</span>); - name = jsonObject.getString(<span class="string">&#8220;name&#8221;</span>); - userid = jsonObject.getString(<span class="string">&#8220;userid&#8221;</span>); - sessionid = jsonObject.getString(<span class="string">&#8220;sessionid&#8221;</span>); - } <span class="keyword">catch</span> (JSONException e) { - <span class="comment">// TODO Auto-generated catch block</span> - e.printStackTrace(); - } - <span class="comment">//根据服务器端返回的标记,判断服务端端验证是否成功</span> - <span class="keyword">if</span>(flag.equals(<span class="string">&#8220;success&#8221;</span>)){ - <span class="comment">//为session传递相应的值,用于在session过程中记录相关用户信息</span> - session.put(<span class="string">&#8220;s_userid&#8221;</span>, userid); - session.put(<span class="string">&#8220;s_username&#8221;</span>, name); - session.put(<span class="string">&#8220;s_sessionid&#8221;</span>, sessionid); - <span class="keyword">return</span> <span class="keyword">true</span>; - } - <span class="keyword">else</span>{ - <span class="keyword">return</span> <span class="keyword">false</span>; - } - } - <span class="keyword">else</span>{ - <span class="keyword">return</span> <span class="keyword">false</span>; - } - } - } <span class="keyword">catch</span> (ClientProtocolException e) { - <span class="comment">// TODO Auto-generated catch block</span> - e.printStackTrace(); - } <span class="keyword">catch</span> (IOException e) { - <span class="comment">// TODO Auto-generated catch block</span> - e.printStackTrace(); - } - <span class="keyword">return</span> <span class="keyword">false</span>; - } - } LoginSuccessActivity.java主要获取php的session唯一的标识id以及用户的一些基本信息,session id则作为本次用户登录状态在服务器的唯一标识,即确定用户的唯一状态进行相关操作。LoginSuccessActivity.java类的方法与GetWebSession.java类似。其主要功能是获取session id后再次发送session id到服务器进行验证,根据封装的session数据验证用户操作权限等。 代码如下: ...

2016年8月22日 · 14 分钟 · 天边的星星