【问题标题】:Thread Issue : NullPointerException线程问题:NullPointerException
【发布时间】:2015-01-09 05:30:42
【问题描述】:

当我尝试将 listView 的最后一项中的文本设置为 MainActivity 中的文本视图时遇到问题。它在“NullPointerException”的运行时崩溃,因为我认为当我在 MainActivity 中调用它时,它没有完成在 ListView 上的下载,所以当 MainActivity 首次启动时,listView 没有完成他的工作和功能getCount(),getItem() 和我的函数 getLastElement() 仍然为空。 问题是我不太擅长处理 Thread (Wait(), notify(),..) 你能帮我解决这一切吗?

这是我的代码和我的 LogCat:

public class MainActivity extends Activity implements OnClickListener{

    private static Context mContext;

    public Button mExit, mHistory, mRating;

    public  TextView mSignal;

    HistoryAdapt myAdapter;

    HistoryItems m_myLastItem;

    ArrayList<HistoryItems> m_myListItem;

    Runnable m_run;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MainActivity.mContext=getApplicationContext();

        mExit=(Button)findViewById(R.id.ExitButton);
        mExit.setOnClickListener(this);

        mHistory=(Button)findViewById(R.id.HistoryButton);
        mHistory.setOnClickListener(this);

        mRating=(Button)findViewById(R.id.RateButton);
        mRating.setOnClickListener(this);

        mSignal=(TextView)findViewById(R.id.SignalOfTheDayTV);

        //### SET LAST ELEMENT INTO TEXTVIEW

        m_myListItem = new ArrayList<HistoryItems>(); 


        myAdapter= new HistoryAdapt(mContext, m_myListItem);

        new Thread(
                new Runnable(){
                    @Override
                    public void run(){

                            try{
                                ///Need to wait until the data to be downloaded inside HistoryAdapt so it can show the last element from the ListView here
                                HistoryParser parser = new HistoryParser();
                                parser.parse(getInputStream(HistoryAct.RSS_LINK));
                            } catch (XmlPullParserException e) {
                                Log.w(e.getMessage(), e);
                            } catch (IOException e) {
                                Log.w(e.getMessage(), e);
                            } finally {
                                //notify that the data finished to download 
                                MainActivity.this.runOnUiThread(
                                    new Runnable(){
                                        public void run(){

                                                m_myLastItem = myAdapter.getLastElement();
                                                //set last signal into TextView
                                                mSignal.setText(m_myLastItem.getTitle());



                                        }
                                    }
                                );
                            }

                    }
                }
            ).run();



//#RATER
       //   AppRater.app_launched(this);

        //  AppRater.showRateDialog(this, null);


        //Get a Tracker (should auto-report)
        ((AppManager) getApplication()).getTracker(AppManager.TrackerName.APP_TRACKER);
    }//oncreate

    private InputStream getInputStream(String link) {
        try {
            URL url = new URL(link);
            return url.openConnection().getInputStream();
        } catch (IOException e) {
            Log.w(Constants.DATA, "Exception while retrieving the input stream", e);
            return null;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.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();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    public static Context getAppContext(){
        return MainActivity.mContext;
    }


    public void ExitState(){
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("You're about to quit Signals4Trading");
        builder.setIcon(R.drawable.five);
        //builder.setMessage("Your device has been registered successfully. You'll receive signals very soon.");
        builder.setMessage("Are you sure you want to quit?");
        builder.setCancelable(false);//can't click on the background of the activity
        builder.setPositiveButton("Yes",new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO Auto-generated method stub

                Toast.makeText(getApplicationContext(),"See you soon", Toast.LENGTH_LONG).show();



                finish();


            }//OnClickListener PositiveButton
        });//anonymous class PositiveButton
        builder.setNegativeButton("No", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO Auto-generated method stub

                Toast.makeText(getApplicationContext(),"Enjoy your visit", Toast.LENGTH_LONG).show();
            }

        });
        AlertDialog alertDialog  = builder.create();
        alertDialog.show();
    }//ExitState

    public void goToHistoryActivity(){
        Intent intent = new Intent(MainActivity.this, HistoryAct.class );
        startActivity(intent);
    }

    public void rateApp(){
        Intent intent = new Intent(MainActivity.this, Rate.class);
        startActivity(intent);
    }



    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch (v.getId()){
        case R.id.ExitButton:
            ExitState();
            break;          

        case R.id.HistoryButton:
            goToHistoryActivity();
            break;

        case R.id.RateButton:
            rateApp();
            break;

        }
    }

    @Override
    protected void onStart() {
        // TODO Auto-generated method stub
        super.onStart();

        //Get an Analytics tracker to report app starts & uncaught exceptions etc.
        GoogleAnalytics.getInstance(this).reportActivityStart(this);
    }

    @Override
    protected void onStop() {
        // TODO Auto-generated method stub
        super.onStop();

        //Stop the analytics tracking
        GoogleAnalytics.getInstance(this).reportActivityStop(this);
    }

}//MainActivity


