那些我们解过的bug之关闭应用

这里写图片描述

bug描述:

测试提了一个bug:
后台开启手电筒,进入相机,提示无法连接到相机。

bug分析:

我重现了一下此bug,确实是在后台开启了手电筒,点击相机,会提示连接相机失败。

我参考了一下我的小米手机,如果后台开启手电筒,进入相机,会直接关闭手电筒,然后进入相机。

好吧,解决方案有了,下面就实现一下了。

bug解决:

baidu一下,就有了实现代码:

定义权限:

AndroidManifest.xml

<uses-permission android:name="android.permission.RESTART_PACKAGES" />

在进入拍照界面:

./packages/apps/MyosCamera/src/com/myos/camera/Camera.java
onCreate方法:

android.util.Log.i("hexiaomint_co","Camera--onCreate");
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);     
manager.restartPackage("com.ape.led");

onResume方法:

android.util.Log.i("hexiaomint_co","Camera--onResume");
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);     
manager.restartPackage("com.ape.led");

ok,此bug打完收工。

启动外部应用的方法

对应的启动应用的方法,有二个,亲测完全可行:
//1.根据package的名称获取应用的Launch Intent

Intent LaunchIntent = getPackageManager().getLaunchIntentForPackage("com.android.deskclock");
startActivity(LaunchIntent);

//2新建一个Intent对象,
//category=LAUNCHER, action=MAIN,
//componentName = new ComponentName(packageName, name)
//setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

