【问题标题】:Make volley http request during app installation在应用安装期间发出 volley http 请求
【发布时间】:2017-02-24 15:19:07
【问题描述】:

我只想发出一次 volley http 请求,并且应该在安装应用程序期间。

我通过在 SQLiteOpenHelper 类的 onCreate() 方法中发出 http 请求来实现这一点,该方法从远程 MySQL 获取数据以供使用。然而,我遇到的问题是,在应用程序安装完成后,应用程序显示为空白屏幕(主 Activity 上托管的片段)。但是当我关闭应用程序并第二次打开时,它能够从 SQLite 获取数据到屏幕上。

我必须在 onCreate() 方法中做些什么来确保应用仅在 volley 请求完成后运行?

这是我的代码。 SQLiteOpenHelper onCreate()

   @Override
    public void onCreate(final SQLiteDatabase db) {
        db.execSQL(CREATE_NOTICE_TABLE);
        db.execSQL(CREATE_ROSTER_TABLE);

        /*Perform One time sync operations from remote MySQL*/
        requestQueue = Volley.newRequestQueue(ContextGetter.getAppContext());

        JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, URL, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                if(response == null || response.length() == 0){
                    return;
                }

                if(response.has("notices")){
                    //Save to notices table
                    try {
                        JSONArray notices = response.getJSONArray("notices");
                        for (int i = 0; i < notices.length(); i++) {
                           JSONObject noticeObject = notices.getJSONObject(i);
                            String noticeID = noticeObject.getString(NOTICE_ID_KEY);
                            String noticeTitle = noticeObject.getString(NOTICE_TITLE_KEY);
                            String noticeBody = noticeObject.getString(NOTICE_BODY_KEY);
                            String dateCreated = noticeObject.getString(NOTICE_DATE_KEY);

                            NoticeItem noticeItem = new NoticeItem();
                            noticeItem.setId(Integer.parseInt(noticeID));
                            noticeItem.setTitle(noticeTitle);
                            noticeItem.setBody(noticeBody);
                            try {
                                noticeItem.setDate(formatDate(dateCreated));
                            } catch (ParseException e) {
                                e.printStackTrace();
                            }

                            //Save to SQLite
                            createNoticeBoard(noticeItem, db);
                        }
                    } catch (JSONException e) {
                        Log.d(TAG, "JSONException: " + e.getMessage());
                    }
                }

                //If roster available
                if(response.has("rosters")){
                    //Save to roster table
                    try {
                        JSONArray rosters = response.getJSONArray("rosters");
                        for (int i = 0; i <rosters.length() ; i++) {
                            JSONObject rosterObject = rosters.getJSONObject(i);
                            String rosterID = rosterObject.getString(ROSTER_ID_KEY);
                            String rosterOwner = rosterObject.getString(ROSTER_OWNER_KEY);
                            String rosterDate = rosterObject.getString(ROSTER_DATE_KEY);
                            String rosterShift = rosterObject.getString(ROSTER_SHIFT_KEY);

                            //Check to verify that the user actually owns that roster later by using shared preference
                            RosterItem rosterItem = new RosterItem();
                            rosterItem.setSyncNumber(Integer.parseInt(rosterID));
                            rosterItem.setStaffNumber(rosterOwner);
                            rosterItem.setShift(rosterShift);
                            try {
                                rosterItem.setDate(formatDate(rosterDate));
                            } catch (ParseException e) {
                                e.printStackTrace();
                            }
                            createRoster(rosterItem, db);
                        }
                    }catch(JSONException e){
                        Log.d(TAG, "JSONException: "+ e.getMessage());
                    }
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {

                Log.d(TAG, "VolleyError "+error.getMessage());
            }
        });


        //Add to requestQueue
        requestQueue.add(request);
    }

片段类

public class NoticeListFragment extends Fragment{

    private static final String TAG = "NoticeListFragment";

    private RecyclerView recyclerView;
    private NoticeListAdapter mNoticeListAdapter;


    public NoticeListFragment() {
        //Requires empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "Notices onCreate() called");

    }


    @Override
    public void onResume() {
        super.onResume();
        updateUI(); //In case data changes
        Log.d(TAG, "onResume() called");
    }


    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {


        //Inflate layout for this fragment
        View rootView = inflater.inflate(R.layout.fragment_notice_list, container, false);

        recyclerView = (RecyclerView) rootView.findViewById(R.id.rv_recycler_view);
        recyclerView.setHasFixedSize(true);

        LinearLayoutManager linearManager = new LinearLayoutManager(getActivity());
        recyclerView.setLayoutManager(linearManager);

        updateUI();
        return rootView;
    }


    /*View Holder*/
    private class NoticeViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
        private NoticeItem mNoticeItem;

        public CardView mCardView;
        public TextView mTextViewTitle;
        public TextView mTextViewDate;
        public TextView mTextViewBody;

        public NoticeViewHolder(View itemView) {
            super(itemView);

            mCardView = (CardView) itemView.findViewById(R.id.card_view);
            mTextViewBody = (TextView) itemView.findViewById(R.id.tv_notice_summary);
            mTextViewTitle = (TextView) itemView.findViewById(R.id.tv_notice_title);
            mTextViewDate = (TextView) itemView.findViewById(R.id.tv_notice_date);

            itemView.setOnClickListener(this);

        }

