Runnable接口概述

Runnable接口用于声明一系列事务,通常用于多线程处理。然而,实现Runnable接口并不意味着启动了一个新的线程,而只是定义了接下来要执行的操作。至于这些操作是在主线程中处理还是在子线程中处理,取决于我们在哪里运行Runnable实例。如果在Handler或View中启动Runnable,那么Runnable事务将在UI线程中运行;如果在Thread中启动Runnable,那么Runnable事务将在非UI线程中运行。

实现Runnable接口只需重写run方法,该方法内部包含需要Runnable处理的事务。run方法无需显式调用,在启动Runnable实例时会自动调用对象的run方法。与继承Thread类相比,实现Runnable接口具有以下优势:

1. Runnable接口实质上是共享代码,类似于函数调用,但比函数调用更灵活,因为Runnable可以选择实际调用的时机,而不必像函数调用那样等待调用结束。

2. 可以避免Java单继承方式的局限性。如果一个新类继承了Thread类,就不能再继承其他类。但是Runnable只是一个接口,所以新类可以继承其他类并同时实现Runnable接口。

启动Runnable的方法有以下几种:

一、使用Handler类的post方法

Handler常用的post方法有以下几种:

- post:立即启动Runnable

- postDelayed:延迟指定时间间隔后启动Runnable

- postAtTime:在指定时间启动Runnable

- removeCallbacks:回收/移除指定的Runnable

二、使用View类的post方法

View类也提供了post和postDelayed两个方法,控件或视图可以直接调用自身的post方法,无需另外声明Handler对象。查看View的post源码,会发现其内部就是调用自身Handler实例的post方法。

三、利用Thread类构造Runnable的新线程

使用Thread方式启动Runnable,只需两个步骤:第一步使用Runnable对象构造一个Thread实例;第二步启动这个Thread实例。示例代码如下:

```java

Thread runTread = new Thread(mRun);

runTread.start();

```

以下是完整的示例代码:

```python

import numpy as np

import pandas as pd

from sklearn.linear_model import LinearRegression

from sklearn.metrics import mean_squared_error, r2_score

# 加载数据集

data = pd.read_csv('data.csv')

X = data[['feature1', 'feature2']].values

y = data['target'].values

# 创建线性回归模型并拟合数据

model = LinearRegression()

model.fit(X, y)

# 预测新数据

new_data = np.array([[5, 3]])

predictions = model.predict(new_data)

print("预测结果:", predictions)

# 评估模型性能

mse = mean_squared_error(y, model.predict(X))

r2 = r2_score(y, model.predict(X))

print("均方误差:", mse)

print("R^2 分数:", r2)

```

以下是重构后的代码:

```java

import android.app.Activity;

import android.os.Bundle;

import android.os.Handler;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.TextView;

public class MainActivity extends Activity implements OnClickListener {

private Handler mHandler = new Handler();

private TextView tv_runnable;

private int mCount = 0;

private int mOffset = 0;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

Button btn_handler = (Button) findViewById(R.id.btn_handler);

Button btn_view = (Button) findViewById(R.id.btn_view);

Button btn_thread = (Button) findViewById(R.id.btn_thread);

btn_handler.setOnClickListener(this);

btn_view.setOnClickListener(this);

btn_thread.setOnClickListener(this);

tv_runnable = (TextView) findViewById(R.id.tv_runnable);

}

private Runnable mRun = new Runnable() {

@Override

public void run() {

if (mCount < 20) {

tv_runnable.scrollBy(-mOffset, -mOffset);

//Thread方式启动Runnable时不能调用invalidate方法

//tv_runnable.invalidate();

tv_runnable.postInvalidate();

mHandler.postDelayed(this, 200);

mCount++;

}

}

};

@Override

public void onClick(View v) {

mCount = 0;

if (v.getId() == R.id.btn_handler) {

mOffset = 10;

mHandler.postDelayed(mRun, 500);

} else if (v.getId() == R.id.btn_view) {

mOffset = 10;

tv_runnable.postDelayed(mRun, 500);

} else if (v.getId() == R.id.btn_thread) {

mOffset = -10;

Thread runTread = new Thread(mRun);

runTread.start();

}

}

}

```

Runnable的适用场景有四个方面: 1、有些事情需要在Activity页面显示出来后才能做,比如广播接收器一般在onStart或者onResume中注册,所以onCreate方法中若想发送广播后就能接收广播,那得延迟一点时间。 2、页面上有些事务不能让用户久等,所以如果事务本身无法设置超时时间的话,就得使用Runnable在事务超时后强行结束该事务。 3、在Runnable内部postDelayed自身,并持续post若干周期刷新视图,可实现动画效果。该功能的例子见《Android开发笔记(十四)圆弧进度动画》 4、有些监听器如果没有得到合适的结果,就要持续监听,直到出现合适的结果为止。该功能的例子见《Android开发笔记(四十六)手机相关事件》

视图刷新中的post方法有四种: invalidate : 在UI线程中刷新视图 postInvalidate : 在非UI线程中刷新视图 postInvalidateDelayed : 在非UI线程中延迟若干时间后刷新视图 为避免误解,这里对invalidate和postInvalidate的区别做进一步的说明: 1、invalidate只能在UI线程中调用,所以如果在Thread方式中调用invalidate就会抛出异常;postInvalidate可在Thread方式中调用,但并不是不能在UI线程中调用,实际上postInvalidate既可在UI线程中调用,也可在非UI线程中调用; 2、invalidate只能立即刷新视图,而post方式还有postInvalidateDelayed方法可以延迟一段时间,查看postInvalidate的源码,会发现postInvalidate其实就是调用延迟0秒的postInvalidateDelayed; 从上面可以看出,invalidate的适用面较窄,而postInvalidate适用面较广,当然还是postInvalidateDelayed用起来更灵活些。