java—在android应用程序中实现线程时出现问题

sf6xfgos  于 2021-07-11  发布在  Java
关注(0)|答案(2)|浏览(413)

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

  1. public class MainActivity extends AppCompatActivity {
  2. MiHiloLooper looper;
  3. ImageView iv1, iv2, iv3;
  4. URL url1, url2, url3;
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9. Toolbar toolbar = findViewById(R.id.toolbar);
  10. setSupportActionBar(toolbar);
  11. iv1 = new ImageView(getApplicationContext());
  12. iv1.setImageResource(R.drawable.hilo1);
  13. iv2 = new ImageView(getApplicationContext());
  14. iv2.setImageResource(R.drawable.hilo2);
  15. iv3 = new ImageView(getApplicationContext());
  16. iv3.setImageResource(R.drawable.hilo3);
  17. try {
  18. url1 = new URL("https://www.dhresource.com/0x0/f2/albu/g8/M00/E6/A3/rBVaV15BFzGAdY2NAARdO9TdaIk347.jpg/20s-3-1500-yards-length-polyester-thread.jpg");
  19. url2 = new URL("https://images-na.ssl-images-amazon.com/images/I/714bmMviZEL._AC_SY450_.jpg");
  20. url3 = new URL("https://www.brildor.com/media/catalog/product/cache/1/image/9df78eab33525d08d6e5fb8d27136e95/h/p/hps025200.jpg");
  21. }catch (MalformedURLException e){
  22. Toast miToast = Toast.makeText(getApplicationContext(), "Se ha produido un error al cargar las imagenes", Toast.LENGTH_LONG);
  23. miToast.setGravity(Gravity.CENTER, 0, 0);
  24. miToast.show();
  25. }
  26. looper = new MiHiloLooper();
  27. looper.post(new Thread( new ImageLoader(url1, iv1) ));
  28. looper.post(new Thread( new ImageLoader(url2, iv2) ));
  29. looper.post(new Thread( new ImageLoader(url3, iv3) ));
  30. FloatingActionButton fab = findViewById(R.id.fab);
  31. fab.setOnClickListener(new View.OnClickListener() {
  32. @Override
  33. public void onClick(View view) {
  34. // Lanzammos los hilos con el OnClick del Toolbar.
  35. }
  36. });
  37. }
  38. @Override
  39. protected void onDestroy() {
  40. looper.terminate();
  41. super.onDestroy();
  42. }
  43. @Override
  44. public boolean onCreateOptionsMenu(Menu menu) {
  45. // Inflate the menu; this adds items to the action bar if it is present.
  46. getMenuInflater().inflate(R.menu.menu_main, menu);
  47. return true;
  48. }
  49. @Override
  50. public boolean onOptionsItemSelected(MenuItem item) {
  51. // Handle action bar item clicks here. The action bar will
  52. // automatically handle clicks on the Home/Up button, so long
  53. // as you specify a parent activity in AndroidManifest.xml.
  54. int id = item.getItemId();
  55. //noinspection SimplifiableIfStatement
  56. if (id == R.id.action_settings) {
  57. return true;
  58. }
  59. return super.onOptionsItemSelected(item);
  60. }
  61. }

imageloader.class类

  1. public class ImageLoader implements Runnable {
  2. ImageView iv;
  3. URL url;
  4. Button button;
  5. public ImageLoader(URL url, ImageView iv){
  6. this.iv = iv;
  7. this.url=url;
  8. }
  9. @Override
  10. public void run() {
  11. try {
  12. InputStream is = url.openStream();
  13. final Drawable drawable =
  14. Drawable.createFromStream(is, "src");
  15. button.post(new Runnable() {
  16. @Override
  17. public void run() {
  18. iv.setImageDrawable(drawable);
  19. }
  20. });
  21. } catch (IOException e) {
  22. Log.e("URL","Error downloading image "+
  23. url.toString());
  24. }
  25. }
  26. }

MIHILOOPER.类

  1. public class MiHiloLooper extends Thread{
  2. Handler handler; //message handler
  3. public MiHiloLooper(){
  4. this.start();
  5. }
  6. @Override
  7. public void run(){
  8. try{
  9. Looper.prepare();
  10. handler = new Handler();
  11. Looper.loop();
  12. }catch(Throwable t){
  13. Log.e("Looper","Error: ", t);
  14. }
  15. }
  16. public void terminate(){
  17. handler.getLooper().quit();
  18. }
  19. public void post(Runnable runnable){
  20. handler.post(runnable);
  21. }
  22. }

这是我的应用程序的结构,我得到的错误是:

  1. 2020-11-21 17:47:11.166 4078-4078/com.example.probandohilos E/AndroidRuntime: FATAL EXCEPTION: main
  2. Process: com.example.probandohilos, PID: 4078
  3. 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
  4. at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778)
  5. at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
  6. at android.app.ActivityThread.-wrap11(Unknown Source:0)
  7. at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
  8. at android.os.Handler.dispatchMessage(Handler.java:106)
  9. at android.os.Looper.loop(Looper.java:164)
  10. at android.app.ActivityThread.main(ActivityThread.java:6494)
  11. at java.lang.reflect.Method.invoke(Native Method)
  12. at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
  13. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
  14. Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.os.Handler.post(java.lang.Runnable)' on a null object reference
  15. at com.example.probandohilos.MiHiloLooper.post(MiHiloLooper.java:27)
  16. at com.example.probandohilos.MainActivity.onCreate(MainActivity.java:53)
  17. at android.app.Activity.performCreate(Activity.java:7009)
  18. at android.app.Activity.performCreate(Activity.java:7000)
  19. at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
  20. at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
  21. at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856) 
  22. at android.app.ActivityThread.-wrap11(Unknown Source:0) 
  23. at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589) 
  24. at android.os.Handler.dispatchMessage(Handler.java:106) 
  25. at android.os.Looper.loop(Looper.java:164) 
  26. at android.app.ActivityThread.main(ActivityThread.java:6494) 
  27. at java.lang.reflect.Method.invoke(Native Method) 
  28. at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
  29. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 

