3. Managing the lifecycle of a Basic Thread

来源:互联网 发布:scratch编程教程 pdf 编辑:程序博客网 时间:2024/05/16 19:31

1. Basics

Lifecycle

可以在Thread.State类中查看Thread的状态。
这里写图片描述

New

在Thread执行之前,Thread对象被创建。这个实例不会建立执行环境。默认Thread构造方法分配新创建的Thread的优先级和所属的Thread Group与创建这个新Thread所在的线程是相同的(The default construction assigns the newly created thread to the same thread group as the thread that is doing the creation, with the same priority)。例如,在UI Thread中创建新的Thread,那么这个新创建的Thread和UI Thread所属同一个Thread Group和Priority。

Runnable

当Thread.start()方法被调用,这个执行环境被建立并且这个Thread开始准备执行。当调度器选择了这个Thread执行,那么这个run()方法会被调用,任务开始被执行。

Blocked/Waiting

  • 被动
    当Thread需要等待资源时,执行能被中止。

  • 主动
    调用Thread.sleep();
    调用Thread.yield():Thread放弃执行,让调度器重新选择哪个Thread执行。这个调度器能自由的选择哪个线程执行,调度器可能还会选择这个Thread执行。

Terminated

当run()方法执行完毕,这个Thread就会终止并释放它占用的资源。这个Thread不会被服用,Thread的建立和销毁是很重的操作,如果需要多次这样操作的话,请使用Thread Pool(线程池)。

Interruptions

Thread能被interrupted,这个请求Thread应该终止,interrupt是由Thread自己决定的。

Thread t = new SimpleThread();t.start(); // 开始线程t.interrupt(); // 请求interruption

Thread自己能调用interrupt,其他Thread也可以调用interrupt这个Thread。发布一个Interruption不会直接的影响Thread的执行,它仅仅是设置了Thread内部的interrupted flag为interrupted。这个Interrupted Thread要检查这个interrupted flag,然后优雅地终止。Thread必须要实现Cancellation Point来允许这个Thread被interrupted并终止:

public class SimpleThread extends Thread {    @Override    public void run() {        // 通过使用isInterrupted()方法检查interrupt flag来实现Cancellation Point        while (isInterrupted() == false) {            // Thread 运行        }        // 任务结束,Thread终止    }}

如果Thread被interrupted会抛出一个InterruptedException的异常。当InterruptedException被抛出后,这个Thread中的interrupted flag就会被重置 – 例如,即使这个Thread已经interrupted,isInterrupted()方法返回false。

相关方法

interrupt():请求线程interrupt;
isInterrupted():检查线程是否interrupted,当InterruptedException抛出后,interrupted flag会被重置,变为false。
这里写图片描述

static Thread.interrupted():这个是静态方法,也是检查线程是否interrupted,但是这个方法会清理interrupted flag。
这里写图片描述

Uncaught Exceptions

如果Thread异常结束,可能抛出unchecked exception。Unchecked exception是RuntimeException的子类,它们不能被try/catch捕获。为了防止unchecked exception不能捕获而不能给通知,我们可以给Thread设置一个UncaughtExceptionHandler,这个UncaughtExceptionHandler在Thread终止前会被调用。我们可以在这个UncaughtException中优雅地终止Thread或者给出提示信息。
我们可以通过方法设置全局Thread的ExceptionHandler,也可以设置某个thread的ExceptionHandler,Thread设置的本地ExceptionHandler的调用优先级比全局的ExceptionHandler高:
这里写图片描述

Unhandled Exceptions on the UI Thread

在Application开启时,Android绑定了一个全局的UncaughtExceptionHandler给这个Application。默认情况这个exception handler绑定了Application中的所用Thread,它对待所有Thread的unhandled exception是一样的:杀死这个进程。


可以参考Android Source Code中的RuntimeInit.java文件(链接:https://android.googlesource.com/platform/frameworks/base/+/master/core/java/com/android/internal/os/RuntimeInit.java)
(RuntimeInit.java的修改记录:https://android.googlesource.com/platform/frameworks/base/+log/master/core/java/com/android/internal/os/RuntimeInit.java
SHA1号addbf90的修改:https://github.com/android/platform_frameworks_base/commit/addbf9015a65ed7704a4fc22f36380dd153698da)

public class RuntimeInit {    ... ...    // Application的UncaughtExceptionHandler    private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {        ... ...    }    protected static final void commonInit() {        ... ...        // 设置UncaughtExceptionHandler        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());        ... ...    }}

设置自己Application的UncaughtExceptionHandler:

// 设置Application自己全局的UncaughtExceptionHandlerThread.setDefaultUncaughtExceptionHandler(new CustomUncaughtExceptionHandler());private static class CustomUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {    private final Thread.UncaughtExceptionHandler defaultHandler;    public CustomUncaughtExceptionHandler() {     // 获取此Application默认的UncaughtExceptionHandler        defaultHandler = Thread.getDefaultUncaughtExceptionHandler();    }    @Override    public void uncaughtException(Thread thread, Throwable throwable) {        // TODO 做一些自定义的操作        Log.i(TAG, "### This is my custom uncaught exception handler ###");        // 使用Thread default uncaught exception handler的变量调用uncaughtException,使得Application接收到unchecked exception时杀掉进程        defaultHandler.uncaughtException(thread, throwable);    }}

2. Thread Management

Definition and Start

使用简单地例子来说明定义Thread,例子中有一个外部类(Outer Class) AnyObject,定义一个方法anyMethod()来start Thread,此方法在UI Thread中调用。

Annoymous inner class

使用匿名内部类的方法来定义并开启Thread代码是很简单的,但是匿名内部类会保留外部类的引用,而导致外部类的引用在Thread结束之前不能被回收。像anyMethod()方法在UiThread中调用,使得context不能及时释放,会使内存占用大,可能会内存溢出。

public class AnyObject {    @UiThread    public void anyMethod() {        new Thread() {            public void run() {                doLongRunningTask();            }        }.start();    }}

Public Thread

把Thread定义在一个单独的类中,这样Thread就不会保留外部类AnyObject的引用了,但是这样会增加类文件数量。

class MyThread extends Thread {    public void run() {        doLongRunningTask();    }}public class AnyObject {    private MyThread myThread;    @UiThread    public void anyThread() {        myThread = new MyThread();        myThread.start();    }}

Static inner class thread definition

使用静态内部类的方式来定义Thread。静态内部类仅仅会保留外部类AnyObject中类对象的引用,而不会保留实例对象的引用。因此,外部类AnyObject中的实例对象内存回收不受静态内部类影响。
推荐使用这种方式定义Thread。

public class AnyObject {    static class MyThread extends Thread {        public void run() {            doLongRunningTask();        }    }    private MyThread myThread;    @UiThread    public void anyMethod() {        myThread = new MyThread();        myThread.start();    }}

Summary

上面三种方式中最好使用静态内部类的方式定义Thread,但是上面的例子中Thread是直接使用new Thread();创建的,这样的Thread的是不好控制的(Thread的数量,以及Thread不能回收利用等等),我们可以使用Thread Pool(线程池)或者HandlerThread来创建,它们能控制Thread执行的数量。

Retention(保留/滞留)

Thread的生命周期不受Android Component的生命周期应用。
例如在Activity中创建一个Thread来处理耗时操作,如果Activity的configuration改变了(如旋转屏幕),那么创建Thread的Activity就被销毁了,这样Thread处理的数据结果就没法返回到configuration改变后新创建的Activity中,为了处理这种情况我们需要保留Thread,即把Thread绑定到configuration改变后新创建的Activity。
因为在Android API 13之后有了Fragment,因此保留Thread的方式在Activity和Fragment中不同。

Retaining a thread in an Activity

Activity中相关的方法:
这里写图片描述

public class ThreadRetainActivity extends Activity {    private static final String TAG = ThreadRetainActivity.class.getSimpleName();    private TextView retainThreadBeforeText = null;    private TextView retainThreadAfterText = null;    private Button retainThreadButton = null;    private MyThread myThread = null;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_retainthread);        Log.i(TAG, "ThreadRetainActivity # UIThread: " + Thread.currentThread().getId() + " - " + Thread.currentThread().getName());        retainThreadBeforeText = (TextView) findViewById(R.id.retain_thread_before_text);        retainThreadAfterText = (TextView) findViewById(R.id.retain_thread_after_text);        retainThreadButton = (Button) findViewById(R.id.retain_thread_btn);        // If there is a retained thread object, it is restored. The new Activity instance is        // registered to the thread.        Object retainObject = getLastNonConfigurationInstance();        if (retainObject != null) {            myThread = (MyThread) retainObject;            myThread.attach(this); // 当activity的configuration改变后(如转屏),把Thread绑定到新建的Activity        }        retainThreadButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                myThread = new MyThread(ThreadRetainActivity.this);                myThread.start();                retainThreadBeforeText.setText(myThread.getId() + " - " + myThread.getName());            }        });    }    @Override    public Object onRetainNonConfigurationInstance() {        // 当activity configuration改变后(转屏),若此Activity创建的Thread还运行的话,把此Thread保留下来,然后绑定到新建的Activity上        if (myThread != null && myThread.isAlive()) {            return myThread;        }        return null;    }    // 实现静态内部类模拟处理耗时操作    private static class MyThread extends Thread {        private WeakReference<ThreadRetainActivity> activity = null;        public MyThread(ThreadRetainActivity activity) {            this.activity = new WeakReference<ThreadRetainActivity>(activity);        }        private void attach(ThreadRetainActivity activity) {            this.activity = new WeakReference<ThreadRetainActivity>(activity);        }        @Override        public void run() {            super.run();            final String text = getTextFromNetwork();            if (activity.get() != null) {                Log.i(TAG, "ThreadRetainActivity # MyThread # run: " + Thread.currentThread().getId() + " - " + Thread.currentThread().getName());                activity.get().retainThreadAfterText.setText(text + Thread.currentThread().getId() + " - " + Thread.currentThread().getName());            }        }        // simulate long operation        private String getTextFromNetwork() {            SystemClock.sleep(5000);            Log.i(TAG, "ThreadRetainActivity # MyThread # getTextFromNetwork");            return "Text is from network. ";        }    }}

Retaining a thread in an Fragment

Fragment是添加到Activity中使用的。在Fragment中,保留它里面的Thread或者其他State,那么直接在Fragment.onCreate()方法中调用setRetainInstance(true);即可。这样当Activity的configuration改变时,这个Fragment就会保留。
ThreadFragment.java

public class ThreadFragment extends Fragment {    private static final String TAG = ThreadFragment.class.getSimpleName();    private ThreadRetainWithFragmentActivity activity = null;    private MyFragmentThread myFragmentThread = null;    @Override    public void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        // 当Activity的configuration改变时,保留Fragment        setRetainInstance(true);    }    @Override    public void onAttach(Context context) {        super.onAttach(context);        // 绑定到Activity上        activity = (ThreadRetainWithFragmentActivity) context;    }    @Override    public void onDetach() {        super.onDetach();        // 解除绑定        activity = null;    }    public void execute() {        myFragmentThread = new MyFragmentThread(activity);        myFragmentThread.start();    }    private static class MyFragmentThread extends Thread {        private WeakReference<ThreadRetainWithFragmentActivity> weakActivity = null;        public MyFragmentThread(ThreadRetainWithFragmentActivity activity) {            weakActivity = new WeakReference<ThreadRetainWithFragmentActivity>(activity);        }        @Override        public void run() {            super.run();            String text = getTextFromNetwork();            if (weakActivity != null) {                weakActivity.get().setText(text);            }        }        // simulate long operation        private String getTextFromNetwork() {            SystemClock.sleep(5000);            Log.i(TAG, "ThreadFragment # MyFragmentThread # getTextFromNetwork");            return "Text is from network. ";        }    }}

ThreadRetainWithFragmentActivity.java

public class ThreadRetainWithFragmentActivity extends Activity {    private ThreadFragment threadFragment = null;    private Button retainFragmentBtn = null;    private TextView retainFragmentText = null;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_retain_fragment_thread);        retainFragmentBtn = (Button) findViewById(R.id.retain_fragment_thread_btn);        retainFragmentText = (TextView) findViewById(R.id.retain_fragment_thread_text);        FragmentManager fm = getFragmentManager();        threadFragment = (ThreadFragment) fm.findFragmentByTag("fragmentthread");        if (threadFragment == null) {            FragmentTransaction ft = fm.beginTransaction();            threadFragment = new ThreadFragment();            ft.add(threadFragment, "fragmentthread");            ft.commit();        }        retainFragmentBtn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                // 启动Fragment中的Thread                threadFragment.execute();            }        });    }    public void setText(String content) {        retainFragmentText.setText(content);    }}

3. Avoiding Memory Leaks

Use Static Inner Classes

Use Weak References

使用静态内部类定义Thread,如果Thread内部需要访问外部类的变量/方法时,使用弱应用的方式来实现。

Stop Worker Thread Execution

Retain Worker Threads

参考 Retaining a thread in an ActivityRetaining a thread in an Fragment

Clean Up the Message Queue

相关方法:

removeCallbacks(Runnable r)removeCallbacks(Runnable r, Object token)removeCallbacksAndMessages(Object token)removeMessaegs(int what)removeMessages(int what, Object object)
原创粉丝点击