Android5.1.1源码 - 分析在最近的APP列表中清理APP的过程

Android5.1.1源码 - 分析在最近的APP列表中清理APP的过程

@(Android研究)[Android|源码|清理APP]


[TOC]


前言

长按HOME键即可打开最近的APP列表,用手指在这个APP列表中的某个APP上向屏幕一边滑动时会关闭这个APP,本文分析的就是关闭APP的过程。

本文是流水文,没有讲清楚前因后果。 其他人看本文可以直接跳转到当前结论部分。

分析

长按HOME键会打开最近的APP列表,打开最近APP列表会执行下面的代码:

IStatusBarService statusBarService = IStatusBarService.Stub.asInterface(
        ServiceManager.getService("statusbar"));
try {
    statusBarService.toggleRecentApps();
} catch (RemoteException e) {
    Slog.e(LOG_TAG, "Error toggling recent apps.");
}

这个方法先获得了IStatusBarService对象,然后调用IStatusBarService对象的toggleRecentApps()方法打开了最近APP列表。

IStatusBarService是一个接口,实际上是StatusBarManagerService对象,StatusBarManagerService类在文件"frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java"中,**statusBarService.toggleRecentApps();**语句调用的toggleRecentApps方法在StatusBarManagerService类中,下面是它的源码:

@Override
public void toggleRecentApps() {
    if (mBar != null) {
        try {
            mBar.toggleRecentApps();
        } catch (RemoteException ex) {}
    }
}

mBar是StatusBarManagerService类的成员变量,它在成员方法registerStatusBar中被赋值,下面是registerStatusBar的源码:

// ================================================================================
// Callbacks from the status bar service.
// ================================================================================
@Override
public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
        int switches[], List<IBinder> binders) {
    
    ......

    mBar = bar;
    
    ......
}

在BaseStatusBar类的start方法中调用了StatusBarManagerService.registerStatusBar方法,下面是调用这个方法的语句:

mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);

mBarService是StatusBarManagerService类的对象,在当前我们关心mCommandQueue,因为mCommandQueue被赋值给了StatusBarManagerService.mBar。

mCommandQueue是BaseStatusBar类的成员变量,下面是对这个变量赋值的语句:

mCommandQueue = new CommandQueue(this, iconList);

这是CommandQueue类的对象,这个类的代码在文件"frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java"中。

回到StatusBarManagerService.toggleRecentApps方法中,在这个方法中执行了**mBar.toggleRecentApps();**语句,在这里调用的toggleRecentApps方法是CommandQueue.toggleRecentApps方法,下面来看一下CommandQueue.toggleRecentApps方法的的源码:

public void toggleRecentApps() {
    synchronized (mList) {
        ......
        mHandler.obtainMessage(MSG_TOGGLE_RECENT_APPS, 0, 0, null).sendToTarget();
    }
}

下面是CommandQueue.mHandler成员变量的赋值的语句源码:

private Handler mHandler = new H();

H类是CommandQueue类的内部私有类,它继承了Handler类。

在CommandQueue.toggleRecentApps方法中调用了H.obtainMessage方法,在H类中的handleMessage方法会接收并处理这个消息,下面来看一下在CommandQueue.H.handleMessage方法中如何处理MSG_TOGGLE_RECENT_APPS消息:

public void handleMessage(Message msg) {
    if (mPaused) {
        this.sendMessageAtFrontOfQueue(Message.obtain(msg));
        return;
    }
    final int what = msg.what & MSG_MASK;
    switch (what) {

        ......

        case MSG_TOGGLE_RECENT_APPS:
            mCallbacks.toggleRecentApps();
            break;

        ......
    }
}

