首页 文章 精选 留言 我的

精选列表

搜索[基础搭建],共10000篇文章
优秀的个人博客,低调大师

学习Hadoop大数据基础框架

什么是大数据?进入本世纪以来,尤其是2010年之后,随着互联网特别是移动互联网的发展,数据的增长呈爆炸趋势,已经很难估计全世界的电子设备中存储的数据到底有多少,描述数据系统的数据量的计量单位从MB(1MB大约等于一百万字节)、GB(1024MB)、TB(1024GB),一直向上攀升,目前,PB(等于1024TB)级的数据系统已经很常见,随着移动个人数据、社交网站、科学计算、证券交易、网站日志、传感器网络数据量的不断加大,国内拥有的总数据量早已超出 ZB(1ZB=1024EB,1EB=1024PB)级别。传统的数据处理方法是:随着数据量的加大,不断更新硬件指标,采用更加强大的CPU、更大容量的磁盘这样的措施,但现实是:数据量增大的速度远远超出了单机计算和存储能力提升的速度。而“大数据”的处理方法是:采用多机器、多节点的处理大量数据方法,而采用这种新的处理方法,就需要有新的大数据系统来保证,系统需要处理多节点间的通讯协调、数据分隔等一系列问题。总之,采用多机器、多节点的方式,解决各节点的通讯协调、数据协调、计算协调问题,处理海量数据的方式,就是“大数据”的思维。其特点是,随着数据量的不断加大,可以增加机器数量,水平扩展,一个大数据系统,可以多达几万台机器甚至更多。 Hadoop最初主要包含分布式文件系统HDFS和计算框架MapReduce两部分,是从Nutch中独立出来的项目。在2.0版本中,又把资源管理和任务调度功能从MapReduce中剥离形成YARN,使其他框架也可以像MapReduce那样运行在Hadoop之上。与之前的分布式计算框架相比,Hadoop隐藏了很多繁琐的细节,如容错、负载均衡等,更便于使用。Hadoop也具有很强的横向扩展能力,可以很容易地把新计算机接入到集群中参与计算。在开源社区的支持下,Hadoop不断发展完善,并集成了众多优秀的产品如非关系数据库HBase、数据仓库Hive、数据处理工具Sqoop、机器学习算法库Mahout、一致性服务软件ZooKeeper、管理工具Ambari等,形成了相对完整的生态圈和分布式计算事实上的标准。大快的大数据通用计算平台(DKH),已经集成相同版本号的开发框架的全部组件。如果在开源大数据框架上部署大快的开发框架,需要平台的组件支持如下:数据源与SQL引擎:DK.Hadoop、spark、hive、sqoop、flume、kafka数据采集:DK.hadoop数据处理模块:DK.Hadoop、spark、storm、hive机器学习和AI:DK.Hadoop、sparkNLP模块:上传服务器端JAR包,直接支持搜索引擎模块:不独立发布大快大数据平台(DKH),是大快公司为了打通大数据生态系统与传统非大数据公司之间的通道而设计的一站式搜索引擎级,大数据通用计算平台。传统公司通过使用DKH,可以轻松的跨越大数据的技术鸿沟,实现搜索引擎级的大数据平台性能。 DKH,有效的集成了整个HADOOP生态系统的全部组件,并深度优化,重新编译为一个完整的更高性能的大数据通用计算平台,实现了各部件的有机协调。因此DKH相比开源的大数据平台,在计算性能上有了高达5倍(最大)的性能提升。 DKH,更是通过大快独有的中间件技术,将复杂的大数据集群配置简化至三种节点(主节点、管理节点、计算节点),极大的简化了集群的管理运维,增强了集群的高可用性、高可维护性、高稳定性。 DKH,虽然进行了高度的整合,但是仍然保持了开源系统的全部优点,并与开源系统100%兼容,基于开源平台开发的大数据应用,无需经过任何改动,即可在DKH上高效运行,并且性能会有最高5倍的提升。 DKH,更是集成了大快的大数据一体化开发框架(FreeRCH), FreeRCH开发框架提供了大数据、搜索、自然语言处理和人工智能开发中常用的二十多个类,通过总计一百余种方法,实现了10倍以上的开发效率的提升。 DKH的SQL版本,还提供了分布式MySQL的集成,传统的信息系统,可无缝的实现面向大数据和分布式的跨越。DKH标准平台技术构架图

优秀的个人博客,低调大师

Scala学习笔记(2)-基础语法

