导航:首页 > 操作系统 > android60demo

android60demo

发布时间:2024-11-28 05:19:07

① 如何修改android系统设置

② 如何在Android上编写高效的Java代码

Java平台一般有三个版本:Java ME(微型版,用于某些手机)、Java SE(标准版,用于台式电脑)、Java EE(企业版,用于服务器端应用)。在谈到Java时,我们通常是指Java SE,因为只有这个版本包含虚拟机和编译器。

首先,Java代码会被编译成称为字节码的中间格式。当字节码在目标电脑上运行时,虚拟机会快速将它解析成目标电脑硬件和操作系统所需要的本机格式。

除了为开发者提供“一次编写,到处运行”的优势,Java还能通过垃圾回收器(GC)实现自动内存管理,开发者可免去手动在代码中释放无用对象的内存。虽然这个功能非常有用,且大大降低了在代码中引入内存问题的风险,但是它会增加运行时的开销,因为需要不停地执行垃圾回收进程。

本文开头将比较Java SE和用于Android开发的Java之间的差异。首先我会介绍开发者习惯的Java
SE语言结构以及它们是如何在Android上运行的。其次,我会介绍如何优化Android中的Java代码,如何优化内存分配,以及如何恰当地处理多线程。

比较Android上的Dalvik Java和Java SE

虽然远在Android出现之前,开发者就能用Java编程语言为移动设备编写应用程序,但它只是Java中功能极为有限的一个版本,称为Java
ME(微型版)。不同的移动设备还需编写不同的代码,因此,写一个应用程序就能在支持Java
ME的任何手机上运行是几乎不可能的。此外,由于当时不存在很好的在线商店,应用发布过程极其复杂。

Android的问世为开发者提供了构建智能手机强大应用的机会,开发者只需用Java编程语言以及他们熟知的标准Java
API编写代码。然而,尽管Android开发者仍使用Java SE编译器来编译应用程序,你会发现,James
Gosling开发的Java和Android设备上的Java存在许多不同之处。

在Android设备上运行的VM(虚拟机)称为Dalvik。它最初由谷歌的Dan
Bornstein开发,适用于CPU和内存受限的移动设备。Java SE和Dalvik Java存在一些差异,主要体现在虚拟机上。Java
SE使用了栈机设计,而Dalvik被设计成了基于寄存器的机器。Android SDK中有一个dx工具,它会把Java
SE栈机器的字节码转换成基于寄存器的Dalvik机器字节码,该转换步骤由IDE自动完成。

基于栈的虚拟机和基于寄存器的虚拟机的定义以及差异将不列入我们的讨论范围。由于历史原因,Android使用基于寄存器的虚拟机。虽然基于寄存器的虚拟机最多可以比基于栈的虚拟机快32%,但这只限于执行时解释字节码的虚拟机(也就是说,解释型虚拟机)。在Android
2.2版本(也称为Froyo)之前,Dalvik虚拟机都是纯解释型的。Froyo版本引入了JIT编译器(即时编译),这是Java
SE很早就有的一个优势。

JIT编译,也称为动态翻译。它在执行前把字节码翻译成本机代码(如图1所示),这样主要有两个好处。首先,它消除了那些纯解释型虚拟机的开销;其次,它能对本机代码执行优化,这通常是静态编译代码无法做到的。例如,JIT编译器可以在它运行的CPU上选择最合适的优化,也可以根据应用程序的输入来分析代码是如何运行的,以便进行下一步的优化。

图1Android Java和Java SE翻译步骤

虽然Android的Dalvik JIT编译器有很大的发展前景,但要达到如Java SE的JIT编译器般稳定、成熟度尚需很长一段时间。不过,Dalvik JIT的出现为Android提供了巨大的性能优势,而且它也在不断得以改善。

JAVA
SE虚拟机和Dalvik虚拟机的另一个区别是,后者进行了优化,可运行在同一个机器上的多个实例中。它在开机时会启动一个叫做zygote的进程,该进程会创建第一个Dalvik实例,由这个实例创建所有其他的实例。当应用程序启动时,zygote进程会收到一个创建新虚拟机实例的请求,并给该应用程序创建一个新进程(如图2所示)。如果开发者已习惯于Java

SE开发,这样的设计可能看起来不切实际,但它有一个很大的优势,可以避免由一个应用程序运行失败导致Dalvik虚拟机崩溃,继而引发多应用程序崩溃。

图2在Android中启动新Dalvik虚拟机实例

Android和Java
SE除了运行的虚拟机不同之外,它们实现API的方式也不一样。Android中属于java和javax包中的API都来自Apache
Harmony(这是一个开源项目,旨在重新实现Java SE软件栈,该项目从2011年11月不再维护)。在开发方面,这些API和Java
SE包中的类似,但也存在一些差别。例如,谷歌对HttpUrlConnection类进行了Java SE版本中所没有的重大升级。

此外,Android平台移除了Java
SE中无关的API。例如,Swing/AWT包被完全移除,因为Android使用不同的UI框架。其他被移除的API还有RMI、CORBA、ImageIO和JMX。它们或者被替换为特定的Android版本(在android包空间内),或者因为一些实际原因根本不存在。

优化Android上的Java代码

经过多年的改进,Java
SE具备了一些简化编写复杂代码结构的新特性。其中的一些特性会让整个流程变得更简单,但开发者需要了解何时以及如何正确地使用它们。另外,由于Java

