一、基础篇
1)UI线程概念
Android为单线程模型。当一个程序第一次启动时,Android会自动创建一个对应的主线程(Main Thread)。它负责把事件分派到相应的控件,用于用户与Android控件进行交互。所以通常又被叫做UI线程
在开发Android应用时必须遵守单线程模型的原则:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行
简单表述为:1、不要阻塞UI线程;2、只能在主线程操作UI。
详见Android帮助文档Dev Guide,左侧栏Processes and Threads。
2)UI线程示例
2.1)UI线程阻塞
不考虑Android单线程模型,将所有任务都在该线程中执行,尤其是某些耗时操作,会使得整个用户界面被阻塞。从用户角度来看,就是按键或触屏后响应很慢甚至毫无响应。而且当应用程序阻塞的时间过长时,Android还会向用户提示一个无响应的对话框(不截了==)。
2.2)非主线程更新UI
1、LogCat会报如下的错误消息:

Uncaught handler: thread Thread-9 exiting due to uncaught exception。

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

2、Android会向用户提示一个强行关闭的对话框(又不截了==)。
2.3)非主线程更新UI
三种方式:①Handler;②View.post(Runnable);③Activity.runOnUiThread(Runnable)。
2.4)好了,开始动手测试吧^^
  1. public class UIThreadActivity extends BaseActivity {
  2. /** 标签 */
  3. private TextView textView;
  4. /** Runnable */
  5. private Runnable runnable = new Runnable() {
  6. @Override
  7. public void run() {
  8. logThreadId(); // 打印当前线程ID
  9. textView.setText(tag + ", I'm Join!"); // 执行UI操作
  10. }
  11. };
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState) {
  14. super.onCreate(savedInstanceState);
  15. setContentView(R.layout.ui_thread);
  16. tag = "UIThreadActivity";
  17. textView = (TextView) findViewById(R.id.textView);
  18. }
  19. /** 1.1 UI线程阻塞 */
  20. public void blockUi(View v) {
  21. tag = "blockUi";
  22. logThreadId(); // 打印当前线程ID
  23. /* 读取网页内容并显示 */
  24. textView.setText("开始读取网页!"); // 该步可能未显示,为什么?
  25. StringBuffer document = loadHtml("http://www.google.com"); // 耗时操作:读取网页源码
  26. textView.setText(null == document ? "读取失败!" : document.toString()); // 显示网页源码
  27. /**
  28. * 1.观察按钮呈按下状态持续时间<br>
  29. * 2.尝试在按钮呈按下状态时,进行按键和触屏操作<br>
  30. */
  31. }
  32. /** 1.2 非主线程更新UI */
  33. public void updateUi(View v) {
  34. tag = "updateUi";
  35. new Thread(runnable).start();
  36. }
  37. /** 2.1 方法1:Handler */
  38. public void methodOne(View v) {
  39. tag = "methodOne";
  40. // Can't create handler inside thread that has not called
  41. // Looper.prepare();
  42. final Handler handler = new Handler();
  43. new Thread(new Runnable() {
  44. @Override
  45. public void run() {
  46. logThreadId(); // 打印当前线程ID
  47. handler.post(runnable);
  48. // handler.postDelayed(runnable, 1000);
  49. }
  50. }).start();
  51. }
  52. /** 2.2 方法2: View.post(Runnable) */
  53. public void methodTwo(View v) {
  54. tag = "methodTwo";
  55. new Thread(new Runnable() {
  56. @Override
  57. public void run() {
  58. logThreadId(); // 打印当前线程ID
  59. textView.post(runnable);
  60. // textView.postDelayed(runnable, 1000);
  61. }
  62. }).start();
  63. }
  64. /** 2.3 方法3:Activity.runOnUiThread(Runnable) */
  65. public void methodThree(View v) {
  66. tag = "methodThree";
  67. new Thread(new Runnable() {
  68. @Override
  69. public void run() {
  70. logThreadId(); // 打印当前线程ID
  71. runOnUiThread(runnable);
  72. }
  73. }).start();
  74. }
  75. /**
  76. * 读取网页源码
  77. */
  78. private StringBuffer loadHtml(String urlStr) {
  79. try {
  80. StringBuffer doc = new StringBuffer();
  81. URL url = new URL(urlStr);
  82. URLConnection conn = url.openConnection();
  83. BufferedReader reader = new BufferedReader(new InputStreamReader(
  84. conn.getInputStream()));
  85. String line = null;
  86. while ((line = reader.readLine()) != null)
  87. doc.append(line);
  88. reader.close();
  89. return doc;
  90. } catch (IOException e) {
  91. e.printStackTrace();
  92. }
  93. return null;
  94. }
  95. }
