保持Service不被Kill掉的方法–双Service守护 & Android实现双进程守护

本文分为两个部分,*部分为双Service守护,第二部分为双进程守护

*部分:

一、Service简介:
java.lang.Object

↳android.content.Context

↳android.content.ContextWrapper

↳android.app.Service

Service是应用程序Application的一个组件(component)。
它的作用有两点:1.用来提供一个长期在后台运行并且不与用户交互的操作,2.也可以为其他应用程序提供服务。
Service必须和其他四大组件一样,使用<service>标签在AndroidManifest.xml中进行声明。
启动service有两种方式Context.startService() 和 Context.bindService()。

注意,除了特别指定外,service并不是单独的进程,一般service在其宿主进程的主线程(UI Thread)中运行【当然也可以在新的线程中startService,这样Service就不是在MainThread了】。这意味着,如果您的服务要做任何 耗时(如 MP3 播放) 或阻塞 (比如网络) 操作,它应该产生它自己的线程,用来做那项工作。(service不是单独的进程也不是单独的线程)

Service提供了两大功能:
Context.startService()用来在后台启动一个服务;
Context.bindService()用来绑定其他服务,以此来获取其他service提供的服务;

 

本地服务 Local Service 用于应用程序内部

它可以启动并运行,直至有人停止了它或它自己停止。在这种方式下,它以调用Context.startService()启动,而以调用Context.stopService()结束。它可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。不论调用了多少次startService()方法,你只需要调用一次stopService()来停止服务。

【用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好】

 

远程服务 Remote Service 用于android系统内部的应用程序之间

它可以通过自己定义并暴露出来的接口进行程序操作。客户端建立一个到服务对象的连接,并通过那个连接来调用服务。连接以调用Context.bindService()方法建立,以调用 Context.unbindService()关闭。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。

【可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可】

 

 

二、Service运行方式和生命周期图:

以startService()启动服务,系统将通过传入的Intent在底层搜索相关符合Intent里面信息的service。如果服务没有启动则先运行onCreate,然后运行onStartCommand (可在里面处理启动时传过来的Intent和其他参数),直到明显调用stopService或者stopSelf才将停止Service。无论运行startService多少次,只要调用一次stopService或者stopSelf,Service都会停止。使用stopSelf(int)方法可以保证在处理好intent后再停止。onStartCommand ,在2.0后被引入用于service的启动函数,2.0之前为public void onStart(Intent intent, int startId) 。

以bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止。onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。采用Context.bindService()方法启动服务时只能调用onUnbind()方法解除调用者与服务解除,服务结束时会调用onDestroy()方法。

 

(注意这个新老API的改变)

void onStart(Intent intent, int startId)
This method was deprecated      in API level 5.    Implement onStartCommand(Intent, int, int) instead.

 

int onStartCommand(Intent intent, int flags, int startId)
Called by the system every time a client explicitly starts the service by calling  startService(Intent), providing the arguments it supplied and a  unique integer token representing the start request.

 

三、Service的优先级

官方文档告诉我们,Android系统会尽量保持拥有service的进程运行,只要在该service已经被启动(start)或者客户端连接(bindService)到它。当内存不足时,需要保持,拥有service的进程具有较高的优先级。

1. 如果service正在调用onCreate,onStartCommand或者onDestory方法,那么用于当前service的进程则变为前台进程以避免被killed。
2. 如果当前service已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,但是比那些不可见的进程更重要,这就意味着service一般不会被killed.
3. 如果客户端已经连接到service (bindService),那么拥有Service的进程则拥有*高的优先级,可以认为service是可见的。
4. 如果service可以使用startForeground(int, Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时killed。
5. 如果有其他的应用组件作为Service,Activity等运行在相同的进程中,那么将会增加该进程的重要性。

 

四、保持service不被kill掉

方法一:

START_STICKY is used for services that are explicitly started and stopped as needed, while START_NOT_STICKY or START_REDELIVER_INTENT are used for services that should only remain running while processing any commands sent to them

onStartCommand方法几个返回值简介:

1、START_STICKY

在运行onStartCommand后service进程被kill后,那将保留在开始状态,但是不保留那些传入的intent。不久后service就会再次尝试重新创建,因为保留在开始状态,在创建     service后将保证调用onstartCommand。如果没有传递任何开始命令给service,那将获取到null的intent。

2、START_NOT_STICKY

在运行onStartCommand后service进程被kill后,并且没有新的intent传递给它。Service将移出开始状态,并且直到新的明显的方法(startService)调用才重新创建。因为如果没有传递任何未决定的intent那么service是不会启动,也就是期间onstartCommand不会接收到任何null的intent。

3、START_REDELIVER_INTENT

在运行onStartCommand后service进程被kill后,系统将会再次启动service,并传入*后一个intent给onstartCommand。直到调用stopSelf(int)才停止传递intent。如果在被kill后还有未处理好的intent,那被kill后服务还是会自动启动。因此onstartCommand不会接收到任何null的intent。

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
flags = START_STICKY;
return super.onStartCommand(intent, flags, startId);
}
【结论】 手动返回START_STICKY,亲测当service因内存不足被kill,当内存又有的时候,service又被重新创建,比较不错,但是不能保证任何情况下都被重建,比如进程被干掉了….