SE大多用于服务器端开发(使用Java企业版的API),因而开发人员专门对服务器端Java代码进行了优化。注解和Java虚拟机对脚本语言的支持就是对服务器端开发进行优化的例证。虽然这些工具在构建后端开发时很强大,但在开发Android客户端代码时,这些特性的作用很小,甚至起反作用。Java开发者已经习惯于无限量的RAM和CPU,而Android开发需要密切关注性能和内存分配。简单地说,开发者需要使用稍微不同的方法对待Android和后端的开发。

然而,随着Android的首次发布,情况有所改变。曾经一些在Android上尽量不用的Java规范重新被推荐,这主要因为Android目前的JIT编译器解决了这些规范导致的性能问题。

本文将讨论编写Android应用程序需要了解的Java代码。我们不会深究Java编程语言的细节,而是重点关注对Android开发重要的东西。不过,开发者仍需了解,大多数适用于Java SE的规则和建议同样适用于Android和Dalvik虚拟机。

Android上的类型安全枚举

Java SE 5.0新增了许多方便开发者的新特性。其中最值得期待的是引入了类型安全枚举。枚举在代码中用来表示属于某一组的几个选择。在早期版本的Java中,可以用多个整型常量解决这个问题。虽然这在技术上可行,但是很容易出错。请看下面的代码:
public class Machine {
public static final int STOPPED = 10;
public static final int INITIALIZING = 20;
public static final int STARTING = 30;
public static final int RUNNING = 40;
public static final int STOPPING = 50;
public static final int CRASHED = 60;
private int mState;

public Machine() {
mState = STOPPED;
}

public int getState() {
return mState;
}

public void setState(int state) {
mState = state;
}
}

问题是,虽然这些常量是期望的,但是没有机制保证setState()方法接收不同的值。如果要在设置方法中添加检查,那么一旦得到的是非预期值,开发者就需要处理错误。开发者所需要的是在编译时检查非法赋值。类型安全的枚举解决了这个问题,如下所示:
public class Machine {
public enum State {
STOPPED, INITIALIZING, STARTING, RUNNING, STOPPING, CRASHED
}
private State mState;

public Machine() {
mState = State.STOPPED;
}

public State getState() {
return mState;
}

public void setState(State state) {
mState = state;
}
}

注意在声明不同类型安全值的地方新加的内部枚举类。这在编译时就会解决非法赋值的问题,所以代码更不容易出错。

如果Dalvik虚拟机还没有JIT编译器优化代码,不建议在Android平台上使用枚举类型,因为和使用整型常量相比,这种设计带来的内存和性能损失更大。这就是为什么在一些老版本的Android

API中还存在如此多的整型常量的原因。如今有了更强的JIT编译器以及一个不断改进的Dalvik虚拟机,开发者不必再担心这个问题,放心大胆地使用类型安全枚举即可。

然而,仍然存在一些情况使用整型常量是更好的选择。像int这样的Java基本类型,不会增加GC的开销。此外,Android SDK中许多已有的API仍然依赖基本类型,比如Handler类——在这种情况下,你没有太多的选择。

Android中增强版的for循环

Java SE 5.0还引入了增强版的for循环,提供了一个通用的缩写表达式来遍历集合和数组。首先,比较以下五种方法:
void loopOne(String[] names) {
int size = names.length;
for (int i = 0; i < size; i++) {
printName(names[i]);
}
}

void loopTwo(String[] names) {
for (String name : names) {
printName(name);
}
}

void loopThree(Collection<String> names) {
for (String name : names) {
printName(name);
}
}

void loopFour(Collection<String> names) {
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
printName(iterator.next());
}
}

// 不要在ArrayList上使用增强版的for循环
void loopFive(ArrayList<String> names) {
int size = names.size();
for (int i = 0; i < size; i++) {
printName(names.get(i));
}
}

上面显示了四种不同遍历集合和数组的方式。前面两种有着相同的性能,所以如果只是读取元素的话,可以放心地对数组使用增强版for循环。对Collection对象来说,增强版for循环和使用迭代器遍历元素有着相同的性能。ArrayList对象应避免使用增强版for循环。

如果不仅需要遍历元素,而且需要元素的位置,就一定要使用数组或者ArrayList,因为所有其他Collection类在这些情况下会更慢。

一般情况下,如果在读取元素几乎不变的数据集时对性能要求很高,建议使用常规数组。然而,数组的大小固定,添加数据会影响性能,所以编写代码时要考虑所有因素。

队列、同步和锁

通常情况下,应用程序会在一个线程中生产数据,在另一个线程中使用它们。常见的例子是在一个线程中获取网络上的数据,在另一个线程(操作UI的主线程)中把这些数据展现给用户。这种模式称为生产者/消费者模式,在面向对象编程课程中,开发者用算法来实现该模式可能要花上几个小时。下面会介绍一些简化生产者/消费者模式实现的现成类。

1. 更智能的队列

虽然已有现成的类并能用更少的代码实现该功能,但许多Java开发者仍然选择使用LinkedList以及同步块实现队列功能。开发者可在java.util.concurrent包中找到同步相关的类。此外,本包还包含信号量、锁以及对单个变量进行原子操作的类。考虑下面使用标准的LinkedList实现线程安全队列的代码。
public class ThreadSafeQueue {
private LinkedList<String> mList = new LinkedList<String>();
private final Object mLock = new Object();

public void offer(String value) {
synchronized (mLock) {
mList.offer(value);
mLock.notifyAll();
}
}

public synchronized String poll() {
synchronized (mLock) {
while (mList.isEmpty()) {
try {
mLock.wait();
} catch (InterruptedException e) {
//简洁起见忽略异常处理
}
}
return mList.poll();
}
}
}