亲,记得看Log日志出的线程id哦。(卖下萌,不介意吧?)
二、提高篇
1)Android消息处理机制
熟悉Android开发的可能知道其应用程序都是由消息驱动的。参考了Windows的消息循环机制,Android系统通过Handler、Thread、Looper以及MessageQueue实现了这种消息机制。相关的类都被定义在了package android.os。
推荐这篇博客咯——解析Android消息处理机制。(这个,那个,还建模==)
2)实用的消息处理方式
2.1)Thread实现消息循环
  1. /** 1.1 Thread实现消息循环 */
  2. public void methodOne(View v) {
  3. tag = "methodOne";
  4. // 创建一个LooperThread对象,实现了消息循环
  5. LooperThread thread = new LooperThread();
  6. // 必须启动这个线程
  7. thread.start();
  8. // 创建一个消息对象并设置信息
  9. Message msg = new Message();
  10. Bundle bundle = new Bundle();
  11. bundle.putString("key", "1.1 Thread实现消息循环");
  12. msg.setData(bundle);
  13. // 发送消息对象
  14. thread.mHandler.sendMessage(msg);
  15. }
  16. /** 实现消息循环的线程(在Android索引文档android.os.Looper的概述里有介绍) */
  17. private class LooperThread extends Thread {
  18. public Handler mHandler;
  19. public void run() {
  20. Looper.prepare();
  21. mHandler = new Handler() {
  22. public void handleMessage(Message msg) {
  23. // process incoming messages here
  24. logThreadId(); // 打印当前线程ID
  25. Log.e(tag, msg.getData().getString("key"));
  26. }
  27. };
  28. Looper.loop();
  29. }
  30. }
如果不使用Looper.prepare()及loop()的话,就不能创建Handler将消息处理加入到Looper中。LogCat会报“Can't create handler inside thread that has not called Looper.prepare();”的错误信息。
2.2)Handler与Looper相关联
如果构造一个无参的Handler对象,它将自动与当前运行线程相关联。可以注意Handler相关例子中打印出的当前线程ID信息。
而与Looper相关联,需要创建一个带参数的Handler对象。注意:此时线程类应该是一个HandlerThread类,一个Looper类的Thread类。
  1. /** 1.2 Handler与Looper相关联 */
  2. public void methodTwo(View v) {
  3. tag = "methodTwo";
  4. // 生成一个HandlerThread对象,使用Looper来处理消息队列
  5. HandlerThread thread = new HandlerThread("MyThread");
  6. // 必须启动这个线程
  7. thread.start();
  8. // 将一个线程绑定到Handler对象上,则该Handler对象就可以处理线程的消息队列
  9. MyHandler myhandler = new MyHandler(thread.getLooper());
  10. // 从Handler中获取消息对象
  11. Message msg = myhandler.obtainMessage();
  12. // 设置消息对象信息
  13. Bundle bundle = new Bundle();
  14. bundle.putString("key", "1.2 Handler与Looper相关联");
  15. msg.setData(bundle);
  16. // 将消息对象发送给目标对象Handler
  17. msg.sendToTarget();
  18. }
  19. /** 重写Handler的消息处理方法 */
  20. private class MyHandler extends Handler {
  21. // 带有参数的构造函数
  22. public MyHandler(Looper looper) {
  23. super(looper);
  24. }
  25. @Override
  26. public void handleMessage(Message msg) {
  27. // process incoming messages here
  28. logThreadId(); // 打印当前线程ID
  29. Log.e(tag, msg.getData().getString("key"));
  30. }
  31. }