秉着简洁而不失完整的的原则,这章只把Scala主要语法和与Java不同的内容列出来,以后章节也如此,内容以简洁为主,太长了自己都被吓着了,也没耐心看下去。 1.Scala解释器使用,这里不做介绍,有IDE这玩意一般用不着。 2.基本数据类型 Java原始类型在scala包里都有对应的类。例如,scala.Boolean对应着Java的boolean。scala.Float对应着Java的float。当你把你的Scala代码编译成Java字节码,Scala编译器将使用Java的原始类型以便获得其带来的性能益处。 3.定义变量 Scala有两种变量,val和var。val类似于Java里的final变量。一旦初始化了,val就不能再赋值了。与之对应的,var如同Java里面的非final变量。var可以在它生命周期中被多次赋值。 val msg1=”hello scala” var msg2=”hello world” 仔细看上面定义的2个变量好像都没有类型声明,这是怎么回事呢?这就要说到Scala的类型推断(type inference),因为你用了一个字符串来初始化msg变量,Scala推断msg的类型是String。你也可以显式地定义类型,有些时候也应该这么做。显式的类型标注不但可以确保Scala编译器推断你倾向的类型,还有助于以后代码的阅读。 val msg : String=”hello scala” 与Java不同的是类型声明在变量后面,用一个”:”分隔。 4.定义函数 上面已经定义了变量,现在开始学习怎么定义一个函数 def max(x: Int, y: Int): Int = { //定义函数 if (x > y) x else y } max(3,7) //调用函数 自己对照看与 java函数有什么不同,函数体只有一句的大括号可以省略写成 : def max(x: Int, y: Int) = if (x > y) x else y 结果类型也去掉了,编译器类型推断可得出,尽管如此带上结果类型也是一个好习惯,便于其他人阅读代码。 Scala的Unit结果类型指的是函数没有返回有用的值。Scala的Unit类型比较接近Java的void类型,而且实际上Java里每一个返回void的方法都被映射为Scala里返回Unit的方法。因此结果类型为Unit的方法,仅仅是为了它们的副作用而运行 注:java里的i++和++i在scala里是行不通的。 本文来自云栖社区合作伙伴“开源中国” 本文作者:山海经 原文链接

优秀的个人博客,低调大师

java并发(一):线程基础

线程的创建很简单,一般是集成Thread类或者实现Runnable接口,我就不细说了。然后,要牢记多线程的3大特性:多线程的三个特性:原子性、可见性、有序性 原子性:是指一个操作是不可中断的。即使是多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。比如,对于一个静态全局变量int i,两个线程同时对它赋值,线程A给他赋值为1,线程B给他赋值为-1。那么不管这两个线程以何种方式。何种步调工作,i的值要么是1,要么是-1.线程A和线程B之间是没有干扰的。这就是原子性的一个特点,不可被中断。 可见性:是指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。显然,对于串行来说,可见性问题是不存在的。 有序性:在并发时,程序的执行可能会出现乱序。给人的直观感觉就是:写在前面的代码,会在后面执行。有序性问题的原因是因为程序在执行时,可能会进行指令重排,重排后的指令与原指令的顺序未必一致。 而共享变量的写操作出错,最重要的是原子性,一般多线程的问题主要抓住这个。 线程安全问题 一般多线程编程都会遇到线程安全的问题,线程安全总体来说是因为多个线程竞争共享资源造成的。比如: public class Test{ private int num = 0; public void add(int value){ this.num = this.num + value; } } 两个线程分别加了2和3到count变量上,两个线程执行结束后count变量的值应该等于5。如果两个线程同时执行这个对象的add()方法,会造成这种现象:线程A先读到num为0,此时恰好线程B也读到num为0,然后A,B同时执行加2和加3的操作,如果A先赋值num为2,然后B又赋值num为3,会造成最后结果为3;或者反过来,造成num为2,使得最后的结果无法预料。 如果线程并没有共享资源,那么多线程执行的代码是安全的,比如:类方法中局部变量或者局部对象引用 public class Test{ public void add(int value){ int num = 0; String a = new String("aa"); num = num + value; } } 还有一种安全的方法,就是每个线程都是执行同一个类不同对象的方法,虽然代码相同,但是不同的对象空间,也不会出现问题,如servlet。 线程状态 线程的状态实现通过 Thread.State 常量类实现,有 6 种线程状态:new(新建)、runnnable(可运行)、blocked(阻塞)、waiting(等待)、time waiting (定时等待)和 terminated(终止)。状态转换图如下: 线程状态流程大致如下: 线程创建后,进入 new 状态 调用 start 或者 run 方法,进入 runnable 状态 JVM 按照线程优先级及时间分片等执行 runnable 状态的线程。开始执行时,进入 running 状态 如果线程执行 sleep、wait、join,或者进入 IO 阻塞等。进入 wait 或者 blocked 状态 线程执行完毕后,线程被线程队列移除。最后为 terminated 状态。 ThreadLocal ThreadLocal与线程同步无关,它虽然提供了一种解决多线程环境下成员变量的问题,但是它并不是解决多线程共享变量的问题。 它的API介绍如下: 该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。 所以ThreadLocal与线程同步机制不同,线程同步机制是多个线程共享同一个变量,而ThreadLocal是为每一个线程创建一个单独的变量副本,故而每个线程都可以独立地改变自己所拥有的变量副本,而不会影响其他线程所对应的副本。可以说ThreadLocal为多线程环境下变量问题提供了另外一种解决思路。 ThreadLocal定义了四个方法: get():返回此线程局部变量的当前线程副本中的值。 initialValue():返回此线程局部变量的当前线程的“初始值”。 remove():移除此线程局部变量当前线程的值。 set(T value):将此线程局部变量的当前线程副本中的值设定为指定值。 除了这四个方法,ThreadLocal内部还有一个静态内部类ThreadLocalMap,该内部类才是实现线程隔离机制的关键,get()、set()、remove()都是基于该内部类操作。ThreadLocalMap提供了一种用键值对方式存储每一个线程的变量副本的方法,key为当前ThreadLocal对象,value则是对应线程的变量副本。 对于ThreadLocal需要注意的有两点: ThreadLocal实例本身是不存储值,它只是提供了一个在当前线程中找到副本值得key。 是ThreadLocal包含在Thread中,而不是Thread包含在ThreadLocal中,有些小伙伴会弄错他们的关系。 下图是Thread、ThreadLocal、ThreadLocalMap的关系 ThreadLocal示例 package com.xushu.multi; public class Test{ private static ThreadLocal<Integer> count = new ThreadLocal<Integer>(){ // 实现initialValue() @Override protected Integer initialValue() { return 0; //这里返回了一个0 } }; public int nextSeq(){ count.set(count.get() + 1); return count.get(); } private static class SeqThread implements Runnable{ private Test te; SeqThread(Test te) { this.te = te; } @Override public void run() { for(int i = 0; i < 3; i++){ System.out.println(Thread.currentThread().getName() + " seqCount :" + te.nextSeq()); } } } public static void main(String[] args) { Test te = new Test(); Thread t1 = new Thread(new SeqThread(te)); Thread t2 = new Thread(new SeqThread(te)); Thread t3 = new Thread(new SeqThread(te)); Thread t4 = new Thread(new SeqThread(te)); t1.start(); t2.start(); t3.start(); t4.start(); } } 可以看出,每个线程都有自己的一个变量副本,所以从根本上避免了读同一个变量。但是,如果在initialValue()方法中,如果return的是一个共有变量,那就是所有的线程都访问同一个变量了,所以ThreadLocal就失效了。这篇文章有解析。 ThreadLocal源码解析 ThreadLocal虽然解决了这个多线程变量的复杂问题,但是它的源码实现却是比较简单的。ThreadLocalMap是实现ThreadLocal的关键,我们先从它入手。 ThreadLocalMap ThreadLocalMap其内部利用Entry来实现key-value的存储,如下: static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } 从上面代码中可以看出Entry的key就是ThreadLocal,而value就是值。同时,Entry也继承WeakReference,所以说Entry所对应key(ThreadLocal实例)的引用为一个弱引用(关于弱引用这里就不多说了,感兴趣的可以关注这篇博客Java 理论与实践: 用弱引用堵住内存泄漏) ThreadLocalMap的源码稍微多了点,我们就看两个最核心的方法getEntry()、set(ThreadLocal> key, Object value)方法。set(ThreadLocal> key, Object value) private void set(ThreadLocal<?> key, Object value) { ThreadLocal.ThreadLocalMap.Entry[] tab = table; int len = tab.length; // 根据 ThreadLocal 的散列值,查找对应元素在数组中的位置 int i = key.threadLocalHashCode & (len-1); // 采用“线性探测法”,寻找合适位置 for (ThreadLocal.ThreadLocalMap.Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); // key 存在,直接覆盖 if (k == key) { e.value = value; return; } // key == null,但是存在值(因为此处的e != null),说明之前的ThreadLocal对象已经被回收了 if (k == null) { // 用新元素替换陈旧的元素 replaceStaleEntry(key, value, i); return; } } // ThreadLocal对应的key实例不存在也没有陈旧元素,new 一个 tab[i] = new ThreadLocal.ThreadLocalMap.Entry(key, value); int sz = ++size; // cleanSomeSlots 清楚陈旧的Entry(key == null) // 如果没有清理陈旧的 Entry 并且数组中的元素大于了阈值,则进行 rehash if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } 这个set()操作和我们在集合了解的put()方式有点儿不一样,虽然他们都是key-value结构,不同在于他们解决散列冲突的方式不同。集合Map的put()采用的是拉链法,而ThreadLocalMap的set()则是采用开放定址法(具体请参考散列冲突处理系列博客)。掌握了开放地址法该方法就一目了然了。 set()操作除了存储元素外,还有一个很重要的作用,就是replaceStaleEntry()和cleanSomeSlots(),这两个方法可以清除掉key == null 的实例,防止内存泄漏。在set()方法中还有一个变量很重要:threadLocalHashCode,定义如下: private final int threadLocalHashCode = nextHashCode(); 从名字上面我们可以看出threadLocalHashCode应该是ThreadLocal的散列值,定义为final,表示ThreadLocal一旦创建其散列值就已经确定了,生成过程则是调用nextHashCode(): private static AtomicInteger nextHashCode = new AtomicInteger(); private static final int HASH_INCREMENT = 0x61c88647; private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); } nextHashCode表示分配下一个ThreadLocal实例的threadLocalHashCode的值,HASH_INCREMENT则表示分配两个ThradLocal实例的threadLocalHashCode的增量,从nextHashCode就可以看出他们的定义。 private Entry getEntry(ThreadLocal<?> key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); } 由于采用了开放定址法,所以当前key的散列值和元素在数组的索引并不是完全对应的,首先取一个探测数(key的散列值),如果所对应的key就是我们所要找的元素,则返回,否则调用getEntryAfterMiss(),如下: private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) { ThreadLocal<?> k = e.get(); if (k == key) return e; if (k == null) expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } return null; } 这里有一个重要的地方,当key == null时,调用了expungeStaleEntry()方法,该方法用于处理key == null,有利于GC回收,能够有效地避免内存泄漏。 get() 返回当前线程所对应的线程变量 public T get() { // 获取当前线程 Thread t = Thread.currentThread(); // 获取当前线程的成员变量 threadLocal ThreadLocalMap map = getMap(t); if (map != null) { // 从当前线程的ThreadLocalMap获取相对应的Entry ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") // 获取目标值 T result = (T)e.value; return result; } } return setInitialValue(); } 首先通过当前线程获取所对应的成员变量ThreadLocalMap,然后通过ThreadLocalMap获取当前ThreadLocal的Entry,最后通过所获取的Entry获取目标值result。 getMap()方法可以获取当前线程所对应的ThreadLocalMap,如下: ThreadLocalMap getMap(Thread t) { return t.threadLocals; } set(T value) 设置当前线程的线程局部变量的值。 public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } 获取当前线程所对应的ThreadLocalMap,如果不为空,则调用ThreadLocalMap的set()方法,key就是当前ThreadLocal,如果不存在,则调用createMap()方法新建一个,如下: void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } initialValue() 返回该线程局部变量的初始值。 protected T initialValue() { return null; } 该方法定义为protected级别且返回为null,很明显是要子类实现它的,所以我们在使用ThreadLocal的时候一般都应该覆盖该方法。该方法不能显示调用,只有在第一次调用get()或者set()方法时才会被执行,并且仅执行1次。 remove() 将当前线程局部变量的值删除。 public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); } 该方法的目的是减少内存的占用。当然,我们不需要显示调用该方法,因为一个线程结束后,它所对应的局部变量就会被垃圾回收。 参考文献 1.并发编程网

优秀的个人博客,低调大师

Android基础回顾(二)| 关于Activity

参考书籍:《第一行代码》 第二版 郭霖 开发工具:AndroidStudio 3.2 Stable Channel 如有错漏,请批评指出! Activity 定义:Activity是Android的四大组件之一,它是一种可以包含用户界面的组件,主要用于和用户进行交互。 手动创建一个Activity的三个步骤 step 1:创建Activity 在项目目录的 app\src\main\java\包名 右键->New->Activity->Empty Activity 创建Activity 先取消Generate Layout File选项的勾选,输入Activity Name ,然后点击 Finish 按钮。这样就完成了第一步。 补充说明: 勾选Generate Layout File表示自动为这个Activity创建一个对应的布局文件(默认勾选)。 勾选Launcher Activity表示自动将这个Activity设置为当前项目的主Activity(每个项目只能有一个主Activity,主Activity即启动app时显示的第一个Activity)。 勾选Backwards Compatibility(AppCompat)表示会为项目启用向下兼容的模式(默认勾选)。 Step 2:创建和加载布局文件 在项目目录的 app\src\main\res\layout 右键->New->XML->Layout XML File 创建布局文件 接下来输入Layout File Name,Root Tags 表示布局文件的根布局,这里默认LinearLayout(线性布局),后面会介绍关于布局的内容。 然后在NewActivity.java文件中为这个活动加载我们创建的布局(添加圈出的代码): 加载布局 其实就是将我们创建的Activity和布局文件关联起来,这样我们打开这个Activity 时显示的就是activity_new这个布局文件所编写的内容。 Step3:注册Activity 打开项目目录中 app\src\main\AndroidManifest.xml 文件,在这里为我们创建的Activity进行注册(实际上,在第一步我们创建这个Activity时就已经自动注册了)。 注册Activity 看起来这一步似乎是多余的,这是因为Android Studio自动帮我们注册了Activity。但是,如果我们的Activity不是通过上面的步骤创建的,而是从别的地方复制到项目中来的,那么我们就需要谨记要自己在AndroidManifest文件中为这个活动进行注册,否则就会出错。 这样,我们就完成了手动创建一个Activity的流程。实际上,在第一步我们只要勾选上Generate Layout File 选项,后面两步AndroidStudio都会自动为我们完成。 补充说明: 关于主Activity,在AndroidManifest.xml文件(上图)中我们可以看到,在MainActivity的标签下,有这样几行代码: <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> 实际上,在<activity>标签内部加入<intent-filter>标签,并添加上面的两句声明,就是将这个Activity指定为主Activity。我们可以在创建Activity时勾选上Launcher Activity,Android Studio就会自动为我们配置这个Activity为主Activity,当然,也可以自己在需要指定为主Activity对应的注册标签<activity>中添加上面的代码。 Toast的使用 Toast 是Android系统提供的一种非常好的提醒方式,在程序中可以使用它将一些短小的信息通知给用户,这些信息会在一段时间后自动消失,并且不会占用任何屏幕空间。 Toast的用法非常简单,通过静态方法makeText()创建出一个Toast对象,然后调用show()方法将Toast显示出来就可以了。 @Override public void onClick(View v) { switch (v.getId()){ case R.id.show_toast_button: Toast.makeText(SecondActivity.this, "You clicked button", Toast.LENGTH_SHORT).show(); break; default: break; } } 上面的代码表示通过点击我们指定的Button触发Toast弹出,我们可以看到makeText()方法有三个参数。 第一个参数是Context,即Toast要求的上下文,我们暂且只需要知道Context是一个抽象类,而Activity是它的子类,因此我们指定的SecondActivity.this就是一个Context对象。 第二个参数是我们要显示的提示信息。 第三个参数是Toast显示的时长,有两个内置常量Toast.LENGTH_SHORT和Toast.LENGTH_LONG可以选择。 效果图如下: 效果图.gif Menu的使用 由于手机屏幕空间十分有限,因此Android提供了Menu,用于展示菜单的同时,也不占用过多屏幕空间。 使用方法: step1:创建菜单布局 首先在res目录下新建一个menu文件夹,用于存放Menu布局文件,右键res->New->Directory,输入文件夹名menu;然后右键menu->New-Menu resource file,输入文件名,点击OK完成。 创建Menu布局文件 接下来根据需要创建菜单项,这里我们创建三个菜单项,代码如下: <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/add_item" android:title="@string/add"/> <item android:id="@+id/del_item" android:title="@string/del"/> <item android:id="@+id/modify_item" android:title="@string/modify"/> </menu> <item>标签就是用来创建具体的某个菜单项; android:id标签是用来给这个菜单项指定一个唯一的标识符; android:title标签是给这个菜单项指定一个名称。 step2:为当前活动创建菜单——重写onCreateOptionsMenu()方法 由于我们要给SecondActivity添加Menu,因此我们在SecondActivity.java文件中重写onCreateOptionsMenu()方法。 @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.second, menu); return true; } 通过getMenuInflater()方法获取到MenuInflater对象,再调用它的inflate()方法,给当前Activity创建菜单。inflate()方法接收两个参数: 第一个参数 用来指定第一步创建的布局文件; 第二个参数用于指定我们的菜单项添加到那个Menu对象中,这里自然是onCreateOptionsMenu()方法传入的menu对象,表示当前Activity的Menu。 返回值为true表示允许菜单显示;否则菜单不显示。 step3:为菜单定义响应事件——重写onOptionsItemSelected()方法 在SecondActivity中重写onOptionsItemSelected()方法,通过item.getItenId()方法来判断我们点击的是哪个菜单项,并为其添加逻辑处理,这里我们弹出一个Toast。 @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case R.id.add_item: Toast.makeText(SecondActivity.this, "添加", Toast.LENGTH_SHORT).show(); break; case R.id.del_item: Toast.makeText(SecondActivity.this, "删除", Toast.LENGTH_SHORT).show(); break; case R.id.modify_item: Toast.makeText(SecondActivity.this, "修改", Toast.LENGTH_SHORT).show(); break; default: break; } return true; } 效果图如下: 效果图 使用Intent实现活动间跳转 显式Intent 显式Intent的使用十分简单,直接上代码: @Override public void onClick(View v) { Intent intent = null; switch (v.getId()){ default: break; case R.id.ll_button: intent = new Intent(MainActivity.this,FirstActivity.class); startActivity(intent); break; case R.id.l2_button: intent = new Intent(MainActivity.this,SecondActivity.class); startActivity(intent); break; } } 首先,我们直接new一个Intent对象,构造方法接收两个参数,第一个参数是上下文,因为当前Activity是MainActivity,所以我们传入MainActivity.this,第二个参数指定想要启动的目标活动,这里实际上有两个button,我们分别传入FirstActivity.class和SecondActivity.class,所以点击两个button时会分别跳转到FirstActivity和SecondActivity。下面看效果: 效果图.gif 隐式Intent 相比于显式Intent,隐式Intent则含蓄了很多,它并明确指出我们要启动那个Activity,而是通过指定一系列action和category等信息,然后由系统进行分析,继而启动合适的活动。 在这里我用两个Activity进行对比,通过下面的步骤对要启动的Activity进行配置,并实现隐式启动: step1:为Activity配置action和category 在AndroidManifest文件中,我们给ThirdActivity和NewActivity分别配置<action>标签和<category>标签: <activity android:name=".ThirdActivity" android:label="@string/thirdactivity"> <intent-filter> <action android:name="com.example.aboutactivity.MY_ACTION"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> <activity android:name=".NewActivity" android:label="@string/new_activity"> <intent-filter> <action android:name="com.example.aboutactivity.MY_ACTION"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.MY_CATEGORY"/> </intent-filter> </activity> 我们给ThirdActivity指定了一个<action>标签和一个<category>标签,给NewActivity指定了一个<action>标签和两个<category>标签,下面我们通过Intent其它的构造方法来隐式指定对应的标签。 step2:给Intent添加<action>和<category>标签 @Override public void onClick(View v) { Intent intent = null; switch (v.getId()){ case R.id.l3_button: intent = new Intent("com.example.aboutactivity.MY_ACTION"); startActivity(intent); break; case R.id.l4_button: intent = new Intent("com.example.aboutactivity.MY_ACTION"); intent.addCategory("android.intent.category.MY_CATEGORY"); startActivity(intent); break; default: break; } } 从上面的代码可以看出来,我们给第一个intent添加了“com.example.aboutactivity.MY_ACTION”这个action,给第二个intent添加了“com.example.aboutactivity.MY_ACTION”这个action和“android.intent.category.MY_CATEGORY”这个category,细心的话可能会注意到,两个intent添加的action是相同的,并且第一个intent没有添加category,这是因为“android.intent.category.DEFAULT”这个category是默认category,会自动添加。而且因为我们给第二个intent添加了额外的category标签,因此两个Activity的action和category可以区分开来,这一点是为了说明,我们需要给不同的Activity指定可以区分的<action>标签和<category>标签,否则系统识别不出我们要启动的Activity。下面看效果吧! 隐式Intent.gif 从上面的效果图我们可以看出,由于两个Activity都具有相同的<action>和默认的<category>所以当我们点击ThirdActivity这个button时,系统询问我们要启动哪个Activity,我想这个现象在我们使用手机的时候也遇到过,而由于NewActivity具有自己独有的<category>标签“android.intent.category.MY_CATEGORY”,因此我们点击NewActivity这个bitton时,系统准确的启动了NewActivity,这就是隐式Intent的奇妙之处了! Activity之间的数据传递 向下一个Activity传递数据 前面我们知道Intent可以用来启动Activity,其实通过Intent我们还可以向下一个Activity传递数据,Intent提供了一系列putExtra()方法的重载,我们可以把想要传递的数据暂存在Intent中,启动另一个Activity后,我们只需要再将数据取出来,就可以了。接下来我们结合Toast以及前面创建好的MainActivity和SecondActivity来做一个演示。 step1:在第一个Activity中将数据暂存到Intent中 首先我们就在MainActivity中将数据暂存到Intent中,代码如下: @Override public void onClick(View v) { Intent intent = null; switch (v.getId()){ case R.id.l2_button: intent = new Intent(MainActivity.this,SecondActivity.class); intent.putExtra("Integer",1024); intent.putExtra("String","Hello World!"); startActivity(intent); break; default: break; } } 数据以键值对的形式暂存在Intent中,除了基本数据类型,还可以传递数组、字符串以及实现了serializable和Parcelable接口的对象。 step2:在启动的Activity中将数据取出 要取出数据,我们首先要通过getIntent()方法获取到用于启动SecondActivity的Intent对象,然后调用对应数据类型的get方法将数据取出。代码如下: @Override public void onClick(View v) { switch (v.getId()){ case R.id.show_toast_button: Intent intent = getIntent(); int num = 0; String str = null; if(intent != null){ num = intent.getIntExtra("Integer", 0); str = intent.getStringExtra("String"); } Toast.makeText(SecondActivity.this, str+"\t"+num , Toast.LENGTH_SHORT).show(); break; default: break; } } 我们在SecondActivity里面通过一个button触发Toast将传递过来的数据展示出来,下面看效果: 向下一个Activity传递数据.gif 返回数据给上一个活动 向下一个Activity传递数据十分简单,接下来说一说如何返回数据给上一个Activity。我们可以模拟这样一个场景:在前面我们通过MainActivity启动了SecondActivity,并同时向其传递了一些数据,那么假设我们在SecondActivity中收到数据时,要返回一个接收到数据的确认消息给MainActivity该怎么实现呢?Activity中有一个startActivityForResult()方法,显然这就是我们所需要的。 step1:通过startActivityFprResult()方法启动Activity 我们来修改MainActivity中的代码: @Override public void onClick(View v) { Intent intent = null; switch (v.getId()){ case R.id.l2_button: intent = new Intent(MainActivity.this,SecondActivity.class); intent.putExtra("Integer",1024); intent.putExtra("String","Hello World!"); startActivityForResult(intent,1024); break; default: break; } } startActivityForResult()方法接收两个参数,第一个是intent,第二个是请求码,用于在后面判断数据的来源。我们先往下看。 step2:在启动的Activity中添加返回数据的逻辑 我们首先在SecondActivity中添加一个button,然后为其添加点击事件。在这里我们要构建一个Intent用于传递数据,将要传递的数据存放到Intent中,然后调用setResult()方法。代码如下: @Override public void onClick(View v) { switch (v.getId()){ case R.id.data_back_button: Intent intent = new Intent(); intent.putExtra("Confirm","收到数据"); setResult(RESULT_OK, intent); finish(); break; default: break; } } 上面的逻辑就是将要返回的数据存放在Intent中,然后调用setResult()方法将数据返回给上一个Activity,setResult()方法接收两个参数,第一个参数用于向上一个Activity返回处理结果,一般只用RESULT_OK和RESULT_CANCELED这两个值。然后调用finish()方法将当前Activity结束,返回上一个Activity。当然,触发条件是点击刚才定义的Button。 step3:获取返回的数据 既然数据已经返回到MainActivity了,并且我们现在也回到MainActivity里面了,那么我们怎么知道返回的数据呢?因为我们是通过startActivityForResult()方法启动的SecondActivity,所以当我们再次回到MainActivity时,会回调onActivityResult()方法。也就是说,通过重写onActivityResult()方法,我们可以对返回的数据进行处理。下面看代码: @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { switch (requestCode){ case 1024: if (resultCode == RESULT_OK && data != null ){ String str = data.getStringExtra("Confirm"); Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show(); } break; default: break; } } onActivityResult()方法的三个参数分别是我们前面传入的请求码、返回数据时传入的处理结果和携带返回数据的Intent对象。可以看到,前面我们提到的请求码和结果码都派了用场,下面看效果: 向上一个Activity返回数据.gif Activity实际场景应用 判断当前是在哪个Activity 当我们在看别人的项目时,经常会遇上这么一个问题:这个app这么多页面,究竟每一个对应哪个Activity呢?其实想要解决这个问题并不难,下面我们来学习一个小技巧。 step1:创建一个BaseActivity类 为什么是BaseActivity类呢?因为这个BaseActivity并不是一个要显示出来的Activity,它只是作为我们Activity的基类,提供一些方法,从而实现我们的目的,并且让它继承自AppCompatActivity。右键包名->New->JavaClass 创建BaseActivity 然后重写onCreate()方法,这里为了演示效果,我们用Toast弹出当前Activity实例的类名。(在实际项目中我们用Log.d()打印出来就行了)代码如下: @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Toast.makeText(BaseActivity.this, getClass().getSimpleName(), Toast.LENGTH_SHORT).show(); } step2:让项目中所有的Activity都继承自BaseActivity 由于我们的BaseActivity是继承自AppCompatActivity的,因此这个修改不会带来什么影响。我们直接看运行效果吧! 知晓当前Activity.gif 随时退出应用 当我们在写一个由很多Activity组成的项目时,很可能要面临一个问题,从ActivityA打开ActivityB,然后从ActivityB打开ActivityC,这时如果我们按Back键,会依次回退到ActivityB->ActivityA->主界面,按Home键也只是返回桌面,并没有关闭应用。但是如果我们在ActivityC需要直接关闭掉这个应用呢?其实也不难,我们只需要创建一个ActivityCollector类作为Activity管理器,这个问题就迎刃而解啦! step1:创建ActivityCollector类,并添加相关方法实现 public class ActivityCollector { private static List<Activity> activities = new ArrayList<>(); public static void addActivity(Activity activity){ activities.add(activity); } public static void removeActivity(Activity activity){ activities.remove(activity); } public static void finishAllActivity(){ for (Activity activity : activities){ if (!activity.isFinishing()){ activity.finish(); } } activities.clear(); } } step2:重写BaseActivity中的相关方法 既然创建了ActivityCollector类,那么我们每创建一个Activity,就要添加到我们的Activity管理器中,每销毁一个Activity,也要从Activity管理器中将之移除。下面是代码: public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Toast.makeText(BaseActivity.this, getClass().getSimpleName(), Toast.LENGTH_SHORT).show(); ActivityCollector.addActivity(this); } @Override protected void onDestroy() { super.onDestroy(); ActivityCollector.removeActivity(this); } } step3:在需要直接退出程序的Activity中添加相关逻辑 例如,我们要在某个Activity中通过点击某个button来关闭应用,那么我们只需要在这个Button的点击事件中调用ActivityCollector.finishAllActivity()就可以啦!关于这个就不写相关示例和演示了,自己动手,印象更深刻! 关于Activity的生命周期和启动模式我单独写了两篇博客:Android笔记(一) | Activity的生命周期Android笔记(二) | Activity的启动模式

