操作栏上不确定的进度条上的空指针异常

Posted

技术标签:

【中文标题】操作栏上不确定的进度条上的空指针异常【英文标题】:Null pointer exception on indeterminate progressbar on actionbar 【发布时间】:2015-09-22 06:02:44 【问题描述】:

所以我开始创建一个使用 jsoup 来废弃股票数据的应用程序。当我通过导航抽屉从家庭活动移动到“股票”活动时,我需要生成的活动显示第一个片段(viewpager 中的 recyclerview),它会抓取并列出所有股票。但是,这总是会导致以下错误。但是,如果将该片段作为第三个片段并如下图所示打开(编辑:尚无法发布图片),则可以正常工作。

9200-9200/com.blueinklabs.investmentportfoliopakistan E/androidRuntime:致命异常:主进程: com.blueinklabs.investmentportfoliopakistan, PID: 9200 java.lang.NullPointerException:尝试调用接口方法 'android.view.MenuItem android.view.MenuItem.setVisible(boolean)' 空对象引用 com.blueinklabs.investmentportfoliopakistan.StocksActivity.showProgressBar(StocksActivity.java:68) 在 com.blueinklabs.investmentportfoliopakistan.StockData$Title.onPreExecute(StockData.java:37) 在 android.os.AsyncTask.executeOnExecutor(AsyncTask.java:591) 在 android.os.AsyncTask.execute(AsyncTask.java:539) 在 com.blueinklabs.investmentportfoliopakistan.StockData.(StockData.java:28) 在 com.blueinklabs.investmentportfoliopakistan.StocksCompleteListFragment.getSymbols(StocksCompleteListFragment.java:63) 在 com.blueinklabs.investmentportfoliopakistan.StocksCompleteListFragment.setupRecyclerView(StocksCompleteListFragment.java:55) 在 com.blueinklabs.investmentportfoliopakistan.StocksCompleteListFragment.onCreateView(StocksCompleteListFragment.java:48) 在 android.support.v4.app.Fragment.performCreateView(Fragment.java:1789) 在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:955) 在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1138) 在 android.support.v4.app.BackStackRecord.run(BackStackRecord.java:740) 在 android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1501) 在 android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:490) 在 android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:141) 在 android.support.v4.view.ViewPager.populate(ViewPager.java:1105) 在 android.support.v4.view.ViewPager.populate(ViewPager.java:951) 在 android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1473) 在 android.view.View.measure(View.java:17547) 在 android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535) 在 android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:573) 在 android.support.design.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:640) 在 android.view.View.measure(View.java:17547) 在 android.support.v4.widget.DrawerLayout.onMeasure(DrawerLayout.java:868) 在 android.view.View.measure(View.java:17547) 在 android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535) 在 android.widget.FrameLayout.onMeasure(FrameLayout.java:436) 在 android.support.v7.internal.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:124) 在 android.view.View.measure(View.java:17547) 在 android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535) 在 android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1436) 在 android.widget.LinearLayout.measureVertical(LinearLayout.java:722) 在 android.widget.LinearLayout.onMeasure(LinearLayout.java:613) 在 android.view.View.measure(View.java:17547) 在 android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535) 在 android.widget.FrameLayout.onMeasure(FrameLayout.java:436) 在 android.view.View.measure(View.java:17547) 在 android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535) 在 android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1436) 在 android.widget.LinearLayout.measureVertical(LinearLayout.java:722) 在 android.widget.LinearLayout.onMeasure(LinearLayout.java:613) 在 android.view.View.measure(View.java:17547) 在 android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5535) 在 android.widget.FrameLayout.onMeasure(FrameLayout.java:436) 在 com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2615) 在 android.view.View.measure(View.java:17547) 在 android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2015) 在 android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1173) 在 android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1379) 在 android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1061) 在 android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:588

我认为问题在于异步任务(如下)发生在 onPrepareOptionsMenu 加载之前。以下类中的 PreExecute 发生错误:

import android.content.Context;
import android.os.AsyncTask;
import android.support.v7.widget.RecyclerView;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Iterator;

public class StockData 
    private ArrayList<String> symbolList;
    Context myContext;
    RecyclerView recyclerV;

    public StockData(Context getcontext, RecyclerView recyclerView)
        myContext = getcontext;
        symbolList = new ArrayList<String>();
        recyclerV = recyclerView;
        new Title().execute();
    

    private class Title extends AsyncTask<Void, Void, Void> 
        String title;

        @Override
        protected void onPreExecute() 
            super.onPreExecute();
            ((StocksActivity) myContext).showProgressBar();
        

        @Override
        protected Void doInBackground(Void... params) 
            Document doc = null;
            try 
                doc = Jsoup.connect("websiteurl").get();
             catch (MalformedURLException e) 
                e.printStackTrace();
             catch (IOException e) 
                e.printStackTrace();
            
            Element table = doc.select("table.mGrid").get(1);
            Elements rows = table.select("tr");
            Iterator<Element> rowIterator = rows.iterator();
            rowIterator.next();

            while (rowIterator.hasNext()) 
                Element row = rowIterator.next();
                Elements cols = row.select("td");
                symbolList.add(cols.get(1).text());
            
            return null;
        

        @Override
        protected void onPostExecute(Void result) 
            ((StocksCompleteListFragment.SimpleStringRecyclerViewAdapter) recyclerV.getAdapter()).changeList(symbolList);
            ((StocksActivity) myContext).hideProgressBar();
        
    

    public ArrayList<String> getSymbolList()
        return symbolList;
    



StocksActivity.java 如下:

import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

/**
 * TODO
 */
public class StocksActivity extends AppCompatActivity 

    private DrawerLayout mDrawerLayout;
    public MenuItem miActionProgressItem;

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) 
        // Store instance of the menu item containing progress
        miActionProgressItem = menu.findItem(R.id.mi_ActionProgress);
        // Extract the action-view from the menu item
        ProgressBar v = (ProgressBar) MenuItemCompat.getActionView(miActionProgressItem);
        // Return to finish
        Toast.makeText(this, "Hi", Toast.LENGTH_SHORT).show();
        return super.onPrepareOptionsMenu(menu);
    

    public void showProgressBar() 
        // Show progress item
        miActionProgressItem.setVisible(true);
    

    public void hideProgressBar() 
        // Hide progress item
        miActionProgressItem.setVisible(false);
    

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        final ActionBar ab = getSupportActionBar();
        ab.setHomeAsUpIndicator(R.drawable.ic_menu);
        ab.setDisplayHomeAsUpEnabled(true);

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);

        final NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        if (navigationView != null) 
            setupDrawerContent(navigationView);
            final Menu navMenu = navigationView.getMenu();
            ((MenuItem) navMenu.findItem(R.id.menuItem1)).setChecked(true);
            final ArrayList<View> mMenuItems = new ArrayList<>(navigationView.getChildCount());
            //for (int i = 0; i < navigationView.getChildCount(); i++)
            navigationView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() 
                @Override
                public void onGlobalLayout() 
                    // Remember to remove the installed OnGlobalLayoutListener
                    navigationView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    // Loop through and find each MenuItem View
                    for (int i = 0; i < 6; i++) 
                        final String id = "menuItem" + (i);
                        final MenuItem item = navMenu.findItem(getResources().getIdentifier(id, "id", getPackageName()));
                        navigationView.findViewsWithText(mMenuItems, item.getTitle(), View.FIND_VIEWS_WITH_TEXT);
                    
                    // Loop through each MenuItem View and apply your custom Typeface
                    for (final View menuItem : mMenuItems) 
                        ((TextView) menuItem).setTypeface(null, Typeface.BOLD);
                        ((TextView) menuItem).setTextSize(14);
                    
                
            );
            //

        

        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
        if (viewPager != null) 
            setupViewPager(viewPager);
        

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                Snackbar.make(view, "Here's a Snackbar", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            
        );

        TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(viewPager);
    

    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
        getMenuInflater().inflate(R.menu.sample_actions, menu);
        miActionProgressItem = menu.findItem(R.id.mi_ActionProgress);
        // Extract the action-view from the menu item
        ProgressBar v = (ProgressBar) MenuItemCompat.getActionView(miActionProgressItem);
        return true;
    

    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
        switch (item.getItemId()) 
            case android.R.id.home:
                mDrawerLayout.openDrawer(GravityCompat.START);
                return true;
        
        return super.onOptionsItemSelected(item);
    

    private void setupViewPager(ViewPager viewPager) 
        Adapter adapter = new Adapter(getSupportFragmentManager());
        adapter.addFragment(new StocksCompleteListFragment(), "Category 1");
        adapter.addFragment(new CheeseListFragment(), "Category 2");
        adapter.addFragment(new CheeseListFragment(), "All Stocks");
        viewPager.setAdapter(adapter);
    

    private void setupDrawerContent(NavigationView navigationView) 
        navigationView.setNavigationItemSelectedListener(
                new NavigationView.OnNavigationItemSelectedListener() 
                    @Override
                    public boolean onNavigationItemSelected(MenuItem menuItem) 
                        mDrawerLayout.closeDrawers();
                        switch (menuItem.getItemId()) 
                            case R.id.menuItem0:
                                Intent mainIntent = new Intent(getApplicationContext(), MainActivity.class);
                                startActivity(mainIntent);
                                finish();
                                break;
                            case R.id.menuItem1:
                                Intent stocksIntent = new Intent(getApplicationContext(), StocksActivity.class);
                                startActivity(stocksIntent);
                                finish();
                                break;
                        
                        return true;
                    
                );
    

    @Override
    public void onBackPressed() 
        Intent mainIntent = new Intent(getApplicationContext(), MainActivity.class);
        startActivity(mainIntent);
        finish();
    

    static class Adapter extends FragmentPagerAdapter 
        private final List<Fragment> mFragments = new ArrayList<>();
        private final List<String> mFragmentTitles = new ArrayList<>();

        public Adapter(FragmentManager fm) 
            super(fm);
        

        public void addFragment(Fragment fragment, String title) 
            mFragments.add(fragment);
            mFragmentTitles.add(title);
        

        @Override
        public Fragment getItem(int position) 
            return mFragments.get(position);
        

        @Override
        public int getCount() 
            return mFragments.size();
        

        @Override
        public CharSequence getPageTitle(int position) 
            return mFragmentTitles.get(position);
        
    