所以,Wifi的HandlerThread,WifiHandler可以这样实例化(推荐博客有述): 
  1. HandlerThread wifiThread = new HandlerThread("WifiService");
  2. wifiThread.start();
  3. mWifiHandler = new WifiHandler(wifiThread.getLooper());
2.3)Hanlder与Thread实现异步
在新线程中执行耗时操作,结束后通过Handler来更新UI。
  1. /** 1.3 Hanlder与Thread实现异步 */
  2. public void methodThree(View v) {
  3. tag = "methodThree";
  4. /* 初始化进度条 */
  5. progressBar.setProgress(0);
  6. progressBar.setVisibility(View.VISIBLE);
  7. // 新线程执行某操作
  8. new ProgressThread(0).start();
  9. }
  10. /** 更新UI */
  11. private Handler myHandler = new Handler() {
  12. @Override
  13. public void handleMessage(Message msg) {
  14. progressBar.setProgress(msg.getData().getInt("key"));
  15. }
  16. };
  17. /** 新线程任务 */
  18. private class ProgressThread extends Thread {
  19. private int progress;
  20. public ProgressThread(int progress) {
  21. this.progress = progress;
  22. }
  23. @Override
  24. public void run() {
  25. try {
  26. while (progress <= 100) {
  27. progress += 5; // 进度+5
  28. // doSomething(); // 执行耗时操作
  29. Thread.sleep(100);
  30. // 从Handler中获取消息对象
  31. Message msg = myHandler.obtainMessage();
  32. // 设置消息对象信息
  33. Bundle b = new Bundle();
  34. // 向Handler发送消息,更新UI
  35. b.putInt("key", progress);
  36. msg.setData(b);
  37. myHandler.sendMessage(msg);
  38. }
  39. } catch (InterruptedException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. }
3)Android异步线程——AsyncTask
当任务需要复杂操作并频繁更新UI时,上述的非主线程访问UI方法会使得代码结构复杂和难以理解。所以Android1.5提供了一个工具类AsyncTask,用以创建与用户界面交互的长时间任务。也在被定义在了package android.os。
AsyncTask定义了3种泛型类型: Params, Progress and Result;4个执行步骤:onPreExecute, doInBackground, onProgressUpdate and onPostExecute。
3.1)泛型类型
1) Params,发送给后台任务处理的参数类型
2) Progress,后台任务过程中发布的进度单位
3) Result,后台任务结束后返回的结果类型

想了解泛型类型的话,可参见我的Java泛型应用浅析一文^^。