优秀的个人博客,低调大师

Go语言基础语法(package)-5

章节 package 作用及定义 1.package 作用及定义 package 是 最基本的分发单位 和 工程管理中依赖关系 的体现 每个GO语言源代码文件开头都拥有一个package声明,表示源码文件所属的代码包 要生成GO语言可执行程序,必须要有main的package包,且必须在该包下有main()函数 同一个路径下只能存在一个package,一个package可以拆成多个源文件组成 2.实践-验证上述package作用及定义 2.1 必须要有 package main 包 且 必须在该包下有main函数 package 包名不为main package main1 import "fmt" func main1() { fmt.Println("测试") } 运行结果如下图所示: package name->main1 2.2 package main 包中没有main函数-func main1() {...} package main1 import "fmt" func main1() { fmt.Println("测试") } 运行结果如下所示: func name->main1 所以package main 下面需要有main函数,否则生成不了可执行文件 2.3 package 名不为main //package 包名与文件夹名要求保持一致 package learn02 import "fmt" func test02() { fmt.Println("i am test") } package learn02 与 项目文件夹结构目录保持一致 package learn02 与 项目文件夹结构目录保持一致 test02.go 源代码编译情况如下所示: 源代码文件可正常编译 test02.go 源文件可正常编译 2.4 同一个路径下只能存在一个package,一个package可以拆成多个源文件组成 目录结构下新建 main02.go 同一个路径下只存在一个package 运行情况 go build main02.go main02.go 可正常编译、但未生成可执行文件 2.5 一个package可以包含多个源文件 项目目录结构如下所示: 一个package可以包含多个源文件