方法二:

提升service优先级

在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = “1000”这个属性设置*高优先级,1000是*高值,如果数字越小则优先级越低,同时适用于广播。

<service
android:name=”com.dbjtech.acbxt.waiqin.UploadService”
android:enabled=”true” >
<intent-filter android:priority=”1000″ >
<action android:name=”com.dbjtech.myservice” />
</intent-filter>
</service>
【结论】目前看来,priority这个属性貌似只适用于broadcast,对于Service来说可能无效

 

方法三:

提升service进程优先级

Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。Android将进程分为6个等级,它们按优先级顺序由高到低依次是:

1.前台进程( FOREGROUND_APP)
2.可视进程(VISIBLE_APP )
3. 次要服务进程(SECONDARY_SERVER )
4.后台进程 (HIDDEN_APP)
5.内容供应节点(CONTENT_PROVIDER)
6.空进程(EMPTY_APP)

当service运行在低内存的环境时,将会kill掉一些存在的进程。因此进程的优先级将会很重要,可以使用startForeground将service放到前台状态。这样在低内存时被kill的几率会低一些。

在onStartCommand方法内添加如下代码:

Notification notification = new Notification(R.drawable.ic_launcher,getString(R.string.app_name), System.currentTimeMillis());

PendingIntent pendingintent = PendingIntent.getActivity(this, 0,new Intent(this, AppMain.class), 0);
notification.setLatestEventInfo(this, “uploadservice”, “请保持程序在后台运行”, pendingintent);
startForeground(0x111, notification);

注意在onDestroy里还需要stopForeground(true),运行时在下拉列表会看到自己的APP在:

 

【结论】如果在*度*度低内存的压力下,该service还是会被kill掉,并且不一定会restart

保持Service不被Kill掉的方法–双Service守护,代码如下:

 

AndroidManifest.xml:
<activity
android:name=”.MainActivity”
android:label=”@string/app_name” >
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>

<service
android:name=”ServiceOne”
android:process=”:remote” >
<intent-filter>
<action android:name=”com.example.servicedemo.ServiceOne” />
</intent-filter>
</service>

<service
android:name=”ServiceTwo”
android:process=”:remote” >
<intent-filter>
<action android:name=”com.example.servicedemo.ServiceTwo” />
</intent-filter>
</service>
MainActivity.java:
package com.example.servicedemo;

import java.util.ArrayList;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Intent serviceOne = new Intent();
serviceOne.setClass(MainActivity.this, ServiceOne.class);
startService(serviceOne);

Intent serviceTwo = new Intent();
serviceTwo.setClass(MainActivity.this, ServiceTwo.class);
startService(serviceTwo);
}

public static boolean isServiceWorked(Context context, String serviceName) {
ActivityManager myManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ArrayList<RunningServiceInfo> runningService = (ArrayList<RunningServiceInfo>) myManager.getRunningServices(Integer.MAX_VALUE);
for (int i = 0; i < runningService.size(); i++) {
if (runningService.get(i).service.getClassName().toString().equals(serviceName)) {
return true;
}
}
return false;
}
}

ServiceOne.java:
package com.example.servicedemo;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class ServiceOne extends Service {

public final static String TAG = “com.example.servicedemo.ServiceOne”;

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, “onStartCommand”);

thread.start();
return START_STICKY;
}

Thread thread = new Thread(new Runnable() {

@Override
public void run() {
Timer timer = new Timer();
TimerTask task = new TimerTask() {

@Override
public void run() {
Log.e(TAG, “ServiceOne Run: “+System.currentTimeMillis());
boolean b = MainActivity.isServiceWorked(ServiceOne.this, “com.example.servicedemo.ServiceTwo”);
if(!b) {
Intent service = new Intent(ServiceOne.this, ServiceTwo.class);
startService(service);
Log.e(TAG, “Start ServiceTwo”);
}
}
};
timer.schedule(task, 0, 1000);
}
});

@Override
public IBinder onBind(Intent arg0) {
return null;
}

}

ServiceTwo.java:
package com.example.servicedemo;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class ServiceTwo extends Service {

public final static String TAG = “com.example.servicedemo.ServiceTwo”;

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, “onStartCommand”);

thread.start();
return START_REDELIVER_INTENT;
}