3.2)执行步骤
1) onPreExecute(),该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。
2) doInBackground(Params...),将在onPreExecute方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
3) onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
4) onPostExecute(Result),在doInBackground执行完成后,onPostExecute方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread。
onPostExecute的参数为doInBackground的返回值,类型由第三个泛型类型所指定。例子里仅是String,也可以是你自己的实体类、接口什么的。
3.3)使用准则
1) Task的实例必须在UI thread中创建
2) execute方法必须在UI thread中调用
3) 不要手动的调用onPreExecute(), onPostExecute(Result),
doInBackground(Params...), onProgressUpdate(Progress...)这几个方法
4) 该task只能被执行一次,否则多次调用时将会出现异常
3.4)任务取消
一个任务在任何时候都能够通过调用cancel(boolean)而取消。调用这个方法,将使得随后调用的isCancelled()返回true。并且在执行完 doInBackground(Object[])后,会去调用onCancelled(Object)而不再是onPostExecute(Object)方法。
为了确保任务能够尽快的被取消,可以在doInBackground(Object[])内定期校验isCancelled()的返回值(例如在循环判断中)。
3.5)样例程序
  1. /** 2 Android异步线程——AsyncTask */
  2. public void methodFour(View v) {
  3. tag = "methodFour";
  4. new LoadHtmlTask(this).execute("http://www.baidu.com"); // 执行读取网页任务
  5. // new LoadHtmlTask(this).execute("http://www.google.com"); // 获取不到内容长度
  6. // new LoadHtmlTask(this).execute("http://www.sina.com"); // 获取大量网页数据
  7. }
  8. /** 读取网页任务 */
  9. private class LoadHtmlTask extends AsyncTask<String, Integer, String> {
  10. private Context mContext;
  11. private ProgressDialog dialog; // 进度框
  12. public LoadHtmlTask(Context context) {
  13. this.mContext = context;
  14. initDialog(); // 初始化进度对话框
  15. }
  16. /** 初始化进度对话框 */
  17. private void initDialog() {
  18. dialog = new ProgressDialog(mContext);
  19. dialog.setMax(100); // 设置最大进度值
  20. dialog.setTitle("Loading..."); // 设置标题
  21. dialog.setCancelable(false); // 设为返回键不可取消
  22. dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); // 设为进度条样式
  23. // 增加取消按钮及其事件
  24. dialog.setButton("取消", new DialogInterface.OnClickListener() {
  25. public void onClick(DialogInterface dialog, int i) {
  26. dialog.dismiss(); // 取消显示
  27. cancel(true); // 取消并中断任务
  28. }
  29. });
  30. }
  31. /** doInBackground之前,在主线程执行 */
  32. @Override
  33. protected void onPreExecute() {
  34. logThreadId("onPreExecute()"); // 打印当前线程ID
  35. dialog.show(); // 显示进度对话框
  36. }
  37. /** onPreExecute()之后,在后台线程执行 */
  38. @Override
  39. protected String doInBackground(String... params) {
  40. logThreadId("doInBackground"); // 打印当前线程ID
  41. // 未传入参数直接返回
  42. if (null == params || params.length <= 0) {
  43. return "请确认输入了网址参数!";
  44. }
  45. try {
  46. // 创建HttpGet对象,params[0]为url
  47. HttpGet httpGet = new HttpGet(params[0]);
  48. // 发送Http Get请求,并返回HttpResponse对象
  49. HttpResponse response = new DefaultHttpClient()
  50. .execute(httpGet);
  51. // 判断响应状态,200表示成功响应
  52. if (response.getStatusLine().getStatusCode() == 200) {
  53. HttpEntity entity = response.getEntity(); // 获取返回结果
  54. long length = entity.getContentLength(); // 获取内容长度(google获取不到)
  55. boolean getLen = length > 0 ? true : false; // 判断是否获取了内容长度
  56. InputStream is = entity.getContent(); // 获取响应内容
  57. if (is != null) {
  58. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  59. byte[] buf = new byte[128];
  60. int ch = -1;
  61. long count = 0;
  62. while ((ch = is.read(buf)) != -1 && !isCancelled()) { // 同时还未取消
  63. baos.write(buf, 0, ch);
  64. count += ch;
  65. if (getLen) { // 获取了内容长度时
  66. // 调用publishProgress()更新进度
  67. publishProgress((int) ((count / (float) length) * 100));
  68. }
  69. }
  70. is.close();
  71. baos.close();
  72. return new String(baos.toByteArray()); // 返回结果
  73. }
  74. return "无返回内容!";
  75. } else {
  76. return "服务器未响应或失败!";
  77. }
  78. } catch (Exception e) {
  79. e.printStackTrace();
  80. }
  81. return "程序异常啦!";
  82. }
  83. /** 调用了publishProgress(),在主线程执行 */
  84. @Override
  85. protected void onProgressUpdate(Integer... values) {
  86. // logThreadId("onProgressUpdate"); // 打印当前线程ID
  87. dialog.setProgress(values[0]);
  88. }
  89. /** 未调用了cancel(),doInBackground()结束后,在主线程执行 */
  90. @Override
  91. protected void onPostExecute(String result) {
  92. logThreadId("onPostExecute(result)"); // 打印当前线程ID
  93. dialog.dismiss(); // 取消显示
  94. showMsg(result); // 显示结果
  95. }
  96. /** 调用了cancel(),doInBackground()结束后,在主线程执行 */
  97. @Override
  98. protected void onCancelled() {
  99. logThreadId("onCancelled"); // 打印当前线程ID
  100. showMsg("用户取消了该任务!");
  101. }
  102. /** 提示框显示消息 */
  103. private void showMsg(String message) {
  104. // message内容过多时,提示框显示会延迟,例如sina
  105. new AlertDialog.Builder(mContext)
  106. .setTitle("消息")
  107. .setMessage(message)
  108. .setNegativeButton("关闭",
  109. new DialogInterface.OnClickListener() {
  110. @Override
  111. public void onClick(DialogInterface dialog,
  112. int which) {
  113. dialog.dismiss();
  114. }
  115. }).show();
  116. }
  117. }
