Android 面试题记录(二)

1. 管理基本线程的生命期

生命期 :New -> Runnable -> Blocked/Waiting(Thread.sleep() | Thread.yield()) -> Terminated

Uncaught Exceptions

  • Thread global handler:static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler);
  • Thread local handler:void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler);
  • Unhandled Exceptions on the UI Thread,参见面试题记录(一)
1
2
3
4
5
6
7
8
9
10
11
12
Thread t = new Thread(new Runnable() {
@Override public void run() {
throw new RuntimeException("Unexpected error occurred");
}
});
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override public void uncaughtException(Thread thread, Throwable throwable) {
// A basic logging of the message. Could be replaced by log to file or a network post.
Log.d(TAG, throwable.toString());
}
});
t.start();

在 Activity 中保持线程

  • public Object onRetainNonConfigurationInstance():Called by the platform before a configuration change occurs, the implementation should return any object that you want to be retained across a configuration change (e.g., a thread) and passed to the new Activity object.
  • public Object getLastNonConfigurationInstance():Called by the platform before a configuration change occurs, it can be called in onCreate or onStart and returns null if the Activity is started for another reason than a configuration change.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class ThreadRetainActivity extends Activity {
private static class MyThread extends Thread {
...
}
private static MyThread t;
private TextView textView;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_retain_thread);
textView = (TextView) findViewById(R.id.text_retain);
Object retainedObject = getLastNonConfigurationInstance();
if (retainedObject != null) {
t = (MyThread) retainedObject;
t.attach(this);
}
}
@Override public Object onRetainNonConfigurationInstance() {
if (t != null && t.isAlive()) {
return t;
}
return null;
}
public void onClickStartThread(View v) {
t = new MyThread(this);
t.start();
}
private void setText(final String text) {
runOnUiThread(new Runnable() {
@Override public void run() {
textView.setText(text);
}
});
}
}

在 Fragment 中保持线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class ThreadRetainWithFragmentActivity extends Activity {
private ThreadFragment mThreadFragment;
private TextView mTextView;
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_retain_thread);
mTextView = (TextView) findViewById(R.id.text_retain);
FragmentManager manager = getFragmentManager();
mThreadFragment = (ThreadFragment) manager.findFragmentByTag("threadfragment");
if (mThreadFragment == null) {
FragmentTransaction transaction = manager.beginTransaction();
mThreadFragment = new ThreadFragment();
transaction.add(mThreadFragment, "threadfragment").commit();
}
}
public void onStartThread(View v) { // Method called to start a worker thread
mThreadFragment.execute();
}
public void setText(final String text) {
runOnUiThread(new Runnable() {
@Override public void run() {
mTextView.setText(text);
}
});
}
}
public class ThreadFragment extends Fragment {
private ThreadRetainWithFragmentActivity mActivity;
private MyThread t;
private class MyThread extends Thread {
@Override public void run() {
final String text = getTextFromNetwork();
mActivity.setText(text);
}
private String getTextFromNetwork() {
SystemClock.sleep(5000); // Simulate network operation
return "Text from network";
}
}
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true); // Retain the platform handle on configuration changes.
}
@Override public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = (ThreadRetainWithFragmentActivity) activity;
}
@Override public void onDetach() {
super.onDetach();
mActivity = null;
}
public void execute() {
t = new MyThread();
t.start();
}
}

2. 使用 HandlerThread(A High-Level Queueing Mechanism)

基础

  • HandlerThread 该线程是一个包装了线程,Looper 和消息队列(MessageQueue)。
1
2
3
4
5
6
7
8
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
mHandler = new Handler(handlerThread.getLooper()) {
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
// Process messages here
}
};
  • 限制 HandlerThread 的可见性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class MyHandlerThread extends HandlerThread {
private Handler mHandler;
public MyHandlerThread() {
super("MyHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
}
@Override protected void onLooperPrepared() {
super.onLooperPrepared();
mHandler = new Handler(getLooper()) {
@Override public void handleMessage(Message msg) {
switch(msg.what) {
case 1: // Handle message
break;
case 2: // Handle message
break;
}
}
};
}
public void publishedMethod1() {
mHandler.sendEmptyMessage(1);
}
public void publishedMethod2() {
mHandler.sendEmptyMessage(2);
}
private int mCount;
private SharedPreferenceThread mThread;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shared_preferences);
mTextValue = (TextView) findViewById(R.id.text_value);
mThread = new SharedPreferenceThread();
mThread.start();
}
public void onButtonClickWrite(View v) {
mThread.write(mCount++);
}
public void onButtonClickRead(View v) {
mThread.read();
}
@Override protected void onDestroy() {
super.onDestroy();
mThread.quit(); // Ensure that the background thread is terminated with the Activity.
}
}

