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();
}
}