三、扩展篇
1)Service中的Toast
Service中不能直接使用Toast提示信息,推荐如下方式:
  1. private Handler handler; // Handler
  2. Override
  3. public void onCreate() {
  4. Log.i("onCreate", "==onCreate==");
  5. super.onCreate();
  6. handler = new Handler(Looper.getMainLooper()); // 使用应用的主消息循环
  7. }
  8. /**
  9. * Toast提示(service中toast不能直接显示)
  10. */
  11. private void showToast(final int resId, final Object... formatArgs) {
  12. handler.post(new Runnable() {
  13. public void run() {
  14. Toast.makeText(getApplicationContext(),
  15. getString(resId, formatArgs), Toast.LENGTH_SHORT)
  16. .show();
  17. }
  18. });
  19. // 以下方式只能显示一次
  20. // Looper.prepare();
  21. // Toast.makeText(this, resId, Toast.LENGTH_SHORT).show();
  22. // Looper.loop();
  23. }
2)Java线程池
一些简单常用的线程池,只需使用Executors类里面提供了一些静态工厂,如下:
1)newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
2)newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
3)newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
4)newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
5)newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
或者使用ThreadPoolExecutor,更加定制化地构造线程池。它们都被定义在package java.util.concurrent。
线程池技术是为了减少频繁创建和销毁线程的系统开销,适用情况有:1)单个任务时间很短、处理请求巨大;2)有突发性大量任务请求;3)需要迅速响应的性能要求等。
  1. public class TestPool implements Runnable {
  2. private static final String TAG = "TestPool"; // 标记
  3. private ExecutorService service; // 线程池
  4. public TestPool() {
  5. // service = Executors.newSingleThreadExecutor(); // 创建一个单任务线程池
  6. service = Executors.newFixedThreadPool(3); // 创建最多同时运行3个任务线程池
  7. }
  8. /** 增加一个线程任务 */
  9. public void addTask() {
  10. service.execute(this);
  11. }
  12. /** 线程任务 */
  13. @Override
  14. public void run() {
  15. log(1); // 显示日志
  16. try {
  17. Thread.sleep(5 * 1000);
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. log(2); // 显示日志
  22. }
  23. /** 显示日志 */
  24. private void log(int which) {
  25. String result = 1 == which ? ",任务开始!" : ",任务结束!";
  26. Log.e(TAG, "线程:" + String.valueOf(Thread.currentThread().getId())
  27. + result);
  28. }
  29. }
3)Java线程同步
多线程并发访问同一数据时,就会有同步的需求。Java内在的同步机制包含:同步块(或方法)和 volatile 变量。同步块(或方法)通过synchronized关键字声明,而volatile可被看做是轻量级的synchronized。
Java为每个object分配了一个monitor,相关方法如下:
1)obj.wait()方法将使本线程挂起,并释放obj对象的monitor。只有其他线程调用obj对象的notify()或notifyAll()时,才可以被唤醒。
2)obj.notifyAll()方法唤醒所有该obj对象相关的沉睡线程,然后被唤醒的众多线程开始竞争obj对象的monitor占有权,最终得到的那个线程会继续执行下去,但其他线程还将继续等待。
3)obj.notify()方法是随机唤醒一个沉睡线程。
4)wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用。
没多线程不需要同步,有多线程不一定需要同步。
  1. public class TestSync {
  2. private Product product;
  3. private Consumer ConsumerA, ConsumerB;
  4. public TestSync() {
  5. product = new Product();
  6. ConsumerA = new Consumer("小怪兽A", product);
  7. ConsumerB = new Consumer("小怪兽B", product);
  8. }
  9. public void produce() {
  10. synchronized (product) {
  11. Log.e("TestSync", "\\(^o^)/,投掷一个果冻!");
  12. product.plus(); // 增加一个产品
  13. /* 优先使用notifyAll(),更容易让jvm找到最适合被唤醒的线程 */
  14. // product.notify(); // 唤醒一个线程
  15. product.notifyAll(); // 唤醒所有线程
  16. }
  17. }
  18. public void start() {
  19. ConsumerA.start();
  20. ConsumerB.start();
  21. }
  22. public void stop() {
  23. ConsumerA.stopEating();
  24. ConsumerB.stopEating();
  25. synchronized (product) {
  26. product.notifyAll(); // 唤醒所有线程
  27. }
  28. }
  29. }
  30. /** 产品 */
  31. class Product {
  32. private int count = 0; // 产品数量
  33. public Product() {
  34. }
  35. /** 是否无产品 */
  36. public boolean isNull() {
  37. return count <= 0 ? true : false;
  38. }
  39. /** 增加产品 */
  40. public void plus() {
  41. count++;
  42. }
  43. /** 减少产品 */
  44. public void minus() {
  45. count--;
  46. }
  47. }
  48. /** 消费者 */
  49. class Consumer extends Thread {
  50. private String name; // 消费者
  51. private Product product; // 产品
  52. private int count = 0; // 数量
  53. private boolean waitEating = true; // 标记
  54. public Consumer(String name, Product product) {
  55. this.name = name;
  56. this.product = product;
  57. }
  58. @Override
  59. public void run() {
  60. while (waitEating) {
  61. synchronized (product) {
  62. while (product.isNull() && waitEating) {
  63. try {
  64. Log.e(name, "(¯﹃¯),等待果冻中...");
  65. product.wait();
  66. } catch (InterruptedException e) {
  67. e.printStackTrace();
  68. }
  69. }
  70. if (waitEating) {
  71. Log.e(name, "~\\(≧▽≦)/~,抢到了果冻!");
  72. product.minus();
  73. count++;
  74. }
  75. }
  76. }
  77. Log.e(name, "(~ o ~)~zZ,吃不下了。计:" + count);
  78. }
  79. public void stopEating() {
  80. waitEating = false;
  81. }
  82. }