串联任务

  • 串联任务模式:由 Handler 定义的任务通过在 handleMessage() 中进行解耦和串联,该模式执行的关键在于 Message.what 参数,任何任务都可以独立地被访问;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class ChainedNetworkActivity extends Activity {
private static final int DIALOG_LOADING = 0;
private static final int SHOW_LOADING = 1;
private static final int DISMISS_LOADING = 2;
Handler dialogHandler = new Handler() {
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case SHOW_LOADING:
showDialog(DIALOG_LOADING);
break;
case DISMISS_LOADING:
dismissDialog(DIALOG_LOADING);
break;
}
}
};
private class NetworkHandlerThread extends HandlerThread {
private static final int STATE_A = 1;
private static final int STATE_B = 2;
private Handler mHandler;
public NetworkHandlerThread() {
super("NetworkHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
}
@Override protected void onLooperPrepared() {
super.onLooperPrepared();
mHandler = new Handler(getLooper()) {
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case STATE_A:
dialogHandler.sendEmptyMessage(SHOW_LOADING);
String result = networkOperation1();
if (result != null) {
sendMessage(obtainMessage(STATE_B, result));
} else {
dialogHandler.sendEmptyMessage(DISMISS_LOADING);
}
break;
case STATE_B:
networkOperation2((String) msg.obj);
dialogHandler.sendEmptyMessage(DISMISS_LOADING);
break;
}
}
};
fetchDataFromNetwork();
}
private String networkOperation1() {
SystemClock.sleep(2000);
return "A string";
}
private void networkOperation2(String data) {
SystemClock.sleep(2000); // Dummy
}
// Publically exposed network operation
public void fetchDataFromNetwork() {
mHandler.sendEmptyMessage(STATE_A);
}
}
private NetworkHandlerThread mThread;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mThread = new NetworkHandlerThread();
mThread.start();
}
@Override protected Dialog onCreateDialog(int id) {
Dialog dialog = null;
switch (id) {
case DIALOG_LOADING:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Loading...");
dialog = builder.create();
break;
}
return dialog;
}
// Ensure that the background thread is terminated with the Activity.
@Override protected void onDestroy() {
super.onDestroy();
mThread.quit();
}
}
  • 有选择地插入任务
1
2
3
4
5
handler.hasMessages(int what)
handler.hasMessages(int what, Object tag)
if (handler.hasMessages(MESSAGE_WHAT) == false) {
handler.sendEmptyMessage(MESSAGE_WHAT);
}

3. 如何使用 Java Executor 框架

Executor

1
2
3
4
5
6
7
8
public interface Executor {
void execute(Runnable command);
}
public class SimpleExecutor implements Executor {
@Override public void execute(Runnable runnable) {
new Thread(runnable).start();
}
}

线程池

  • 固定数量:Executors.newFixedThreadPool(n),使用时,可重配置数量,((ThreadPoolExecutor)executor).setCorePoolSize(4);
  • 动态大小:Executors.newCachedThreadPool()
  • 单线程:Executors.newSingleThreadExecutor()

4. 如何自定义线程池

ThreadPoolExecutor 配置

1
2
3
4
5
6
ThreadPoolExecutor executor = new ThreadPoolExecutor(
int corePoolSize, // 该值设定的允许的线程数量,线程数量 < 该值
int maximumPoolSize, // 允许同时并发的数量
long keepAliveTime, // 允许线程存活以等待下一任务的时间
TimeUnit unit, // 配置存活时间的单位
BlockingQueue<Runnable> workQueue); // 任务队列

设计

  • 线程数量 <- CPU 核心数量;另外需要记住的是:线程数量是依据线程创建和消亡而动态变化的!
1
2
3
int N = Runtime.getRuntime().availableProcessors(); // 获取 CPU 核心数量
// Brian Goetz 建议:N+1个线程的数量性能最好;
// Kirk Pepperdine 则推荐: 2*N 个线程性能也不错;
  • Bounded or unbounded task queue:LinkedBlockingQueue(无数量限制), PriorityBlockingQueue(有数量限制), and ArrayBlockingQueue.
  • 线程配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class LowPriorityThreadFactory implements ThreadFactory {
private static int count = 1;
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("LowPrio " + count++);
t.setPriority(4);
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override public void uncaughtException(Thread t, Throwable e) {
Log.d(TAG, "Thread = " + t.getName() + ", error = " + e.getMessage());
}
});
return t;
}
}
Executors.newFixedThreadPool(10, new LowPriorityThreadFactory());
  • 扩展 ThreadPoolExecutor
  • void beforeExecute(Thread t, Runnable r):在执行线程之前执行;
  • void afterExecute(Runnable r, Throwable t):在线程执行结束后(不管有没有异常)执行;
  • void terminated():在没有线程执行任务的前提下,线程池关闭后执行;
  • 生命期
  • Shutdown: 在调用 ExecutorService.shutdown() 后,线程池仍会继续执行未完的线程任务,但新的任务添加请求则会被拒接;
  • Stop: 在调用 ExecutorService.shutdownNow() 后,正在执行的任务线程会被中止,未被执行的任务则被移除;
  • Terminated: 在调用 ThreadPoolExecutor.terminated() 后的状态,所有数据结构将被释放;

