2. 在Android手机上为什么我想完全关闭的程序关不了?" />
当前位置: 代码迷 >> Android >> 2. 在Android手机上为什么我想完全关闭的程序关不了?
  详细解决方案

2. 在Android手机上为什么我想完全关闭的程序关不了?

热度:242   发布时间:2016-04-28 05:26:31.0
Android启动早于系统应用的第三方应用,杀不死自动重启的第三方应用

1.为什么第三方应用能早于System的app启动?

      Android应用的启动顺序网上有一大堆资料可以查阅了,这里就不细述了,这里不阐述ROM启动还有bootloader,软件启动的大致流程应该是

  • 启动kernel
  • 运行servicemanager 把一些native的服务用命令启动起来(包括wifi, power, rild, surfaceflinger, mediaserver等等)
  • 启动Dalivk中的第一个进程Zygote -> 启动java 层的系统服务system_server(包括PowerManagerService, ActivityManagerService , telephony.registry, DisplayManagerService等等)该服务中的这些部件跟native的那些服务关联在一起
  • 启动Luncher和Persistent的App,该程序是系统级的在AndroidManifest.xml里声明android:persistent="true"
  • 发出ACTION_BOOT_COMPLETED广播给其他应用。

       在这里需要注意的是声明android:persistent属性为true的APP被kill掉后还是会自动重启的。系统中我们已知android:persistent属性为true的APP肯定有Phone App,也就是说第三方应用应当至少晚于Phone APP启动,如何判断呢?最简单的办法看其PID的大小,PID值越小越先启动。有其第三方应用可以先于Phone APP启动。我们探其应用的AndroidManifest.xml (PS:如何看APK的代码,网上有你懂的apktool等),发现其在AndroidManifest里定义的静态Receiver的intent-filter的属性如下:

       <receiver android:name="com.anguanjia.safe.AAAReceiver">            <span style="color:#FF0000;"><intent-filter android:priority="2147483647"></span>                <action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />                <action android:name="android.net.wifi.WIFI_STATE_CHANGED" />                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />                <action android:name="android.intent.action.ANY_DATA_STATE" />                <action android:name="android.net.wifi.STATE_CHANGE" />            </intent-filter>            <intent-filter android:priority="2147483647">                <action android:name="android.intent.action.MEDIA_UNMOUNTED" />                <action android:name="android.intent.action.MEDIA_MOUNTED" />                <action android:name="android.intent.action.MEDIA_REMOVED" />                <action android:name="android.intent.action.MEDIA_CHECKING" />                <action android:name="android.intent.action.MEDIA_EJECT" />                <data android:scheme="file" />            </intent-filter>

2147483647 这个值是什么?好大,哦,原来是int的最大值!我们来看下google 文档