过去 8 小时我一直在尝试解决这个问题,这是我第一次在这个论坛上发帖,如果我发错了,请原谅。我已经搜索并尝试了一些我认为相关的解决方案。

http://i.stack.imgur.com/Wxxr6.png

编辑:这是片段 StocksCompleteListFragment.java 的类

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;

import java.util.ArrayList;
import java.util.List;

public class StocksCompleteListFragment extends Fragment 

    ArrayList<String> list;
    RecyclerView rv;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
        rv = (RecyclerView) inflater.inflate(
                R.layout.fragment_stocks_completelist, container, false);
        setupRecyclerView(rv);
        return rv;
    

    private void setupRecyclerView(RecyclerView recyclerView) 
        recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext()));
        recyclerView.setAdapter(new SimpleStringRecyclerViewAdapter(getActivity(),
                getSymbols(recyclerView)));

    


    private ArrayList<String> getSymbols(RecyclerView recyclerView) 

        list = new ArrayList<>();
        StockData stockDataClass = new StockData(getActivity(), recyclerView);
        list = stockDataClass.getSymbolList();
        return list;
    

    public static class SimpleStringRecyclerViewAdapter
            extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> 

        private final TypedValue mTypedValue = new TypedValue();
        private int mBackground;
        private List<String> mValues;

        public static class ViewHolder extends RecyclerView.ViewHolder 
            public String mBoundString;

            public final View mView;
            public final ImageView mImageView;
            public final TextView mTextView;

            public ViewHolder(View view) 
                super(view);
                mView = view;
                mImageView = (ImageView) view.findViewById(R.id.avatar);
                mTextView = (TextView) view.findViewById(android.R.id.text1);
            

            @Override
            public String toString() 
                return super.toString() + " '" + mTextView.getText();
            
        

        public void changeList (ArrayList<String> tempStringList) 
            mValues = tempStringList;
            notifyDataSetChanged();
        

        public String getValueAt(int position) 
            return mValues.get(position);
        

        public SimpleStringRecyclerViewAdapter(Context context, List<String> items) 
            context.getTheme().resolveAttribute(R.attr.selectableItemBackground, mTypedValue, true);
            mBackground = mTypedValue.resourceId;
            mValues = items;
        

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.completestocks_listitem, parent, false);
            view.setBackgroundResource(mBackground);
            return new ViewHolder(view);
        

        @Override
        public void onBindViewHolder(final ViewHolder holder, int position) 
            holder.mBoundString = mValues.get(position);
            holder.mTextView.setText(mValues.get(position));

            holder.mView.setOnClickListener(new View.OnClickListener() 
                @Override
                public void onClick(View v) 
                    Context context = v.getContext();
                    Intent intent = new Intent(context, CheeseDetailActivity.class);
                    intent.putExtra(CheeseDetailActivity.EXTRA_NAME, holder.mBoundString);

                    context.startActivity(intent);
                
            );

            Glide.with(holder.mImageView.getContext())
                    .load(Cheeses.getRandomCheeseDrawable())
                    .fitCenter()
                    .into(holder.mImageView);
        

        @Override
        public int getItemCount() 
            return mValues.size();
        
    

【问题讨论】:

尝试将您的 showProgressBarhideProgressBar 更改为您的 AsyncTask 并将对菜单的引用作为参数传递。您收到该错误是因为当您调用这些方法时,不存在对 MenuItem 的引用。 日志说你的上下文“myContext”为空。你能展示一下使用 StockData 的代码吗? Eric,根据您的建议,我使用片段类中的 getActivity() 方法从 StocksActivity.java 访问 MenuItem。然后将它传递给异步类(StockData),并将方法转移到这个类。仍然显示空指针。严重卡住了! 约翰,我在后期编辑中插入了片段类。如果您能提供帮助会很高兴! 据我所知,菜单项在 onPrepareOptionsMenu(Menu menu) 方法中的 StocksActivity 实例化之前被调用。但我也需要立即启动 asynctask,所以我不知道还能做什么。 【参考方案1】:

在评论中解释它可能是什么有点困难,所以我会在这个答案中帮助你调试。

所以,StockActivity 加载片段,StocksCompleteListFragment 执行AsyncTask(最后一个访问StockActivity 的菜单项)。 AsyncTaskStockData 的内部类)应该收到对 MenuItem 的引用。

我会尝试,就像我之前告诉你的那样,将访问MenuItem 的两种方法移到AsyncTask

private class Title extends AsyncTask<Void, Void, Void> 
    // ...

    @Override
    protected void onPreExecute() 
        super.onPreExecute();
        showProgressBar();
    

    @Override
    protected void onPostExecute(Void result) 
        ((StocksCompleteListFragment.SimpleStringRecyclerViewAdapter) recyclerV.getAdapter()).changeList(symbolList);
        hideProgressBar();
    


    // I don't need them to be public if I move them here
    //
    private void showProgressBar() 
    // Show progress item
        item.setVisible(true);
    

    private void hideProgressBar() 
        // Hide progress item
        item.setVisible(false);
    

之后,您应该将MenuItem 传递给StocksCompleteListFragment 所以:

添加一个接收它的构造函数

public class StocksCompleteListFragment extends Fragment 
    private MenuItem item;

    // Default constructor
    public StocksCompleteListFragment() 

    public StocksCompleteListFragment(MenuItem item) 
        this.item = item;
    

    // ...  

然后将其传递给 Fragment 类

// StocksActivity.java

adapter.addFragment(new StocksCompleteListFragment(miActionProgressItem ), "Category 1");

稍后将项目从 StocksCompleteListFragment 传递给 StockData 类,以便内部 AsyncTask 可以访问它

public class StockData 
    private ArrayList<String> symbolList;
    Context myContext;
    RecyclerView recyclerV;
    private MenuItem item;

    public StockData(Context getcontext, RecyclerView recyclerView)
        myContext = getcontext;
        symbolList = new ArrayList<String>();
        recyclerV = recyclerView;
        new Title().execute();
    

    public StockData(Context getcontext, RecyclerView recyclerView, MenuItem item) 
        this(getcontext, recyclerView);
        this.item = item;
    

最后编辑您的代码段,调用AsyncTask 以传递MenuItem

private ArrayList<String> getSymbols(RecyclerView recyclerView) 

    list = new ArrayList<>();
    StockData stockDataClass = new StockData(getActivity(), recyclerView, item);
    list = stockDataClass.getSymbolList();
    return list;

如果我在正确的轨道上,当您调用 AsyncTask 时,MenuItem 应该已经存在。 我是这样看的:

StockActivity(完全加载,操作栏和一切)-> 传递菜单项-> StocksCompleteListFragment(调用AsyncTask)-> 传递菜单项-> AsyncTask 隐藏/显示菜单项。

这就是我的看法,一定有更简单的选择,但我想不出更简单的方法。

试试这个,我宁愿这是一个评论而不是一个答案,但我不能写这么多。

继续努力,明天我会回来跟踪这个帖子。希望对你有一点帮助。

【讨论】:

感谢您的帮助,但这仍然不起作用:(我在创建片段之前放置了两个日志,以了解菜单在那个阶段是否为空,并且它是。所以很可能一个进度条在设置之前询问菜单参考的情况。解释为什么它作为第三个片段工作。if (miActionProgressItem == null) Log.w("myApp", "MenuItem is Null"); else Log .w("myApp", "MenuItem is Filled"); adapter.addFragment(new StocksCompleteListFragment(miActionProgressItem), "Category 1"); 我已经研究了将进度条放在片段本身顶部的替代实现,因此如果无法解决,它应该可以工作 好吧,我找不到其他方法,我真的很抱歉。如果您发现了另一种方式,如果您分享它并将其标记为正确答案应该会很好。 埃里克,非常感谢。尽管尝试了很多,但我没有找到任何问题的答案。所以我从操作栏中的进度条转移到我的片段中的进度条。

以上是关于操作栏上不确定的进度条上的空指针异常的主要内容,如果未能解决你的问题,请参考以下文章

getLatitude 上的空指针异常

OnGridImageSelectedListener.onGridImageSelected 上的空指针异常

获取地图上的空指针异常

片段中的 EditText 上的空指针异常 [重复]

自定义页面适配器上的空指针异常

带有 ListView 的导航抽屉。 ListView 上的空指针异常