0

I am trying to resolve the connection between Context and Classes in this code and my understanding of this concept. The MainActivity.this does not work here. It came with the surrounding code.

package com.Table;

import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.HorizontalScrollView;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;

import com.example.tablefreezepane.DatabaseHandler;
import com.example.tablefreezepane.MainActivity;

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

public class AsyncInsertData extends AsyncTask<String, String, String> {

    DatabaseHandler databaseHandler;
    String type;
    long timeElapsed;

    protected AsyncInsertData(String type){
        this.type  = type;
        this.databaseHandler = new DatabaseHandler(MainActivity.this);
    }

    // @type - can be 'normal' or 'fast'
    //@Override
    //protected void onPreExecute() {
    //    super.onPreExecute();
    //    tvStatus.setText("Inserting " + editTextRecordNum.getText() + " records...");
    //}

    @Override
    protected String doInBackground(String... aurl) {

        try {

            // get number of records to be inserted
            int insertCount = 20;

            // empty the table
            databaseHandler.deleteRecords();

            // keep track of execution time
            long lStartTime = System.nanoTime();

            if (type.equals("normal")) {
                databaseHandler.insertNormal(insertCount);
            } else {
                databaseHandler.insertFast(insertCount);
            }

            // execution finised
            long lEndTime = System.nanoTime();

            // display execution time
            timeElapsed = lEndTime - lStartTime;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    protected void onPostExecute(String unused) {
        Toast.makeText(getApplicationContext(),"This is an Android Toast Message", Toast.LENGTH_LONG).show();
        //tvStatus.setText("Done " + choice + " inserting " + databaseHandler.countRecords() + " records into table: [" + this.databaseHandler.tableName + "]. Time elapsed: " + timeElapsed / 1000000 + " ms.");
    }

   }

Reading or copying code is not a problem. The problem exists in the overall thought model of the Context parameter in the this.databaseHandler = new DatabaseHandler(MainActivity.this); statement. The line code has to be there in order to call the methods in the databaseHandler class.

package com.example.tablefreezepane;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;
import android.widget.TextView;


public class DatabaseHandler extends SQLiteOpenHelper {

    // for our logs
    public static final String TAG = "DatabaseHandler.java";

    public TextView tvstatus;

    // database version
    private static final int DATABASE_VERSION = 7;

    // database name
    protected static final String DATABASE_NAME = "NinjaDatabase2";

    // table details
    public String tableName = "locations";
    public String fieldObjectId = "id";
    public String fieldObjectName = "name";
    public String fieldObjectDescription = "description";

    // constructor
    public DatabaseHandler(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    // creating table
    @Override
    public void onCreate(SQLiteDatabase db) {

        String sql = "";
        this.tvstatus.setText("Creating table");

        sql += "CREATE TABLE " + tableName;
        sql += " ( ";
        sql += fieldObjectId + " INTEGER PRIMARY KEY AUTOINCREMENT, ";
        sql += fieldObjectName + " TEXT, ";
        sql += fieldObjectDescription + " TEXT ";
        sql += " ) ";

        db.execSQL(sql);
        this.tvstatus.setText("Table created...");

        // create the index for our INSERT OR REPLACE INTO statement.
        // this acts as the WHERE name="name input" AND description="description input"
        // if that WHERE clause is true, I mean, it finds the same name and description in the database,
        // it will be REPLACEd. 
        // ELSE, what's in the database will remain and the input will be INSERTed (new record)
        String INDEX = "CREATE UNIQUE INDEX locations_index ON " 
                        + tableName + " (name, description)";

        db.execSQL(INDEX);
    }

    /*
     * When upgrading the database, it will drop the current table and recreate.
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        String sql = "DROP TABLE IF EXISTS " + tableName;
        db.execSQL(sql);

        onCreate(db);
    }

    // insert data using transaction and prepared statement
    public void insertFast(int insertCount) {

        // you can use INSERT only
        String sql = "INSERT OR REPLACE INTO " + tableName + " ( name, description ) VALUES ( ?, ? )";

        SQLiteDatabase db = this.getWritableDatabase();

        /*
         * According to the docs http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
         * Writers should use beginTransactionNonExclusive() or beginTransactionWithListenerNonExclusive(SQLiteTransactionListener) 
         * to start a transaction. Non-exclusive mode allows database file to be in readable by other threads executing queries.
         */
        //db.beginTransactionNonExclusive();
        db.beginTransaction();

        SQLiteStatement stmt = db.compileStatement(sql);

        for(int x=1; x<=insertCount; x++){

            stmt.bindString(1, "Name # " + x);
            stmt.bindString(2, "Description # " + x);

            stmt.execute();
            stmt.clearBindings();

        }

        db.setTransactionSuccessful();
        db.endTransaction();

    }

    // inserts the record without using transaction and prepare statement
    public void insertNormal(int insertCount){
        try{

            SQLiteDatabase db = this.getWritableDatabase();

            for(int x=1; x<=insertCount; x++){

                ContentValues values = new ContentValues();
                values.put(fieldObjectName, "Name # " + x);
                values.put(fieldObjectDescription, "Description # " + x);

                db.insert(tableName, null, values);

            }

            db.close();

        }catch(Exception e){
            e.printStackTrace();
        } 
    }

    // deletes all records
    public void deleteRecords(){

        SQLiteDatabase db = this.getWritableDatabase();
        db.execSQL("delete from "+ tableName);
        db.close();
    }

    // count records
    public int countRecords(){

        SQLiteDatabase db = this.getWritableDatabase();

        Cursor cursor = db.rawQuery("SELECT count(*) from " + tableName, null);
        cursor.moveToFirst();

        int recCount = cursor.getInt(0);

        cursor.close();
        db.close();

        return recCount;
    }

}

Any guidance, links to graphic models, to how the this.databaseHandler = new DatabaseHandler(MainActivity.this); statement is constructed would be appreciated.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Paul Moore
  • 136
  • 1
  • 16

1 Answers1

2

The line:

this.databaseHandler = new DatabaseHandler(MainActivity.this);

expects a valid Context in the constructor. Activity extends ContextThemeWrapper and therefore is a valid object to pass, but the actual context within the object must be valid.

It looks like you have copied code from an Activity that had all of this code included in the MainActivity object and when MainActivity is created from the framework, the context is valid.

If you instantiate an Activity as if it were a regular class, it will be created but the Context will not be valid. The Android framework must instantiate it and during the construction of the class, will give it a valid Context. You can't create your own valid Context because Android needs to define it.

This is a good post about what Context is:

What is 'Context' on Android?

I'm not aware of a graphic that describes or explains it. It is somewhat abstract, as it pertains to a lot of information about the Android environment, including some visual aspects (like screen information) and also non-visual aspects (locations of files, security, permissions, etc.).

Community
  • 1
  • 1
Jim
  • 10,172
  • 1
  • 27
  • 36
  • Yes. What you have mentioned here is what I finally realized.. In Android studio I can then enter databashandler.method and the intellisense fills in the rest. I get crashes because something is instantiated but not initialized(hope this assessment is correct). Am able to get the logcat and put debug messages in to track where the problem is. But the results are always the same. The call to the other class methods crash the app. I need concrete in this abstract jungle. If there is any more you can offer I would appreciate it. – Paul Moore Apr 06 '15 at 22:46
  • I should add the my log.d in databasehandler.oncreate() is never in the logcat stream. The other class methods are available in Android Studio but that does not give me the correct usage of them. The creation of the database table, to my knowledge, never happens. – Paul Moore Apr 06 '15 at 22:51