虽然这段代码是正确的,并有可能在考试中得满分,但实现和测试这样一段代码只是在浪费时间。实际上,所有前面的代码可用下面一行代替。
LinkedBlockingQueue<String> blockingQueue =
new LinkedBlockingQueue<String>();

上面的一行代码能像前面的例子一样提供相同类型的阻塞队列,甚至能提供额外的线程安全操作。java.util.concurrent包含许多可选的队列以及并发映射类,所以,一般情况下,建议使用它们,而不是像之前的示例那样使用更多代码。

2. 更智能的锁

Java提供的synchronized关键字允许开发者创建线程安全的方法和代码块。synchronized关键字易于使用,也很容易滥用,对性能造成负面影响。当需要区分读数据和写数据时,synchronized关键字并不是最有效的。幸好,java.util.concurrent.locks包中的工具类对这种情况提供了很好的支持。
public class ReadWriteLockDemo {
private final ReentrantReadWriteLock mLock;
private String mName;
private int mAge;
private String mAddress;

public ReadWriteLockDemo() {
mLock = new ReentrantReadWriteLock();
}

public void setPersonData(String name, int age, String address) {
ReentrantReadWriteLock.WriteLock writeLock = mLock.writeLock();
try {
writeLock.lock();
mName = name;
mAge = age;
mAddress = address;
} finally {
writeLock.unlock();
}
}

public String getName() {
ReentrantReadWriteLock.ReadLock readLock = mLock.readLock();
try {
readLock.lock();
return mName;
} finally {
readLock.unlock();
}
}

// 重复代码不再赘述
}

上面的代码展示了在什么地方使用ReentrantReadWriteLock,它允许多个并发线程对数据进行只读访问,并确保同一时间只有一个线程写入相同的数据。

在代码中使用synchronized关键字仍然是处理锁问题的有效方法,但无论何种情况下,都要考虑ReentrantReadWriteLock是否是

③ 怎么让Android程序一直后台运行,像QQ一样不被杀死

方法:
对于一个service,可以首先把它设为在前台运行:
public void MyService.onCreate() {
super.onCreate();
Notification notification = new Notification(android.R.drawable.my_service_icon,
"my_service_name",
System.currentTimeMillis());
PendingIntent p_intent = PendingIntent.getActivity(this, 0,
new Intent(this, MyMainActivity.class), 0);
notification.setLatestEventInfo(this, "MyServiceNotification, "MyServiceNotification is Running!", p_intent);
Log.d(TAG, String.format("notification = %s", notification));
startForeground(0x1982, notification); // notification ID: 0x1982, you can name it as you will.
}

重要设置-------------------------------
相较于/data/app下的应用,放在/system/app下的应用享受更多的特权,比如若在其Manifest.xml文件中设置persistent属性为true,则可使其免受out-of-memory killer的影响。如应用程序'Phone'的AndroidManifest.xml文件:
<application android:name="PhoneApp"
android:persistent="true"
android:label="@string/dialerIconLabel"
android:icon="@drawable/ic_launcher_phone">
...
</application>
设置后app提升为系统核心级别,任何情况下不会被kill掉, settings->applications里面也会屏蔽掉stop操作。

这样设置前的log: Proc #19: adj=svc /B 4067b028 255:com.xxx.xxx/10001 (started-services)
# cat /proc/255/oom_adj

设置后的log: PERS #19: adj=core /F 406291f0 155:com.xxx.xxx/10001 (fixed)
# cat /proc/155/oom_adj
-12 # 这是CORE_SERVER_ADJ
注:init进程的oom_adj为-16(即SYSTEM_ADJ): cat /proc/1/oom_adj

Android相关部分分析:
在文件frameworks/base/services/java/com/android/server/am/ActivityManagerService.java中有以下的代码:
final ProcessRecord addAppLocked(ApplicationInfo info) {
ProcessRecord app = getProcessRecordLocked(info.processName, info.uid);

if (app == null) {
app = newProcessRecordLocked(null, info, null);
mProcessNames.put(info.processName, info.uid, app);
updateLruProcessLocked(app, true, true);
}

if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT))
== (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) {
app.persistent = true;
app.maxAdj = CORE_SERVER_ADJ; // 这个常数值为-12。
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
startProcessLocked(app, "added application", app.processName);
}

return app;
}

可见要想成为core service (即app.maxAdj = CORE_SERVER_ADJ(-12)),应用程序需要FLAG_SYSTEM和FLAG_PERSISTENT两个标志,FLAG_SYSTEM指的是应用位于/system/app下,FLAG_PERSISTENT就是指persistent属性。

而对于frameworks/base/services/java/com/android/server/SystemServer.java,则调用
ActivityManagerService.setSystemProcess();
把自己的 app.maxAdj 设置成SYSTEM_ADJ,即-16。

原理:
Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。由此带来三个问题:
1) 回收规则: 什么时候回收与回收哪一个?
2) 避免误杀: 如何阻止被回收?
3) 数据恢复与保存: 被回收了怎么办?

Android将进程分为6个等级,它们按优先级顺序由高到低依次是:
1.前台进程( FOREGROUND_APP)
2.可视进程(VISIBLE_APP )
3. 次要服务进程(SECONDARY_SERVER )
4.后台进程 (HIDDEN_APP)
5.内容供应节点(CONTENT_PROVIDER)
6.空进程(EMPTY_APP)

