在一个项目中会包括着多个Activity,系统中使用任务栈来存储创建的Activity实例,任务栈是一种“后进先出”的栈结构。举个栗子,若我们多次启动同一个Activity。系统会创建多个实例依次放入任务栈中。当按back键返回时,每按一次,一个Activity出栈,直到栈空为止。当栈中无不论什么Activity。系统就会回收此任务栈。

上面这个样例中的Activity并没有设置启动模式,你会发现多次启动同一个Activity。而系统却创建了多个实例,白白浪费内存,这样的情况Android早就替我们考虑好了。Android为Activity 的创建提供了4种启动模式,而依据实际应用场景的不同。为Activity 选择不同的启动模式,最大化降低了每次都须要在栈中创建一个新的Activity的压力,降低内存使用。

启动模式的具体说明和使用场景?以下依据这篇博文来一一解惑。

standard 模式

这是默认模式,每次激活Activity时都会创建Activity实例,并放入任务栈中。使用场景:大多数Activity。

singleTop 模式

如果在任务的栈顶正好存在该Activity的实例,就重用该实例( 会调用实例的 onNewIntent() ),否则就会创建新的实例并放入栈顶,即使栈中已经存在该Activity的实例,只要不在栈顶,都会创建新的实例。使用场景如新闻类或者阅读类App的内容页面。

singleTask 模式

如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的 onNewIntent() )。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移出栈。如果栈中不存在该实例,将会创建新的实例放入栈中。使用场景如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。

singleInstance 模式

在一个新栈中创建该Activity的实例,并让多个应用共享该栈中的该Activity实例。一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例( 会调用实例的 onNewIntent() )。其效果相当于多个应用共享一个应用,不管谁激活该 Activity 都会进入同一个应用中。使用场景如闹铃提醒,将闹铃提醒与闹铃设置分离。singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,在此启动,首先打开的是B。

一. Android启动模式具体解释

img

1. Standard 标准模式

说明: Android创建Activity时的默认模式,假设没有为Activity设置启动模式的话,默觉得标准模式。每次启动一个Activity都会又一次创建一个新的实例入栈,无论这个实例是否存在。

生命周期:如上所看到的,每次被创建的实例Activity 的生命周期符合典型情况,它的onCreate、onStart、onResume都会被调用。

举例:此时Activity 栈中以此有A、B、C三个Activity,此时C处于栈顶,启动模式为Standard 模式

若在C Activity中加入点击事件,须要跳转到还有一个同类型的C Activity。结果是还有一个C Activity进入栈中,成为栈顶。

img

2. SingleTop 栈顶复用模式

说明:分两种处理情况:须要创建的Activity已经处于栈顶时,此时会直接复用栈顶的Activity。不会再创建新的Activity;若须要创建的Activity不处于栈顶,此时会又一次创建一个新的Activity入栈,同Standard模式一样。

生命周期:若情况一中栈顶的Activity被直接复用时,它的onCreate、onStart不会被系统调用,由于它并没有发生改变。可是一个新的方法 onNewIntent会被回调(Activity被正常创建时不会回调此方法)。

举例:此时Activity 栈中以此有A、B、C三个Activity,此时C处于栈顶,启动模式为SingleTop 模式。情况一:在C Activity中加入点击事件,须要跳转到还有一个同类型的C Activity。

结果是直接复用栈顶的C Activity。

情况二:在C Activity中加入点击事件,须要跳转到还有一个A Activity。结果是创建一个新的Activity入栈。成为栈顶。

img

3. SingleTask 栈内复用模式

说明:若须要创建的Activity已经处于栈中时,此时不会创建新的Activity,而是将存在栈中的Activity上面的其他Activity所有销毁,使它成为栈顶。

生命周期:同SingleTop 模式中的情况一同样。仅仅会又一次回调Activity中的 onNewIntent方法

举例:此时Activity 栈中以此有A、B、C三个Activity。此时C处于栈顶,启动模式为SingleTask 模式