android:priority
    The priority that should be given to the parent component with regard to handling intents of the type described by the filter. This attribute has meaning for both activities and broadcast receivers:

        It provides information about how able an activity is to respond to an intent that matches the filter, relative to other activities that could also respond to the intent. When an intent could be handled by multiple activities with different priorities, Android will consider only those with higher priority values as potential targets for the intent.

        It controls the order in which broadcast receivers are executed to receive broadcast messages. Those with higher priority values are called before those with lower values. (The order applies only to synchronous messages; it's ignored for asynchronous messages.)

    Use this attribute only if you really need to impose a specific order in which the broadcasts are received, or want to force Android to prefer one activity over others.

    The value must be an integer, such as "100". Higher numbers have a higher priority. The default value is 0. The value must be greater than -1000 and less than 1000.

        这个值是receiver的优先级,值越大优先级越高,按优先顺序执行,但是文档介绍优先级值大小是-1000~1000. 该应用的是int的最大值, 但android平台没有对android:priority值进行检查。在开机后该应用Receiver的intent-filter的优先级最高,在该filter里的intent被系统发送出来(android.intent.action.MEDIA_MOUNTE, android.net.wifi.WIFI_STATE_CHANGED等等),这个时候App会根据这个intent而被启动起来。

         这里需要注意的是该Receiver是个静态的,一定是要注册在AndroidManifest里。当Wifi成功注册后会发出WIFI_STATE_CHANGED的消息, 或者其他的部件完成一些事件后也会发出类似的消息,而这些消息的发出又早于属性为persistent的系统级APP的启动, 由此就会发生第三方应用早于系统级APP的启动的情况。


2. 在Android手机上为什么我想完全关闭的程序关不了?

        有一种理论是Android手机为了有更好的用户体验,会后台自动启动一些程序, 这样我们前台在操作的时候会感觉手机更流畅平滑。但是如果程序运行过多,对CPU 内存的开销过大,往往会导致系统越用越慢,乃至手机挂掉的问题,在内存管理这快Android有两种机制去解决这个问题,一个是在framework层在 trimApplication方法中去实现,另外一个就是在kernel里的lowmemorykiller, 这里不再细述。
        但是对于用户来说,我就是想完全关闭第三方程序,以免过多使用我的流量或者偷偷的做一些我不希望的操作。貌似没有办法去关闭,那为什么呢? 我这里先讲述其中一种情况。
          Service顾名思义是服务,运行在前后台后都可以,即可以运行在当前进程也可以运行在其他的进程里,Service可以为多个APP共享使用,是通过binder机制来实现的。当我Kill掉一个带有服务的进程(没有调用stopService()), 过一会该应用会自动重启。下面是代码的调用顺序,自下往上查看。

com.android.server.am.ActiveServices.scheduleServiceRestartLocked(ActiveServices.java)

com.android.server.am.ActiveServices.killServicesLocked (ActiveServices.java)

com.android.server.am.ActivityManagerService.cleanUpApplicationRecordLocked(ActivityManagerService.java)

com.android.server.am.ActivityManagerService.handleAppDiedLocked(ActivityManagerService.java)

com.android.server.am.ActivityManagerService.appDiedLocked(ActivityManagerService.java)

com.android.server.am.ActivityManagerService$AppDeathRecipient.binderDied(ActivityManagerService.java)

        从代码流程上我们看出该service被restart,进程也根据该service启动起来, service就运行在重启的进程里。

        在这种情况下是不是就真没办法了呢,当然不是,如果我们在service中覆盖onStartCommand这个函数并且返回值为START_NOT_STICKY,在我们kill该进程后则不会自动重启,我们想关闭的应用也可以完全关闭了,不会再自动重启了。

    public int <span style="color:#FF0000;"><strong>onStartCommand</strong></span>(Intent intent, int flags, int startId) {        return <span style="color:#FF0000;"><strong>START_NOT_STICKY</strong></span>;    }

Framwork实现代码

frameworks/base/services/java/com/android/server/am/ActiveServices.java                    case Service.START_NOT_STICKY: {                        // We are done with the associated start arguments.                        r.findDeliveredStart(startId, true);                        if (r.getLastStartId() == startId) {                            // There is no more work, and this service                            // doesn't want to hang around if killed.                            r.<span style="color:#FF0000;">stopIfKilled</span> = true;   // 该变量设置为true                        }                        break;                    }                if (sr.startRequested && (sr.<span style="color:#FF0000;">stopIfKilled</span> || canceled)) {    //进入到该条件中                    if (sr.pendingStarts.size() == 0) {                        sr.startRequested = false;                        if (sr.tracker != null) {                            sr.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),                                    SystemClock.uptimeMillis());                        }                        if (!sr.hasAutoCreateConnections()) {                            // Whoops, no reason to restart!                            bringDownServiceLocked(sr);  //执行在这里,不会重启App                        }                    }                }

        重写onStartCommand方法且返回值为START_NOT_STICKY的代码调用顺序,自下而上查看。

com.android.server.am.ActiveServices.bringDownServiceLocked(ActiveServices.java)

com.android.server.am.ActiveServices.killServicesLocked(ActiveServices.java)

com.android.server.am.ActivityManagerService.cleanUpApplicationRecordLocked(ActivityManagerService.java)

com.android.server.am.ActivityManagerService.handleAppDiedLocked(ActivityManagerService.java)

com.android.server.am.ActivityManagerService.appDiedLocked(Activi tyManagerService.java)

com.android.server.am.ActivityManagerService$AppDeathRecipient.binderDied(ActivityManagerService.java)

        附上Google doc 对于onStartCommand返回值的说明

For started services, there are two additional major modes of operation they can decide to run in, depending on the value they return from onStartCommand():START_STICKY is used for services that are explicitly started and stopped as needed, whileSTART_NOT_STICKY orSTART_REDELIVER_INTENT are used for services that should only remain running while processing any commands sent to them. See the linked documentation for more detail on the semantics.

       在这里对这个返回值做下解释:

当服务进程因某种原因(内存不够,强制关闭等)被kill掉时,START_STICKY在系统有充足的内存后重新创建service, 在onStartCommand中handle的是null intent.

START_NOT_STICKY通知系统不在重新创建该service. 还有一个返回值START_REDELIVER_INTENT重新创建service并且伴随着原来intent的去处理。

  相关解决方案