Thread thread = new Thread(new Runnable() {

@Override
public void run() {
Timer timer = new Timer();
TimerTask task = new TimerTask() {

@Override
public void run() {
Log.e(TAG, “ServiceTwo Run: ” + System.currentTimeMillis());
boolean b = MainActivity.isServiceWorked(ServiceTwo.this, “com.example.servicedemo.ServiceOne”);
if(!b) {
Intent service = new Intent(ServiceTwo.this, ServiceOne.class);
startService(service);
}
}
};
timer.schedule(task, 0, 1000);
}
});

@Override
public IBinder onBind(Intent arg0) {
return null;
}

}
第二部分:

做过android开发的人应该都知道应用会在系统资源匮乏的情况下被系统杀死!当后台的应用被系统回收之后,如何重新恢复它呢?网上对此问题有很多的讨论。这里先总结一下网上流传的各种解决方案,看看这些办法是不是真的可行。
1.提高优先级
这个办法对普通应用而言,应该只是降低了应用被杀死的概率,但是如果真的被系统回收了,还是无法让应用自动重新启动!

2.让service.onStartCommand返回START_STICKY
通过实验发现,如果在adb shell当中kill掉进程模拟应用被意外杀死的情况(或者用360手机卫士进行清理操作),如果服务的onStartCommand返回START_STICKY,在eclipse的进程管理器中会发现过一小会后被杀死的进程的确又会出现在任务管理器中,貌似这是一个可行的办法。但是如果在系统设置的App管理中选择强行关闭应用,这时候会发现即使onStartCommand返回了START_STICKY,应用还是没能重新启动起来!

3.android:persistent=”true”
网上还提出了设置这个属性的办法,通过实验发现即使设置了这个属性,应用程序被kill之后还是不能重新启动起来的!

4.让应用成为系统应用
实验发现即使成为系统应用,被杀死之后也不能自动重新启动。但是如果对一个系统应用设置了persistent=”true”,情况就不一样了。实验表明对一个设置了persistent属性的系统应用,即使kill掉会立刻重启。一个设置了persistent=”true”的系统应用,在android中具有core service优先级,这种优先级的应用对系统的low memory killer是免疫的!

OK,说了半天,只有core service优先级的应用才能保证在被意外杀死之后做到立刻满血复活。而普通应用要想成为系统应用就必须要用目标机器的签名文件进行签名,但这样又造成了应用无法保证兼容所有不同厂商的产品。那么该怎么办呢?这里就来说一说双进程守护。网上也有人提到过双进程守护的办法,但是很少能搜索到类似的源码!如果从进程管理器重观察会发现新浪微博或者360卫视都有两个相关的进程,其中一个就是守护进程,由此可以猜到这些商业级的软件也采用了双进程守护的办法。

什么是双进程守护呢?顾名思义就是两个进程互相监视对方,发现对方挂掉就立刻重启!不知道应该把这样的一对进程是叫做相依为命呢还是难兄难弟好呢,但总之,双进程守护的确是一个解决问题的办法!相信说到这里,很多人已经迫切的想知道如何实现双进程守护了。这篇文章就介绍一个用NDK来实现双进程保护的办法,不过首先说明一点,下面要介绍的方法中,会损失不少的效率,反应到现实中就是会使手机的耗电量变大!但是这篇文章仅仅是抛砖引玉,相信看完之后会有更多高人指点出更妙的实现办法。

需要了解些什么?
这篇文章中实现双进程保护的方法基本上是纯的NDK开发,或者说全部是用C++来实现的,需要双进程保护的程序,只需要在程序的任何地方调用一下JAVA接口即可。下面几个知识点是需要了解的:
1.linux中多进程;
2.unix domain套接字实现跨进程通信;
3.linux的信号处理;
4.exec函数族的用法;

其实这些东西本身并不是多复杂的技术,只是我们把他们组合起来实现了一个双进程守护而已,没有想象中那么神秘!在正式贴出代码之前,先来说说几个实现双进程守护时的关键点:
1.父进程如何监视到子进程(监视进程)的死亡?
很简单,在linux中,子进程被终止时,会向父进程发送SIG_CHLD信号,于是我们可以安装信号处理函数,并在此信号处理函数中重新启动创建监视进程;
2.子进程(监视进程)如何监视到父进程死亡?
当父进程死亡以后,子进程就成为了孤儿进程由Init进程领养,于是我们可以在一个循环中读取子进程的父进程PID,当变为1就说明其父进程已经死亡,于是可以重启父进程。这里因为采用了循环,所以就引出了之前提到的耗电量的问题。
3.父子进程间的通信
有一种办法是父子进程间建立通信通道,然后通过监视此通道来感知对方的存在,这样不会存在之前提到的耗电量的问题,在本文的实现中,为了简单,还是采用了轮询父进程PID的办法,但是还是留出了父子进程的通信通道,虽然暂时没有用到,但可备不时之需!

 