特征:
1.如果一个进程里面同时包含service和可视的activity,那么这个进程应该归于可视进程,而不是service进程。
2.另外,如果其他进程依赖于它的话,一个进程的等级可以提高。例如,一个A进程里的service被绑定到B进程里的组件上,进程A将总被认为至少和B进程一样重要。
3.系统中的phone服务被划分到前台进程而不是次要服务进程.

在android中,进程的oom_adj值也就代表了它的优先级。oom_adj值越高代表该进程优先级越低。文件/init.rc中有以下属性设置:
setprop ro.FOREGROUND_APP_ADJ 0
setprop ro.VISIBLE_APP_ADJ 1
setprop ro.SECONDARY_SERVER_ADJ 2
setprop ro.HIDDEN_APP_MIN_ADJ 7
setprop ro.CONTENT_PROVIDER_ADJ 14
setprop ro.EMPTY_APP_ADJ 15
/init.rc中,将PID为1的进程(init进程)的oom_adj设置为SYSTEM_ADJ(-16):
# Set init its forked children's oom_adj.
write /proc/1/oom_adj -16

查看本机设置:
cat /sys/mole/lowmemorykiller/parameters/adj
0,1,2,7,14,15

回收时机:
文件/init.rc中:
setprop ro.FOREGROUND_APP_MEM 1536 // 6M
setprop ro.VISIBLE_APP_MEM 2048 // 8M
setprop ro.SECONDARY_SERVER_MEM 4096 // 16M
setprop ro.HIDDEN_APP_MEM 5120 // 20M
setprop ro.CONTENT_PROVIDER_MEM 5632 // 22.4M
setprop ro.EMPTY_APP_MEM 6144 // 24M
这些数字也就是对应的内存阈值,一旦低于该值,Android便开始按顺序关闭相应等级的进程。
注意这些数字的单位是page: 1 page = 4 kB。所以上面的六个数字对应的就是(MB): 6,8,16,20,22,24。

查看现在的内存阈值设置:
cat /sys/mole/lowmemorykiller/parameters/minfree

要想重新设置该值(对应不同的需求):
echo "1536,2048,4096,5120,15360,23040">/sys/mole/lowmemorykiller/parameters/minfree
这样当可用内存低于90MB的时候便开始杀死"空进程",而当可用内存低于60MB的时候才开始杀死"内容供应节点"类进程。

具体的回收实现在ActivityManagerService.java中的函数trimApplications():
1.首先移除package已被卸载的无用进程;
2.基于进程当前状态,更新oom_adj值,然后进行以下操作:
1) 移除没有activity在运行的进程;
2) 如果AP已经保存了所有的activity状态,结束这个AP。
3. 最后,如果目前还是有很多activities 在运行,那么移除那些activity状态已经保存好的activity。

更新oom_adj的值:
在ActivityManagerService.java文件的ComputeOomAdjLocked() 中计算出进程的oom_adj,例如:
if (app == TOP_APP) {
// The last app on the list is the foreground app.
adj = FOREGROUND_APP_ADJ;
app.adjType = "top-activity";
}

Android kernel中的low memory killer
Android的Low Memory Killer根据需要(当系统内存短缺时)杀死进程释放其内存,源代码在kernel/drivers/misc/lowmemorykiller.c中。简单说,就是寻找一个最合适的进程杀死,从而释放它占用的内存。
最合适的进程是:
• oom_adj越大
• 占用物理内存越多

一旦一个进程被选中,内核会发送SIGKILL信号将之杀死:
for_each_process(p) {
……
if(selected == NULL || p->oomkilladj > selected->oomkilladj ||
(p->oomkilladj == selected->oomkilladj && tasksize > selected_tasksize))
{
selected = p;
}
}
if(selected != NULL) {
force_sig(SIGKILL, selected);
}

查看LRU列表:adb shell mpsys activity
当activitydemo在前台时:
包含Service的进程的优先级比较高,在computeOomAdjLocked中将其分为了两小类:
static final int MAX_SERVICE_INACTIVITY = 30*60*1000;
if (now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) {
if (adj > SECONDARY_SERVER_ADJ) {
adj = SECONDARY_SERVER_ADJ;
app.adjType = "started-services";
app.hidden = false;
}
}
if (adj > SECONDARY_SERVER_ADJ) {
app.adjType = "started-bg-services";
}
完全让进程不被kill是不可能的,我们可以通过一些操作,使进程被kill的几率变小:
1) 提高进程的优先级:
* 后台操作采用运行于前台的Service形式,因为一个运行着service的进程比一个运行着后台activity的等级高;
* 按back键使得进程中的activity在后台运行而不是destory,需重载back按键(没有任何activity在运行的进程优先被杀).
* 依赖于其他优先级高的进程;

2) 强制修改进程属性:
* 在进程中设置:setPersistent(true);
* 在Manifest文件中设置(如上)。

④ android 编程中怎么关闭activity线程

在Activity开启的子线程并不会自动随Activity的destroy而关闭,所以必须手动去关闭子线程或者通过boolean的方式让子线程结束运行。开启的子线程有for循环的要更加注意。