情况一:在C Activity中加入点击事件,须要跳转到还有一个同类型的C Activity。结果是直接用栈顶的C Activity。情况二:在C Activity中加入点击事件,须要跳转到还有一个A Activity。

结果是将A Activity上面的B、C所有销毁,使A Activity成为栈顶。

img

4. SingleInstance 单实例模式

说明: SingleInstance比較特殊,是全局单例模式,是一种加强的SingleTask模式。它除了具有它所有特性外,还加强了一点:具有此模式的Activity仅仅能单独位于一个任务栈中。

这个经常使用于系统中的应用,比如Launch、锁屏键的应用等等,整个系统中仅仅有一个!所以在我们的应用中一般不会用到。了解就可以。

举例:比方 A Activity是该模式,启动A后。系统会为它创建一个单独的任务栈,由于栈内复用的特性。兴许的请求均不会创建新的Activity,除非这个独特的任务栈被系统销毁。

二.启动模式的使用方式

1. 在 Manifest.xml中指定Activity启动模式

一种静态的指定方法,在Manifest.xml文件里声明Activity的同一时候指定它的启动模式,这样在代码中跳转时会依照指定的模式来创建Activity。样例例如以下:

2. 启动Activity时。在Intent中指定启动模式去创建Activity

一种动态的启动模式,在new 一个Intent后,通过Intent的addFlags方法去动态指定一个启动模式。样例例如以下:

Intent intent = new Intent();intent.setClass(context, MainActivity.class);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);

注意:以上两种方式都能够为Activity指定启动模式,可是二者还是有差别的。

(1)优先级:动态指定方式即另外一种比第一种优先级要,若两者同一时候存在,以另外一种方式为准。

(2)限定范围:第一种方式无法为Activity直接指定 FLAG_ACTIVITY_CLEAR_TOP 标识,另外一种方式无法为Activity指定 singleInstance 模式。

三. Activity 的 Flags

标记位既能够设定Activity的启动模式,如同上面介绍的,在动态指定启动模式,比方 FLAG_ACTIVITY_NEW_TASKFLAG_ACTIVITY_SINGLE_TOP 等。它还能够影响Activity 的运行状态 ,比方 FLAG_ACTIVITY_CLEAN_TOPFLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 等。

以下介绍几个基本的标记位,切勿死记,理解几个就可以,须要时再查官方文档。

1. FLAG_ACTIVITY_NEW_TASK

作用是为Activity指定 “SingleTask”启动模式。跟在AndroidMainfest.xml指定效果同样。

2. FLAG_ACTIVITY_SINGLE_TOP

作用是为Activity指定 “SingleTop”启动模式,跟在AndroidMainfest.xml指定效果同样。

3. FLAG_ACTIVITY_CLEAN_TOP

具有此标记位的Activity,启动时会将与该Activity在同一任务栈的其他Activity出栈。一般与SingleTask启动模式一起出现。它会完毕SingleTask的作用。但事实上SingleTask启动模式默认具有此标记位的作用

4.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

具有此标记位的Activity不会出如今历史Activity的列表中,使用场景:当某些情况下我们不希望用户通过历史列表回到Activity时,此标记位便体现了它的效果。它等同于在xml中指定Activity的属性:

android:excludeFromRecents=”trure”

四. 启动模式的实际应用场景

这四种模式中的Standard模式是最普通的一种,没有什么特别注意。而SingleInstance模式是整个系统的单例模式,在我们的应用中一般不会应用到。所以,这里就具体解说 SingleTopSingleTask模式的运用场景:

1. SingleTask模式的运用场景

最常见的应用场景就是保持我们应用开启后仅仅有一个Activity的实例。最典型的样例就是应用中展示的主页(Home页)。

假设用户在主页跳转到其他页面,运行多次操作后想返回到主页,假设不使用SingleTask模式,在点击返回的过程中会多次看到主页,这明显就是设计不合理了。

2. SingleTop模式的运用场景