腾讯的面试官问我:应用程序死了如何恢复?确实,双进程守护只能做到进程被杀死后重新启动,但是重启后如何恢复到之前的状态这是一个问题。因为进程被意外杀死的情况,onSaveInstance是来不及执行的,所以程序的状态没法保存!对于双进程守护来说,不知道是不是可以再父进程进入后台以后(onStop),把数据收集起来保存到子进程中,然后父进程重启以后从子进程中取出这些信息呢?这是一个办法,但是上面说明的双进程守护程序的实现中还做不到,因为父进程重启以后,子进程也挂掉重新建立了,要想实现优雅的恢复,还得在做出点改进才是!只能实时保存数据到数据库等。

Android全局异常捕获并弹窗提示

Android 难免有崩溃的时候,但是崩溃了该如何处理呢?虽然那天有位同仁说 “既然崩溃了,用户体验就差了,心里会想这是毛APP,下次也不想用了” ,所以检查BUG以防崩溃是必须的,但是也需要一个后备方案,崩溃了能友好些,我们也能收集一些崩溃的信息。
说到全局捕获异常的UncaughtExceptionHandler,就不得不说期间遇到的各种坑:
1. 初始化肯定在Application,网上说的Activity启各种不认同。但在Application启就存在不能弹AlertDialog的问题(目前不确定,不知道是自己哪里没处理好还是的确是这个问题,有时间再验证一下)
2. 崩溃不一定是单次,在多层Activity中,崩溃一个顶层的Activity可能导致下层的Activity连续崩溃,所以uncaughtException可能会捕获到多次崩溃信息(具体影响后面会说到)
先来张崩溃后的效果图:
背景是另一个APP,当前的APP已崩溃并弹出该提示

%title插图%num
实现流程:
写个类继承于UncaughtExceptionHandler,实现方法
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultHandler != null) {
// 如果用户没有处理则让系统默认的异常处理器来处
mDefaultHandler.uncaughtException(thread, ex);
} else {
// 跳转到崩溃提示Activity
Intent intent = new Intent(mContext, CrashDialog.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
System.exit(0);// 关闭已奔溃的app进程
}
}

然后转handleException方法处理异常:
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}

// 收集错误信息
getCrashInfo(ex);

return true;
}

上面的代码很清楚了,如果异常被捕获到并且异常信息不会NULL,处理完则跳转到CrashDialog。为什么跳Activity用Dialog样式,而不直接弹AlertDialog,是因为的确弹不出来。
收集错误信息:
private void getCrashInfo(Throwable ex) {
Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.close();
String errorMessage = writer.toString();
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String mFilePath = Environment.getExternalStorageDirectory() + “/” + App.ERROR_FILENAME;
FileTxt.WirteTxt(mFilePath, FileTxt.ReadTxt(mFilePath) + ‘\n’ + errorMessage);
} else {
Log.i(App.TAG, “哦豁,说好的SD呢…”);
}
}

是的,我把错误信息写到了存储并在刚才的CrashDialog中读取。为什么不直接传值呢?因为刚说到的坑第2条,多次崩溃的情况下,将导致直接传值只会传*后一次崩溃信息,而*后一次崩溃信息并不是主要引发崩溃的点,收集上来的错误信息可读性不大。那为什么我不写个全局变量来存储呢?因为尝试过,不知道是机型问题(Huawei Mate7 – API 23)还是全部问题,变量压根就不记录数据,*后只有将信息依次写到存储。
主要代码
App.java
package cn.qson.androidcrash;

/**
* @author x024
*/

import android.app.Application;

public class App extends Application {

public final static String TAG = “x024”;
public final static String ERROR_FILENAME = “x024_error.log”;

@Override
public void onCreate() {
super.onCreate();

CrashHanlder.getInstance().init(this);

}
}

CrashHanlder.java
package cn.qson.androidcrash;

/**
* @author x024
*/

import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;

import android.content.Context;
import android.content.Intent;
import android.os.Environment;
import android.util.Log;

/**
* 收集错误报告并上传到服务器
*
* @author x024
*
*/
public class CrashHanlder implements UncaughtExceptionHandler {
private Thread.UncaughtExceptionHandler mDefaultHandler;
// CrashHandler实例
private static CrashHanlder INSTANCE = new CrashHanlder();
// 程序的Context对象
private Context mContext;

private CrashHanlder() {
}

public static CrashHanlder getInstance() {
return INSTANCE;
}

/**
* 初始化
*
* @param context
*/
public void init(Context context) {
mContext = context;
// 获取系统默认的UncaughtException处理
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// 设置该CrashHandler为程序的默认处理
Thread.setDefaultUncaughtExceptionHandler(this);
}

/**
* 异常捕获
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultHandler != null) {
// 如果用户没有处理则让系统默认的异常处理器来处
mDefaultHandler.uncaughtException(thread, ex);
} else {
// 跳转到崩溃提示Activity
Intent intent = new Intent(mContext, CrashDialog.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
System.exit(0);// 关闭已奔溃的app进程
}
}

/**
* 自定义错误捕获
*
* @param ex
* @return true:如果处理了该异常信息;否则返回false.
*/
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}

