android:text=“测试卖票”
android:textAllCaps=“false”
android:textSize=“26sp”
app:layout_constraintLeft_toLeftOf=“parent”
app:layout_constraintRight_toRightOf=“parent”
app:layout_constraintTop_toBottomOf="@+id/btn_test_runnable" />
</androidx.constraintlayout.widget.ConstraintLayout>
3、线程池的应用
========
new Thread的弊端:
-
a.每次new Thread新建对象性能差.
-
b.线程缺乏统一管理,可能无限制新建线程,相互之间竞争,即可能占用过多系统资源导致死机或oom.
-
c.缺乏更多功能,如定时执行、定期执行、线程中断。
3.1、缓存线程池
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,如无可回收,则新建线程。
private void testCache() {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
Log.i(Constant.TAG, Thread.currentThread().getName() + " " + index);
}
});
}
}
3.2、定长线程池
newFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
private void testFixed() {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
Log.i(Constant.TAG, Thread.currentThread().getName() + " " + index);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
3.3、单个线程池
newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。
private void testSingle() {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
Log.i(Constant.TAG, Thread.currentThread().getName() + " " + index);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
3.4、定时线程池
newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。
private void testScheduled() {
Log.i(Constant.TAG, “开始执行 testScheduled”);
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
// 定时3秒后执行
// scheduledExecutorService.schedule(new Runnable() {
// @Override
// public void run() {
// Log.i(Constant.TAG, “延时3秒执行”);
// }
// },3, TimeUnit.SECONDS);
// 定时3秒后执行,每隔2秒执行一次
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Log.i(Constant.TAG, “每隔2秒执行一次”);
}
}, 3, 2, TimeUnit.SECONDS);
}
4、异步消息处理机制
==========
当我们学会使用多线程编程以后,就需要思考一个问题:线程之间如何通讯?
4.1、分析
详细了解Android SDK提供的几个线程间通讯的类.
1、Handler
Handler在android里负责发送和处理消息,通过它可以实现其他线程与Main线程之间的消息通讯。
2、Looper
Looper负责管理线程的消息队列和消息循环。
3、Message
Message是线程间通讯的消息载体。两个码头之间运输货物,Message充当集装箱的功能 ,里面可以存放任何你想要传递的消息.
4、MessageQueue
MessageQueue是消息队队列,先进先出,它的作用是保存有待线程处理的消息.
它们四者之间的关系是,在其他线程中调用Handler.sendMsg()方法(参数是Message对象),
将需要Main线程处理的事件添加到Main例的MessageQueue中,Main线程通过MainLooper从消息队列中取出Handler发过来的这个消息时,会回调Handler的handlerMessage()方法。
4.2、用法
4.2.1、send
1、sendEmptyMessage(int)
2、sendMessage(Message)
3、sendMessageAtTime(Message , long)
4、sendMessageDelayed(Message , long)
sendMessage类方法允许你安排一个带数据的Message对象到队列中,等待处理。
4.2.2、post
1、post(Runnable)
2、postAtTime(Runnable, long)
3、postDelayed(Runnable, long)
post方法允许你排列一个Runnable对象到主线程队列中,等待执行。
4.3、总结
1、传递Message。用于接受子线程发送的数据,并用此数据配合主线程更新UI。
在Android中,对于U1的操作通常需要放在主线程中进行操作。如果在子线程中有关于UI的操作,
那么就需要把数据消息作为一个Message对象发送到消息队列中,然后,用Handler中的handleMessge方法处理传过来的数据信息,并操作UI,类似sendMessage(Message msg)
方法实现发送消息的操作。在初始化Handler对象时重写的handleMessage方法来接收Message并进行相关操作。
2、传递Runnable对象。用于通过Handler绑定的消息队列,安排不同操作的执行顺序。Handler对象在进行初始化的时候,会默认地自动绑定消息队列。利用类post方法,可以将Runnable对象发送到消息队列中,按照队列的机制按顺序执行不同的Runnable对象中的run方法.
4.4、补充
4.4.1、View的post()方法
public boolean post(Runnable action) {
//1 View自己创建的Handler
Handler handler;
if (mAttachInfo != null) {
//2 获取View所依附的Thread的Handler
handler = mAttachInfo.mHandler;
} else {
getRunQueue().post(action);
return true;
}
//3 执行post方法 所以View.post方法本质上还是handler.post
return handler.post(action);
}
4.4.2、Activity的runOnUiThread方法
@Override
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
4.5、代码
4.5.1、MainActivity.java
public class MainActivity extends AppCompatActivity {
private Button btn1;
private Button btnTestPb;
private ProgressBar pb;
private int progress = 0;
@SuppressWarnings(“HandlerLeak”)
private final Handler handler = new Handler() {
// 接收消息等待处理
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
btn1.setText(“12345”);
break;
case 2:
String str = (String) msg.obj;
btn1.setText(str);
break;
case 3:
if (progress < 100) {
progress += 10;
pb.setProgress(progress);
handler.sendEmptyMessageDelayed(3, 2000);
}
break;
default:
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn1 = findViewById(R.id.btn_test1);
btnTestPb = findViewById(R.id.btn_test_pb);
pb = findViewById(R.id.pb);
btn1.setOnClickListener(view -> test1());
btnTestPb.setOnClickListener(view -> testTimer());
findViewById(R.id.btn_test_post).setOnClickListener(view -> testPost());
}
private void testPost() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// handler.post(new Runnable() {
// @Override
// public void run() {
// btn1.setText(“6789”);
// }
// });
// View的post方法
// btn1.post(new Runnable() {
// @Override
// public void run() {
// btn1.setText(“View 6789”);
// }
// });
// Activity的runOnUiThread
runOnUiThread(new Runnable() {
@Override
public void run() {
btn1.setText(“runOnUiThread 6789”);
}
});
}
}).start();
}
private void testTimer() {
pb.setVisibility(View.VISIBLE);
handler.sendEmptyMessageDelayed(3, 2000);
}
private void test1() {
new Thread(new Runnable() {
@Override
public void run() {
// 模拟一个网络请求
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// handler.sendEmptyMessage(1);
// handler.sendEmptyMessageDelayed(1,2000);
Message message = Message.obtain();
message.what = 2;
message.obj = “ABC”;
handler.sendMessage(message);
// handler.sendMessageDelayed(message,2000);
}
}).start();
}
}
4.5.2、布局文件
<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:app=“http://schemas.android.com/apk/res-auto”
xmlns:tools=“http://schemas.android.com/tools”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_mark"
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:text=“MainActivity”
android:textSize=“30sp”
app:layout_constraintLeft_toLeftOf=“parent”
app:layout_constraintRight_toRightOf=“parent”
app:layout_constraintTop_toTopOf=“parent” />
<Button
android:id="@+id/btn_test1"
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:text=“测试 Handler”
android:textAllCaps=“false”
android:textSize=“20sp”
app:layout_constraintLeft_toLeftOf=“parent”
app:layout_constraintRight_toRightOf=“parent”
app:layout_constraintTop_toBottomOf="@+id/tv_mark" />
<Button
android:id="@+id/btn_test_pb"
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:text=“测试 进度条”
android:textAllCaps=“false”
android:textSize=“20sp”
app:layout_constraintLeft_toLeftOf=“parent”
app:layout_constraintRight_toRightOf=“parent”
app:layout_constraintTop_toBottomOf="@+id/btn_test1" />
<ProgressBar
android:id="@+id/pb"
android:layout_width=“match_parent”
android:layout_height=“20dp”
android:max=“100”
android:visibility=“gone”
style="?android:attr/progressBarStyleHorizontal"
app:layout_constraintLeft_toLeftOf=“parent”
app:layout_constraintRight_toRightOf=“parent”
app:layout_constraintTop_toBottomOf="@+id/btn_test_pb"
/>
<Button
android:id="@+id/btn_test_post"
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:text=“测试 post”
android:textAllCaps=“false”
android:textSize=“20sp”
app:layout_constraintLeft_toLeftOf=“parent”
app:layout_constraintRight_toRightOf=“parent”
app:layout_constraintTop_toBottomOf="@+id/pb" />
</androidx.constraintlayout.widget.ConstraintLayout>
5、异步任务AsyncTask
===============
为了更加方便我们在子线程中更新UI元素,Android 从 1.5版本就引入了一个AsyncTask类,使用它就可以非常灵活方便地从子线程切换到UI线程。
AsyncTask :异步任务,从字面上来说,就是在我们的UI主线程运行的时候,异步的完成 一些操作。
AsyncTask允许我们执行一个异步的任务在后台。我们可以将耗时的操作放在异步任务当中来执行,
并随时将任务执行的结果返回给我们的UI线程来更新我们的U1控件。通过AsyncTask我们可以轻松地解决多线程之间的通信问题。
怎么来理解AsyncTask呢?通俗一点来说,AsyncTask就相当于Android给我们提供了一 个多线程编程的一个框架,其介于Thread和Handler之间,我们如果要定义一个AsyncTask,就需要定义一个类来继承AsyncTask这个抽象类,并实现其唯一的一个 doInBackgroud 抽象方法。
5.1、用法
首先来看一下AsyncTask的基本用法,由于AsyncTask是一个抽象类,所以如果我们想使用它,就必须要创建
一个子类去继承它。在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的用途如下:
1. Params
在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
2. Progress
后台任执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位.
3. Result
当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型.
因此,一个最简单的自定义AsyncTask就可以写成如下方式:
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {…}
5.2、分析
这里我们把AsyncTask的第一个泛型参数指定为Void ,表示在执行AsyncTask的时候不需 要传入参数给后台任务.
第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单位。
第三个泛型参数指定为Boolean ,则表示使用布尔型数据来反馈执行结果。
当然,目前我们自定义的DownloadTask还是一个空任务,并不能进行任何实际的操作. 我们还需要去重写AsyncTask中的几个方法才能完成对任务的定制。经常需要去重写的方 法有以下四个:
1. onPreExecute()
这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示 一个进度条对话框等。
2. doInBackground(Params…)
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务.任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void ,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用 publishProgress(Progress…)方法来完成.
3. onProgressUpdate(Progress…)
当在后台任务中调用了publishProgress(Progress…)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
4. onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据采进行一些UI操作,比如说提醒任务执行的结果,以及关闭进度条对话框等.
5.3、总结
AsyncTask和Handler对比
- AsyncTask实现的原理和适用的优缺点
AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程。
使用的优点:
简单,快捷,过程可控
使用的缺点:
在使用多个异步操作,并需要进行Ui变更时,就变得复杂起来.
2)Handler异步实现的原理和适用的优缺点
在Handler异步实现时,涉及到Handler, Looper, Message,Thread四个对象,实现异步的流程是主线程启动Thread (子线程),子线程运行并生成Message通过Handler发送出去,然后Looper取出消息队列中的Message再分发给Handler进行UI变更。
使用的优点:
结构清晰,功能定义明确。对于多个后台任务时,简单,清晰
使用的缺点:
在单个后台异步处理时,显得代码过多,结构过于复杂。
5.4、代码
5.4.1、MainActivity.java
public class MainActivity extends AppCompatActivity {
private Button btn1;
private ProgressBar pb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn1 = findViewById(R.id.btn_test1);
pb = findViewById(R.id.pb);
btn1.setOnClickListener(view -> test1());
}
private
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
void test1() {
new TestTask().execute();
}
class TestTask extends AsyncTask<Void, Integer, Boolean> {
int progress;
// 当前还在主线程中,做一些准备工作
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.i(Constant.TAG, “onPreExecute: 准备下载”);
pb.setVisibility(View.VISIBLE);
}
// 在异步线程里面执行
@Override
protected Boolean doInBackground(Void… voids) {
Log.i(Constant.TAG, “doInBackground: 正在下载”);
try {
// 永久循环,模拟下载文件
while (true) {
// 每隔1秒下载10%
Thread.sleep(1000);
progress += 10;
// 通知主线程当前的进度是多少
publishProgress(progress);
if (progress >= 100) {
break;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
return true;
}
// 当前切换到了主线程,可以根据传递的参数做UI的更新
@Override
protected void onProgressUpdate(Integer… values) {
Log.i(Constant.TAG, "onProgressUpdate: 下载回调 " + values[0]);
super.onProgressUpdate(values);
pb.setProgress(values[0]);
}
// 切换到主线程里面执行 doInBackground中返回的数据就到了这里
@Override
protected void onPostExecute(Boolean bool) {
if (bool){
Log.i(Constant.TAG, “onPostExecute: 下载成功”);
pb.setVisibility(View.GONE);
}else {
Log.i(Constant.TAG, “onPostExecute: 下载失败”);
}
}
}
}
5.4.2、布局文件
<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:app=“http://schemas.android.com/apk/res-auto”
xmlns:tools=“http://schemas.android.com/tools”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_mark"
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:text=“MainActivity”
android:textSize=“30sp”
app:layout_constraintLeft_toLeftOf=“parent”
app:layout_constraintRight_toRightOf=“parent”
app:layout_constraintTop_toTopOf=“parent” />
<Button
android:id="@+id/btn_test1"
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:text=“测试 Handler”
android:textAllCaps=“false”
android:textSize=“20sp”
app:layout_constraintLeft_toLeftOf=“parent”
app:layout_constraintRight_toRightOf=“parent”
app:layout_constraintTop_toBottomOf="@+id/tv_mark" />
<ProgressBar
android:id="@+id/pb"
android:layout_width=“match_parent”
android:layout_height=“20dp”
android:max=“100”
android:visibility=“gone”
style="?android:attr/progressBarStyleHorizontal"
app:layout_constraintLeft_toLeftOf=“parent”
app:layout_constraintRight_toRightOf=“parent”
app:layout_constraintTop_toBottomOf="@+id/btn_test1"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
6、设计自己的图片轮播器
============
6.1、加载轮播图片
1、本地加载图片
2、异步获取网络图片
6.2、滑动轮播图片
同种无限循环轮播的实现方式:
1、欺骗适配器
在适配器中将getcount的值设置为无限大
2、拖造数据源
有4张图片,霁现无限循环。在viewpager中设置6个view ,第一个为4张图片的最后1张,第6张为4张图片
的第1张。图片顺序如下数字:
3-0-1-2-3-0
0-1-2-3为正常的4个图片。3,0为添加的两个图片view
滑动的顺序:进入页面显示0图片,向右滑动到0时,将0页设置为0 ,则可以继续向右滑动。
同理当向左滑动到3时,将3页设置为3。
6.3、代码
6.3.1、BitmapTask.java
public class BitmapTask extends AsyncTask<Integer, Void, Bitmap> {
@SuppressLint(“StaticFieldLeak”)
private final Context context;
private int res;
@SuppressLint(“StaticFieldLeak”)
private final ImageView imageView;
public BitmapTask(Context context, ImageView imageView) {
this.context = context;
this.imageView = imageView;
}
@Override
protected Bitmap doInBackground(Integer… integers) {
res = integers[0];
// 根据图片ID获取到对应的bitmap
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), res);
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
// 显示加载成功的图片
imageView.setImageBitmap(bitmap);
}
}
6.3.2、Images.java
public class Images {
public static final int[] imageArray = new int[]{
R.mipmap.keji01,
R.mipmap.keji02,
R.mipmap.keji03,
R.mipmap.keji04,
};
}
6.3.3、ViewPagerAdapter.java
public class ViewPagerAdapter extends PagerAdapter {
private Context context;
private LayoutInflater layoutInflater;
private int[] datas;
public ViewPagerAdapter(Context context, int[] datas) {
this.context = context;
this.datas = datas;
layoutInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return Integer.MAX_VALUE;
// return datas.length;
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
// 渲染每一页的数据
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
View layout = layoutInflater.inflate(R.layout.viewpager_item, null);
ImageView iv = layout.findViewById(R.id.item_iv);
// 设置显示的图片
// iv.setImageResource(datas[position]);
// 设置显示加载中的等待图片
iv.setImageResource(R.mipmap.ic_launcher);
// 异步任务加载图片
BitmapTask bitmapTask = new BitmapTask(context, iv);
bitmapTask.execute(datas[position%datas.length]);
// bitmapTask.execute(datas[position]);
// 添加到viewpager
container.addView(layout);
return layout;
}
}
布局文件:viewpager_item.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”
android:layout_width=“match_parent”
android:layout_height=“match_parent”>
<ImageView
android:id="@+id/item_iv"
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:scaleType=“fitXY”/>
6.3.4、MainActivity.java
public class MainActivity extends AppCompatActivity {
private ViewPager vp;
private ViewPagerAdapter adapter;
private int[] datas = new int[Images.imageArray.length + 2];
// 自动轮播定时器
private ScheduledExecutorService scheduledExecutorService;
// 当前图片的索引号
private int currentIndex;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vp = findViewById(R.id.vp);
/**