假设你在当前的Activity中又要启动同类型的Activity,此时建议将此类型Activity的启动模式指定为SingleTop,能够降低Activity的创建,节省内存!

3. 注意:复用Activity时的生命周期回调

这里还须要考虑一个Activity跳转时携带页面參数的问题

由于当一个Activity设置了SingleTop或者SingleTask模式后,跳转此Activity出现复用原有Activity的情况时,此Activity的onCreate方法将不会再次运行。onCreate方法仅仅会在第一次创建Activity时被运行。

而一般onCreate方法中会进行该页面的数据初始化、UI初始化,假设页面的展示数据无关页面跳转传递的參数,则不必操心此问题,若页面展示的数据就是通过getInten() 方法来获取,那么问题就会出现:getInten()获取的一直都是老数据,根本无法接收跳转时传送的新数据!

以下,通过一个样例来具体解释:

Manifest.xml

publicclassCourseDetailActivityextendsBaseActivity{......@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); setContentView(R.layout.activity_course_detail_layout); initData(); initView(); }//初始化数据privatevoidinitData() { Intent intent = getIntent(); mCourseID = intent.getStringExtra(COURSE_ID); }//初始化UIprivatevoidinitView() { …… } ……}

以上代码中的CourseDetailActivity在配置文件里设置了启动模式是SingleTop模式,依据上面启动模式的介绍可得知,当CourseDetailActivity处于栈顶时。再次跳转页面到CourseDetailActivity时会直接复用原有的Activity,并且此页面须要展示的数据是从getIntent()方法得来,可是initData()方法不会再次被调用,此时页面就无法显示新的数据。

当然这样的情况系统早就为我们想过了,这时我们须要另外一个回调 onNewIntent(Intent intent)方法。此方法会传入最新的intent,这样我们就能够解决上述问题。这里建议的方法是又一次去setIntent。然后又一次去初始化数据和UI。代码例如以下所看到的:

/*

* 复用Activity时的生命周期回调

*/@OverrideprotectedvoidonNewIntent(Intent intent) {super.onNewIntent(intent); setIntent(intent); initData(); initView(); }

这样,在一个页面中能够反复跳转并显示不同的内容。

转自:https://www.cnblogs.com/claireyuancy/p/7387696.html

#####简介:
在Android API21,新添加了一个属性:android:elevation,用以在xml定义View的深度(高度),也即z方向的值。
除了elevation之外,类似于已有的translationX、translationY,也相对应地新增了一个translationZ,用以在属性动画中动态改变Z值(使用View.setTranslationZ())

1
Z = elevation + translationZ

#####在引入了这个属性之后,主要影响有两个:
影响View的阴影
影响View相互阻挡顺序
#####1. 影响View的阴影
Z值会对View的阴影外观造成影响,但是不是对View大小造成影响。
拥有更大Z值的View会有一个更大但是更柔和的阴影——这跟我们生活的实际体验是一致的,官方给的效果图:
阴影效果图
#####2. 影响View相互阻挡顺序
拥有更大Z值的View会挡住Z值比较小的View——即更大Z值的View会在最上层。
譬如,在正常的FrameLayout中,子View的绘制顺序是从上到下,也就是说,最后一个子View会显示到最上面,如果位置跟前面的View有重合,则会盖住前面的View。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/viewA"
android:background="#f00"
android:text="A"
android:gravity="center"
android:layout_width="100dp"
android:layout_height="100dp" />

<TextView
android:id="@+id/viewB"
android:background="#0f0"
android:text="B"
android:gravity="center"
android:layout_marginLeft="60dp"
android:layout_marginTop="60dp"
android:layout_width="100dp"
android:layout_height="100dp" />

<!--ViewC有了一个elevation属性 -->
<TextView
android:id="@+id/viewC"
android:layout_marginLeft="180dp"
android:background="#f00"
android:text="C"
android:gravity="center"
android:elevation="1dp"
android:layout_width="100dp"
android:layout_height="100dp" />
<TextView
android:id="@+id/viewD"
android:background="#0f0"
android:text="D"
android:gravity="center"
android:layout_marginLeft="240dp"
android:layout_marginTop="60dp"
android:layout_width="100dp"
android:layout_height="100dp" />
</FrameLayout>

比如,上面的xml代码,效果如下图所示:
image.png

  • 先看ViewA、ViewB,因为ViewB是第二个子View,ViewA是第一个,所以B会覆盖在A的上面。
  • 然后ViewC、D,跟AB相比较,区别就在于ViewC多了一个elevation属性,有了一个比ViewD更大的Z值,所以,即使它在ViewD的前面,但是依然能够盖住D~

#####注意 设置Elevation 属性失效的情况:

1
2
3
4
5
6
7
8
9
10
1. 控件必须设置背景色,且不能为透明。

2. 阴影是绘制于父控件上的,所以控件与父控件的边界之间需有足够空间绘制出阴影才行。

3. 有网友提出图片尽量使用.png, 防止图片过大导致oom或者elevation失效

4. 经过本人测试,除了上述原因外,还有:background是图片时、background直接设置具体颜色值时容易无效如:#ffaacc,background是shape时效果最好

5.设置elevation的View最好是ViewGroup子类

####设计模式分类

创建型模式
工厂方式模式 建造者模式 抽象工程模式
原型模式 单例模式
结构型模式
桥接模式 代理模式 享元模式
外观模式 装饰器模式 组合模式
适配器模式
行为型模式
备忘录模式 解释器模式 命令模式
中介者模式 观察者模式 策略模式
状态模式 模板方法模式 访问者模式
迭代子模式 责任链模式
1
2
3
4
5
6
7
String str="112,123,123,123";//根据逗号分隔到List数组中

                String str2=str.replace(" ", "")//去掉所用空格

List<String> list= Arrays.asList(str2.split(","))
//list的结果就是[113,123,123,123]

一个APP通常就是一个进程对应一个虚拟机

获取内存大小:

img

GC只在Heap剩余空间不足的时候才触发垃圾回收

GC触发时,所有的线程都是会被暂停的

每个App分配的内存限制,随不同设备而不同

吃内存大户:图片

App内存优化方法:1:数据结构优化 2:对象复用

数据结构优化:

频繁字符串拼接用StringBuillder(字符串通过+的方式拼接,会产生中间字符串内存块,这些都是没用的并且更耗时更低效)!

ArrayMap,SparseArray替换HashMap(ArrayMap,SparseArray效率更高,内存更小,使用方法和HashMap一样的,HashMap一个entry需要额外占用的32B)

内存抖动(突然间生成很多对象,变量或内存空间后用了没多久又不用了,如此反复就是内存抖动,内存像波浪形一会高一会低)

再小的class耗费0.5KB(尽量复用class)

对象复用:

复用系统自带的资源

ListView/GridView的ConvertView复用

避免在OnDraw方法里面执行对象的创建(在OnDraw中对象的变更会重绘View)

避免内存泄漏:

内存泄漏会导致剩余可用的Heao越来越少,频繁触发GC

尤其Activity泄漏(如果有耗时操作为执行完,则GC无法回收还在被引用的资源)

用Application Context而不是Activity Context

注意Cursor对象是否及时关闭(数据库操作时)

强引用和软引用的意义:

img

优化OOM问题的方法:

注意临时BitMap对象的及时回收

避免BitMap的浪费

Try catch某些大内存分配的操作

加载BitMap:缩放比例,解码格式,局部加载

**OOM绝大部分发生在图片
**

Android系统中,默认提供三种字体:”sans”, “serif”, “monospace”

使用:

img

自定义字体样式:

我们先把要使用的字体文件放入到工具中,操作如下:

新建一个名叫assets的文件夹,然后把字体文件复制到里面

img

设置单个TextView的字体:

img

或者创建一个自定义TextView控件,我们新建一个类,名叫FontCustom,写入代码:

img

新建一个类名叫MyTextView继承TextView,重写2个参数的构造方法

img

使用:复制MyTextView的路径到activity_main中,替换原有的TextView,我这里的路径是

com.example.fengjun.fontdiy.MyTextView

img

常用字体文件下载地址:字体量版找字网

1
2
3
4
5
//解决之道:在EditText的父级控件中找一个,设置成
android:focusable="true"
android:focusableInTouchMode="true"
//这样,就把EditText默认的行为截断了!

github地址:https://github.com/florent37/GlidePalette

使用准备:

compile’com.github.florent37:glidepalette:2.1.2’

compile’com.github.bumptech.glide:glide:4.6.1’

demo:

img

img

自己写的demo

提取出的颜色

Palette默认会解析出图像的16种特征颜色种类,但是这六种颜色是你最经常用到的:

vibrant(鲜艳色)

dark vibrant(鲜艳色中的暗色)

light vibrant(鲜艳色中的亮色)

muted(柔和色)

dark muted(柔和色中的暗色)

light muted(柔和色中的亮色)

获取提取的颜色

你获取Palette对象之后,可以通过以下这些内置getter函数直接获取这六个颜色。你需要传入默认颜色防止Palette无法解析到指定颜色种类,返回的类型是24位RGB颜色数值。

img

获取Swatch

你也可以选择先获取Swatch对象,然后再通过Swatch提供的方法获取颜色的相关信息:

img

注意:getwatch()可能会返回一个null值,所以在使用前检查一下是必须的。if (swatch != null) {}

swatch对象对应的颜色方法:

getPopulation(): 像素的数量

getRgb(): RGB颜色

getHsl(): HSL颜色

getBodyTextColor(): 用于内容正文文本的颜色

getTitleTextColor(): 标题文本的颜色

1
2
3
4
5
private int getArea(int x1, int y1, int x2, int y2, int x3, int y3) {
int Area = 0;
Area = abs((x1 * y2 + x2 * y3 + x3 * y1 - x1 * y3 - x2 * y1 - x3 * y2) / 2);
return Area;
}

线程停止

Thread提供了一个stop()方法,但是stop()方法是一个被废弃的方法。为什么stop()方法被废弃而不被使用呢?原因是stop()方法太过于暴力,会强行把执行一半的线程终止。这样会就不会保证线程的资源正确释放,通常是没有给与线程完成资源释放工作的机会,因此会导致程序工作在不确定的状态下

那我们该使用什么来停止线程呢

Thread.interrupt(),我们可以用他来停止线程,他是安全的,可是使用他的时候并不会真的停止了线程,只是会给线程打上了一个记号,至于这个记号有什么用呢我们可以这样来用。

publicclassMythread extendsThread{

publicvoidrun(){

super.run();

for(inti =0;i<50000;i++){

if(this.interrupted()){

System.out.println(“停止”);

break;

}

}

System.out.println(“i=”+(i+1));

}

}

publicclassRun{

try{

MyThread thread = newMyThread();

thread.start();

thread.sleep(1000);

thread.interrupt(); //打上标记

}catch(Exception e){

System.out.println(“main”);

e.printStackTrace();

}

System.out.println(“end!”)

}

虽然这样就会停止下来 ,可是For后面的语句还是会执行。

异常法 退出线程

publicclassMythread extendsThread{

publicvoidrun(){

super.run();

try{

for(inti =0;i<50000;i++){

if(this.interrupted()){

System.out.println(“停止”);

thrownewException();

}

}

System.out.println(“i=”+(i+1));

}catch(Exception e){

System.out.println(“抛出异常了”);

e.printStackTrace();

}

}

}

解释 如果当我们打上了一个标记我们就可以检测到已经打上的时候就返回个true,进入if里面返回了一个异常 这样就终止了。这样做使的线程可以在我们可控的范围里停止

用什么方法去看什么状态呢

this.interrupted():看看当前线程是否是中断状态,执行后讲状态表示改为false this.isInterrupeted():看看线程对象是否已经是中断状态,但是不清除中断状态标记。