1 package com.lsw;
2
3 import android.app.Activity;
4 import android.os.Bundle;
5 import android.os.Handler;
6 import android.os.Message;
7 import android.util.Log;
8 public class ThreadDemoActivity extends Activity {
9 private static final String TAG = "ThreadDemo";
10 private int count = 0;
11 private Handler mHandler = new MyHandler();
12 boolean stopThread=false;
13
14 private Runnable mRunnable = new Runnable() {
15
16 public void run() {
17
18 while (!stopThread)
19 {
20 count++;
21 try
22 {
23 Thread.sleep(2000);
24 }
25 catch (InterruptedException e)
26 {
27 // TODO Auto-generated catch block
28 e.printStackTrace();
29 }
30
31 //虽然Message的构造函数是public的,但是最好是使用Message.obtain( )或Handler.obtainMessage( )函数来获取Message对象,因为Message的实现中包含了回收再利用的机制,可以提供效率。
32 Message message=mHandler.obtainMessage();
33 message.what=0;
34 message.obj=count;
35 mHandler.sendMessage(message);
36 }
37 }
38 };
39
40 @Override
41 public void onCreate(Bundle savedInstanceState) {
42 super.onCreate(savedInstanceState);
43 setContentView(R.layout.main);
44 //开启子线程
45 new Thread(mRunnable).start();
46 }
47
48 protected void onDestroy() {
49 System.out.println("-----------onDestroy------");
50 stopThread=true;
51 super.onDestroy();
52 };
53
54 class MyHandler extends Handler{
55
56 @Override
57 public void handleMessage(Message msg)
58 {
59 // TODO Auto-generated method stub
60 Log.e(TAG, Thread.currentThread().getName() + " " +msg.obj);
61 setTitle("" +msg.obj);
62 }
63 }
64
65 }

⑤ 怎么让Android程序一直后台运行,像QQ一样不被杀死

强烈建议不要这么做,不仅仅从用户角度考虑,作为Android开发者也有责任去维护Android的生态环境。现在很多Android开发工程师,主力机居然是iPhone而不是Android设备,感到相当悲哀。

从技术角度概括一下现在普遍的防杀方法Service设置成START_STICKY,kill 后会被重启(等待5秒左右),重传Intent,保持与重启前一样通过 startForeground将进程设置为前台进程,做前台服务,优先级和前台应用一个级别,除非在系统内存非常缺,否则此进程不会被 kill双进程Service:让2个进程互相保护,其中一个Service被清理后,另外没被清理的进程可以立即重启进程QQ黑科技:在应用退到后台后,另起一个只有 1 像素的页面停留在桌面上,让自己保持前台状态,保护自己不被后台清理工具杀死在已经root的设备下,修改相应的权限文件,将App伪装成系统级的应用(Android4.0系列的一个漏洞,已经确认可行)Android系统中当前进程(Process)fork出来的子进程,被系统认为是两个不同的进程。当父进程被杀死的时候,子进程仍然可以存活,并不受影响。鉴于目前提到的在Android-Service层做双守护都会失败,我们可以fork出c进程,多进程守护。死循环在那检查是否还存在,具体的思路如下(Android5.0以下可行)用C编写守护进程(即子进程),守护进程做的事情就是循环检查目标进程是否存在,不存在则启动它。在NDK环境中将1中编写的C代码编译打包成可执行文件(BUILD_EXECUTABLE)。主进程启动时将守护进程放入私有目录下,赋予可执行权限,启动它即可。联系厂商,加入白名单

⑥ Android上如何做出毛玻璃模糊的效果

上图展示了一种很典型的视觉效果——文字的背景不再是固定的,而是将底层的相应区域模糊化,好似盖了一层毛玻璃。

其原理也很简单,分为三步走:

里面涉及到的技术点有两个:

ViewTreeObserver里面有一个监听器为OnPreDrawListener

当它执行时,布局文件经过了 measured laid out displayed ,即将被绘制到屏幕,此时调用它的 getDrawingCache() 方法可以获得其Bitmap。完整方法如下:

方案有两种:

两种方案都可以进行对Bitmap对象的模糊处理,但当模糊半径增大时,StackBlur能够保持较好的性能,且不受Renderscript半径25px的限制。

在GitHub项目有一个项目 blurring ,其实现了StackBlur算法的Java实现版FastBlur,并给出两种方案效率对比demo。经过测试,

看起来,Renderscript的性能更好,应该是Android上对Renderscript做了优化。尽管如此,考虑到Android中渲染一帧的时间应该不超过16ms(60fps),这样的性能并不友好。 blurring 作者想出了另外一种思路:

这时候,效率提升非常明显:

生成的模糊图片当然有所不同,但是都是模糊背景,所以对用户而言没有太大差别。

好了,至此Android上制作毛玻璃背景模糊效果的技术都确定了。

我在 blurring 基础上做了封装,接口如下:

相对应类有两个:

⑦ 想让android应用常驻后台,不被杀死,各位大神有什么高招

方法: 对于一个service,可以首先把它设为在前台运行: public void MyService.onCreate() { super.onCreate(); Notification notification = new Notification(android.R.drawable.my_service_icon, "my_service_name", System.currentTimeMillis()); PendingIntent p_intent = PendingIntent.getActivity(this, 0, new Intent(this, MyMainActivity.class), 0); notification.setLatestEventInfo(this, "MyServiceNotification, "MyServiceNotification is Running!", p_intent); Log.d(TAG, String.format("notification = %s", notification)); startForeground(0x1982, notification); // notification ID: 0x1982, you can name it as you will. } 重要设置------------------------------- 相较于/data/app下的应用,放在/system/app下的应用享受更多的特权,比如若在其Manifest.xml文件中设置persistent属性为true,则可使其免受out-of-memory killer的影响。如应用程序'Phone'的AndroidManifest.xml文件: <application android:name="PhoneApp" android:persistent="true" android:label="@string/dialerIconLabel" android:icon="@drawable/ic_launcher_phone"> ... </application> 设置后app提升为系统核心级别,任何情况下不会被kill掉, settings->applications里面也会屏蔽掉stop操作。 这样设置前的log: Proc #19: adj=svc /B 4067b028 255:com.xxx.xxx/10001 (started-services) # cat /proc/255/oom_adj 设置后的log: PERS #19: adj=core /F 406291f0 155:com.xxx.xxx/10001 (fixed) # cat /proc/155/oom_adj -12 # 这是CORE_SERVER_ADJ 注:init进程的oom_adj为-16(即SYSTEM_ADJ): cat /proc/1/oom_adj Android相关部分分析: 在文件frameworks/base/services/java/com/android/server/am/ActivityManagerService.java中有以下的代码: final ProcessRecord addAppLocked(ApplicationInfo info) { ProcessRecord app = getProcessRecordLocked(info.processName, info.uid); if (app == null) { app = newProcessRecordLocked(null, info, null); mProcessNames.put(info.processName, info.uid, app); updateLruProcessLocked(app, true, true); } if ((info.flags&(ApplicationInfo.FLAG_SYSTEMApplicationInfo.FLAG_PERSISTENT)) == (ApplicationInfo.FLAG_SYSTEMApplicationInfo.FLAG_PERSISTENT)) { app.persistent = true; app.maxAdj = CORE_SERVER_ADJ; // 这个常数值为-12。 } if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) { mPersistentStartingProcesses.add(app); startProcessLocked(app, "added application", app.processName); } return app; } 可见要想成为core service (即app.maxAdj = CORE_SERVER_ADJ(-12)),应用程序需要FLAG_SYSTEM和FLAG_PERSISTENT两个标志,FLAG_SYSTEM指的是应用位于/system/app下,FLAG_PERSISTENT就是指persistent属性。 而对于frameworks/base/services/java/com/android/server/SystemServer.java,则调用 ActivityManagerService.setSystemProcess(); 把自己的 app.maxAdj 设置成SYSTEM_ADJ,即-16。 原理: Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。由此带来三个问题: 1) 回收规则: 什么时候回收与回收哪一个? 2) 避免误杀: 如何阻止被回收? 3) 数据恢复与保存: 被回收了怎么办? Android将进程分为6个等级,它们按优先级顺序由高到低依次是: 1.前台进程( FOREGROUND_APP) 2.可视进程(VISIBLE_APP ) 3. 次要服务进程(SECONDARY_SERVER ) 4.后台进程 (HIDDEN_APP) 5.内容供应节点(CONTENT_PROVIDER) 6.空进程(EMPTY_APP) 特征: 1.如果一个进程里面同时包含service和可视的activity,那么这个进程应该归于可视进程,而不是service进程。 2.另外,如果其他进程依赖于它的话,一个进程的等级可以提高。例如,一个A进程里的service被绑定到B进程里的组件上,进程A将总被认为至少和B进程一样重要。 3.系统中的phone服务被划分到前台进程而不是次要服务进程. 在android中,进程的oom_adj值也就代表了它的优先级。oom_adj值越高代表该进程优先级越低。文件/init.rc中有以下属性设置: setprop ro.FOREGROUND_APP_ADJ 0 setprop ro.VISIBLE_APP_ADJ 1 setprop ro.SECONDARY_SERVER_ADJ 2 setprop ro.HIDDEN_APP_MIN_ADJ 7 setprop ro.CONTENT_PROVIDER_ADJ 14 setprop ro.EMPTY_APP_ADJ 15 /init.rc中,将PID为1的进程(init进程)的oom_adj设置为SYSTEM_ADJ(-16): # Set init its forked children's oom_adj. write /proc/1/oom_adj -16 查看本机设置: cat /sys/mole/lowmemorykiller/parameters/adj 0,1,2,7,14,15 回收时机: 文件/init.rc中: setprop ro.FOREGROUND_APP_MEM 1536 // 6M setprop ro.VISIBLE_APP_MEM 2048 // 8M setprop ro.SECONDARY_SERVER_MEM 4096 // 16M setprop ro.HIDDEN_APP_MEM 5120 // 20M setprop ro.CONTENT_PROVIDER_MEM 5632 // 22.4M setprop ro.EMPTY_APP_MEM 6144 // 24M 这些数字也就是对应的内存阈值,一旦低于该值,Android便开始按顺序关闭相应等级的进程。 注意这些数字的单位是page: 1 page = 4 kB。所以上面的六个数字对应的就是(MB): 6,8,16,20,22,24。 查看现在的内存阈值设置: cat /sys/mole/lowmemorykiller/parameters/minfree 要想重新设置该值(对应不同的需求): echo "1536,2048,4096,5120,15360,23040">/sys/mole/lowmemorykiller/parameters/minfree 这样当可用内存低于90MB的时候便开始杀死"空进程",而当可用内存低于60MB的时候才开始杀死"内容供应节点"类进程。 具体的回收实现在ActivityManagerService.java中的函数trimApplications(): 1.首先移除package已被卸载的无用进程; 2.基于进程当前状态,更新oom_adj值,然后进行以下操作: 1) 移除没有activity在运行的进程; 2) 如果AP已经保存了所有的activity状态,结束这个AP。 3. 最后,如果目前还是有很多activities 在运行,那么移除那些activity状态已经保存好的activity。 更新oom_adj的值: 在ActivityManagerService.java文件的ComputeOomAdjLocked() 中计算出进程的oom_adj,例如: if (app == TOP_APP) { // The last app on the list is the foreground app. adj = FOREGROUND_APP_ADJ; app.adjType = "top-activity"; } Android kernel中的low memory killer Android的Low Memory Killer根据需要(当系统内存短缺时)杀死进程释放其内存,源代码在kernel/drivers/misc/lowmemorykiller.c中。简单说,就是寻找一个最合适的进程杀死,从而释放它占用的内存。 最合适的进程是: • oom_adj越大 • 占用物理内存越多 一旦一个进程被选中,内核会发送SIGKILL信号将之杀死: for_each_process(p) { …… if(selected == NULL p->oomkilladj > selected->oomkilladj (p->oomkilladj == selected->oomkilladj && tasksize > selected_tasksize)) { selected = p; } } if(selected != NULL) { force_sig(SIGKILL, selected); } 查看LRU列表:adb shell mpsys activity 当activitydemo在前台时: 包含Service的进程的优先级比较高,在computeOomAdjLocked中将其分为了两小类: static final int MAX_SERVICE_INACTIVITY = 30*60*1000; if (now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) { if (adj > SECONDARY_SERVER_ADJ) { adj = SECONDARY_SERVER_ADJ; app.adjType = "started-services"; app.hidden = false; } } if (adj > SECONDARY_SERVER_ADJ) { app.adjType = "started-bg-services"; } 完全让进程不被kill是不可能的,我们可以通过一些操作,使进程被kill的几率变小: 1) 提高进程的优先级: * 后台操作采用运行于前台的Service形式,因为一个运行着service的进程比一个运行着后台activity的等级高; * 按back键使得进程中的activity在后台运行而不是destory,需重载back按键(没有任何activity在运行的进程优先被杀). * 依赖于其他优先级高的进程; 2) 强制修改进程属性: * 在进程中设置:setPersistent(true); * 在Manifest文件中设置(如上)。

⑧ 如何防止android app被kill

相较于/data/app下的应用,放在/system/app下的应用享受更多的特权,比如若在其Manifest.xml文件中设置persistent属性为true,则可使其免受out-of-memory
killer的影响。如应用程序'Phone'的AndroidManifest.xml文件:

<application
android:name="PhoneApp"
android:persistent="true"
android:label="@string/dialerIconLabel"
android:icon="@drawable/ic_launcher_phone">
...
</application>
设置后app提升为系统核心级别,任何情况下不会被kill掉, settings->applications里面也会屏蔽掉stop操作。

这样设置前的log: Proc #19: adj=svc /B 4067b028 255:com.xxx.xxx/10001 (started-services)

# cat /proc/255/oom_adj

4

设置后的log: PERS #19: adj=core /F 406291f0 155:com.xxx.xxx/10001 (fixed)

# cat /proc/155/oom_adj

-12 # 这是CORE_SERVER_ADJ
注:init进程的oom_adj为-16(即SYSTEM_ADJ): cat /proc/1/oom_adj

Android相关部分分析:
在文件frameworks/base/services/java/com/android/server/am/ActivityManagerService.java中有以下的代码:
final
ProcessRecord addAppLocked(ApplicationInfo info) {
ProcessRecord
app = getProcessRecordLocked(info.processName, info.uid);

if
(app == null) {
app
= newProcessRecordLocked(null, info, null);
mProcessNames.put(info.processName,
info.uid, app);
updateLruProcessLocked(app,
true, true);
}

if
((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT))
==
(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) {
app.persistent
= true;
app.maxAdj
= CORE_SERVER_ADJ; //
这个常数值为-12。
}
if
(app.thread == null && mPersistentStartingProcesses.indexOf(app)
< 0) {
mPersistentStartingProcesses.add(app);
startProcessLocked(app,
"added application", app.processName);
}

return
app;
}


见要想成为core service (即app.maxAdj =
CORE_SERVER_ADJ(-12)),应用程序需要FLAG_SYSTEM和FLAG_PERSISTENT两个标志,FLAG_SYSTEM指
的是应用位于/system/app下,FLAG_PERSISTENT就是指persistent属性。

而对于frameworks/base/services/java/com/android/server/SystemServer.java,则调用
ActivityManagerService.setSystemProcess();
把自己的 app.maxAdj 设置成SYSTEM_ADJ,即-16。

原理:
Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。由此带来三个问题:
1)
回收规则: 什么时候回收与回收哪一个?
2)
避免误杀: 如何阻止被回收?
3)
数据恢复与保存: 被回收了怎么办?

Android将进程分为6个等级,它们按优先级顺序由高到低依次是:
1.前台进程(
FOREGROUND_APP)
2.可视进程(VISIBLE_APP
)
3.
次要服务进程(SECONDARY_SERVER )
4.后台进程
(HIDDEN_APP)
5.内容供应节点(CONTENT_PROVIDER)
6.空进程(EMPTY_APP)

特征:
1.如果一个进程里面同时包含service和可视的activity,那么这个进程应该归于可视进程,而不是service进程。
2.另外,如果其他进程依赖于它的话,一个进程的等级可以提高。例如,一个A进程里的service被绑定到B进程里的组件上,进程A将总被认为至少和B进程一样重要。
3.系统中的phone服务被划分到前台进程而不是次要服务进程.

在android中,进程的oom_adj值也就代表了它的优先级。oom_adj值越高代表该进程优先级越低。文件/init.rc中有以下属性设置:
setprop
ro.FOREGROUND_APP_ADJ 0
setprop
ro.VISIBLE_APP_ADJ 1
setprop
ro.SECONDARY_SERVER_ADJ 2
setprop
ro.HIDDEN_APP_MIN_ADJ 7
setprop
ro.CONTENT_PROVIDER_ADJ 14
setprop
ro.EMPTY_APP_ADJ 15
/init.rc中,将PID为1的进程(init进程)的oom_adj设置为SYSTEM_ADJ(-16):
#
Set init its forked children's oom_adj.
write
/proc/1/oom_adj -16

