Android多线程编程

线程的基本用法

(1)1.新建一个类继承自Thread,并重写run方法

​ 2.启动线程

1
2
3
4
5
6
7
class MyThread extends Thread{
@Override
public void run(){
//处理具体逻辑
}
}
new MyThread().start();

(2) 使用Runnable接口的方式来定义一个线程

1
2
3
4
5
6
7
8
class MyThread implements Runnable{
@Override
public void run(){
//处理具体逻辑
}
}
MyYhread myThread=new MyYhread();
new Thread(myThread).start();

(3)匿名类的方法

1
2
3
4
5
6
new Thread(new Runnable(){
@Override
public void run(){
//处理具体逻辑
}
}).start();

在子线程中更新UI

1.新建一个Handler对象,并重写了父类的方法,在这里对Message进行具体处理,这里是主线程

2.在子线程中创建Message对象,并发送

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
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
public static final int UPDATE_TEXT=1;
private TextView textView;
@SuppressLint("HandlerLeak")
private Handler handler=new Handler(){
@SuppressLint("SetTextI18n")
public void handleMessage(Message msg){
if (msg.what == UPDATE_TEXT) {
//在这里进行UI操作
textView.setText("Nice to meet you");
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView=(TextView) findViewById(R.id.text);
Button changeText=(Button) findViewById(R.id.change_text);
changeText.setOnClickListener(this);
}

@Override
public void onClick(View view) {
if(view.getId()==R.id.change_text){
new Thread(new Runnable() {
@Override
public void run() {
Message msg=new Message();
msg.what=UPDATE_TEXT;
handler.sendMessage(msg);
}
}).start();
}
}
}

异步消息处理机制

AsyncTask

1.创建一个子类去继承它,需要传入三个参数如下

Params:在后台任务中使用

Progress:可以作为进度单位

Result:返回值类型

2.重写父类方法如下:

onPreExecute():界面上的初始化操作

doInBackground(Params…):所有代码都在子线程中运行,在这里处理耗时任务

onProgressUpdate(Progress…):对UI进行操作

onPostExecute(Result):后台任务执行完毕并通过return语句返回时,这个方法被调用

3.启动任务

new DownloadTask().execute();


服务

1.定义一个服务(已经自动注册了),并重写onCreate,onStartCommand,onDestroy方法

2.启动和停止服务

3.将活动与服务进行联系

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
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
downloadBinder=(MyService.DownloadBinder) service;
downloadBinder.startDownloads();
downloadBinder.getProgress();
}

@Override
public void onServiceDisconnected(ComponentName componentName) {

}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button start=(Button) findViewById(R.id.start_service);
Button stop=(Button) findViewById(R.id.stop_service);
Button bind=(Button) findViewById(R.id.bind_service);
Button unbind=(Button) findViewById(R.id.unbind_service);
Button startMyIntent=(Button)findViewById(R.id.start_intent_service);
start.setOnClickListener(this);
stop.setOnClickListener(this);
bind.setOnClickListener(this);
unbind.setOnClickListener(this);
startMyIntent.setOnClickListener(this);
}

@Override
public void onClick(View view) {
if(view.getId()==R.id.start_service){
Intent startIntent=new Intent(this,MyService.class);
startService(startIntent);//启动服务
}
if(view.getId()==R.id.stop_service){
Intent stopIntent=new Intent(this,MyService.class);
stopService(stopIntent);//停止服务
}
if(view.getId()==R.id.bind_service){
Intent bindIntent=new Intent(this,MyService.class);
//绑定服务
bindService(bindIntent,connection,BIND_AUTO_CREATE);

}
if(view.getId()==R.id.unbind_service){
//解绑服务
unbindService(connection);
}
}
}
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
public class MyService extends Service {
//建立联系
private DownloadBinder mBinder=new DownloadBinder();
class DownloadBinder extends Binder{
public void startDownloads(){
Log.d("MyService", "startDownloads: ");
}
public int getProgress(){
Log.d("MyService", "getProgress: ");
return 0;
}
}
public MyService() {
}

@Override
public IBinder onBind(Intent intent) {
return mBinder;
}

@SuppressLint("ForegroundServiceType")
@Override
public void onCreate() {
super.onCreate();
Log.d("MyService", "onCreate: ");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("MyService", "onStartCommand: ");
new Thread(new Runnable() {
@Override
public void run() {
//处理具体逻辑
stopSelf();
}
}).start();
return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyService", "onDestroy:");
}
}

服务更多的技巧

使用前台服务

使用startForeground方法

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
 @SuppressLint("ForegroundServiceType")
@Override
public void onCreate() {
super.onCreate();
Log.d("MyService", "onCreate: ");
startForegroundService();
}
@SuppressLint("ForegroundServiceType")
private void startForegroundService() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
String channelId = "my_channel_id";

// 创建通知渠道(仅适用于 Android 8.0 及以上)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
channelId,
"My Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
notificationManager.createNotificationChannel(channel);
}