优秀的个人博客,低调大师

Python3基础——装饰器

1、Python是一种面向对象的编程语言,在Python中所有的都可以是Python的对象。即可以在函数内创建函数——函数也可以是变量!(亦可称之为:内嵌函数) 2、如果内部函数引用了外部函数定义的对象(即某函数调用的是该函数以外定义的对象,但该对象不是全局变量),那么此时内部函数叫做:闭包函数,所引用的外部变量叫:自由变量。 什么是闭包函数?——函数内部定义函数;并且引用了外部变量但不是全局变量。 3、Python装饰器 Python装饰器本质上是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器返回的值也是一个函数对象。 3.1 变量与引用示例: 结果: 3.2 装饰器示例一: 运行结果: 3.3 装饰器示例二: 步骤1:执行代码第一行 定义变量为author 的函数srartEnd(即函数srartEnd就是装饰器) 步骤2:获取变量 author 的值,即author的值为 Jsh 步骤3:获取到author的值后,执行函数srartEnd 步骤4:执行函数srartEnd的内容,即定义变量为 fun 的函数a 步骤5:返回函数a的值 步骤6:@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次被赋值 即:变量 author的值依旧为 Jsh 步骤7:定义变量为name的函数b( 即闭包函数 b ) 步骤8:返回函数b的值 步骤9:@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次被赋值 即:变量 author的值依旧为 Jsh 步骤10:申明函数 hello 的变量name的值为 Xiaohai 步骤11:打印 This author is Jsh 步骤12:打印 Start 步骤13:执行变量为name的函数fun 此函数的返回值为函数a 的变量 步骤14、15:打印函数hello的结果 步骤16:打印 End 运行结果:

