1.handler的使用
public class MainActivity extends AppCompatActivity {
MyHandler myHandler;
private static final int MSG_INDEX_1 = 1;
private static final int MSG_INDEX_2 = 2;
private static final int MSG_INDEX_3 = 3;
private TextView tv1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv1 = findViewById(R.id.tv1);
Message message = new Message();
myHandler = new MyHandler(this);
message.what = MSG_INDEX_1;
myHandler.sendMessageDelayed(message,100);
myHandler.sendEmptyMessageDelayed(MSG_INDEX_2, 10000);
myHandler.sendEmptyMessageDelayed(MSG_INDEX_3, 5000);
}
static class MyHandler extends Handler {
WeakReference<MainActivity> mWeakReference;
public MyHandler(MainActivity activity) {
mWeakReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MSG_INDEX_1:
Toast.makeText(mWeakReference.get(), "1111", Toast.LENGTH_SHORT).show();
break;
case MSG_INDEX_2:
Toast.makeText(mWeakReference.get(), "2222", Toast.LENGTH_SHORT).show();
break;
case MSG_INDEX_3:
Toast.makeText(mWeakReference.get(), "3333", Toast.LENGTH_SHORT).show();
break;
default:
}
}
}
}
// 其中有我们容易忽略的handler的创建部分 以下为handler的构造方法
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
// 注意点1 之后用到
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 注意点2 之后用到
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
2.sendMessage
sendMessage有好几种方式,其最终调用的都是public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis)
我们以sendMessageDelayed为例开始分析
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
// MessageQueue与Message结合一起组成了一个队列结构
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
// 消息准备 入队列
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 消息 入队列
return queue.enqueueMessage(msg, uptimeMillis);
}
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
我们需要着重分析入队的方法 以下面代码为例 分析Message与MessageQueue为什么构成了一个队列 是如何入队列的
myHandler = new MyHandler(this);
Message message = new Message();
message.what = MSG_INDEX_1;
myHandler.sendMessageDelayed(message,100);
myHandler.sendEmptyMessageDelayed(MSG_INDEX_2, 10000);
myHandler.sendEmptyMessageDelayed(MSG_INDEX_3, 5000);
其他逻辑不难理解 主要分析enqueueMessage内部逻辑
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {// 不成立
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
...
}
return true;
}
这个入队的逻辑 简单来说是按照执行时刻入队 越先执行的msg 越先入队 比如我的代码执行入队操作后的结构类似下面这样
3.handleMessge
那么handleMessge是在哪里触发的呢
实际上我们忽略了Handler是在Activity主线程执行的 在ActivityThread的main函数 有两句重要的执行
Looper.prepareMainLooper以及Looper.loop
public static void main(String[] args) {
...
// 初始化主线程的Looper
Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
// sThreadLocal 中存储了ThreadLocalMap 该map以当前线程为key looper为value 确保一个线程只有一个looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 一个线程只创建一次looper
sThreadLocal.set(new Looper(quitAllowed));
}
至于Looper.loop则是开启死循环 遍历MessageQueue中的Message 让handler执行handleMessage
4.Looper.loop方法
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
...
final MessageQueue queue = me.mQueue;
...
// 死循环遍历消息队列
for (;;) {
// 取出队头元素
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
// msg.target 是handler 即执行handler的dispatchMessage方法
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
..
// 回收消息
msg.recycleUnchecked();
}
}
// Handler.java
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
5.handler的整体工作流程
1.looper.prepare
主要负责创建Looper对象 looper对象每个线程只有一个 保存在Looper的静态变量ThreadLocal<Looper> sThreadLocal中
ThreadLocal保存形式为保存在ThreadLocalMap 是一个Map key为当前线程 value为looper
保证一个线程只有一个looper
2.创建handler
创建handler时 就将handler关联到相关线程的looper和messageQueue 形成 Handler- thread-looper-queue的对应关系
其中有个异常 如果没有在创建Handler之前调用looper.prepare(创建looper) 抛出以下异常
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
3.sendMessage
该方法主要是构建消息队列 按照消息的执行时间将Message形成队列 该队列会保存在Looper中
4.looper.loop
该方法主要是从消息队列取出对头元素 交由handler执行,之后会触发Handler的handleMessage方法
6.handler框架模拟
本节所有文件都是自己新建的,目的是模拟ActivityThread中handler,looper等相关类的生命周期 与Android原生的文件同名文件没有关系
1.Message
public class Message {
// 何时执行Message
public long when;
// 用于区分各个Message
public int what;
// 标记当前Message由哪个handler处理
public Handler target;
// 链表结构 指向下一个Message
Message next;
// 用于通过Message传递信息给Handler
public Object obj;
}
2.MessageQueue
public class MessageQueue {// 不是严格意义的队列 入队列按照when入队列 出队则是和队列的规则一致
// 虽然只有一个Message 然而Message.next 指向下一个Message 依次类推 知道某一个Message的next指向null 其实组成一个队列
Message mMessages;
// 按照when的value 从小到大入队列
boolean enqueueMessage(Message msg, long when) {
System.out.println("enqueueMessage msg.what " +msg.what);
synchronized (this) {
msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
Message prev;
for (; ; ) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
}
return true;
}
// 从队列头部出队列
Message next() {
for (; ; ) {
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = System.currentTimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null);
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
} else {
// Got a message.
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
return msg;
}
} else {
// No more messages.
}
}
}
}
}
3.Handler
public class Handler {
MessageQueue mQueue;
public Handler() {
Looper looper = Looper.myLooper();
if(looper == null){// 创建Handler之前必须调用Looper.prepare
// 即创建Handler之前 必须能根据当前线程取出当前线程的Looper 否则报错
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 将一一对应的Looper-Queue中的Queue赋值给当前handler中的Queue
// 最终形成 Handler-Looper-Thread 的相互对应的关系
// | |
// Q u e u e
mQueue= looper.mQueue;
}
// 发送消息 本质是入队列
public void sendMessage(Message message) {
sendMessageDelayed(message,0);
}
// 发送消息 本质是入队列
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, System.currentTimeMillis() + delayMillis);
}
// 发送消息 本质是入队列
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
public void handleMessage(Message msg) {
}
// 发送消息 本质是入队列
private boolean enqueueMessage(MessageQueue queue, Message msg,
long uptimeMillis) {
// 确认Message交由本handler处理
msg.target = this;
return queue.enqueueMessage(msg, uptimeMillis);
}
}
4.Looper
public class Looper {
// Looper内部只有一个Looper实例 存储在sThreadLocal
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
public MessageQueue mQueue;
public static Looper myLooper() {
return sThreadLocal.get();
}
public Looper() {
// 一个looper匹配唯一一个队列mQueue
mQueue = new MessageQueue();
}
public static void prepare() {
if (sThreadLocal.get() != null) {
// 确保一个Looper只返回唯一一个Looper实例
throw new RuntimeException("Only one Looper may be created per thread");
}
// 在sThreadLocal中存储Looper实例 键值对为 thread-looper
sThreadLocal.set(new Looper());
}
/*
* ThreadLocal的set方法
* public void set(T value) {
* // 获取当前thread
* Thread t = Thread.currentThread();
* // 以当前thread为key Looper为value 存储在ThreadLocalMap
* // 即 ThreadLocalMap 存储了唯一的键值对 thread-looper
* // 一个thread只有一个looper 且必须有一个(否则loop时报错)
* ThreadLocalMap map = getMap(t);
* if (map != null)
* map.set(this, value);
* else
* createMap(t, value);
* }
*/
/**
* Run the message queue in this thread. Be sure to call
* {link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
// 由调用sendMessage的handler调用handleMessage
msg.target.handleMessage(msg);
} catch (Exception exception) {
throw exception;
}
// 回收消息部分省略
// msg.recycleUnchecked();
}
}
}
5.TextView
public class TextView {
private Thread mThread;
public TextView(){
mThread = Thread.currentThread();
}
public void setText(CharSequence text){
checkThread();
System.out.println("update "+text);
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new RuntimeException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
}
6.Activity
public class Activity {
public void onCreate(){
System.out.println("onCreate execute");
}
public void onResume(){
System.out.println("onResume execute");
}
public TextView findViewById(int tv) {
return new TextView();
}
}
7.MyActivity
public class MyActivity extends Activity{
private TextView mTextView;
private Handler mHandler = new Handler(){
// 由于Handler在主线程创建 Handler构造方法Looper.myLooper时取得了主线程的Looper
// handleMessage在主线程执行
public void handleMessage(Message msg) {
System.out.println("msg.what 必定=999 "+msg.what);
mTextView.setText((String)msg.obj);
System.out.println("handleMessage在主线程执行: "+Thread.currentThread());
};
};
@Override
public void onCreate() {
super.onCreate();
mTextView = findViewById(RR.id.text_view);
new Thread(){
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// mTextView.setText("");
// Only the original thread that created a view hierarchy can touch its views.
// 子线程禁止直接操作UI
// 如果想在子线程创建Handler 需要调用Looper.prepare 否则抛出异常
// Can't create handler inside thread that has not called Looper.prepare()
// Looper.prepare();
// Handler my = new Handler();
// Looper.loop();
// 开启子线程
System.out.println("child thread "+Thread.currentThread());
Message message = new Message();
message.obj = "update text";
message.what = 999;
// 向在主线程中创建的mHandler发送消息 消息入栈 之前在ActivityThread已经开启轮询Looper.loop();
// 之后则会被mHandler handle这个message
mHandler.sendMessage(message);
};
}.start();
}
@Override
public void onResume() {
super.onResume();
}
}
8.RR(模拟R文件)
public class RR {
public final static class id{
public static final int text_view = 0x0011;
}
}
9.ActivityThread
public class ActivityThread {
final H mH = new H();
public static final int RESUME_ACTIVITY = 107;
public static void main(String[] args) {
System.out.println("当前是主线程: "+Thread.currentThread());
// 初始化当前Thread的looper
Looper.prepare();
ActivityThread thread = new ActivityThread();
// 开始Activity的生命周期
thread.attach(false);
// 开启消息的轮询
Looper.loop();
}
private void attach(boolean b) {
Activity myActivity = new MyActivity();
// onCreate执行
myActivity.onCreate();
Message message = new Message();
// 将Activity传递进Message
message.obj = myActivity;
message.what = RESUME_ACTIVITY;
// message在mH的队列中入队 在轮询之后 会由mH处理Message
mH.sendMessage(message);
}
private class H extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.what == RESUME_ACTIVITY){// 处理attach中的message
Activity mainActivity = (Activity) msg.obj;
mainActivity.onResume();
}
}
}
}
执行结果:
当前是主线程: Thread[main,5,main]
onCreate execute
enqueueMessage msg.what 107
onResume execute
child thread Thread[Thread-2,5,main]
enqueueMessage msg.what 999
msg.what 必定=999 999
update update text
handleMessage在主线程执行: Thread[main,5,main]
7.handler相关的常见面试题
1.MessageQueue 是什么结构?
MessageQueue 是队列的数据结构,以message为节点构成先入先出的队列
2.ThreadLocal 是怎么保证线程安全的
ThreadLocal 是线程隔离的 即使保存了一个static的ThreadLocal 实例 实际上使用的时候ThreadLocal 的get与set方法都还是根据当前线程来存取
观察ThreadLocal的set与get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
可以知道ThreadLocal 之所以是线程隔离的 是因为每个Thread有自己的ThreadLocalMap,该map以当前线程为key 想要保存的值为value,这意味着ThreadLocal 会根据当前线程的不同,存取的值也在不同的线程的ThreadLocalMap中
借助
https://zhuanlan.zhihu.com/p/133433717
的例子
public class Test {
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
// 复写initialValue方法为ThreadLocal设置一个初始值,并获取调用了threadLocal的线程id
@Override
protected Integer initialValue() {
System.out.println("current thread id:"
+ Thread.currentThread().getId());
System.out.println("threadLocal==>"+threadLocal);
return 10;
}
};
public static void main(String[] args) {
// main方法就对应一个线程了,我们在主线程中对threadLocal的值进行修改
System.out.println("mian thread");
System.out.println("threadLocal in main thread:" + threadLocal.get());
threadLocal.set(100); // 改变threadLocal的值
System.out.println("threadLocal in main thread again:"
+ threadLocal.get());
System.out.println("start new thread");
// 新创一个线程,并获取threadLocal的值
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("threadLocal in new thread:"
+ threadLocal.get());
}
});
thread.start();
}
}
乍一看 test使用了类变量threadLocal 应该所有线程使用同一个实例
然而输出如下
mian thread
current thread id:1
threadLocal==>Father.Test$1@15db9742
threadLocal in main thread:10
threadLocal in main thread again:100
start new thread
current thread id:11
threadLocal==>Father.Test$1@15db9742
threadLocal in new thread:10
这是因为虽然threadLocal 是同一个变量 但是观察上面ThreadLocal的set与get方法 可知 它存储在Map的key是当前的Thread,因此实际上不同的线程存储在不同的ThreadLocalMap中。
3.使用handler的常见错误
何时会引发java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
为什么在主进程中创建Handler没有这样的问题呢?
在子线程创建Handler并且不调用looper的prepare方法
至于主进程中创建Handler没有这个问题,自然是主进程自己已经帮我们做了
// ActivityThread.java
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
// 准备主进程的looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// 启动looper的循环
// 跟踪这里
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
// Looper.java
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
...
try {
// 分配msg
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
}
}
// Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 处理msg
handleMessage(msg);
}
}
ActivityThread.java
private class H extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case RELAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case PAUSE_ACTIVITY:
...
}
}