package com.Signals4Trading.push.android;

import java.util.List;





import android.content.Context;
import android.content.Intent;
import android.os.StrictMode;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class HistoryAdapt extends BaseAdapter {


    private final List<HistoryItems>items;
    private final Context context;

    public HistoryAdapt(Context context,List<HistoryItems>items){
        this.context=context;
        this.items=items;

    }//constructor

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return items.size();
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return items.get(position);
    }

    @Override
    public long getItemId(int id) {
        // TODO Auto-generated method stub
        return id;
    }


 //###FUNCTION THAT RETURN LAST ELEMENT


     public HistoryItems getLastElement(){
        return items.get(items.size()-1); 
    }




    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        ViewHolder holder;
        if (convertView == null) {
            convertView = View.inflate(context, R.layout.historyitems, null);
            holder = new ViewHolder();
            holder.itemTitle = (TextView) convertView.findViewById(R.id.itemTitleTV);
            holder.itemDate=(TextView) convertView.findViewById(R.id.itemDateTV);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }


         holder.itemTitle.setText(items.get(position).getTitle());
        holder.itemDate.setText(items.get(position).getDate());

        }

        return convertView;
    }

    class ViewHolder {
        TextView itemTitle;
        TextView itemDate;
    }


}
//HistoryAdapt

11-13 15:18:34.702: E/AndroidRuntime(7737): FATAL EXCEPTION: main
11-13 15:18:34.702: E/AndroidRuntime(7737): Process: com.Signals4Trading.push.android, PID: 7737
11-13 15:18:34.702: E/AndroidRuntime(7737): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.Signals4Trading.push.android/com.Signals4Trading.push.android.MainActivity}: 
java.lang.ArrayIndexOutOfBoundsException: length=0; index=-1
11-13 15:18:34.702: E/AndroidRuntime(7737):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2184)

【问题讨论】:

  • HistoryAdapt.java:51 有什么内容?也许items 没有定义。
  • 不太确定,但您可能需要Loader
  • 我编辑我的代码并在返回之前验证该项目不为空。它仍然在同一个问题上崩溃:nullpointerexception

标签: java android multithreading listview nullpointerexception


【解决方案1】:
Thread run = new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub

            }

    run =  new Thread( new Runnable(){
                public void run(){

            if(!HistoryAdapt.hasBeenInitiated){

                try {
                    ///Need to wait until the data to be downloaded inside HistoryAdapt so it can show the last element from the ListView here

                        HistoryParser parser = new HistoryParser();
                        parser.parse(getInputStream(HistoryAct.RSS_LINK));
                } catch (XmlPullParserException e) {
                    Log.w(e.getMessage(), e);
                } catch (IOException e) {
                    Log.w(e.getMessage(), e);
                }finally{

                    //notify that the data finished to download 
                }

            }
                }
            });

        run.start();
        run.join();

使用这个,但它会等到你的运行线程完成。

【讨论】:

  • OP 知道这一点。他在问如何做到这一点。
  • 这完全没有意义。启动一个线程,然后等待它。你也可以放弃线程。活动无响应的可能来源...
  • 你对使用观察者对象有什么看法?
  • 是的,它是最好的解决方案,但是如果代码很大,它会在您的代码中进行架构更改,我会为您提供答案,等待一段时间,直到您可以使用上面的代码。谢谢
【解决方案2】:

您在Activity 中的onCreate() 呼叫正在呼叫:

m_myLastItem= HistoryAdapt.getLastElement();

但是,HistoryAdapt 的构造函数还没有被调用,这就是你在其中初始化静态字段items 的地方。我建议您在以实例方式使用它时重新考虑该类及其用法,但将其部分编码,就好像它是一个静态类一样。一个好的经验法则是不要让你的静态方法依赖于正在构造的类。那时它只是一个实例方法。

【讨论】:

  • 好的,请查看我在 mainActivity 中编辑的代码。我触发了“!HasBeenInitiate”。由于与以前相同的问题在运行时仍然崩溃
【解决方案3】:

看起来您正在尝试使用可运行对象执行异步操作,然后在异步操作完成之前您尝试使用它应该设置的值。因此,我假设您正在尝试创建一个后台线程,该线程等待数据并在加载完成时更新/通知 UI/UI 线程。这意味着异步操作是在 OnCreate 期间由 UI 线程创建和启动的,并且在运行时 UI 线程可以愉快地继续执行 OnCreate 方法并加载 UI 等。

