0

In my application I want run code every 60sec and this my code for get data from server.
For this I write below codes.
But after 60sec when run code, show me ForceClose error and show below message in logCat.

Show me error for my views into my fragment!

My codes :

public class MainReminderFragment extends Fragment {

    @BindView(R.id.mainExplore_noExploreTxt)
    TextView mainExplore_noExploreTxt;
    @BindView(R.id.newsPageLoadLay)
    RelativeLayout newsPageLoadLay;
    @BindView(R.id.toolbarTitleTxt)
    TextView toolbarTitleTxt;
    private Context context;
    public List<Datum> model = new ArrayList<>();
    public ReminderAdapter reminderAdapter;
    private SharedPrefrencesHandler prefrencesHandler;
    private String token = "";
    private LinearLayoutManager layoutManager;
    private MainActivity mainActivity;
    private InterfaceApi api;
    private boolean isRunning = false;
    public ProgressBar mainExplore_progressBar;
    public RecyclerView mainExplore_recyclerView;

    public MainReminderFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_main_reminder, container, false);
        //Initialize
        ButterKnife.bind(this, view);
        context = getActivity();
        prefrencesHandler = new SharedPrefrencesHandler(context);
        reminderAdapter = new ReminderAdapter(context, model);
        layoutManager = new LinearLayoutManager(context);
        mainExplore_progressBar = view.findViewById(R.id.mainExplore_progressBar);
        mainExplore_recyclerView = view.findViewById(R.id.mainExplore_recyclerView);
        isRunning = true;
        mainActivity = (MainActivity) getActivity();
        api = ApiClient.getClient().create(InterfaceApi.class);
        //Toolbar name
        toolbarTitleTxt.setText(context.getResources().getString(R.string.reminder));
        //RecyclerView
        mainExplore_recyclerView.setLayoutManager(layoutManager);
        mainExplore_recyclerView.setHasFixedSize(true);
        //Get token
        token = prefrencesHandler.getFromShared(SharedPrefrencesKeys.TOKEN.name());

        //Get data
        getData();
        if (getActivity() != null) {
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            while (true) {
                                try {
                                    Thread.sleep(60000);
                                } catch (Exception e) {
                                }
                                if (isRunning) {
                                    getData();
                                }
                            }
                        }
                    }).start();
                }
            });
        }
        return view;
    }

    //SendData method
    private SerialReminderListSendData sendData(int page) {
        SerialReminderListSendData listSendData = new SerialReminderListSendData();
        listSendData.setPageIndex(page);
        listSendData.setPageSize(200);

        return listSendData;
    }

    //Get data
    private void getData() {
        Call<SerialReminderListResponse> call = api.getListSerialReminder(token, "2", sendData(1));

        mainExplore_progressBar.setVisibility(View.VISIBLE);

        call.enqueue(new Callback<SerialReminderListResponse>() {
            @Override
            public void onResponse(Call<SerialReminderListResponse> call, Response<SerialReminderListResponse> response) {
                if (response.body().getData() != null && response.body().getStatusCode() != 401
                        && response.body().getStatusCode() != 402) {
                    if (response.body().getData().size() > 0) {
                        model.clear();
                        model.addAll(response.body().getData());
                        reminderAdapter.notifyDataSetChanged();
                        mainExplore_recyclerView.setAdapter(reminderAdapter);
                        //Gone no explore
                        mainExplore_noExploreTxt.setVisibility(View.GONE);
                    } else {
                        mainExplore_noExploreTxt.setVisibility(View.VISIBLE);
                        mainExplore_recyclerView.setVisibility(View.GONE);
                    }
                }

                mainExplore_progressBar.setVisibility(View.GONE);
            }

            @Override
            public void onFailure(Call<SerialReminderListResponse> call, Throwable t) {
                mainExplore_progressBar.setVisibility(View.GONE);
            }
        });
    }
}

Error Message in logCat :

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7130)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1115)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:5284)
at android.view.View.invalidateInternal(View.java:13615)
at android.view.View.invalidate(View.java:13579)
at android.view.View.setFlags(View.java:11422)
at android.view.View.setVisibility(View.java:8014)
at com.app.test.Fragments.MainPageFrags.MainReminderFragment.getData(MainReminderFragment.java:182)
at com.app.test.Fragments.MainPageFrags.MainReminderFragment.access$100(MainReminderFragment.java:47)
at com.app.test.Fragments.MainPageFrags.MainReminderFragment$1$1.run(MainReminderFragment.java:142)
at java.lang.Thread.run(Thread.java:761)

How can I fix it? please help me

BoboGoooool
  • 79
  • 1
  • 1
  • 6

3 Answers3

2

Every time you try to change the properties of an View you can only do that from the Thread which created it in the first place. And that is the main Thread / UI Thread. So for for example if you call

mainExplore_noExploreTxt.setVisibility(View.GONE);

you have to call it from the main thread. There is an utility method for that which is runOnUiThread

runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mainExplore_noExploreTxt.setVisibility(View.GONE);
        }
    });

Besides that you don't want to use Thread.sleep or something similiar. There are lot of android specific solutions for that. In this case you might want to use a Handler with postDelayed.

Murat Karagöz
  • 35,401
  • 16
  • 78
  • 107
0

Problem comes from here:

 getData();
    if (getActivity() != null) {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                new Thread(new Runnable() { // <-- here
                    @Override
                    public void run() {
                        while (true) {
                            try {
                                Thread.sleep(60000);
                            } catch (Exception e) {
                            }
                            if (isRunning) {
                                getData();
                            }
                        }
                    }
                }).start();
            }
        });
    }

Even though you use runOnUiThread for the first thread, but you create another thread inside it and this new thread has no access to views.

But I should say that I think your approach is very bad. You can easily use Timer for your code like this:

new Timer(new TimerTask() {
    @Override
    public void run() {
        getDate();
    }
}, 0, 60000).start();

and change:

mainExplore_progressBar.setVisibility(View.GONE);

to

mainExplore_progressBar.post(new Runnable() {
    @Override
    public void run() {
        mainExplore_progressBar.setVisibility(View.GONE);
    }
}

Update: you can use:

new Timer(new TimerTask() {
    @Override
    public void run() {
        getActivity().runOnUiThread(new Runnable() {
                        public void run() {
                            getData();
                        }
                    });
    }
}, 0, 60000).start();

too and leave getData() method unchanged.

Afshin
  • 8,839
  • 1
  • 18
  • 53
0

Try this code :

if (getActivity() != null) {
    Thread thread = new Thread() {
        @Override
        public void run() {
            float i;
            try {
                for (i = 0; i <= 100; i++) {
                    getActivity().runOnUiThread(new Runnable() {
                        public void run() {
                            getData();
                        }
                    });
                    sleep(6000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
    thread.start();
}
Mohammad Nouri
  • 2,245
  • 4
  • 17
  • 34