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) { 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";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( channelId, "My Channel", NotificationManager.IMPORTANCE_DEFAULT ); notificationManager.createNotificationChannel(channel); }
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) { 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; String downloadUrl=strings[0]; String fileName=downloadUrl.substring(downloadUrl.lastIndexOf("/")); 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; } 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(); 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=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";
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);
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); } }
|