多线程实现3种方式:
- 1.extends Thread/implements Runnable
- AsyncTask
- Rxjava 实现
实现的功能是在子线程里读取根目录的所有文件名和根目录下的一个文件的读取,并把内容显示在主线程
1.extends Thread/implements Runnable
-
extends Thread
public class MyThread extends Thread {
public String directory= Environment.getExternalStorageDirectory().getPath().toString();
public String filename=directory+"/cyc.doc";
public String filename_copy=directory+"Download/copy/facebook-144-0-027-91.apk";
public Handler mHandler=null;
public MyThread(Handler handler){
mHandler=handler;
}
@Override
public void run() {
super.run();
try {
StringBuilder sb=new StringBuilder();
File mfile=new File(directory);
File[]files=mfile.listFiles();
for (int i=0;i<files.length;i++){
File file1=files[i];
sb.append(file1.getPath()+"\n");
Log.d("file:",file1.getPath());
}
Message message1=new Message();
Bundle bundle1=new Bundle();
bundle1.putString("content",sb.toString());
message1.what=MainActivity.CONTENT;
message1.setData(bundle1);
mHandler.sendMessage(message1);
BufferedReader bufferedReader=new BufferedReader(new FileReader(filename));
String s;
while ((s=bufferedReader.readLine())!=null){
Message message=new Message();
Bundle bundle=new Bundle();
bundle.putString("content",s+"\n");
message.what=MainActivity.CONTENT;
message.setData(bundle);
mHandler.sendMessage(message);
}
bufferedReader.close();//流关闭内存释放空间
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
}
-
implements Runable
public class MyThread2 implements Runnable {
public String directory= Environment.getExternalStorageDirectory().getPath().toString();
public String filename=directory+"/cyc.doc";
private Handler mHandler;
public MyThread2(Handler handler){
this.mHandler=handler;
}
@Override
public void run() {
try {
StringBuilder sb=new StringBuilder();
File mfile=new File(directory);
File[]files=mfile.listFiles();
for (int i=0;i<files.length;i++){
File file1=files[i];
sb.append(file1.getPath()+"\n");
Log.d("file:",file1.getPath());
}
Message message1=new Message();
Bundle bundle1=new Bundle();
bundle1.putString("content",sb.toString());
message1.what= MainActivity.CONTENT;
message1.setData(bundle1);
mHandler.sendMessage(message1);
BufferedReader bufferedReader=new BufferedReader(new FileReader(filename));
String s;
while ((s=bufferedReader.readLine())!=null){
Message message=new Message();
Bundle bundle=new Bundle();
bundle.putString("content",s+"\n");
message.what=MainActivity.CONTENT;
message.setData(bundle);
mHandler.sendMessage(message);
}
bufferedReader.close();//流关闭内存释放空间
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
}
2.AsyncTask
class MyThread_AsyncTask extends AsyncTask<Integer,Integer,String>{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
content_tv.setText(s);
}
@Override
protected String doInBackground(Integer... integers) {
StringBuilder sb1=new StringBuilder();
try {
File mfile=new File(directory);
File[]files=mfile.listFiles();
for (int i=0;i<files.length;i++){
File file1=files[i];
sb1.append(file1.getPath()+"\n");
Log.d("file:",file1.getPath());
}
BufferedReader bufferedReader=new BufferedReader(new FileReader(filename));
String s;
while ((s=bufferedReader.readLine())!=null){
sb1.append(s+"\n");
}
bufferedReader.close();//流关闭内存释放空间
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
return sb1.toString();
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
}
3.Rxjava
1) 创建 Observer
Observer 即观察者,它决定事件触发的时候将有怎样的行为。 RxJava 中的 Observer 接口的实现方式:
Observer<String> observer = new Observer<String>() {
@Override
public void onNext(String s) {
Log.d(tag, "Item: " + s);
}
@Override
public void onCompleted() {
Log.d(tag, "Completed!");
}
@Override
public void onError(Throwable e) {
Log.d(tag, "Error!");
}
};
除了 Observer 接口之外,RxJava 还内置了一个实现了 Observer 的抽象类:Subscriber。
不仅基本使用方式一样,实质上,在 RxJava 的 subscribe 过程中,Observer 也总是会先被转换成一个 Subscriber 再使用。所以如果你只想使用基本功能,选择 Observer 和 Subscriber 是完全一样的。它们的区别对于使用者来说主要有两点:
onStart(): 这是 Subscriber 增加的方法。它会在 subscribe 刚开始,而事件还未发送之前被调用,可以用于做一些准备工作,例如数据的清零或重置。这是一个可选方法,默认情况下它的实现为空。需要注意的是,如果对准备工作的线程有要求(例如弹出一个显示进度的对话框,这必须在主线程执行), onStart() 就不适用了,因为它总是在 subscribe 所发生的线程被调用,而不能指定线程。要在指定的线程来做准备工作,可以使用 doOnSubscribe() 方法,具体可以在后面的文中看到。
unsubscribe(): 这是 Subscriber 所实现的另一个接口 Subscription 的方法,用于取消订阅。在这个方法被调用后,Subscriber 将不再接收事件。一般在这个方法调用前,可以使用 isUnsubscribed() 先判断一下状态。 unsubscribe() 这个方法很重要,因为在 subscribe() 之后, Observable 会持有 Subscriber 的引用,这个引用如果不能及时被释放,将有内存泄露的风险。所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(例如 onPause() onStop() 等方法中)调用 unsubscribe() 来解除引用关系,以避免内存泄露的发生。
- 创建 Observable
Observable 即被观察者,它决定什么时候触发事件以及触发怎样的事件。 RxJava 使用 create() 方法来创建一个 Observable ,并为它定义事件触发规则:
- Subscribe (订阅)
创建了 Observable 和 Observer 之后,再用 subscribe() 方法将它们联结起来,整条链子就可以工作了
可以看到,subscriber() 做了3件事:
调用 Subscriber.onStart() 。这个方法在前面已经介绍过,是一个可选的准备方法。
调用 Observable 中的 OnSubscribe.call(Subscriber) 。在这里,事件发送的逻辑开始运行。从这也可以看出,在 RxJava 中, Observable 并不是在创建的时候就立即开始发送事件,而是在它被订阅的时候,即当 subscribe() 方法执行的时候。
将传入的 Subscriber 作为 Subscription 返回。这是为了方便 unsubscribe().
例子
int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe<Drawable>() {
@Override
public void call(Subscriber<? super Drawable> subscriber) {
Drawable drawable = getTheme().getDrawable(drawableRes));
subscriber.onNext(drawable);
subscriber.onCompleted();
}
}).subscribe(new Observer<Drawable>() {
@Override
public void onNext(Drawable drawable) {
imageView.setImageDrawable(drawable);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
}
});
整体代码
subscription = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
StringBuilder sb1 = new StringBuilder();
try {
File mfile = new File(directory);
File[] files = mfile.listFiles();
for (int i = 0; i < files.length; i++) {
File file1 = files[i];
sb1.append(file1.getPath() + "\n");
Log.d("file:", file1.getPath());
}
BufferedReader bufferedReader = new BufferedReader(new FileReader(filename));
String s;
while ((s = bufferedReader.readLine()) != null) {
sb1.append(s + "\n");
}
bufferedReader.close();//流关闭内存释放空间
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
subscriber.onNext(sb1.toString());
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onNext(String s) {
content_tv.setText(s);
}
});
防止内存泄露
在onDestory()方法里进行内存回收
mHandler.removeCallbacksAndMessages(null);//移除mHandler所持有的Runnable和message
activity在关闭时把未完成的Message去掉,防止activity里有handler的引用,activity回收不掉;
if (subscription.isUnsubscribed()){
subscription.unsubscribe();//防止内存泄露
}
取消订阅,订阅不取消,activity也不能回收
if(!myThread3.isCancelled()){
myThread3.cancel(true);
}
AysncTask
由于Java内部类的特点,AsyncTask内部类会持有外部类的隐式引用。即使从代码上看我在AsyncTask里没有持有外部的任何引用,但是写在Activity里,对context仍然会有个强引用,这样如果线程超过Activity生命周期,Activity还是无法回收造成内存泄露。
那问题怎么解决呢,有两种办法:第一,在Activity生命周期结束前,去cancel AsyncTask,因为Activity都要销毁了,这个时候再跑线程,绘UI显然已经没什么意义了。第二,如果一定要写成内部类的形式,对context采用WeakRefrence,在使用之前判断是否为空。
网友评论