转载于:https://blog.51cto.com/4610653/840370

AsyncTask 很好相关推荐

  1. [学习总结]7、Android AsyncTask完全解析,带你从源码的角度彻底理解

    我们都知道,Android UI是线程不安全的,如果想要在子线程里进行UI操作,就需要借助Android的异步消息处理机制.之前我也写过了一篇文章从源码层面分析了Android的异步消息处理机制,感兴 ...

  2. Android AsyncTask源码解析

    在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行.在单线程模型中始终要记住两条法则: 1. 不要阻塞UI线程 2. 确保只 ...

  3. AsyncTask原理

    为什么要用AsyncTask 我们知道,Android应用的主线程(UI 线程,是线程不安全的,负责前台用户界面的绘制以及响应用户的操作)肩负着绘制用户界面和及时响应用户操作的重任,为了避免" ...

  4. Android高级:内部类的理解,多态,run和start,wait和seelp,线程安全,堆和栈,synchronized 和volatile ,AsyncTask,Binder的机,view的原理

    目录 成员内部类.静态内部类.局部内部类和匿名内部类的理解,以及项目中的应用 哪些情况下的对象会被垃圾回收机制处理掉? Java中实现多态的机制是什么? String为什么要设计成不可变的? Obje ...

  5. Android性能优化典范第五季

    原文链接:http://hukai.me/android-performance-patterns-season-5/ 前言 本季内容大致有:多线程并发的性能问题,介绍了AsyncTask,Handl ...

  6. android系统优化(18)--系统性能优化第5季

    1)Threading Performance 在程序开发的实践当中,为了让程序表现得更加流畅,我们肯定会需要使用到多线程来提升程序的并发执行性能.但是编写多线程并发的代码一直以来都是一个相对棘手的问 ...

  7. Android性能优化典范(五)

    作者简介: 胡凯(@胡凯me),腾讯Android工程师,热爱开源与分享,维护Android官方培训课程协作项目,关注Android应用性能优化的总结与分享,推崇Android官方最佳实践.个人博客: ...

  8. Android面试题线程篇

    Android面试题线程篇,由本人整理汇总,后续将推出系列篇,如果喜欢请持续关注和推荐. 开启线程的三种方式? java有三种创建线程的方式,分别是继承Thread类.实现Runable接口和使用线程 ...

  9. 《疯狂Android讲义》学习笔记一

    接触Android一年了,自学了不久就到公司里实习了,在公司的项目毕竟还是模块级的,很多Android基础知识平常接触不到.最近想想通过读一些书,如<疯狂Android讲义>.<An ...

