繁体   English   中英

NullPointerException TextView.setText(CharSequence),尚不清楚其如何仍然为null

[英]NullPointerException TextView.setText(CharSequence), not clear how its still null

我一直在努力尝试让我的应用再次启动。 我修改了提取比特币价格数据的方式,以适应用户的偏好。 但是,由于这样做,我从TextView currentPriceDisplay元素方法中获得了NullPointerException。 currentPriceDisplay.setText(getmBitcoinPrice());

我在整个代码中都修改了值,以防止该变量实际为null,我还检查了nav_header_main.xml文件,以查看是否引用了正确的TextView。 所以在这一点上,我很沮丧,我必须去做日常工作。 如果您可以看一下我的代码,看看是否发现任何很棒的东西。

主要活动:

package com.instacoind.www.coind;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.util.Log;
import android.view.View;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import java.util.Set;

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    private Double mBitcoinPrice = 0.0;
    public Double getmBitcoinPrice(){
        return mBitcoinPrice;
    }
    public void setmBitcoinPrice(Double price){
        this.mBitcoinPrice = price;
    }
    private Double mBitcoinPriceOld = 0.0;
    public Double getmBitcoinPriceOld(){
        return mBitcoinPriceOld;
    }
    public void setmBitcoinPriceOld(Double price){
        this.mBitcoinPriceOld = price;
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(mBitcoinPrice != 0.0) {
                    Snackbar.make(view, "Last Change: " + priceChange(), Snackbar.LENGTH_LONG)
                            .setAction("Action", null).show();
                }else{
                    Snackbar.make(view, "SYNC FAILED ERROR::01", Snackbar.LENGTH_LONG)
                            .setAction("Action", null).show();
                }
            }
        });

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @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();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            Intent start_settings = new Intent(this, SettingsActivity.class);
            startActivity(start_settings);
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onStart(){
        super.onStart();
        updatePrice();
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.nav_camera) {
            // Handle the camera action
        } else if (id == R.id.nav_gallery) {

        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

    public void updatePrice(){
        GetBitcoinPrice worker = new GetBitcoinPrice();
        worker.execute(getPrefFiat(this), getPrefPriceIndex(this));
        refreshPriceDisplays();
    }
    public static String getPrefFiat(Context context){
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        return prefs.getString(context.getString(R.string.pref_currency_key),
                context.getString(R.string.pref_currency_def));
    }
    public static String getPrefPriceIndex(Context context){
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        return prefs.getString(context.getString(R.string.pref_source_key),
                context.getString(R.string.pref_source_default));
    }
    public String priceChange(){
        Double past = getmBitcoinPriceOld();
        Double pres = getmBitcoinPrice();
        Double change = 0.0;
        String fin = "price_change";
        if(past != null){
            if(pres >= past){
                change = pres - past;
                fin = "+ " + Double.toString(change);
            }else if(pres <= past){
                change = past - pres;
                fin = "- " + Double.toString(change);
            }
        }else{
            setmBitcoinPriceOld(getmBitcoinPrice());
        }
        return fin;
    }
    public void refreshPriceDisplays(){
        TextView PriceDisplay = (TextView) findViewById(R.id.current_price_display);
        String error = "No Bitcoin Price";
        try {
            Log.v("RefreshPriceDisplays", "mBitcoinPrice: " + Double.toString(getmBitcoinPrice()));
            PriceDisplay.setText(Double.toString(getmBitcoinPrice()));
        }catch(NullPointerException e) {
            Log.v("RefreshPriceDisplays", "mBitcoinPrice NULL");
            PriceDisplay.setText("ERR");
        }
    }


    private class GetBitcoinPrice extends AsyncTask<String, Void, Double> {

        public String mPrefFiat;
        public String mPrefIndex;

        protected Double doInBackground(String... params) {

            // These two need to be declared outside the try/catch
            // so that they can be closed in the finally block.
            HttpURLConnection urlConnection = null;
            BufferedReader reader = null;

            // Will contain the raw JSON response as a string.
            String bitcoinJsonStr = null;
            this.mPrefFiat = params[0];
            this.mPrefIndex = params[1];



            try {
                // Construct the URL for the OpenWeatherMap query
                // Possible parameters are avaiable at OWM's forecast API page, at
                // http://openweathermap.org/API#forecast
                URL url = new URL(buildUrl(this.mPrefFiat,this.mPrefIndex));

                // Create the request to Index Source, and open the connection
                urlConnection = (HttpURLConnection) url.openConnection();
                urlConnection.setRequestMethod("GET");
                urlConnection.connect();

                // Read the input stream into a String
                InputStream inputStream = urlConnection.getInputStream();
                StringBuffer buffer = new StringBuffer();
                if (inputStream == null) {
                    // Nothing to do.
                    bitcoinJsonStr = null;
                }
                reader = new BufferedReader(new InputStreamReader(inputStream));

                String line;
                while ((line = reader.readLine()) != null) {
                    // Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
                    // But it does make debugging a *lot* easier if you print out the completed
                    // buffer for debugging.
                    buffer.append(line + "\n");
                }

                if (buffer.length() == 0) {
                    // Stream was empty.  No point in parsing.
                    bitcoinJsonStr = null;
                }
                bitcoinJsonStr = buffer.toString();
            } catch (IOException e) {
                Log.e("GetBitcoinPrice", "Error ", e);
                // If the code didn't successfully get the price index data, there's no point in attemping
                // to parse it.
                bitcoinJsonStr = null;
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (final IOException e) {
                        Log.e("GetBitcoinPrice", "Error closing stream", e);
                    }
                }
            }
            if(this.mPrefIndex == "https://api.coindesk.com/v1/bpi/currentprice.json")
                return parseCoindesk(bitcoinJsonStr);
            else if (this.mPrefIndex == "https://api.coinbase.com/v2/prices/spot?currency=")
                return parseCoinbase(bitcoinJsonStr);
            else if (this.mPrefIndex == "https://www.okcoin.com/api/v1/ticker.do?symbol=")
                return parseOkcoin(bitcoinJsonStr);
            else if (this.mPrefIndex == "https://cex.io/api/ticker/BTC/")
                return parseCex(bitcoinJsonStr);
            else
                return 0.0;
        }
        private Double parseCex(String bitcoinJsonStr){
            Double price = 0.0;

            try {
                JSONObject obj = new JSONObject(bitcoinJsonStr);
                price = obj.getDouble("bid");
                Log.v("ConvertPriceStr", "Current Price is " + Double.toString(price));
                if (price == null){
                    Log.e("CEXParsing", "Parsed to Null");
                }

            }catch (JSONException x){
                Log.e("ConvertPriceStr", "Error Parsing bitcoinJsonStr");
            }
            return price;
        }
        private Double parseCoindesk(String bitcoinJsonStr){
            Double price = 0.0;

            try {
                JSONObject obj = new JSONObject(bitcoinJsonStr);
                JSONObject bpi = obj.getJSONObject("bpi");
                JSONObject usd = bpi.getJSONObject("USD");
                price = usd.getDouble("rate_float");
                Log.v("ConvertPriceStr", "Current Price is " + Double.toString(price));

            }catch (JSONException x){
                Log.e("ConvertPriceStr", "Error Parsing bitcoinJsonStr");
            }
            return price;
        }
        private Double parseCoinbase(String bitcoinJsonStr){
            Double price = 0.0;
            try{
                JSONObject obj = new JSONObject(bitcoinJsonStr);
                JSONObject bpi = obj.getJSONObject("data");
                price = bpi.getDouble("amount");
                Log.v("ParseCoinbase", "Current Price is " + Double.toString(price));
            }catch(JSONException e){
                Log.e("ParseCoinbase", "Error Parsing bitcoinJsonStr");
            }
            return price;
        }
        private Double parseOkcoin(String bitcoinJsonStr){
            Double price = 0.0;
            try{
                JSONObject obj = new JSONObject(bitcoinJsonStr);
                JSONObject bpi = obj.getJSONObject("ticker");
                price = bpi.getDouble("buy");
                Log.v("ParseOkCoin", "Current Price is " + Double.toString(price));
            }catch(JSONException e){
                Log.e("ParseOkCoin", "Error Parsing bitcoinJsonStr");
            }
            return price;
        }
        private String buildUrl(String prefFiat, String prefIndex){
            String url = null;
            try {
                if (prefIndex == "https://api.coinbase.com/v2/prices/spot?currency=") {
                    url = prefIndex + prefFiat;
                } else if (prefIndex == "https://api.coindesk.com/v1/bpi/currentprice.json") {
                    url = prefIndex + prefFiat;
                } else if (prefIndex == "https://www.okcoin.com/api/v1/ticker.do?symbol=") {
                    String modFiat;
                    modFiat = "btc_" + prefFiat;
                    url = prefIndex + modFiat;
                } else if (prefIndex == "https://cex.io/api/ticker/BTC/") {
                    String modFiat;
                    modFiat = prefIndex + prefFiat;
                    url = modFiat;
                } else {
                    url = "https://api.coindesk.com/v1/bpi/currentprice.json";
                }
            }catch(NullPointerException e){
                Log.e("URL Builder", "Error Constructing Proper URL", e);
            }
            return url;
        }

        protected void onPostExecute(Double result){
            setmBitcoinPriceOld(getmBitcoinPrice());
            setmBitcoinPrice(result);
        }
    }
}