// 创建 Notification
Notification notification = new NotificationCompat.Builder(this, channelId)
.setContentTitle("Service Running")
.setContentText("Foreground service is running")
.setSmallIcon(R.mipmap.ic_launcher)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.build();

// 启动前台服务
startForeground(1, notification);
}

使用IntentService

1.新建一个类继承自IntentService

2.在Manifest.xml中注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");//调用父类的有参构造函数
}

@Override
protected void onHandleIntent(@Nullable Intent intent) {
//该方法已经在子线程中运行
//打印当前线程的id
Log.d("MyIntentService", "Thread id is "+Thread.currentThread().getId());
}

@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService", "onDestroy executed");
}
}


服务最佳实例

1.添加依赖

2.定义一个回调接口

3.编写下载功能

4.创建一个下载的服务,保证可以一直在后台运行

5.将服务与活动建立联系并编写运行逻辑和授予权限

1
implementation ("com.squareup.okhttp3:okhttp:4.12.0")
1
2
3
4
5
6
7
8
public interface DownloadListener {
void onProgress(int progress);
void onSuccess();
void onFailed();
void onPaused();
void onCanceled();
}

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
public class DownloadTask extends AsyncTask<String,Integer,Integer> {
public static final int TYPE_SUCCESS=0;
public static final int TYPE_FAILED=1;
public static final int TYPE_PAUSED=2;
public static final int TYPE_CANCELED=3;
private DownloadListener listener;
private boolean isCanceled=false;
private boolean isPaused=false;
private int lastProgress;
public DownloadTask(DownloadListener listener){
this.listener=listener;
}

@Override
protected Integer doInBackground(String... strings) {
//用于从网络读取数据
InputStream is=null;
//用于写入下载的文件
RandomAccessFile savedFile=null;
//表示下载文件的存储位置
File file=null;
try{
//记录已下载的文件长度
long downloadLength=0;
//获取URL地址
String downloadUrl=strings[0];
//解析下载的文件名
String fileName=downloadUrl.substring(downloadUrl.lastIndexOf("/"));
//指定将文件下载到Environment.DIRECTORY_DOWNLOADS目录下
String directory= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
file=new File(directory+fileName);
//判断该目录是否已经存在要下载的文件
if(file.exists()){
//读取已下载的字节数
downloadLength=file.length();
}
//获取下载文件的总长度
long contentLength=getContentLength(downloadUrl);
if(contentLength==0){
return TYPE_FAILED;
} else if (contentLength==downloadLength) {
//已下载字节和文件总字节相等,说明已经下载完成了
return TYPE_SUCCESS;
}
//利用OkHttp发送一条网络请求
OkHttpClient client=new OkHttpClient();
Request request=new Request.Builder()
//断点下载,指定从哪个字节开始下载
.addHeader("RANGE","bytes="+downloadLength+"-")
.url(downloadUrl)
.build();
Response response=client.newCall(request).execute();
if(response!=null){
//获取字节流
is=response.body().byteStream();
//用于读写文件,“rw”模式表示读写模式
savedFile=new RandomAccessFile(file,"rw");
//跳过已下载的字节
savedFile.seek(downloadLength);
//创建一个字节数组,用于存储从网络读取的数据
byte[] b=new byte[1024];
//用于记录总共下载的字节数
int total=0;
//用于存储每次从网络读取的字节数
int len;
while((len=is.read(b))!=-1){
if(isCanceled){
return TYPE_CANCELED;
} else if (isPaused) {
return TYPE_PAUSED;
}else{
total+=len;
//将从网络上读取的数据写入到文件中
savedFile.write(b,0,len);
//计算已下载的百分比
int progress=(int) ((total+downloadLength)*100/contentLength);
//发布下载进度
publishProgress(progress);
}
}
//关闭字节流
response.body().close();
return TYPE_SUCCESS;
}

}catch (Exception e){
e.printStackTrace();
}finally {
try{
if(is!=null){
is.close();
}
if(savedFile!=null){
savedFile.close();
}
if(isCanceled&&file!=null){
file.delete();
}
}catch (Exception e){
e.printStackTrace();
}
}
return TYPE_FAILED;
}

@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
int progress=values[0];
if(progress>lastProgress){
listener.onProgress(progress);
lastProgress=progress;
}
}