mCallbacks在CommandQueue类的构造方法中被赋值,CommandQueue的第一个参数赋值给了mCallbacks,在前文中在BaseStatusBar类中创建了CommandQueue对象,给CommandQueue构造方法传递的第一个参数是this,BaseStatusBar类是一个抽象类,PhoneStatusBar类继承了它,也就是说传递给CommandQueue的this是PhoneStatusBar对象,**mCallbacks.toggleRecentApps();**语句调用的是PhoneStatusBar对象对象的toggleRecentApps方法,但是在PhoneStatusBar类中没有toggleRecentApps方法,在PhoneStatusBar继承的BaseStatusBar类中找到了这个方法。

PhoneStatusBar类的代码在"frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java"文件中。

下面是BaseStatusBar.toggleRecentApps方法的源码:

@Override
public void toggleRecentApps() {
    int msg = MSG_TOGGLE_RECENTS_APPS;
    ......
    mHandler.sendEmptyMessage(msg);
}

mHandler是BaseStatusBar类的成员函数,它是BaseStatusBar的内部类H的对象,这个H对象继承了Handler类,在toggleRecentApps方法中mHandler调用了sendEmptyMessage方法发送了一条MSG_TOGGLE_RECENTS_APPS消息,BaseStatusBar.H.handleMessage方法中会处理这条消息。(分析到这里我忍不住吐槽一句,这弯弯绕又绕了回来,累不累!)

下面来看BaseStatusBar.H.handleMessage方法中如何处理MSG_TOGGLE_RECENTS_APPS消息:

public void handleMessage(Message m) {
    switch (m.what) {

     ......

     case MSG_TOGGLE_RECENTS_APPS:
         if (mDeviceProvisioned) {
             toggleRecents();
         }
         break;

     ......

    }
}

mDeviceProvisioned不用管,toggleRecents方法在BaseStatusBar和PhoneStatusBar类中都有实现代码,先看PhoneStatusBar类中的实现,因为上文提过BaseStatusBar是抽象类PhoneStatusBar继承了BaseStatusBar类,下面是PhoneStatusBar.toggleRecents方法的源码:

@Override
protected void toggleRecents() {
    // Toggle the recents visibility flag
    mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE;
    notifyUiVisibilityChanged(mSystemUiVisibility);
    super.toggleRecents();
}

前两行语句设置了一下状态,然后就调用父类的toggleRecents方法,PhoneStatusBar的父类是BaseStatusBar。

下面是BaseStatusBar.toggleRecents方法的源码:

protected void toggleRecents() {
    if (mRecents != null) {
        sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
        mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
    }
}

mRecents的值在BaseStatusBar.start方法中由**mRecents = getComponent(RecentsComponent.class);**语句赋值,getComponent返回的是RecentsComponent类型,RecentsComponent是一个接口,实际上返回的是Recents类的对象,Recents类代码在文件"frameworks/base/packages/SystemUI/src/com/android/systemui/recent/Recents.java"中。

下面是Recents.toggleRecents方法的代码:

@Override
public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
    ......

    if (DEBUG) Log.d(TAG, "toggle recents panel");
    try {
        TaskDescription firstTask = RecentTasksLoader.getInstance(mContext).getFirstTask();

        Intent intent = new Intent(RecentsActivity.TOGGLE_RECENTS_INTENT);
        intent.setClassName("com.android.systemui",
                "com.android.systemui.recent.RecentsActivity");
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);

        ......

            startActivitySafely(intent, opts.toBundle());
        }
    } catch (ActivityNotFoundException e) {
        Log.e(TAG, "Failed to launch RecentAppsIntent", e);
    }
}

Recents.toggleRecents方法启动了RecentsActivity。

RecentsActivity类的代码在文件"frameworks/base/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java"中,RecentsActivity的布局文件设置代码:

setContentView(R.layout.status_bar_recent_panel);

status_bar_recent_panel.xml布局文件中包含一个"com.android.systemui.recent.RecentsHorizontalScrollView"组件,进去APP列表就是显示在这个组件中,而这个组件对应RecentsHorizontalScrollView类。

RecentsHorizontalScrollView类在文件"frameworks/base/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java"中。

经过了一系列操作执行到了RecentsPanelView.handleSwipe方法中,在这个方法中删除了相应的APP任务,下面是这个方法的源码:

public void handleSwipe(View view) {
    TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription;
    if (ad == null) {
        Log.v(TAG, "Not able to find activity description for swiped task; view=" + view +
                " tag=" + view.getTag());
        return;
    }
    if (DEBUG) Log.v(TAG, "Jettison " + ad.getLabel());
    mRecentTaskDescriptions.remove(ad);
    mRecentTasksLoader.remove(ad);

    // Handled by widget containers to enable LayoutTransitions properly
    // mListAdapter.notifyDataSetChanged();

    if (mRecentTaskDescriptions.size() == 0) {
        dismissAndGoBack();
    }

    // Currently, either direction means the same thing, so ignore direction and remove
    // the task.
    final ActivityManager am = (ActivityManager)
            getContext().getSystemService(Context.ACTIVITY_SERVICE);
    if (am != null) {
        am.removeTask(ad.persistentTaskId);

        // Accessibility feedback
        setContentDescription(
                getContext().getString(R.string.accessibility_recents_item_dismissed, ad.getLabel()));
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
        setContentDescription(null);
    }
}

**am.removeTask(ad.persistentTaskId);**语句删除了APP任务,这个ActivityManager.removeTask方法的调用最终执行到ActivityManagerService.removeTask方法中。

关于TaskDescription.persistentTaskId的值的获得可以参考RecentTasksLoader.loadFirstTask方法中的代码。

当前结论

最终结束掉APP的操作在ActivityManagerService.cleanUpRemovedTaskLocked方法中,ActivityManagerService类在文件"frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java"中,下面是ActivityManagerService.cleanUpRemovedTaskLocked方法的源码:

private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess) {
    mRecentTasks.remove(tr);
    tr.removedFromRecents();
    ComponentName component = tr.getBaseIntent().getComponent();
    if (component == null) {
        Slog.w(TAG, "No component for base intent of task: " + tr);
        return;
    }

    if (!killProcess) {
        return;
    }

    // Determine if the process(es) for this task should be killed.
    final String pkg = component.getPackageName();
    ArrayList<ProcessRecord> procsToKill = new ArrayList<ProcessRecord>();
    ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
    for (int i = 0; i < pmap.size(); i++) {

        SparseArray<ProcessRecord> uids = pmap.valueAt(i);
        for (int j = 0; j < uids.size(); j++) {

            // 在这里做了一些判断,判断哪些任务不被结束。

            ProcessRecord proc = uids.valueAt(j);
            if (proc.userId != tr.userId) {
                // Don't kill process for a different user.
                continue;
            }
            if (proc == mHomeProcess) {
                // Don't kill the home process along with tasks from the same package.
                continue;
            }
            if (!proc.pkgList.containsKey(pkg)) {
                // Don't kill process that is not associated with this task.
                continue;
            }

            for (int k = 0; k < proc.activities.size(); k++) {
                TaskRecord otherTask = proc.activities.get(k).task;
                if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
                    // Don't kill process(es) that has an activity in a different task that is
                    // also in recents.
                    return;
                }
            }

            // 除了不被删除的任务,剩下的任务都可以被结束。
            // Add process to kill list.
            procsToKill.add(proc);
        }
    }

    // Find any running services associated with this app and stop if needed.
    mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));

    // 结束进程
    // Kill the running processes.
    for (int i = 0; i < procsToKill.size(); i++) {
        ProcessRecord pr = procsToKill.get(i);
        if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
            pr.kill("remove task", true);
        } else {
            pr.waitingToKill = "remove task";
        }
    }
}

在上面的代码中获得了组件的包名,局部变量pkg,保存这个包名。经过实际测试,如果要保护某个APP不被这个方法kill掉,只要当"pkg.equals(<要保护APP的包名>)"返回true时直接使用"return;"语句返回,那么这个APP就不会被这个方法kill掉。测试的方法是直接修改了Android5.1.1的源码。

转载于:https://my.oschina.net/ibuwai/blog/526614

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值