// 收集错误信息
getCrashInfo(ex);

return true;
}

/**
* 收集错误信息
*
* @param ex
*/
private void getCrashInfo(Throwable ex) {
Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.close();
String errorMessage = writer.toString();
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String mFilePath = Environment.getExternalStorageDirectory() + “/” + App.ERROR_FILENAME;
FileTxt.WirteTxt(mFilePath, FileTxt.ReadTxt(mFilePath) + ‘\n’ + errorMessage);
} else {
Log.i(App.TAG, “哦豁,说好的SD呢…”);
}

}

}

CrashDialog.java
package cn.qson.androidcrash;

/**
* @author x024
*/

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class CrashDialog extends Activity {

private String mFilePath;
private Button btnExit, btnRestart;
private Boolean StorageState = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crash);
CrashDialog.this.setFinishOnTouchOutside(false);
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
mFilePath = Environment.getExternalStorageDirectory() + “/” + App.ERROR_FILENAME;
StorageState = true;
} else {
Log.i(App.TAG, “哦豁,说好的SD呢…”);
}

new Thread(upLog).start();
initView();
}

private void initView() {
btnExit = (Button) findViewById(R.id.cash_exit);
btnRestart = (Button) findViewById(R.id.cash_restart);

btnExit.setOnClickListener(mOnClick);
btnRestart.setOnClickListener(mOnClick);

}

OnClickListener mOnClick = new OnClickListener() {

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.cash_exit:
exit();
break;
case R.id.cash_restart:
restart();
break;
default:
break;
}
}
};

// 上传错误信息
Runnable upLog = new Runnable() {
@Override
public void run() {
try {

String Mobile = Build.MODEL;
String maxMemory = “” + getmem_TOLAL() / 1024 + “m”;
String nowMemory = “” + getmem_UNUSED(CrashDialog.this) / 1024 + “m”;
String eMessage = “未获取到错误信息”;
if (StorageState) {
eMessage = FileTxt.ReadTxt(mFilePath).replace(“‘”, “”);
}
Log.i(App.TAG, “Mobile:” + Mobile + ” | maxMemory:” + maxMemory + ” |nowMemory:” + nowMemory
+ ” |eMessage:” + eMessage);

/**
* 可以在这调你自己的接口上传信息
*/
} catch (Exception e) {
e.printStackTrace();
}
}
};

private void exit() {
FileTxt.deleteFile(mFilePath);
System.exit(0);
android.os.Process.killProcess(android.os.Process.myPid());
}

private void restart() {
Intent intent = getBaseContext().getPackageManager()
.getLaunchIntentForPackage(getBaseContext().getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
exit();
}

@Override
public void onBackPressed() {
super.onBackPressed();
exit();
}

// 获取可用内存
public static long getmem_UNUSED(Context mContext) {
long MEM_UNUSED;
ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
am.getMemoryInfo(mi);

MEM_UNUSED = mi.availMem / 1024;
return MEM_UNUSED;
}

// 获取剩余内存
public static long getmem_TOLAL() {
long mTotal;
String path = “/proc/meminfo”;
String content = null;
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(path), 8);
String line;
if ((line = br.readLine()) != null) {
content = line;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
int begin = content.indexOf(‘:’);
int end = content.indexOf(‘k’);

content = content.substring(begin + 1, end).trim();
mTotal = Integer.parseInt(content);
return mTotal;
}

}

完整代码:http://download.csdn.net/detail/hx7013/9710757

Android 监听应用程序安装和卸载

*、             新建监听类:BootReceiver继承BroadcastReceiver