我不明白我怎么能得到一个 NullPointerException ,如果我已初始化 Looper 以及它需要的所有参数。
如果你告诉我哪里错了,提前谢谢!

pkmbmrz7

pkmbmrz71#

原因是 mHandler 为空,因为 MiHiloLooper 线程与发布同步创建 Runnable/messages 使用its post 方法。
所以,当你试图 looper.post() 在线程创建结束之前(即 run() 结束),然后你将面对这个npe。
为了便于说明,要使代码在没有npe的情况下运行,可以使用 Thread.sleep() 为了确保 MiHiloLooper 线程是在向其循环程序发布任何runnable之前创建的。我在这里等了几秒钟,以确保 looper 线程已创建并 mHandler 不为空。
同样,这是为了说明的目的,当然不是一种生产方式。

  1. looper = new MiHiloLooper();
  2. try {
  3. Thread.sleep(2000); // delay of 2 sec
  4. } catch (InterruptedException e) {
  5. e.printStackTrace();
  6. }
  7. looper.post(new Thread( new ImageLoader(url1, iv1) ));
  8. looper.post(new Thread( new ImageLoader(url2, iv2) ));
  9. looper.post(new Thread( new ImageLoader(url3, iv3) ));

因此,要解决您的问题,您需要使用调用 post() 方法。

  1. looper = new MiHiloLooper();
  2. looper.post(new Thread( new ImageLoader(url1, iv1) ));
  3. looper.post(new Thread( new ImageLoader(url2, iv2) ));
  4. looper.post(new Thread( new ImageLoader(url3, iv3) ));

你可以用一个监听器接口来解决这个问题,只要你的线程被创建,监听器接口就会被触发。
下面是自定义线程的新代码,我将侦听器作为构造函数参数传递;并调用了它的回调 onReady() 每当处理程序初始化时,我确信 mHandler 将不再为空。

  1. public class MiHiloLooper extends Thread{
  2. Handler handler; //message handler
  3. private CreationListener mCreationListener; // Listener for thread creation
  4. public interface CreationListener {
  5. void onReady();
  6. }
  7. public MiHiloLooper(CreationListener listener){
  8. mCreationListener = listener;
  9. this.start();
  10. }
  11. @Override
  12. public void run(){
  13. try{
  14. Looper.prepare();
  15. handler = new Handler();
  16. mCreationListener.onReady(); // Here I am sure that handler is not null
  17. Looper.loop();
  18. }catch(Throwable t){
  19. Log.e("Looper","Error: ", t);
  20. }
  21. }
  22. public void terminate(){
  23. handler.getLooper().quit();
  24. }
  25. public void post(Runnable runnable){
  26. handler.post(runnable);
  27. }
  28. }

然后在你的 MainActivity 添加一个侦听器作为构造函数参数。

  1. looper = new MiHiloLooper(new MiHiloLooper.CreationListener() {
  2. @Override
  3. public void onReady() {
  4. looper.post(new Thread( new ImageLoader(url1, iv1) ));
  5. looper.post(new Thread( new ImageLoader(url2, iv2) ));
  6. looper.post(new Thread( new ImageLoader(url3, iv3) ));
  7. }
  8. });

旁注:您可以添加 synchronized 给你的 post() 方法以避免提交给可运行程序的任何争用条件 MiHiloLooper 线程。

展开查看全部
b1zrtrql

b1zrtrql2#

根本原因
在线程上调用start()时,它不会立即执行,系统需要为该线程分配资源,然后在run()方法中运行代码,在该方法中初始化处理程序。这就解释了为什么在访问处理程序时会出现npe(nullpointerexception)。
解决方案
如果你想创建一个有活套的线程,android为你提供handlerthreadapi。因此,请将代码更改为:
mihilooper.java文件

  1. class MiHiloLooper extends HandlerThread {
  2. private Handler handler;
  3. public MiHiloLooper() {
  4. this("MiHiloHandlerThread");
  5. }
  6. public MiHiloLooper(String name) {
  7. super(name);
  8. start();
  9. }
  10. public Handler getHandler() {
  11. if (handler == null) {
  12. handler = new Handler(getLooper());
  13. }
  14. return handler;
  15. }
  16. }

主活动.java

  1. looper = new MiHiloLooper();
  2. looper.getHandler().post(new ImageLoader(url1, iv1));
  3. looper.getHandler().post(new ImageLoader(url2, iv2));
  4. looper.getHandler().post(new ImageLoader(url3, iv3));

顺便说一下,imageloader类中有一行

  1. button.post(new Runnable() {
  2. @Override
  3. public void run() {
  4. iv.setImageDrawable(drawable);
  5. }
  6. });

这个 button 变量未在任何地方初始化,因此它将使应用程序崩溃。在这种情况下,可以替换为 iv 变量。

  1. iv.post(new Runnable() {
  2. @Override
  3. public void run() {
  4. iv.setImageDrawable(drawable);
  5. }
  6. });

最后,您不应该创建新线程并将其传递给处理程序的post()方法。
不要

  1. looper.post(new Thread(new ImageLoader(url1, iv1)));

  1. looper.getHandler().post(new ImageLoader(url1, iv1));
展开查看全部

相关问题