Android LoaderManager访问远程MSSQL数据库表

时间:2017-11-30 03:47:13

标签: android sql-server cursor loader

在我的Android开发中遇到了一个问题困扰我好几天,无法解决它。以下是背景信息:

  1. 我在我的一个Fragements中使用LoaderManager,它应该通过访问远程MSSQL数据库表(命名为'todolist')来初始化加载器并返回一个Cursor;

  2. 我已将LoaderManager的初始化代码放在snippets public void onCreate()或public View onCreateView()中。但两个都返回一个错误“java.lang.NullPointerException:尝试在*****。****的空对象引用上调用接口方法'int android.database.Cursor.getCount()'。** 。** **** ******* **** COM data.AllToDoListFragment.onLoadFinished(AllToDoListFragment.java:208)“;。。。

  3. 显然,我的代码中的Cursor数据尚未初始化;

  4. 所以我的问题可以归结为:

    4.1。在onCreateLoader代码段中找到一种方法来访问我的MSSQL数据库表,以便它可以成功地返回一个游标或:

    4.2。有人告诉我如何在asyncTask中执行此操作。

  5. BTW,我知道如何使用AsyncTask从我的远程MSSQL数据库表中检索我的数据,它可以显示在我的RecycleView中。但是当我了解到Cursor是一个更好的解决方案的选项,所以我想知道如何做到这一点。 不幸的是,我找不到任何与互联网相关的有用提示。

    以下是AllToDoListFragment.java的代码片段

    public class AllToDoListFragment extends Fragment implements
            LoaderManager.LoaderCallbacks<Cursor>,
            NewToDoListAdapter.NewToDoListAdapterOnClickHandler{
    
    /*
     * The columns of data that we are interested in displaying within our MainActivity's list of
     * weather data.
     */
    public static final String[] MAIN_TODOLIST_PROJECTION = {
            ToDoListContract.ToDoListEntry._ID,
            ToDoListContract.ToDoListEntry.COLUMN_PROJECTNAME,
            ToDoListContract.ToDoListEntry.COLUMN_RAISEDDATE,
            ToDoListContract.ToDoListEntry.COLUMN_DEADLINE,
            ToDoListContract.ToDoListEntry.COLUMN_RESP1NAME,
            ToDoListContract.ToDoListEntry.COLUMN_ISSUE,
    };
    
    /*
     * We store the indices of the values in the array of Strings above to more quickly be able to
     * access the data from our query. If the order of the Strings above changes, these indices
     * must be adjusted to match the order of the Strings.
     */
    public static final int INDEX_TODOLIST_ID = 0;
    public static final int INDEX_TODOLIST_PROJECTNAME = 1;
    public static final int INDEX_TODOLIST_RAISEDDATE = 2;
    public static final int INDEX_TODOLIST_DEADLINE = 3;
    public static final int INDEX_TODOLIST_RESP1NAME = 4;
    public static final int INDEX_TODOLIST_ISSUE = 5;
    
    
    /*
     * This ID will be used to identify the Loader responsible for loading our ToDoList. In
     * some cases, one Activity can deal with many Loaders. However, in our case, there is only one.
     * We will still use this ID to initialize the loader and create the loader for best practice.
     * Please note that 45 was chosen arbitrarily. You can use whatever number you like, so long as
     * it is unique and consistent.
     */
    private static final int ID_TODOLIST_LOADER = 144;
    
    private NewToDoListAdapter mNewToDoListAdapter;
    private RecyclerView mRecyclerView;
    private int mPosition = RecyclerView.NO_POSITION;
    
    private ProgressBar mLoadingIndicator;
    
    public void onCreate(Bundle savedInstanceState) {
        /*
         * Ensures a loader is initialized and active. If the loader doesn't already exist, one is
         * created and (if the activity/fragment is currently started) starts the loader. Otherwise
         * the last created loader is re-used.
         */
        // TODO 4: got to check if the following Loader is working or not
        getLoaderManager().initLoader(ID_TODOLIST_LOADER, null, this);
    
        super.onCreate(savedInstanceState);
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        final View rootView = inflater.inflate(R.layout.fragment_all_to_do_list, container, false);
    
        /*
         * Using findViewById, we get a reference to our RecyclerView from xml. This allows us to
         * do things like set the adapter of the RecyclerView and toggle the visibility.
         */
        mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview_newtdl);
    
        /*
         * The ProgressBar that will indicate to the user that we are loading data. It will be
         * hidden when no data is loading.
         *
         * Please note: This so called "ProgressBar" isn't a bar by default. It is more of a
         * circle. We didn't make the rules (or the names of Views), we just follow them.
         */
        mLoadingIndicator = (ProgressBar) rootView.findViewById(R.id.pb_loading_indicator_newtdl);
    
        /*
         * A LinearLayoutManager is responsible for measuring and positioning item views within a
         * RecyclerView into a linear list. This means that it can produce either a horizontal or
         * vertical list depending on which parameter you pass in to the LinearLayoutManager
         * constructor. In our case, we want a vertical list, so we pass in the constant from the
         * LinearLayoutManager class for vertical lists, LinearLayoutManager.VERTICAL.
         *
         * There are other LayoutManagers available to display your data in uniform grids,
         * staggered grids, and more! See the developer documentation for more details.
         *
         * The third parameter (shouldReverseLayout) should be true if you want to reverse your
         * layout. Generally, this is only true with horizontal lists that need to support a
         * right-to-left layout.
         */
        LinearLayoutManager layoutManager =
                new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
    
        /* setLayoutManager associates the LayoutManager we created above with our RecyclerView */
        mRecyclerView.setLayoutManager(layoutManager);
    
        /*
         * Use this setting to improve performance if you know that changes in content do not
         * change the child layout size in the RecyclerView
         */
        mRecyclerView.setHasFixedSize(true);
    
        /*
         * The NewToDoListAdapter is responsible for linking our weather data with the Views that
         * will end up displaying our weather data.
         *
         * Although passing in "this" twice may seem strange, it is actually a sign of separation
         * of concerns, which is best programming practice. The ForecastAdapter requires an
         * Android Context (which all Activities are) as well as an onClickHandler. Since our
         * MainActivity implements the ForecastAdapter ForecastOnClickHandler interface, "this"
         * is also an instance of that type of handler.
         */
        // TODO 3: got to check if the paras below are valid or not
        mNewToDoListAdapter = new NewToDoListAdapter(getContext(), this);
    
        /* Setting the adapter attaches it to the RecyclerView in our layout. */
        mRecyclerView.setAdapter(mNewToDoListAdapter);
    
    
        showLoading();
    
    
        // TODO 5: SyncUtils might not be needed here
        //          becuase asynctask is used
        //SunshineSyncUtils.initialize(this);
    
        return rootView;
    }
    
    /**
     * Called by the {@link android.support.v4.app.LoaderManagerImpl} when a new Loader needs to be
     * created. This Fragment only uses one loader, so we don't necessarily NEED to check the
     * loaderId, but this is certainly best practice.
     *
     * @param loaderId The loader ID for which we need to create a loader
     * @param bundle   Any arguments supplied by the caller
     * @return A new Loader instance that is ready to start loading.
     */
    @Override
    public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
        switch (loaderId) {
            case ID_TODOLIST_LOADER:
                /* URI for all rows of ToDoList data in our todolist table */
                Uri todolistQueryUri = ToDoListContract.ToDoListEntry.CONTENT_URI;
                /* Sort order: Ascending by date */
                String sortOrder = ToDoListContract.ToDoListEntry.COLUMN_DEADLINE + " ASC";
                /*
                 * A SELECTION in SQL declares which rows you'd like to return. In our case, we
                 * want all todolist data that is stored in our todolist table.
                 * We created a handy method to do that in our WeatherEntry class.
                 */
                String selection = ToDoListContract.ToDoListEntry.getSqlSelectDeadlineFromLastMonth();
    
                return new CursorLoader(getActivity(),
                        todolistQueryUri,
                        MAIN_TODOLIST_PROJECTION,
                        selection,
                        null,
                        sortOrder);
    
            default:
                throw new RuntimeException("Loader Not Implemented: " + loaderId);
        }
    }
    
    /**
     * Called when a Loader has finished loading its data.
     *
     * NOTE: There is one small bug in this code. If no data is present in the cursor do to an
     * initial load being performed with no access to internet, the loading indicator will show
     * indefinitely, until data is present from the ContentProvider. This will be fixed in a
     * future version of the course.
     *
     * @param loader The Loader that has finished.
     * @param data   The data generated by the Loader.
     */
    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mNewToDoListAdapter.swapCursor(data);
        if (mPosition == RecyclerView.NO_POSITION) mPosition = 0;
        mRecyclerView.smoothScrollToPosition(mPosition);
        if (data.getCount() != 0) showNewToDoListDataView();
    }
    
    /**
     * Called when a previously created loader is being reset, and thus making its data unavailable.
     * The application should at this point remove any references it has to the Loader's data.
     *
     * @param loader The Loader that is being reset.
     */
    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        /*
         * Since this Loader's data is now invalid, we need to clear the Adapter that is
         * displaying the data.
         */
        mNewToDoListAdapter.swapCursor(null);
    }
    
    /**
     * This method is for responding to clicks from our list.
     *
     * @param id unique id number for the todolist item.
     * @see ToDoListContract.ToDoListEntry#_ID
     */
    @Override
    public void onClick(long id) {
        Intent tdlDetailIntent = new Intent(getActivity(), TDLDetailActivity.class);
        Uri uriForIDClicked = ToDoListContract.ToDoListEntry.buildToDoListUriWithID(id);
        tdlDetailIntent.setData(uriForIDClicked);
        startActivity(tdlDetailIntent);
    }
    
    /**
     * This method will make the View for the weather data visible and hide the error message and
     * loading indicator.
     * <p>
     * Since it is okay to redundantly set the visibility of a View, we don't need to check whether
     * each view is currently visible or invisible.
     */
    private void showNewToDoListDataView() {
        /* First, hide the loading indicator */
        mLoadingIndicator.setVisibility(View.INVISIBLE);
        /* Finally, make sure the weather data is visible */
        mRecyclerView.setVisibility(View.VISIBLE);
    }
    
    /**
     * This method will make the loading indicator visible and hide the todolist View and error
     * message.
     * <p>
     * Since it is okay to redundantly set the visibility of a View, we don't need to check whether
     * each view is currently visible or invisible.
     */
    private void showLoading() {
        /* Then, hide the weather data */
        mRecyclerView.setVisibility(View.INVISIBLE);
        /* Finally, show the loading indicator */
        mLoadingIndicator.setVisibility(View.VISIBLE);
    }
    }
    

2 个答案:

答案 0 :(得分:0)

我可以尝试下面的...我已经从联系人中检索了个人资料名称。

public class AllToDoListFragment extends Fragment implements  android.support.v4.app.LoaderManager.LoaderCallbacks<Cursor> {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
   // return super.onCreateView(inflater, container, savedInstanceState);
    View rootView = inflater.inflate(R.layout.fragment_1, container, false);
    getLoaderManager().initLoader(0, null, this);

    return  rootView;
}


@Override
public android.support.v4.content.Loader<Cursor> onCreateLoader(int id, Bundle args) {
    return new android.support.v4.content.CursorLoader(getActivity(),
            // Retrieve data rows for the device user's 'profile' contact.
            Uri.withAppendedPath( ContactsContract.Profile.CONTENT_URI,ContactsContract.Contacts.Data.CONTENT_DIRECTORY),
            ProfileQuery.PROJECTION,

            //Don't select anything here null will return all available fields

            null,
            null,
            null);
}

@Override
public void onLoadFinished(android.support.v4.content.Loader<Cursor> loader, Cursor cursor) {
    ArrayList<String> DataArray = new ArrayList<String>();
    String name=null;
    String photoUrl=null;

    cursor.moveToFirst();
    while (!cursor.isAfterLast()) {
        //here where you get your data and its type
        String  TypeName=cursor.getString(ProfileQuery.EMAIL);//this will give you field name
        String  Data=cursor.getString(ProfileQuery.FAMILY_NAME);//this will give you field data

        String email = cursor.getString(ProfileQuery.EMAIL);;
        String familyName = cursor.getString(ProfileQuery.FAMILY_NAME);
        String url = cursor.getString(ProfileQuery.PHOTO);


        if (TypeName !=null){
            name = TypeName;

        }
        if(url !=null){
            photoUrl =url;
        }

        cursor.moveToNext();


    }

  Log.e("*****name",name);

}