首先,看起来您正在创建一个可运行但没有运行它。如果你想在不同的(异步)线程中运行 runnable,试试这个:

new Thread(
    new Runnable()
    {
        @Override
        public void Run()
        {
            //your code here
        }
    }
).Run();

*注意:从 C# 翻译而来,可能不是 100% 正确(大写?),但应该很容易上手。

从您的代码中并不完全清楚,但我假设您的可运行文件中的代码用数据填充 HistoryAdapt 类。注意不要以这种方式产生并发问题。如果两个线程试图同时访问同一个对象,您可能会遇到问题,因此请确保在单独的线程中使用它们之前正确“锁定”对象。在 java 中,您可以使用“synch”子句来执行此操作:

synchronized(this){
    this = newvalue;   
}

这确保了当大括号内的代码正在执行时,“this”对象不能被其他线程访问。然后其他线程会等到代码执行完毕并释放对象后再继续。

其次,您应该知道,您创建的可运行/线程基本上是一个运行一次然后释放的线程(因此不是专用线程)。在这种情况下,监视器/等待并不是真正必要的。您可以简单地将更新 UI 的代码 (mSignal.setText(m_myLastItem.getTitle());) 包含在 try/catch 语句的 finally 子句中。当您尝试从单独的线程更新/编辑 UI 元素时,请务必使用 runOnUiThread 方法,如下所示:

MainActivity.this.runOnUiThread(new Runnable() {
    public void run() {
        m_myLastItem = HistoryAdapt.getLastElement();
        //set last signal into TextView
        mSignal.setText(m_myLastItem.getTitle());
    }
});

这样,线程是在 OnCreate 中创建的,它的工作与 UI 线程分开,然后在完成后立即更新 UI。您可能遇到的一个可能问题是 mSignal.setText() 可能会在 UI 尚未准备好更新时被调用,但不确定在这种情况下这是否真的是一个问题。此外,在其他地方使用此技术时,您可能需要引用您的活动以在线程内使用(您不能总是从定义线程/可运行的地方获得引用),这很容易,只是在您的应用程序中的某处对您的活动进行静态引用并引用它(我更喜欢 App_Session 类用于在应用程序范围内使用的静态变量)。

希望我正确理解了这个问题,这会有所帮助。无论如何,祝你好运!

*编辑:完整示例

new Thread(
    new Runnable()
    {
        @Override
        public void run()
        {
            if(!HistoryAdapt.hasBeenInitiated)
            {
                try
                {
                    ///Need to wait until the data to be downloaded inside HistoryAdapt so it can show the last element from the ListView here
                    HistoryParser parser = new HistoryParser();
                    parser.parse(getInputStream(HistoryAct.RSS_LINK));
                } catch (XmlPullParserException e) {
                    Log.w(e.getMessage(), e);
                } catch (IOException e) {
                    Log.w(e.getMessage(), e);
                } finally {
                    //notify that the data finished to download 
                    MainActivity.this.runOnUiThread(
                        new Runnable()
                        {
                            public void run()
                            {
                                if (!HistoryAdapt.hasBeenInitiated)
                                {
                                    m_myLastItem = HistoryAdapt.getLastElement();
                                    //set last signal into TextView
                                    mSignal.setText(m_myLastItem.getTitle());
                                }
                            }
                        }
                    );
                }
            }
        }
    }
).run();
  • 很遗憾,由于我无法运行 Java(我使用 xamarin),因此无法对此进行测试,但应该是正确的,也许不需要 @override 并且我不确定它应该是“运行”还是“运行”。不过我相信你会想出来的;)

【讨论】:

  • 使用观察者对象怎么样?
  • 我从来不需要使用观察者,但从我读到的内容来看,对于这种情况,它似乎过于复杂。您可以使用 Observer,但您必须创建一个实现 IObserveable 的类,创建它的实例,注册侦听器,并且(当事件被触发且不再需要时)取消注册侦听器并处置对象。上面的解决方案似乎更简单,而且如果你掌握了它,你就会很好地理解多线程编程的基础知识(这非常有用:))。
  • 我还是觉得,他还是用Loader比较好。
  • Loader 看起来确实很有趣,尽管它仅在 Android 3.0 之后才可用。我从未使用过它,但有兴趣看到它的实际效果。 Maye您可以提供示例或示例链接吗?不管哪种解决方案的工作量较小,上述方法确实适用于许多不同的事情(各种异步操作),并且无论哪种方式都非常方便:)。
  • 请查看我编辑的代码。我试图重现你向我学到的东西。不幸的是,它在运行时仍然崩溃
猜你喜欢
  • 2021-09-03
  • 1970-01-01
  • 2011-02-15
相关资源
最近更新 更多