线程池生命期

使用例子和陷阱

  • 一般情况下,线程池创建的线程即使终止后也不会被系统所回收,若要回收,可调用语句:executor.allowCoreThreadTimeOut(true);
  • 处理预先加载的任务:一般情况下,线程池创建后等待任务,但可以使用如示方法在线程池创建后不必等待任务 prestartAllCoreThreads() or prestartCoreThread();
  • 陷阱:创建零线程的线程池,当任务队列满了才会触发一个线程执行任务,如下所示,当第11个任务插入队列时才会触发执行一个线程:
1
2
3
4
5
6
int N = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
0,
N*2,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(10));

5. 如何使用AsyncTask

1
2
3
4
5
6
7
8
9
10
11
12
public class FullTask extends AsyncTask<Params, Progress, Result> {
@Override
protected void onPreExecute() { ... }
@Override
protected Result doInBackground(Params... params) { ... }
@Override
protected void onProgressUpdate(Progress... progress) { ... }
@Override
protected void onPostExecute(Result result) { ... }
@Override
protected void onCancelled(Result result) { ... }
}

AsyncTask概览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// 示例:下载图片
public class FileDownloadActivity extends Activity {
private static final String[] DOWNLOAD_URLS = {
"http://developer.android.com/design/media/devices_displays_density@2x.png",
"http://developer.android.com/design/media/iconography_launcher_example2.png",
"http://developer.android.com/design/media/iconography_actionbar_focal.png",
"http://developer.android.com/design/media/iconography_actionbar_colors.png"
};
DownloadTask mFileDownloaderTask;
ProgressBar mProgressBar;
LinearLayout mLayoutImages;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file_download);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mProgressBar.setMax(DOWNLOAD_URLS.length);
mLayoutImages = (LinearLayout) findViewById(R.id.layout_images);
mFileDownloaderTask = new DownloadTask(this);
mFileDownloaderTask.execute(DOWNLOAD_URLS);
}
@Override protected void onDestroy() {
super.onDestroy();
mFileDownloaderTask.setActivity(null);
mFileDownloaderTask.cancel(true);
}
private static class DownloadTask extends AsyncTask<String, Bitmap, Void> {
private FileDownloadActivity mActivity;
private int mCount = 0;
public DownloadTask(FileDownloadActivity activity) {
mActivity = activity;
}
public void setActivity(FileDownloadActivity activity) {
mActivity = activity;
}
@Override protected void onPreExecute() {
super.onPreExecute();
mActivity.mProgressBar.setVisibility(View.VISIBLE);
mActivity.mProgressBar.setProgress(0);
}
@Override protected Void doInBackground(String... urls) {
for (String url : urls) {
if (!isCancelled()) {
Bitmap bitmap = downloadFile(url);
publishProgress(bitmap);
}
}
return null;
}
@Override protected void onProgressUpdate(Bitmap... bitmaps) {
super.onProgressUpdate(bitmaps);
if (mActivity != null) {
mActivity.mProgressBar.setProgress(++mCount);
ImageView iv = new ImageView(mActivity);
iv.setImageBitmap(bitmaps[0]);
mActivity.mLayoutImages.addView(iv);
}
}
@Override protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (mActivity != null) {
mActivity.mProgressBar.setVisibility(View.GONE);
}
}
@Override protected void onCancelled() {
super.onCancelled();
if (mActivity != null) {
mActivity.mProgressBar.setVisibility(View.GONE);
}
}
private Bitmap downloadFile(String url) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream((InputStream) new URL(url).getContent());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
}
}

API

  • execute(Params…)
  • execute(Runnable):Added in API level 11, onPreExecute, onPostExecute, 和 onCancelled 方法将不会被调用,并且执行的进度也不会被更新(and progress can not be published)
  • executeOnExecutor(Executor, Params…):Added in API level 11
  • AsyncTask.THREAD_POOL_EXECUTOR, 任务将并发执行, 在 KitKat(4.4)版本, 线程池的大小将基于 CPU 的核心数量:N+1 个核心线程以及最多2N+1 个线程,并且队列可拥有多达128个任务,所以在4核心的平台上,可拥有最多137个任务。
  • AsyncTask.SERIAL_EXECUTOR,保证线程安全,该调度器将任务存储在一个没有限制数量的队列里,并将其一个个传递给 THREAD_POOL_EXECUTOR 线程池去执行。









在各个平台执行的不同区别
API 级别executeexecuteOnExecutor
1~3线性序列还未存在
4~7-并发还未存在
11~12并发线性序列/并发(自定义)
13+线性序列线性序列/并发(自定义)