查看本机设置:
cat /sys/mole/lowmemorykiller/parameters/adj
0,1,2,7,14,15

回收时机:
文件/init.rc中:
setprop
ro.FOREGROUND_APP_MEM 1536 // 6M
setprop
ro.VISIBLE_APP_MEM 2048 // 8M
setprop
ro.SECONDARY_SERVER_MEM 4096 // 16M
setprop
ro.HIDDEN_APP_MEM 5120 // 20M
setprop
ro.CONTENT_PROVIDER_MEM 5632 // 22.4M
setprop
ro.EMPTY_APP_MEM 6144 // 24M
这些数字也就是对应的内存阈值,一旦低于该值,Android便开始按顺序关闭相应等级的进程。
注意这些数字的单位是page: 1 page = 4 kB。所以上面的六个数字对应的就是(MB): 6,8,16,20,22,24。

查看现在的内存阈值设置:
cat /sys/mole/lowmemorykiller/parameters/minfree

要想重新设置该值(对应不同的需求):
echo "1536,2048,4096,5120,15360,23040">/sys/mole/lowmemorykiller/parameters/minfree
这样当可用内存低于90MB的时候便开始杀死"空进程",而当可用内存低于60MB的时候才开始杀死"内容供应节点"类进程。

具体的回收实现在ActivityManagerService.java中的函数trimApplications():
1.首先移除package已被卸载的无用进程;
2.基于进程当前状态,更新oom_adj值,然后进行以下操作:
1)
移除没有activity在运行的进程;
2)
如果AP已经保存了所有的activity状态,结束这个AP。
3.
最后,如果目前还是有很多activities 在运行,那么移除那些activity状态已经保存好的activity。

更新oom_adj的值:
在ActivityManagerService.java文件的ComputeOomAdjLocked() 中计算出进程的oom_adj,例如:
if
(app == TOP_APP) {
//
The last app on the list is the foreground app.
adj
= FOREGROUND_APP_ADJ;
app.adjType
= "top-activity";
}

Android kernel中的low memory killer
Android的Low Memory Killer根据需要(当系统内存短缺时)杀死进程释放其内存,源代码在kernel/drivers/misc/lowmemorykiller.c中。简单说,就是寻找一个最合适的进程杀死,从而释放它占用的内存。
最合适的进程是:
• oom_adj越大
• 占用物理内存越多

一旦一个进程被选中,内核会发送SIGKILL信号将之杀死:
for_each_process(p)
{
……
if(selected
== NULL || p->oomkilladj
> selected->oomkilladj ||
(p->oomkilladj
== selected->oomkilladj && tasksize > selected_tasksize))
{
selected
= p;
}
}
if(selected
!= NULL) {
force_sig(SIGKILL,
selected);
}

查看LRU列表:adb shell mpsys activity
当activitydemo在前台时:
包含Service的进程的优先级比较高,在computeOomAdjLocked中将其分为了两小类:
static
final int MAX_SERVICE_INACTIVITY = 30*60*1000;
if
(now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) {
if
(adj > SECONDARY_SERVER_ADJ) {
adj
= SECONDARY_SERVER_ADJ;
app.adjType
= "started-services";
app.hidden
= false;
}
}
if
(adj > SECONDARY_SERVER_ADJ) {
app.adjType
= "started-bg-services";
}

完全让进程不被kill是不可能的,咱们可以通过一些操作,使进程被kill的几率变小:
1)
提高进程的优先级:
*
后台操作采用运行于前台的Service形式,因为一个运行着service的进程比一个运行着后台activity的等级高;
*
按back键使得进程中的activity在后台运行而不是destory,需重载back按键(没有任何activity在运行的进程优先被杀).
*
依赖于其他优先级高的进程;

2)
强制修改进程属性:
*
在进程中设置:setPersistent(true);

*
在Manifest文件中设置(如上)。

阅读全文

与android60demo相关的资料

热点内容
墨镜慧眼怎么下载厂商的app 浏览:59
iphone加密专线 浏览:491
aes产生加密文件 浏览:411
编程实现蓝牙通信 浏览:766
怎么恢复掉签的app 浏览:847
服务器部署ip地址 浏览:321
涉密场所周边安全防护距离算法 浏览:672
安卓fpse模拟器怎么设置加速 浏览:946
建行app怎么生成电子签章 浏览:508
获取当前时间javadate 浏览:73
带密码的wifi如何加密 浏览:237
服务器怎么变成阵列 浏览:718
web前端黑客技术pdf 浏览:71
育儿百科全书pdf 浏览:600
任务栏启动命令 浏览:912
编译优化等级区别 浏览:757
unix网关命令 浏览:877
想自己做网站要学编程吗 浏览:597
租个服务器开个私服需要什么 浏览:273
图片换成pdf格式 浏览:664