@Override
public void onLoaderReset(android.support.v4.content.Loader<Cursor> loader) {

}


private interface ProfileQuery {
    String[] PROJECTION = {
            ContactsContract.CommonDataKinds.Email.ADDRESS,
            ContactsContract.CommonDataKinds.Email.IS_PRIMARY,
            ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
            ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
            ContactsContract.CommonDataKinds.Phone.NUMBER,
            ContactsContract.CommonDataKinds.Phone.IS_PRIMARY,
            ContactsContract.CommonDataKinds.Photo.PHOTO_URI,
            ContactsContract.Contacts.Data.MIMETYPE
    };

    // int ADDRESS = 0;
    //  int NUMBER = 1;

    /** Column index for the email address in the profile query results */
    int EMAIL = 0;
    /** Column index for the primary email address indicator in the profile query results */
    int IS_PRIMARY_EMAIL = 1;
    /** Column index for the family name in the profile query results */
    int FAMILY_NAME = 2;
    /** Column index for the given name in the profile query results */
    int GIVEN_NAME = 3;
    /** Column index for the phone number in the profile query results */
    int PHONE_NUMBER = 4;
    /** Column index for the primary phone number in the profile query results */
    int IS_PRIMARY_PHONE_NUMBER = 5;
    /** Column index for the photo in the profile query results */
    int PHOTO = 6;
    /** Column index for the MIME type in the profile query results */
    int MIME_TYPE = 7;
}

答案 1 :(得分:0)

对于那些关心的人,我发布问题的方向可能有错误的方向。使用Webservice(如RESTFul)可能会更好。仍然深入研究细节,并在准备就绪后发布更准确的解决方案。