Android 异步任务

InfoAsyncTask.java

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
package com.xyl.app1111;

import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;

import com.xyl.app1111.utils.HttpUtils;

public class InfoAsyncTask extends AsyncTask<String, Void, String> {

private Context context;
private ProgressDialog dialog;
public interface OnLoadDataListener{
public void onSuccess(String json);
public void onError();
}
private OnLoadDataListener onLoadDataListener;
public void setOnLoadDataListener(OnLoadDataListener onLoadDataListener){
this.onLoadDataListener = onLoadDataListener;
}

public InfoAsyncTask(Context context) {
this.context = context;
}

/**
* 在子线程开启之前被调用,运行在主线程当中,通常做进度对话框的初始化工作
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(context);
dialog.setMessage("正在加载中,请稍后");
dialog.show();
}

/**
* 开启线程,进行耗时操作
* @param params
* @return
*/
@Override
protected String doInBackground(String... params) {
String s = HttpUtils.getStringByConnection(params[0]);
return s;
}

/**
* 计算下载的进度,在此处更新进度条
* @param values
*/
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}

/**
* 运行在主线程当中,将耗时操作的结果以参数的形式显示在参数当中,result为doInBackground的返回值
* @param result
*/
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
dialog.dismiss();
if (onLoadDataListener!=null) {
if (!TextUtils.isEmpty(result)) {
onLoadDataListener.onSuccess(result);
}
else{
onLoadDataListener.onError();
}
}
}
}

异步请求底层模拟

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
package com.animee.app1111.utils;


import android.os.Handler;
import android.os.Message;

import androidx.annotation.NonNull;

/**
* 作者:animee on 2022/11/11 09:51
*/
public abstract class AnimeeTask<T,M> {

protected void onPreExecute(){

}
protected abstract M doInBackground(T...params);
protected void onPostExecute(M result){

}

public AnimeeTask(){
onPreExecute();
}

public void execute(T...value){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
M m = doInBackground(value);
//Message对象获取方法有三个
Message msg = Message.obtain();
msg.what = 1;
msg.obj = m;
handler.sendMessage(msg);

}
});
thread.start();
}
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
if (msg.what==1) {
M m = (M) msg.obj;
onPostExecute(m);
}
return false;
}
});
}

知识点

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
1.android处理网络请求等耗时操作的方法,有两种:
1.异步任务 AsyncTask
2.多线程和线程池 Handler

2.为什么要使用异步任务?
1.Android是一个单线程模型,同时也是一个多线程操作系统。
2.把耗时操作(读取文件,加载网络请求)放到子线程当中执行。
原因:android只能在主线程当中更新ui,即UI线程,不能在其他线程当中做更新ui的操作。
这样保证了程序的稳定性和准确性。
稳定性:避免了多线程同时操作ui,造成了线程混乱。
准备性:把耗时操作都放到子线程当中执行,就会避免了ANR(Android Not Responding)应用程序无响应异常。

3.AsyncTask为何而生?
1.可以封装,简化异步操作
2.获取网络数据,传递给主线程更新UI。

4.构建AsyncTask子类的参数、
AsyncTask<params,progress,result>是一个抽象类。
通常用于被继承,继承这个asyncTask就需要指定三个泛型参数。
Params:启动任务时输入参数的类型
Progress:后台任务执行中返回进度值的类型
Result: 后台任务执行完返回结果的类型。

5.继承AsyncTask当中需要重写的四个方法。

onPreExecute():后台操作前被调用,主要用于一些UI组件的初始化。(不做耗时操作)
doInBackground(Params...):必须重写,异步任务要执行的操作必须在这个方法中执行。(执行耗时操作)
onPostExecute(Result):在doInBackground方法执行完成后,自动调用此方法,同时doInBackground方法返回的数据
会作为此方法的参数。一般在此方法中去执行更新ui的操作。
onProgressUpdate(Progress...):更新了任务执行就进度后,会触发的方法。在doInBackground方法当中调用
publishProgress(Progress...)方法,此方法才会被系统调用。


6.AsyncTask的具体实现过程。
1.写一个类,继承asyncTask,同时传入指定的三个泛型
2.重写必要的两个方法。
doInBackground();
onPostExecute();
3.重写可选的两个方法。
onPreExecute();
onProgressUpdate();
4.在ui线程当中实例化,自定义的子类的对象。
5.在ui线程当中,把asynctask的对象去执行execute的方法,就是执行了异步任务。


7.ScrollView : 滚动组件
父类是:Framelayout
内部只能包含一个子类控件,通常这个控件是LinearLayout,可以在线性布局当中去放置其他控件。

8、android的view有一个表示显示状态的属性visibility。
对应的值有三个:
visible:设置控件可见
invisible:设置控件不可见
gone:设置控件隐藏
invisible和gone的主要区别:当设置为invisible时,界面保留view控件所占据的空间。
当设定为gone时,view隐藏并且不保留view控件所占据的空间。

9.AsyncTask的不足之处。
1.AsyncTask如果在同一个界面当中加载很多接口,会开启大量的线程,会导致系统资源严重被占用,
然后就会产生应用程序FC错误。(FC:应用程序强制关闭,崩溃)
2.AsyncTask执行doinBackground方法时,即使调用了取消方法,也不能取消,要等到doinBackground
方法中的代码执行结束后才能取消。
3.线程池是不能维护的,当产生大量异步操作的时候线程池就满了,然后就会产生异常。

10.异步和同步
同步:在同一个线程当中执行一系列的操作。
异步:多个线程当中完成工作,最后通过多线程通信达到最终效果。

11.AsyncTask的取消。
1.要在UI线程当中执行AsyncTask的cancel方法。
2.要在doInBackground方法当中做一个判断,判断条件是是否被取消isCancelled(),如果未被取消才执行逻辑代码。
3.cancel(true) :只要是执行了这个方法,AsyncTask立即被取消。
cancel(false) : 执行了这个方法,要等本次while中的内容执行完才被取消。

12.AsyncTask的注意事项:
1.必须在UI线程当中创建AsyncTask的实例。
2.必须在ui线程当中执行AsyncTask的execute的方法。
3.重写的四个方法是系统自动调用的,我们不能手动的调用。
4.每个asyncTask的实例只能被执行一次,多次执行就会出现异常。

滚动分页加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   //ListView 滚动监听接口
AbsListView.OnScrollListener listener = new AbsListView.OnScrollListener() {
// 当滚动状态发生改变时,回调的方法
// SCROLL_STATE_IDLE 停止状态 SCROLL_STATE_TOUCH_SCROLL 手指触摸滚动
// SCROLL_STATE_FLING 惯性滚动
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE
&& lastVisibleItem==adapter.getCount()) {
//显示尾布局,并且开始加载
footerView.setVisibility(View.VISIBLE);
page++;
String url = creatURL();
loadDatas(url);
}
}
// firstVisibleItem:当前可见第一条的位置 visibleItemCount 可见条目数量 totalItemCount 一共有多少条
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
lastVisibleItem = firstVisibleItem+visibleItemCount-1;
}
};

下拉刷新

build.gradle

1
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"

使用

1
2
3
4
5
6
7
8
9
10
11
12
binding.srLayout.setColorSchemeColors(Color.RED);
binding.srLayout.setOnRefreshListener(onRefreshListener);
// 下拉刷新的监听接口
SwipeRefreshLayout.OnRefreshListener onRefreshListener = new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
page=1;
String url = creatURL();
mDatas.clear();
loadDatas(url);
}
};

通知(Notification)

发送简单通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 发送简单通知
*/
private void sendNormalNotify() {
//创建通知的装饰对象
Notification.Builder builder = new Notification.Builder(this);
//设置通知显示内容
builder.setContentTitle("好消息, 好消息!!!!");
builder.setContentText("新冠疫情神奇消失了。。。");
builder.setSmallIcon(R.mipmap.ib_luck_normal);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.jinniu);
builder.setLargeIcon(bitmap);
builder.setAutoCancel(true);
builder.setOngoing(false);//侧滑能不能取消通知
builder.setPriority(Notification.PRIORITY_HIGH);
builder.setTicker("放心Go向你发来了双11问候,快来看我.....");
//设置点击预先跳转意图
Intent intent = new Intent(this, NewsInfoActivity.class);
builder.setContentIntent(PendingIntent.getActivity(this, 100, intent, PendingIntent.FLAG_ONE_SHOT));
//获取通知对象
Notification notification = builder.build();
//发送通知
manager.notify(1, notification);
}

完整实例

注意: Andorid

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
84
85
86
87
88
89
90
import androidx.appcompat.app.AppCompatActivity;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.view.View;

/**
* Notification 通知信息
* 在应用中如果需要提示用户一些信息,显示在通知栏,可以发送Notification
*
* */
public class NotifyActivity extends AppCompatActivity {

NotificationManager manager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notify);
manager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
createChannel();

}
private final String CHANNEL_ID = "com.animee.app1111";
//渠道名
private final String CHANNEL_NAME = "渠道名-简单通知";
//渠道重要级
private final int CHANNEL_IMPORTANCE = NotificationManager.IMPORTANCE_DEFAULT;
private void createChannel() {
//创建通知渠道,Android8.0及以上需要
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
NotificationChannel notificationChannel = new NotificationChannel(
CHANNEL_ID, CHANNEL_NAME, CHANNEL_IMPORTANCE);
manager.createNotificationChannel(notificationChannel);
}
public void onClick(View view) {

switch (view.getId()) {
case R.id.notify_btn1:
sendNormalNotify();
break;
}
}
/** 发送简单通知*/
private void sendNormalNotify() {
// 创建通知的装饰对象
Notification.Builder builder = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
builder = new Notification.Builder(this, CHANNEL_ID);
}
else{
builder = new Notification.Builder(this);
}
// 设置通知显示内容
builder.setContentTitle("好消息,好消息!!!");
builder.setContentText("新冠疫情神奇消失了.......");
builder.setSmallIcon(R.mipmap.ic_launcher);

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.jinniu);
builder.setLargeIcon(bitmap);

builder.setAutoCancel(true);
builder.setOngoing(false); //侧滑能不能取消通知
builder.setPriority(Notification.PRIORITY_HIGH);
builder.setTicker("放心Go向你发来了双11问候,快来看我......");
// 设置点击预先跳转意图
Intent intent = new Intent(this, NewsInfoActivity.class);

builder.setContentIntent(PendingIntent
.getActivity(this,100,intent,PendingIntent.FLAG_IMMUTABLE));
// 获取通知对象
Notification notification = builder.build();

// 发送通知
manager.notify(1,notification);
}



}

发送大图通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void sendBigImgNotify() {
NotificationCompat.Builder builder
= new NotificationCompat.Builder(this);
NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
Bitmap bm1 = BitmapFactory.decodeResource(getResources(), R.mipmap.jinniu);
style.bigLargeIcon(bm1);
style.setBigContentTitle("美图来袭.....");
style.setSummaryText("每日壁纸更新更多内容,点击查看");
builder.setStyle(style);
builder.setShowWhen(true);
//必须设置小图标
builder.setSmallIcon(R.mipmap.ib_luck_normal);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
builder.setChannelId(CHANNEL_ID);
}
Notification nf = builder.build();
manager.notify(2, nf);
}

发送多条消息通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void sendListNotify() {
// 创建通知的装饰对象
Notification.Builder builder = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
builder = new Notification.Builder(this, CHANNEL_ID);
}
else{
builder = new Notification.Builder(this);
}
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentTitle("今日卫健委发出通告");
Notification.InboxStyle style = new Notification.InboxStyle();
style.addLine("取消健康码");
style.addLine("取消隔离");
style.addLine("取消核酸检测");
builder.setStyle(style);
Notification notify = builder.build();
manager.notify(3, notify);
}