Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/190.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在Android应用程序中实现线程时出现问题_Java_Android_Java Threads - Fatal编程技术网

Java 在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

我正在开发一个简单的应用程序,为了在Android环境中练习线程,我得到了一个常见错误,但我不知道为什么,可能是因为线程的行为,或者我不知道为什么

主课

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
方法与post
Runnable/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));