        //Bind properties to views

        private void bindNotice(NoticeItem noticeItem){
            mNoticeItem = noticeItem;
            mTextViewTitle.setText(noticeItem.getTitle());
            mTextViewDate.setText(noticeItem.getDate());
            mTextViewBody.setText(noticeItem.getSummary());

        }

        @Override
        public void onClick(View view) {

            Intent intent = NoticePagerActivity.newIntent(getActivity(), mNoticeItem.getId());
            startActivity(intent);
        }
    }

    /*Adapter*/
    private class NoticeListAdapter extends RecyclerView.Adapter<NoticeViewHolder>{

        //private Context mContext;
        private List<NoticeItem> listItems;


        //Provide a suitable constructor (depends on the kind of dataset you have)
        public NoticeListAdapter(List<NoticeItem> data) {
            //this.mContext = context;
            this.listItems = data;
        }


        @Override
        public NoticeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            //Create a new view
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.notice_lists_card, parent, false);
            //Set the view size, margin, padding and layout parameters
            NoticeViewHolder vh = new NoticeViewHolder(view);

            return vh;
        }

        @Override
        public void onBindViewHolder(NoticeViewHolder holder, int position){

            final NoticeItem noticeItem = listItems.get(position);
            //Bind data properties to views here...
            holder.bindNotice(noticeItem);

        }

        @Override
        public int getItemCount() {
            return listItems.size();
        }

        public void setNotices(List<NoticeItem> notices){
                listItems = notices;
        }
    }

    //Bind adapter to recycler view
    private void updateUI(){
        NoticeLab noticeLab = NoticeLab.get(getActivity());
        List<NoticeItem> notices = noticeLab.getNotices();

        if(mNoticeListAdapter == null){
            mNoticeListAdapter = new NoticeListAdapter(notices);
            recyclerView.setAdapter(mNoticeListAdapter);
        }else{

            mNoticeListAdapter.setNotices(notices);
            mNoticeListAdapter.notifyDataSetChanged();
        }

    }
}

【问题讨论】:

  • 你为什么不直接实现一个启动画面?
  • 好的。我已经有一个启动画面。你的意思是我应该在启动画面期间进行 http 调用?
  • 是的。 slpashscreen 意味着您的应用程序当前处于不可用状态,它必须进行一些初始化(从服务器获取一些数据以用于 ex)才能可用。看起来这就是你想要实现的目标。
  • @AnixPasBesoin 但我的意思是我只想这样做一次。 (在应用程序首次安装期间)用户启动应用程序时始终会出现启动画面。如何使用启动画面来确保初始化只发生一次?
  • 这意味着闪屏无法解决问题。因为我希望初始化只运行一次。

标签: android android-fragments android-sqlite android-volley


【解决方案1】:

我只想发出一次 volley http 请求,并且应该在安装应用程序期间。

安装应用时您无法控制。

我必须在 onCreate() 方法中做些什么特别的事情来确保应用程序仅在 volley 请求完成后运行?

Volley 是异步的。这就是使用 Volley 的全部意义所在。在您调用 requestQueue.add(request); 之后,您的 onCreate() 方法会立即继续执行,而 Volley 在后台线程上执行网络 I/O。

一些选项是:

  1. 摆脱所有 Volley 代码,将 APK 中的起始数据打包为资产,using SQLiteAssetHelper 可在您的应用首次运行时部署打包的数据库。

  2. 不要使用 Volley。相反,使用带有同步网络 I/O 选项的东西(HttpURLConnection、OkHttp 等),并在此处执行同步网络 I/O。您应该始终在后台线程上使用您的 SQLiteOpenHelper 子类,以防需要创建或更新数据库。因此,您的 SQLiteOpenHelperonCreate() 方法应该始终在后台线程上调用,并且您不需要另一个后台线程来进行网络 I/O。然后,您可以确定在 onCreate() 结束时您的起始数据已经存在...除非您没有 Internet 连接,或者您的服务器已关闭等。

  3. 将所有初始化逻辑移至其他位置,例如 IntentService。让它创建数据库(使用IntentService 自己的后台线程)并让它执行网络I/O(同样,使用同步API,因为IntentService 有自己的后台线程)。仅在 IntentService 完成其工作后才启动您的 UI。您可以更好地通过某种重试策略来处理连接错误,同时在该工作进行时向用户呈现一些临时 UI(例如,ProgressBar)。

【讨论】:

  • 您的第二个选项对我来说看起来不错。但在我的情况下,我该如何实现这一点。有什么指点吗?
  • @ultrasamad:我强烈推荐第三个选项HttpURLConnection 已经存在了大约 15 年,并且在书籍、网站等中得到了很好的介绍。使用 OkHttp 执行同步 GET 请求 @987654322 @.
  • 我围绕 IntentService 做了一些阅读,但我的问题是 IntentService 将确保操作(初始化网络调用)仅运行一次,而不是在应用程序的后续启动期间运行。我对此很天真。
  • @ultrasamad:如果数据库文件不存在,就知道需要下载启动数据。见getDatabasePath() on Context
  • 同时我在Async Task下实现了Okhttp的同步get请求,但是还是UI不等待请求完成的问题
猜你喜欢
  • 2020-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-07
  • 1970-01-01
  • 2017-11-06
  • 1970-01-01
相关资源
最近更新 更多