最新文章

  1. AlarmManager与PendingIntent的联合使用(二)
  2. activemq配置与启动
  3. lnmp yum安装mysql_centos5 yum安装lnmp
  4. CS231n课程笔记5.4:超参数的选择交叉验证
  5. 用awk 取出ifconfig eth0中IP的方法
  6. 软件架构-里氏替换原则
  7. Python学习札记(八) Basic5 循环
  8. commons-httpclient 实现get和post请求
  9. word字体放大后只显示一半_word字体显示不全或是显示一半怎么回事如何解决
  10. Wii 补充运动利器
  11. 在实时控制系统中使用传感器优化数据可靠性的3个技巧
  12. minecraft_如何轻松地在Minecraft版本之间切换
  13. 深度学习 黑白图片 着色
  14. 四个月宝宝厌奶期症状?
  15. Java调用用户芝麻信用分
  16. 通过跟踪源码证明在Java中通过执行Start()方法创建线程
  17. Android Studio Codota安装
  18. 最新代千元机!Redmi Note 9正式发布:性能提升100% 1299元起售!
  19. 快手+中科大 | 全曝光推荐数据集KuaiRec 2.0版本
  20. java实现蒲丰投针求_蒲丰投针问题

热门文章

  1. gis插入的文本怎么搞成两行_逻辑图怎么画?图文详解带你绘制逻辑图
  2. oracle数据库静态启动,ORACLE数据库的连接
  3. 网页在兼容模式和急速模式下前者报错、后者正常的原因查找
  4. MD5消息摘要算法和SHA-1安全散列算法
  5. VB 小技巧自定义TextBox文本框右键菜单
  6. VB中的New 与 CreateObject的区别
  7. C++由(int)a引发的思考及浮点数在内存中的表示
  8. Linux下设置和修改IP
  9. 股市常胜将军都懂得适时休息
  10. 机房收费系统重构版:那个系统我们一起遇到的问题