所以最终,创建Handler的同时则自动在当前线程sThreadLocal中设置Looper对象,并获取该对象,进而获取消息队列。这样一来,Handler自然和消息队列以及线程关联起来了。
Looper通过loop()方法建立消息循环:
public static void loop() {
//里面调用了sThreadLocal.get()获得刚才创建的Looper对象
final Looper me = myLooper();
if (me == null) {//如果Looper为空则会抛出异常
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) {
//由于刚创建MessageQueue就开始轮询,队列里是没有消息的,等到Handler sendMessage enqueueMessage 后队列里才有消息
return;
}
msg.target.dispatchMessage(msg);
}
}
在上面的loop方法中,我们看到通过dispatchMessage方法来处理消息,根据适才看过的Message代码,target是Handler类型,所以说,Handler将消息投递到消息队列后,消息队列有将消息分发给Handler来处理。我们来看看消息处理函数的具体实现:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//callback在message的构造方法中初始化或者使用handler.post(Runnable)时候才不为空
handleCallback(msg);
} else {
if (mCallback != null) {
//mCallback是一个Callback对象,通过无参的构造方法创建出来的handler,该属性为null,此段不执行
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//最终执行handleMessage方法
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
如果message的Runnable类型变量callback不为空,即对应上述Handler投递方式中的post方法,则调用handleCallBack()进而调用callback的run()。如果callback为空,则检查mCallBack是否为null,不为空就调用mCallBack的handleMessage()方法。
============================================================================
为了更方便地在子线程中进行更新 UI 操作,Android 基于异步处理消息机制封装了一个工具类 AsyncTask。AsyncTask可以很容易且正确地使用UI线程,AsyncTask允许进行后台操作,并在不显示使用工作线程或Handler机制的情况下,将结果反馈给UI线程。但是AsyncTask只能用于短时间的操作(最多几秒就应该结束的操作),如果需要长时间运行在后台,就不适合使用AsyncTask了,只能去使用Java提供的其他API来实现。
由于AsyncTask是一个抽象类,所以我们必须通过继承使用AsyncTask,下面我们给出一个例子,下载的异步任务,点击“开始下载”,开始下载,下载后显示下载文件名称以及字节数。
public class MainActivity extends Activity implements Button.OnClickListener {
TextView textView = null;
Button btnDownload = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView)findViewById(R.id.textView);
btnDownload = (Button)findViewById(R.id.btnDownload);
Log.i(“iSpring”, "MainActivity -> onCreate, Thread name: " + Thread.currentThread().getName());
}
@Override
public void onClick(View v) {
//要下载的文件地址
String[] urls = {
“https://blog.csdn.net/fjnu_se/article/details/80965142”,
“https://blog.csdn.net/fjnu_se/article/details/80965139”,
“https://blog.csdn.net/fjnu_se/article/details/80965134”,
“https://blog.csdn.net/fjnu_se/article/details/80965133”,
“https://blog.csdn.net/fjnu_se/article/details/80933809”
};
DownloadTask downloadTask = new DownloadTask();
downloadTask.execute(urls);//执行一个异步任务
}
/**
-
public abstract class AsyncTask<Params, Progress, Result>
-
三种泛型类型分别表示:参数类型、后台任务执行的进度类型、返回的结果类型
-
在此例中,Params泛型是String类型,Progress泛型是Object类型,Result泛型是Long类型
*/
private class DownloadTask extends AsyncTask<String, Object, Long> {
/**
-
在execute(Params…params)被调用后立即执行,执行在UI线程,
-
一般用来在执行后台任务对UI做一些标记。
*/
@Override
protected void onPreExecute() {
Log.i(“iSpring”, "DownloadTask -> onPreExecute, Thread name: " + Thread.currentThread().getName());
super.onPreExecute();
btnDownload.setEnabled(false);
textView.setText(“开始下载…”);
}
/**
-
在onPreExcecute()完成后立即执行,用于执行较为耗时的操作,此方法将接受输入参数返回计算结果。
-
在执行过程中可以调用publishProgress(Progress…values)来更新进度条信息。
-
@param params
-
@return
*/
@Override
protected Long doInBackground(String… params) {
Log.i(“iSpring”, "DownloadTask -> doInBackground, Thread name: " + Thread.currentThread().getName());
//totalByte表示所有下载的文件的总字节数
long totalByte = 0;
//params是一个String数组
for(String url: params){
//遍历Url数组,依次下载对应的文件
Object[] result = downloadSingleFile(url);
int byteCount = (int)result[0];
totalByte += byteCount;
//在下载完一个文件之后,我们就把阶段性的处理结果发布出去
publishProgress(result);
//如果AsyncTask被调用了cancel()方法,那么任务取消,跳出for循环
if(isCancelled()){
break;
}
}
//将总共下载的字节数作为结果返回
return totalByte;
}
/**
- 下载文件后返回一个Object数组:下载文件的字节数以及下载的博客的名字
*/
private Object[] downloadSingleFile(String str){
Object[] result = new Object[2];
int byteCount = 0;
String blogName = “”;
HttpURLConnection conn = null;
try{
URL url = new URL(str);
conn = (HttpURLConnection)url.openConnection();
InputStream is = conn.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int length = -1;
while ((length = is.read(buf)) != -1) {
baos.write(buf, 0, length);
byteCount += length;
}
String respone = new String(baos.toByteArray(), “utf-8”);
int startIndex = respone.indexOf(“
if(startIndex > 0){
startIndex += 7;
int endIndex = respone.indexOf(“”);
if(endIndex > startIndex){
//解析出博客中的标题
blogName = respone.substring(startIndex, endIndex);
}
}
}catch(MalformedURLException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally {
if(conn != null){
conn.disconnect();
}
}
result[0] = byteCount;
result[1] = blogName;
return result;
}
/**
-
执行在UI线程。在调用publishProgress(Progress…progress)时,此方法被执行,
-
直接将进度信息更新到UI组件上。
-
@param values
*/
@Override
protected void onProgressUpdate(Object… values) {
Log.i(“iSpring”, "DownloadTask -> onProgressUpdate, Thread name: " + Thread.currentThread().getName());
super.onProgressUpdate(values);
int byteCount = (int)values[0];
String blogName = (String)values[1];
String text = textView.getText().toString();
text += “\n博客《” + blogName + “》下载完成,共” + byteCount + “字节”;
textView.setText(text);
}
/**
-
执行在UI线程。当后台操作结束时,此方法将会被调用,
-
doInBackground()函数返回的计算结果将作为参数传递到此方法中,直接将结果显示到UI组件中。
-
@param aLong
*/
@Override
protected void onPostExecute(Long aLong) {
Log.i(“iSpring”, "DownloadTask -> onPostExecute, Thread name: " + Thread.currentThread().getName());
super.onPostExecute(aLong);
String text = textView.getText().toString();
text += “\n全部下载完成,总共下载了” + aLong + “个字节”;
textView.setText(text);
btnDownload.setEnabled(true);
}
@Override
protected void onCancelled() {
Log.i(“iSpring”, "DownloadTask -> onCancelled, Thread name: " + Thread.currentThread().getName());
super.onCancelled();
textView.setText(“取消下载”);
btnDownload.setEnabled(true);
}
}
}
在上面的每个方法中的注解可得知,当我们使用AsyncTask时,需要在代码中调用execute()方法,创建异步任务类继承AsyncTask后,doInBackground是一个抽象方法,必须覆写,而onPreExecute、onProgressUpdate、onPostExecute、onCancelled这几个方法是空方法,需要的时候也可以覆写它们,至于publishProgress是final修饰的方法,只能调用,一般在doInBackground中调用来更新进度条,这些方法顺序为:onPreExecute–>doInBackground–>publishProgress–>onProgressUpdate–>onPostExecute。
使用同时,我们需要注意几点:
-
异步任务的实例必须在UI线程中创建。
-
execute方法必须在UI线程中调用。
-
不能在doInBackground中更改UI组件。
-
一个任务实例只能执行一次。
先看AsyncTask的源码解析:
当我们执行异步任务时,将在主线程调用execute方法,该方法源码如下:
//该方法运行在主线程
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params… params) {
//将任务放到线程池中执行,线程池中线程都是子线程
return executeOnExecutor(sDefaultExecutor, params);
}
可见,调用execute后,进而调用了executeOnExecutor方法,传进去的参数有两个:sDefaultExecutor以及params,params是参数值,sDefaultExecutor是ThreadPoolExecutor的实例,用于管理提交AsyncTask的任务,源码如下:
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
//生产线程的工厂
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, “AsyncTask #” + mCount.getAndIncrement());
}
};
//存放任务的阻塞队列
private static final BlockingQueue sPoolWorkQueue =
new LinkedBlockingQueue(10);
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
//线性执行的执行器
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//内部交互的handler
private static final InternalHandler sHandler = new InternalHandler();
//默认的Executor(顺序执行)
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
/**
*顺序执行的Executor
*/
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
//将任务交给THREAD_POOL_EXECUTOR执行
THREAD_POOL_EXECUTOR.execute(mActive);
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
总结
其实要轻松掌握很简单,要点就两个:
- 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
- 多练。 (视频优势是互动感强,容易集中注意力)
你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。
以上就是总结的关于在面试的一些总结,希望对大家能有些帮助,除了这些面试中需要注意的问题,当然最重要的就是刷题了,这里放上我之前整理的一份超全的面试专题PDF,大家有兴趣的可以自行领取或者私信我:
还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
【Android核心高级技术PDF文档,BAT大厂面试真题解析】点击:Android架构视频+BAT面试专题PDF+学习笔记即可获取!查看免费领取方式!
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)**
[外链图片转存中…(img-b1EXYKWl-1710819088178)]
总结
其实要轻松掌握很简单,要点就两个:
- 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
- 多练。 (视频优势是互动感强,容易集中注意力)
你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。
以上就是总结的关于在面试的一些总结,希望对大家能有些帮助,除了这些面试中需要注意的问题,当然最重要的就是刷题了,这里放上我之前整理的一份超全的面试专题PDF,大家有兴趣的可以自行领取或者私信我:
还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
【Android核心高级技术PDF文档,BAT大厂面试真题解析】点击:Android架构视频+BAT面试专题PDF+学习笔记即可获取!查看免费领取方式!
[外链图片转存中…(img-ZIciMcPU-1710819088178)]
这里只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢在关注一下~