Intent intent = new Intent();
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setAction(Intent.ACTION_MAIN);
intent.setComponent(new ComponentName("com.android.deskclock","com.android.deskclock.DeskClock"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

源码查看:

心里有点莫名的冲动,想围观一下关闭应用的实现过程,查看了一下源码:

./frameworks/base/core/java/android/app/ActivityManager.java

/**
* @deprecated This is now just a wrapper for
* {@link #killBackgroundProcesses(String)}; the previous behavior here
* is no longer available to applications because it allows them to
* break other applications by removing their alarms, stopping their
* services, etc.
*/
@Deprecated
public void restartPackage(String packageName) {
        killBackgroundProcesses(packageName);
}



/**
* Have the system immediately kill all background processes associated
* with the given package.  This is the same as the kernel killing those
* processes to reclaim memory; the system will take care of restarting
* these processes in the future as needed.
* <p>You must hold the permission
* {@link android.Manifest.permission#KILL_BACKGROUND_PROCESSES} to be able to
* call this method.
* @param packageName The name of the package whose processes are to
* be killed.
*/
public void killBackgroundProcesses(String packageName) {
        try {           ActivityManagerNative.getDefault().killBackgroundProcesses(packageName,
                    UserHandle.myUserId());
        } catch (RemoteException e) {
        }
}

从代码,可以看出,restartPackage方法已经过期了,我们最好是采用killBackgroundProcesses方法,

ActivityManager mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
try {           mActivityManager.killBackgroundProcesses("com.ape.led");
} catch (Exception e) {
            e.printStackTrace();
}

我们在源码中还可以查看到这个方法forceStopPackage:

if (mActiveItem.mItem.mBackground) {
// Background process.  Just kill it.   mAm.killBackgroundProcesses(mActiveItem.mItem.mPackageInfo.packageName);
finish();
} else {
// Heavy-weight process.  We'll do a force-stop on it.
mAm.forceStopPackage(mActiveItem.mItem.mPackageInfo.packageName);
 finish();
}

killBackgroundProcesses和forceStopPackage方法的区别,我们可以查看此文章:
http://blog.csdn.net/huxueyan521/article/details/8921976

继续,我们查看IActivityManager.java:
./frameworks/base/core/java/android/app/IActivityManager.java

其定义了几个相关的接口(killBackgroundProcesses, killAllBackgroundProcesses, forceStopPackage):

public void killBackgroundProcesses(final String packageName, int userId)
            throws RemoteException;
public void killAllBackgroundProcesses() throws RemoteException;
public void forceStopPackage(final String packageName, int userId) throws RemoteException;

我们查看ActivityManagerService.java:
./frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

下面是这三个接口的实现:

@Override
public void killBackgroundProcesses(final String packageName, int userId) {
        if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
                != PackageManager.PERMISSION_GRANTED &&
                checkCallingPermission(android.Manifest.permission.RESTART_PACKAGES)
                        != PackageManager.PERMISSION_GRANTED) {
            String msg = "Permission Denial: killBackgroundProcesses() from pid="
                    + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid()
                    + " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES;
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        }

        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                userId, true, ALLOW_FULL_ONLY, "killBackgroundProcesses", null);
        long callingId = Binder.clearCallingIdentity();
        try {
            IPackageManager pm = AppGlobals.getPackageManager();
            synchronized(this) {
                int appId = -1;
                try {
                    appId = UserHandle.getAppId(pm.getPackageUid(packageName, 0));
                } catch (RemoteException e) {
                }
                if (appId == -1) {
                    Slog.w(TAG, "Invalid packageName: " + packageName);
                    return;
                }
                killPackageProcessesLocked(packageName, appId, userId,
                        ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
            }
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
    }

@Override
public void killAllBackgroundProcesses() {
        if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
                != PackageManager.PERMISSION_GRANTED) {
            String msg = "Permission Denial: killAllBackgroundProcesses() from pid="
                    + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid()
                    + " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES;
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        }

        long callingId = Binder.clearCallingIdentity();
        try {
            synchronized(this) {
                ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
                final int NP = mProcessNames.getMap().size();
                for (int ip=0; ip<NP; ip++) {
                    SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
                    final int NA = apps.size();
                    for (int ia=0; ia<NA; ia++) {
                        ProcessRecord app = apps.valueAt(ia);
                        if (app.persistent) {
                            // we don't kill persistent processes
                            continue;
                        }
                        if (app.removed) {
                            procs.add(app);
                        } else if (app.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
                            app.removed = true;
                            procs.add(app);
                        }
                    }
                }

                int N = procs.size();
                for (int i=0; i<N; i++) {
                    removeProcessLocked(procs.get(i), false, true, "kill all background");
                }
                mAllowLowerMemLevel = true;
                updateOomAdjLocked();
                doLowMemReportIfNeededLocked(null);
            }
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
    }

@Override
public void forceStopPackage(final String packageName, int userId) {
        if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
                != PackageManager.PERMISSION_GRANTED) {
            String msg = "Permission Denial: forceStopPackage() from pid="
                    + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid()
                    + " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES;
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        }
        final int callingPid = Binder.getCallingPid();
        userId = handleIncomingUser(callingPid, Binder.getCallingUid(),
                userId, true, ALLOW_FULL_ONLY, "forceStopPackage", null);
        long callingId = Binder.clearCallingIdentity();
        try {
            IPackageManager pm = AppGlobals.getPackageManager();
            synchronized(this) {
                int[] users = userId == UserHandle.USER_ALL
                        ? getUsersLocked() : new int[] { userId };
                for (int user : users) {
                    int pkgUid = -1;
                    try {
                        pkgUid = pm.getPackageUid(packageName, user);
                    } catch (RemoteException e) {
                    }
                    if (pkgUid == -1) {
                        Slog.w(TAG, "Invalid packageName: " + packageName);
                        continue;
                    }
                    try {
                        pm.setPackageStoppedState(packageName, true, user);
                    } catch (RemoteException e) {
                    } catch (IllegalArgumentException e) {
                        Slog.w(TAG, "Failed trying to unstop package "
                                + packageName + ": " + e);
                    }
                    if (isUserRunningLocked(user, false)) {
                        forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
                    }
                }
            }
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
    }

killBackgroundProcesses方法的关键是:

killPackageProcessesLocked(packageName, appId, userId,
                        ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");

killAllBackgroundProcesses方法的关键是:

int N = procs.size();
                for (int i=0; i<N; i++) {
                    removeProcessLocked(procs.get(i), false, true, "kill all background");
                }
                mAllowLowerMemLevel = true;
                updateOomAdjLocked();
                doLowMemReportIfNeededLocked(null);

forceStopPackage方法的关键是:

forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);

ActivityManagerNative的一些使用方法:

下面是一些我们在app中可能用到的一些关于ActivityManagerNative的方法:

ActivityManagerNative的常用方法:

import android.app.ActivityManagerNative;

ActivityManagerNative.getDefault().updateConfiguration(mCurConfig);
ActivityManagerNative.getDefault().getConfiguration()
ActivityManagerNative.getDefault().updatePersistentConfiguration(mCurConfig);
ActivityManagerNative.getDefault().stopAppSwitches();
mIActivityManager = ActivityManagerNative.getDefault();

获取顶层activity的名字:

   private String getTopActivityName() {
        List<ActivityManager.RunningTaskInfo> taskList;

        try {
            taskList = ActivityManagerNative.getDefault().getTasks(1, 0);
        } catch (RemoteException e) {
            return null;
        }

        if ((taskList != null)
                && (taskList.get(0) != null)
                && (taskList.get(0).topActivity != null)
                && (taskList.get(0).topActivity.getClassName() != null)) {

            return taskList.get(0).topActivity.getClassName();
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hfreeman2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值