@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
if(integer==TYPE_SUCCESS){
listener.onSuccess();
} else if (integer==TYPE_FAILED) {
listener.onFailed();
} else if (integer==TYPE_PAUSED) {
listener.onPaused();
} else if (integer==TYPE_CANCELED) {
listener.onCanceled();
}
}
public void pauseDownload(){
isPaused=true;
}
public void cancelDownload(){
isCanceled=true;
}
private long getContentLength(String downloadUrl) throws IOException {
OkHttpClient client=new OkHttpClient();
Request request=new Request.Builder()
.url(downloadUrl)
.build();
Response response=client.newCall(request).execute();
if(response!=null&&response.isSuccessful()){
long contentLendth=response.body().contentLength();
response.close();
return contentLendth;
}
return 0;
}
}

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
public class DownloadService extends Service {
private DownloadTask downloadTask;
private String downloadUrl;
public DownloadService() {
}
private DownloadListener listener=new DownloadListener() {
@SuppressLint("NotificationPermission")
@Override
public void onProgress(int progress) {
getNotificationManager().notify(1,getNotification("Downloading...",progress));
}

@SuppressLint("NotificationPermission")
@Override
public void onSuccess() {
downloadTask=null;
//关闭前台通知
stopForeground(true);
//创建通知告知用户下载成功
getNotificationManager().notify(1,getNotification("Download Success",-1));
Toast.makeText(DownloadService.this, "Download Success", Toast.LENGTH_SHORT).show();
}

@SuppressLint("NotificationPermission")
@Override
public void onFailed() {
downloadTask=null;
stopForeground(true);
getNotificationManager().notify(1,getNotification("Download Failed",-1));
Toast.makeText(DownloadService.this, "Download Failed", Toast.LENGTH_SHORT).show();
}

@Override
public void onPaused() {
downloadTask=null;
Toast.makeText(DownloadService.this, "Paused", Toast.LENGTH_SHORT).show();
}

@Override
public void onCanceled() {
downloadTask=null;
stopForeground(true);
Toast.makeText(DownloadService.this, "Canceled", Toast.LENGTH_SHORT).show();
}
};
private DownloadBinder mBinder=new DownloadBinder();
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
class DownloadBinder extends Binder{
@SuppressLint("ForegroundServiceType")
public void startDownload(String url){
if(downloadTask==null){
downloadUrl=url;
//创建DownloadTask对象
downloadTask=new DownloadTask(listener);
//开启下载
downloadTask.execute(downloadUrl);
startForeground(1,getNotification("Downloading...",0));
Toast.makeText(DownloadService.this, "Downloading...", Toast.LENGTH_SHORT).show();
}
}
public void pauseDownload(){
if(downloadTask!=null){
downloadTask.pauseDownload();
}
}
public void cancelDownload(){
if(downloadTask!=null){
downloadTask.cancelDownload();
}
else{
if(downloadUrl!=null){
//取消下载时需将文件删除,并将通知关闭
String fileName=downloadUrl.substring(downloadUrl.lastIndexOf("/"));
String directory= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
File file=new File(directory+fileName);
if(file.exists()){
file.delete();
}
getNotificationManager().cancel(1);
stopForeground(true);
Toast.makeText(DownloadService.this, "Canceled", Toast.LENGTH_SHORT).show();
}
}
}
}
private NotificationManager getNotificationManager(){
return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
private Notification getNotification(String title,int progress){
Intent intent=new Intent(this,MainActivity.class);
@SuppressLint("UnspecifiedImmutableFlag") PendingIntent pi=PendingIntent.getActivities(this,0, new Intent[]{intent},0);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
String channelId = "my_channel_id";

// 创建通知渠道(仅适用于 Android 8.0 及以上)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId, "My Channel", NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
NotificationCompat.Builder builder=new NotificationCompat.Builder(this, channelId);
// 创建 Notification

builder.setContentTitle(title);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher));
builder.setContentIntent(pi);

if(progress>0){
builder.setContentText(progress+"%");
builder.setProgress(100,progress,false);
}
return builder.build();
}
}
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
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private DownloadService.DownloadBinder downloadBinder;
//使活动与服务建立联系
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
downloadBinder=(DownloadService.DownloadBinder) iBinder;
}

@Override
public void onServiceDisconnected(ComponentName componentName) {

}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button start=(Button) findViewById(R.id.start_download);
Button pause=(Button) findViewById(R.id.pause_download);
Button cancel=(Button) findViewById(R.id.cancel_download);
start.setOnClickListener(this);
pause.setOnClickListener(this);
cancel.setOnClickListener(this);
//启动服务
Intent intent=new Intent(this,DownloadService.class);
startService(intent);
//绑定服务
bindService(intent,connection,BIND_AUTO_CREATE);
//申请权限
if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
}
}

@Override
public void onClick(View view) {
if(downloadBinder==null){
return;
}
if(view.getId()==R.id.start_download){
String url="https://raw.githubusercontent.com/guolindev/eclipse/master/eclipse-inst-win64.exe";
downloadBinder.startDownload(url);
} else if (view.getId()==R.id.pause_download) {
downloadBinder.pauseDownload();
} else if (view.getId()==R.id.cancel_download) {
downloadBinder.cancelDownload();
}
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode==1){
if(grantResults.length>0&&grantResults[0]!=PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "拒绝授权程序无法使用", Toast.LENGTH_SHORT).show();
finish();
}
}
}

@Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
}