优秀的个人博客,低调大师

Docker基础操作手册

Docker操作总结 @Date 2018.08.24 基本操作 登录远程镜像仓库 docker login --username=hua registry.cn-beijing.aliyuncs.com 镜像操作 根据指定路径的Dockerfile打镜像, 指定本地Tag名 docker build -t tag -f docker-config/Dockerfile . 修改本地tag改为远程专有地址仓库tag docker tag local_tag registry.cn-beijing.aliyuncs.com/[命名空间]/[仓库名称]:[版本] PUSH本地镜像到远程仓库 docker push registry.cn-beijing.aliyuncs.com/[命名空间]/[仓库名称]:[版本] 删除无容器运行的镜像 docker rmi [imageID] 利用全拼删除有重复镜像ID的镜像 docker rmi [REPOSITORY]:TAG 删除所有镜像 docker rmi $(docker images -q) 容器操作 启动容器,支持传入参数 docker run -it -d -p 7001:7001 -e spring.profiles.active="dev" [REPOSITORY]:[TAG] 停止容器 docker stop [容器ID] 停止所有容器 docker stop $(docker ps -a -q) 删除已停止的容器 docker container rm [容器ID] 删除所有容器 docker rm $(docker ps -a -q) 使用docker-compose编排模板 # 使用目录下默认的docker-compose.yaml创建容器 docker-compose up -d # 使用指定的yaml文件创建容器 docker-compose -f xxx.yaml up -d # 指定容器ID创建多个scale docker-compose -f xxx.yaml scale id=数量

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册