publicclass BootReceiver extends BroadcastReceiver {
@Override

public void onReceive(Context context,Intent intent) {
//接收广播:系统启动完成后运行程序

if(intent.getAction().equals(“android.intent.action.BOOT_COMPLETED”)) {
Intent newIntent = new Intent(context,WatchInstall.class);

newIntent.setAction(“android.intent.action.MAIN”);             newIntent.addCategory(“android.intent.category.LAUNCHER”);           newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            context.startActivity(newIntent);

}

//接收广播:设备上新安装了一个应用程序包后自动启动新安装应用程序。

if(intent.getAction().equals(“android.intent.action.PACKAGE_ADDED”)) {
String packageName =intent.getDataString().substring(8);

System.out.println(“—————” + packageName);

Intent newIntent = new Intent();

newIntent.setClassName(packageName,packageName+ .MainActivity”);

newIntent.setAction(“android.intent.action.MAIN”);            newIntent.addCategory(“android.intent.category.LAUNCHER”);             newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

context.startActivity(newIntent);

}

//接收广播:设备上删除了一个应用程序包。

if(intent.getAction().equals(“android.intent.action.PACKAGE_REMOVED”)){
System.out.println(“********************************”);

}

}

第二、             修改AndroidManifest.xml配置文件

<?xmlversion=”1.0″ encoding=”UTF-8″?>

<manifestxmlns:android=”http://schemas.android.com/apk/res/android”

package=”org.me.watchinstall”>

<application>

<receiverandroid:name=”.BootReceiver”

android:label=”@string/app_name”>

<intent-filter>

<actionandroid:name=”android.intent.action.BOOT_COMPLETED”/>

<categoryandroid:name=”android.intent.category.LAUNCHER” />

</intent-filter>

<intent-filter>

<actionandroid:name=”android.intent.action.PACKAGE_ADDED” />

<actionandroid:name=”android.intent.action.PACKAGE_REMOVED” />

<data android:scheme=”package” />

<!– 注意!!这句必须要加,否则接收不到BroadCast –>

</intent-filter>

</receiver>

<activityandroid:name=”.WatchInstall”android:label=”WatchInstall”>

<intent-filter>

<actionandroid:name=”android.intent.action.MAIN”/>

<categoryandroid:name=”android.intent.category.LAUNCHER”/>

</intent-filter>

</activity>

</application>

</manifest>

【Android】Service学习之本地服务

 Service是在一段不定的时间运行在后台,不和用户交互应用组件。每个Service必须在manifest中 通过<service>来声明。可以通过contect.startservice和contect.bindserverice来启动。
    Service和其他的应用组件一样,运行在进程的主线程中。这就是说如果service需要很多耗时或者阻塞的操作,需要在其子线程中实现。
    service的两种模式(startService()/bindService()不是完全分离的):
  • 本地服务 Local Service 用于应用程序内部。
    它可以启动并运行,直至有人停止了它或它自己停止。在这种方式下,它以调用Context.startService()启动,而以调用Context.stopService()结束。它可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。不论调用了多少次startService()方法,你只需要调用一次stopService()来停止服务。
    用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好。
  • 远程服务 Remote Service 用于android系统内部的应用程序之间。
    它可以通过自己定义并暴露出来的接口进行程序操作。客户端建立一个到服务对象的连接,并通过那个连接来调用服务。连接以调用Context.bindService()方法建立,以调用 Context.unbindService()关闭。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。
    可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。
生命周期
    Service的生命周期并不像Activity那么复杂,它只继承了onCreate(),onStart(),onDestroy()三个方法,当我们*次启动Service时,先后调用了onCreate(),onStart()这两个方法,当停止Service时,则执行onDestroy()方法,这里需要注意的是,如果Service已经启动了,当我们再次启动Service时,不会在执行onCreate()方法,而是直接执行onStart()方法。
    而启动service,根据onStartCommand的返回值不同,有两个附加的模式:
    1. START_STICKY 用于显示启动和停止service。
    2. START_NOT_STICKY或START_REDELIVER_INTENT用于有命令需要处理时才运行的模式。
     服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不同。
1. 使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。
如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。
如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。
采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
2. 使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。
采用Context.bindService()方法启动服务时只能调用onUnbind()方法解除调用者与服务解除,服务结束时会调用onDestroy()方法。
    官方文档告诉我们,一个service可以同时start并且bind,在这样的情况,系统会一直保持service的运行状态如果service已经start了或者BIND_AUTO_CREATE标志被设置。如果没有一个条件满足,那么系统将会调用onDestory方法来终止service.所有的清理工作(终止线程,反注册接收器)都在onDestory中完成。
拥有service的进程具有较高的优先级
    官方文档告诉我们,Android系统会尽量保持拥有service的进程运行,只要在该service已经被启动(start)或者客户端连接(bindService)到它。当内存不足时,需要保持,拥有service的进程具有较高的优先级。
1. 如果service正在调用onCreate,onStartCommand或者onDestory方法,那么用于当前service的进程则变为前台进程以避免被killed。

2. 如果当前service已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,但是比那些不可见的进程更重要,这就意味着service一般不会被killed.

3. 如果客户端已经连接到service (bindService),那么拥有Service的进程则拥有*高的优先级,可以认为service是可见的。

4. 如果service可以使用startForeground(int, Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时killed。

如果有其他的应用组件作为Service,Activity等运行在相同的进程中,那么将会增加该进程的重要性。
本地service
1.不需和Activity交互的本地服务
  1. public class LocalService extends Service {
  2. private static final String TAG = “LocalService”;
  3. @Override
  4. public IBinder onBind(Intent intent) {
  5. Log.i(TAG, “onBind”);
  6. return null;
  7. }
  8. @Override
  9. public void onCreate() {
  10. Log.i(TAG, “onCreate”);
  11. super.onCreate();
  12. }
  13. @Override
  14. public void onDestroy() {
  15. Log.i(TAG, “onDestroy”);
  16. super.onDestroy();
  17. }
  18. @Override
  19. public void onStart(Intent intent, int startId) {
  20. Log.i(TAG, “onStart”);
  21. super.onStart(intent, startId);
  22. }
  23. }

Activity:

  1. public class ServiceActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.servicedemo);
  6. ((Button) findViewById(R.id.startLocalService)).setOnClickListener(
  7. new View.OnClickListener(){
  8. @Override
  9. public void onClick(View view) {
  10. // TODO Auto-generated method stub
  11. startService(new Intent(“com.demo.SERVICE_DEMO”));
  12. }
  13. });
  14. ((Button) findViewById(R.id.stopLocalService)).setOnClickListener(
  15. new View.OnClickListener(){
  16. @Override
  17. public void onClick(View view) {
  18. // TODO Auto-generated method stub
  19. stopService(new Intent(“com.demo.SERVICE_DEMO”));
  20. }
  21. });
  22. }
  23. }

在AndroidManifest.xml添加:

  1. <service android:name=“.LocalService”>
  2. <intent-filter>
  3. <action android:name=“com.demo.SERVICE_DEMO” />
  4. <category android:name=“android.intent.category.default” />
  5. </intent-filter>
  6. </service>
否则启动服务时会提示new Intent找不到”com.demo.SERVICE_DEMO”。
    对于这类不需和Activity交互的本地服务,是使用startService/stopService的*好例子。
    运行时可以发现*次startService时,会调用onCreate和onStart,在没有stopService前,无论点击多少次startService,都只会调用onStart。而stopService时调用onDestroy。再次点击stopService,会发现不会进入service的生命周期的,即不会再调用onCreate,onStart和onDestroy。
    而onBind在startService/stopService中没有调用。
2.本地服务和Activity交互
    对于这种case,官方的sample(APIDemo\app.LocalService)是*好的例子:
  1. /**
  2. * This is an example of implementing an application service that runs locally
  3. * in the same process as the application. The {@link LocalServiceController}
  4. * and {@link LocalServiceBinding} classes show how to interact with the
  5. * service.
  6. *
  7. * <p>Notice the use of the {@link NotificationManager} when interesting things
  8. * happen in the service. This is generally how background services should
  9. * interact with the user, rather than doing something more disruptive such as
  10. * calling startActivity().
  11. */
  12. public class LocalService extends Service {
  13. private NotificationManager mNM;
  14. /**
  15. * Class for clients to access. Because we know this service always
  16. * runs in the same process as its clients, we don’t need to deal with
  17. * IPC.
  18. */
  19. public class LocalBinder extends Binder {
  20. LocalService getService() {
  21. return LocalService.this;
  22. }
  23. }
  24. @Override
  25. public void onCreate() {
  26. mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
  27. // Display a notification about us starting. We put an icon in the status bar.
  28. showNotification();
  29. }
  30. @Override
  31. public int onStartCommand(Intent intent, int flags, int startId) {
  32. Log.i(“LocalService”, “Received start id “ + startId + “: “ + intent);
  33. // We want this service to continue running until it is explicitly
  34. // stopped, so return sticky.
  35. return START_STICKY;
  36. }
  37. @Override
  38. public void onDestroy() {
  39. // Cancel the persistent notification.
  40. mNM.cancel(R.string.local_service_started);
  41. // Tell the user we stopped.
  42. Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
  43. }
  44. @Override
  45. public IBinder onBind(Intent intent) {
  46. return mBinder;
  47. }
  48. // This is the object that receives interactions from clients. See
  49. // RemoteService for a more complete example.
  50. private final IBinder mBinder = new LocalBinder();
  51. /**
  52. * Show a notification while this service is running.
  53. */
  54. private void showNotification() {
  55. // In this sample, we’ll use the same text for the ticker and the expanded notification
  56. CharSequence text = getText(R.string.local_service_started);
  57. // Set the icon, scrolling text and timestamp
  58. Notification notification = new Notification(R.drawable.stat_sample, text,
  59. System.currentTimeMillis());
  60. // The PendingIntent to launch our activity if the user selects this notification
  61. PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
  62. new Intent(this, LocalServiceController.class), 0);
  63. // Set the info for the views that show in the notification panel.
  64. notification.setLatestEventInfo(this, getText(R.string.local_service_label),
  65. text, contentIntent);
  66. // Send the notification.
  67. // We use a layout id because it is a unique number. We use it later to cancel.
  68. mNM.notify(R.string.local_service_started, notification);
  69. }
  70. }
   这里可以发现onBind需要返回一个IBinder对象。也就是说和上一例子LocalService不同的是,