XML档案:

activity_main.xml:

    <include
        layout="@layout/app_bar_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" />

</android.support.v4.widget.DrawerLayout>

app_bar_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.instacoind.www.coind.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@drawable/refresh_60_60" />

</android.support.design.widget.CoordinatorLayout>

content_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.instacoind.www.coind.MainActivity"
    tools:showIn="@layout/app_bar_main">

</RelativeLayout>

nav_header_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/nav_header_height"
    android:background="@drawable/side_nav_bar"
    android:gravity="bottom"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:theme="@style/ThemeOverlay.AppCompat.Dark">

    <ImageView
        android:id="@+id/coin_display"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentLeft="true"
        android:paddingTop="@dimen/nav_header_vertical_spacing"
        android:src="@drawable/bitcoin_80_80"
        android:contentDescription="@string/coin_content_description"/>

    <TextView
        android:id="@+id/bpi_source_display"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/nav_header_vertical_spacing"
        android:text="BPI_Source"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentBottom="true"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1" />

    <TextView
        android:id="@+id/def_currency_display"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="5dp"
        android:layout_toRightOf="@+id/bpi_source_display"
        android:layout_toEndOf="@+id/bpi_source_display"
        android:layout_alignParentBottom="true"
        android:text="DEF_Currency" />
    <TextView
        android:id="@+id/current_price_display"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingTop="40dp"
        android:paddingStart="80dp"
        android:paddingLeft="80dp"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_toRightOf="@+id/coin_display"
        android:textAppearance="@style/TextAppearance.AppCompat.Headline"/>

</RelativeLayout>

我认为问题来自以下四种方法:parseCoindesk,parseCoinbase,parseCex,parseOkcoin

在每一个中(例如parseCex):

            if (price == null){
                Log.e("CEXParsing", "Parsed to Null");// You just print a Log!
            }

        }catch (JSONException x){
            Log.e("ConvertPriceStr", "Error Parsing bitcoinJsonStr");
        }
        return price;//when price is null it can still return!
    }

因此,您应该尝试像这样返回:

return price == null ? 0.0 : price;

好吧,比我想的要更长的时间,直到我可以真正坐下来再进行研究。 抱歉,我已经解决了崩溃的问题。

发生这种情况是因为我在onCreate / AsyncTask完成之前调用了refreshPriceDisplays()方法。 一旦将refreshPriceDisplays()移到onPostExecute中,一切就变得非常好。

关于这个问题的第一个答案,您对我的逻辑提出了一些意见,并且这些问题也已得到解决,也非常感谢您的投入!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM