Java 在Android应用程序中实现线程时出现问题
我正在开发一个简单的应用程序,为了在Android环境中练习线程,我得到了一个常见错误,但我不知道为什么,可能是因为线程的行为,或者我不知道为什么 主课Java 在Android应用程序中实现线程时出现问题,java,android,java-threads,Java,Android,Java Threads,我正在开发一个简单的应用程序,为了在Android环境中练习线程,我得到了一个常见错误,但我不知道为什么,可能是因为线程的行为,或者我不知道为什么 主课 public class MainActivity extends AppCompatActivity { MiHiloLooper looper; ImageView iv1, iv2, iv3; URL url1, url2, url3; @Override protected void onCr
public class MainActivity extends AppCompatActivity {
MiHiloLooper looper;
ImageView iv1, iv2, iv3;
URL url1, url2, url3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
iv1 = new ImageView(getApplicationContext());
iv1.setImageResource(R.drawable.hilo1);
iv2 = new ImageView(getApplicationContext());
iv2.setImageResource(R.drawable.hilo2);
iv3 = new ImageView(getApplicationContext());
iv3.setImageResource(R.drawable.hilo3);
try {
url1 = new URL("https://www.dhresource.com/0x0/f2/albu/g8/M00/E6/A3/rBVaV15BFzGAdY2NAARdO9TdaIk347.jpg/20s-3-1500-yards-length-polyester-thread.jpg");
url2 = new URL("https://images-na.ssl-images-amazon.com/images/I/714bmMviZEL._AC_SY450_.jpg");
url3 = new URL("https://www.brildor.com/media/catalog/product/cache/1/image/9df78eab33525d08d6e5fb8d27136e95/h/p/hps025200.jpg");
}catch (MalformedURLException e){
Toast miToast = Toast.makeText(getApplicationContext(), "Se ha produido un error al cargar las imagenes", Toast.LENGTH_LONG);
miToast.setGravity(Gravity.CENTER, 0, 0);
miToast.show();
}
looper = new MiHiloLooper();
looper.post(new Thread( new ImageLoader(url1, iv1) ));
looper.post(new Thread( new ImageLoader(url2, iv2) ));
looper.post(new Thread( new ImageLoader(url3, iv3) ));
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Lanzammos los hilos con el OnClick del Toolbar.
}
});
}
@Override
protected void onDestroy() {
looper.terminate();
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
ImageLoader.class
public class ImageLoader implements Runnable {
ImageView iv;
URL url;
Button button;
public ImageLoader(URL url, ImageView iv){
this.iv = iv;
this.url=url;
}
@Override
public void run() {
try {
InputStream is = url.openStream();
final Drawable drawable =
Drawable.createFromStream(is, "src");
button.post(new Runnable() {
@Override
public void run() {
iv.setImageDrawable(drawable);
}
});
} catch (IOException e) {
Log.e("URL","Error downloading image "+
url.toString());
}
}
}
米希洛普勒班
public class MiHiloLooper extends Thread{
Handler handler; //message handler
public MiHiloLooper(){
this.start();
}
@Override
public void run(){
try{
Looper.prepare();
handler = new Handler();
Looper.loop();
}catch(Throwable t){
Log.e("Looper","Error: ", t);
}
}
public void terminate(){
handler.getLooper().quit();
}
public void post(Runnable runnable){
handler.post(runnable);
}
}
这是我的应用程序的结构,我得到的错误是:
2020-11-21 17:47:11.166 4078-4078/com.example.probandohilos E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.probandohilos, PID: 4078
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.probandohilos/com.example.probandohilos.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.os.Handler.post(java.lang.Runnable)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.os.Handler.post(java.lang.Runnable)' on a null object reference
at com.example.probandohilos.MiHiloLooper.post(MiHiloLooper.java:27)
at com.example.probandohilos.MainActivity.onCreate(MainActivity.java:53)
at android.app.Activity.performCreate(Activity.java:7009)
at android.app.Activity.performCreate(Activity.java:7000)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
如果我已经初始化了循环器及其所需的所有参数,我不明白如何获得NullPointerException
如果你告诉我错误在哪里,请提前感谢 之所以mHandler
为空,是因为mihilooper
线程是使用其post
方法与postRunnable/messages
同步创建的
looper = new MiHiloLooper();
looper.post(new Thread( new ImageLoader(url1, iv1) ));
looper.post(new Thread( new ImageLoader(url2, iv2) ));
looper.post(new Thread( new ImageLoader(url3, iv3) ));
因此,当您尝试在线程创建结束之前(即在其run()
结束之前)执行looper.post()
时,您将面临此NPE
为了便于说明,要使代码在没有NPE的情况下运行,您可以使用Thread.sleep()
一段时间,以确保在将任何可运行线程发布到其循环器之前创建mihilooper
线程。这里我等待几秒钟,以确保创建了looper
线程,并且mHandler
不为空
同样,这是为了说明的目的,当然不是一种生产方式
looper = new MiHiloLooper();
try {
Thread.sleep(2000); // delay of 2 sec
} catch (InterruptedException e) {
e.printStackTrace();
}
looper.post(new Thread( new ImageLoader(url1, iv1) ));
looper.post(new Thread( new ImageLoader(url2, iv2) ));
looper.post(new Thread( new ImageLoader(url3, iv3) ));
因此,为了解决您的问题,您需要使用调用post()
方法的行异步运行下面代码中的第一行(线程创建)
looper = new MiHiloLooper();
looper.post(new Thread( new ImageLoader(url1, iv1) ));
looper.post(new Thread( new ImageLoader(url2, iv2) ));
looper.post(new Thread( new ImageLoader(url3, iv3) ));
您可以使用侦听器接口来解决这个问题,该接口将在创建线程时触发
这是自定义线程的新代码,我将侦听器作为构造函数参数传递;并在处理程序初始化时调用其回调函数onReady()
,因此我确信mHandler
将不再为空
public class MiHiloLooper extends Thread{
Handler handler; //message handler
private CreationListener mCreationListener; // Listener for thread creation
public interface CreationListener {
void onReady();
}
public MiHiloLooper(CreationListener listener){
mCreationListener = listener;
this.start();
}
@Override
public void run(){
try{
Looper.prepare();
handler = new Handler();
mCreationListener.onReady(); // Here I am sure that handler is not null
Looper.loop();
}catch(Throwable t){
Log.e("Looper","Error: ", t);
}
}
public void terminate(){
handler.getLooper().quit();
}
public void post(Runnable runnable){
handler.post(runnable);
}
}
然后在MainActivity
中添加一个侦听器作为构造函数参数
looper = new MiHiloLooper(new MiHiloLooper.CreationListener() {
@Override
public void onReady() {
looper.post(new Thread( new ImageLoader(url1, iv1) ));
looper.post(new Thread( new ImageLoader(url2, iv2) ));
looper.post(new Thread( new ImageLoader(url3, iv3) ));
}
});
旁注:您可以将synchronized
添加到post()
方法中,以避免提交给mihilooper
线程的可运行程序出现任何争用条件。根本原因
当调用线程时,它不会立即执行,系统需要为线程分配资源,然后在初始化处理程序的方法中运行代码。这就解释了为什么在立即访问处理程序时会出现NPE(NullPointerException)
解决方案
如果你想创建一个有活套的线程,Android会为你提供API。因此,请将代码更改为:
mihilooper.java
MainActivity.java
顺便说一下,ImageLoader类中有一行
按钮
变量未在任何地方初始化,因此它将使应用程序崩溃。在这种情况下,可以替换为iv
变量
iv.post(new Runnable() {
@Override
public void run() {
iv.setImageDrawable(drawable);
}
});
最后,您不应该创建新线程并将其传递给处理程序的方法
不要
做
很高兴听到这个消息。顺便说一下,如果我的答案解决了你的问题,那么你可以把它作为答案。这将有助于我今后改进我的答案。
button.post(new Runnable() {
@Override
public void run() {
iv.setImageDrawable(drawable);
}
});
iv.post(new Runnable() {
@Override
public void run() {
iv.setImageDrawable(drawable);
}
});
looper.post(new Thread(new ImageLoader(url1, iv1)));
looper.getHandler().post(new ImageLoader(url1, iv1));