1. 添加了一个public内部类继承Binder,并添加getService方法来返回当前的Service对象;
2. 新建一个IBinder对象——new那个Binder内部类;
3. onBind方法返还那个IBinder对象。
Activity:
  1. /**
  2. * <p>Example of binding and unbinding to the {@link LocalService}.
  3. * This demonstrates the implementation of a service which the client will
  4. * bind to, receiving an object through which it can communicate with the service.</p>
  5. */
  6. public class LocalServiceBinding extends Activity {
  7. private boolean mIsBound;
  8. private LocalService mBoundService;
  9. @Override
  10. protected void onCreate(Bundle savedInstanceState) {
  11. super.onCreate(savedInstanceState);
  12. setContentView(R.layout.local_service_binding);
  13. // Watch for button clicks.
  14. Button button = (Button)findViewById(R.id.bind);
  15. button.setOnClickListener(mBindListener);
  16. button = (Button)findViewById(R.id.unbind);
  17. button.setOnClickListener(mUnbindListener);
  18. }
  19. private ServiceConnection mConnection = new ServiceConnection() {
  20. public void onServiceConnected(ComponentName className, IBinder service) {
  21. // This is called when the connection with the service has been
  22. // established, giving us the service object we can use to
  23. // interact with the service. Because we have bound to a explicit
  24. // service that we know is running in our own process, we can
  25. // cast its IBinder to a concrete class and directly access it.
  26. mBoundService = ((LocalService.LocalBinder)service).getService();
  27. // Tell the user about this for our demo.
  28. Toast.makeText(LocalServiceBinding.this, R.string.local_service_connected,
  29. Toast.LENGTH_SHORT).show();
  30. }
  31. public void onServiceDisconnected(ComponentName className) {
  32. // This is called when the connection with the service has been
  33. // unexpectedly disconnected — that is, its process crashed.
  34. // Because it is running in our same process, we should never
  35. // see this happen.
  36. mBoundService = null;
  37. Toast.makeText(LocalServiceBinding.this, R.string.local_service_disconnected,
  38. Toast.LENGTH_SHORT).show();
  39. }
  40. };
  41. private OnClickListener mBindListener = new OnClickListener() {
  42. public void onClick(View v) {
  43. // Establish a connection with the service. We use an explicit
  44. // class name because we want a specific service implementation that
  45. // we know will be running in our own process (and thus won’t be
  46. // supporting component replacement by other applications).
  47. bindService(new Intent(LocalServiceBinding.this,
  48. LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
  49. mIsBound = true;
  50. }
  51. };
  52. private OnClickListener mUnbindListener = new OnClickListener() {
  53. public void onClick(View v) {
  54. if (mIsBound) {
  55. // Detach our existing connection.
  56. unbindService(mConnection);
  57. mIsBound = false;
  58. }
  59. }
  60. };
  61. }
    明显看出这里面添加了一个名为ServiceConnection类,并实现了onServiceConnected(从IBinder获取Service对象)和onServiceDisconnected(set Service to null)。
    而bindService和unbindService方法都是操作这个ServiceConnection对象的。
AndroidManifest.xml里添加:
  1. <service android:name=“.app.LocalService” />
  2. <activity android:name=“.app.LocalServiceBinding” android:label=“@string/activity_local_service_binding”>
  3. <intent-filter>
  4. <action android:name=“android.intent.action.MAIN” />
  5. <category android:name=“android.intent.category.SAMPLE_CODE” />
  6. </intent-filter>
  7. </activity>
这里没什么特别的,因为service没有需要什么特别的action,所以只是声明service而已,而activity和普通的没差别。
运行时,发现调用次序是这样的:
bindService:
1.LocalService : onCreate
2.LocalService : onBind
3.Activity: onServiceConnected

unbindService: 只是调用onDestroy
可见,onStart是不会被调用的,而onServiceDisconnected没有调用的原因在上面代码的注释有说明。
介绍onStartCommand()需要用到的几个常量 (引自官方文档)
START_NOT_STICKY

If the system kills the service after onStartCommand() returns,  do not recreate the service, unless there are pending intents to deliver. This is the safest option to avoid running your service when not necessary and when your application can simply restart any unfinished jobs.

START_STICKY

If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand(), but  do not redeliver the last intent. Instead, the system calls onStartCommand() with a null intent, unless there were pending intents to start the service, in which case, those intents are delivered. This is suitable for media players (or similar services) that are not executing commands, but running indefinitely and waiting for a job.

START_REDELIVER_INTENT

If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand() with the last intent that was delivered to the service. Any pending intents are delivered in turn. This is suitable for services that are actively performing a job that should be immediately resumed, such as do wnloading a file.

 

Running a Service in the Foreground
    具体内容查看官方文档,主要是使用 startForeground() 和 stopForeground()方法。