How to cache data that comes from API in the app using the Room DB to make app work well in offline mode?
//This class explains how I insert data to room, but I don't know to update when user open the app for second time
private class InsertMain extends AsyncTask<Void, Void, Main> {
private Response main;
private WeakReference<SplashActivity> activityReference;
private Exception exceptionToBeThrown;
public InsertMain(SplashActivity context, Response main) {
activityReference = new WeakReference<>(context);
this.main = main;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
//Toast.makeText(SplashActivity.this, "جاري تحميل البيانات!", Toast.LENGTH_SHORT).show();
}
#Override
protected Main doInBackground(Void... mVoids) {
Main newData;
if (activityReference.get().responseDao != null) {
activityReference.get().responseDao.deleteContent(1);
}
try {
activityReference.get().roomDatabase.responseDao().insert(response);
} catch (Exception e) {
e.printStackTrace();
}
if (activityReference.get() != null) {
newData = new Gson().fromJson(activityReference.get().roomDatabase.responseDao().getContent(1), new TypeToken<Main>() {
}.getType());
return newData;
} else
return null;
}
#Override
protected void onPostExecute(Main mMain) {
super.onPostExecute(mMain);
handler = new Handler();
handler.postDelayed(task, 1000);
}
}
Related
I am using AsyncTask for loading data, data comes from server, but due to so much data I want to load data on a background thread.
Here is my code
private void loadData() {
new AsyncTask<Void, ArrayList<DataModel>, ArrayList<DataModel>>() {
#Override
protected void onPreExecute() {
super.onPreExecute();
DialogHelper.showProgress(MyActivity.this);
}
#Override
protected ArrayList<DataModel> doInBackground(Void... voids) {
try {
return MyParser.parseData(jsonDataAsResultFromServer);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(ArrayList<DataModel> list) {
super.onPostExecute(list);
DialogHelper.hideProgress();
if (list.size() > 0) {
myAdapter = new MyAdapter(MyActivity.this);
myAdapter.addAll(list);
recyclerview.setAdapter(myAdapter);
}
}
}.execute();
}
I want a method (present in MainActivity) to call from the doInBackground(File... file) method of MyAsynctask class (present in MainActivity) and that method have to work in background also because it is time taking and my app stops working for a moment and there is no any dialog appearing which I have called in onPreExecute() method how to solve the problem here is my code it is working very fine but taking too much time and my app looks hanged but it is not actually.
ArrayList<HashMap<String,String>> getPlayList(File rootFolder) {
ArrayList<HashMap<String,String>> fileList = new ArrayList<>();
try {
File[] files = rootFolder.listFiles();
//here you will get NPE if directory doesn't contains any file,handle it like this.
if (files != null) {
for (File file : files) {
if (file.isDirectory() && !file.isHidden()) {
if (true) {
fileList.addAll(getPlayList(file));
}
else {
break;
}
} else if (file.getName().endsWith(".pdf")) {
HashMap<String, String> song = new HashMap<>();
song.put("file_path", file.getAbsolutePath());
song.put("file_name", file.getName());
fileList.add(song);
}
}
}
return fileList;
} catch (Exception e) {
return null;
}
}
And Asynctask class is below..
private class AsyncTaskExample extends AsyncTask<File, String, ArrayList> {
#Override
protected ArrayList doInBackground(File... file) {
ArrayList<HashMap<String,String>> songList=getPlayList(folder);
if(songList!=null){
for(int i=0;i<songList.size();i++){
final String fileName=songList.get(i).get("file_name");
final String filePath=songList.get(i).get("file_path");
//saving filePath and filName in SQLite Database..
saveFileToDatabase(filePath, fileName);
}
}
return songList;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
p = new ProgressDialog(MainActivity.this);
p.setMessage("Please wait...Loading..");
p.setIndeterminate(false);
p.setCancelable(false);
p.show();
}
#Override
protected void onPostExecute(ArrayList arrayList) {
super.onPostExecute(arrayList);
if (mFiles != null)
{
p.dismiss();
}
else
{
p.show();
}
}
}
How to do the work efficiently?
Solution
And the problem was these lines of code given below:
AsyncTaskExample taskExample = new AsyncTaskExample();
folder = new File(Environment.getExternalStorageDirectory().getAbsolutePath());
//I was writing this below line..
taskExample.doInBackground(folder);
//but it should be like in line below..
taskExample.execute(folder);
I would suggest you to define a callback, and pass it through in your asynctask's constructor so you can notify your main activity to show the dialog whenever it's needed.
Callback
public interface AsyncTaskExampleCallback {
public void onShowDialog();
public void onHideDialog();
}
Async task
class AsyncTaskExample extends AsyncTask<File, String, ArrayList> {
#Nullable
private AsyncTaskExampleCallback callback = null;
public AsyncTaskExample(#NotNull AsyncTaskExampleCallback callback) {
this.callback = callback;
}
#Override
protected ArrayList doInBackground(File... file) {
ArrayList<HashMap<String, String>> songList = getPlayList(folder);
if (songList != null) {
for (int i = 0; i < songList.size(); i++) {
final String fileName = songList.get(i).get("file_name");
final String filePath = songList.get(i).get("file_path");
//saving filePath and filName in SQLite Database..
saveFileToDatabase(filePath, fileName);
}
}
return songList;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
if (callback != null) {
callback.onShowDialog();
}
}
#Override
protected void onPostExecute(ArrayList arrayList) {
super.onPostExecute(arrayList);
if (mFiles != null) {
if (callback != null) {
callback.onHideDialog();
}
} else {
if (callback != null) {
callback.onShowDialog();
}
}
}
}
Main Activity
public class MainActivity extends AppCompatActivity implements AsyncTaskExampleCallback {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new AsyncTaskExample(this).execute();
}
#Override
public void onShowDialog() {
//Show dialog here
}
#Override
public void onHideDialog() {
//Hide dialog here
}
}
I've created an AsyncTask class to handle sending and receiving from my server. What I'm trying to do is fire an event or callback when the data is received so I can use said data to manipulate the UI.
AsyncTask class:
public class DataCollectClass extends AsyncTask<Object, Void, JSONObject> {
private JSONObject collected;
#Override
protected void onPreExecute() {
super.onPreExecute();
}
//#Override -Commented out because it doesn't like the override
protected void onPostExecute() {
try {
Log.d("Net", this.collected.getString("message"));
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
protected JSONObject doInBackground(Object... params) {
OkHttpClient client = new OkHttpClient();
// Get Parameters //
String requestURI = (String) params[0];
RequestBody formParameters = (RequestBody) params[1];
Request request = new Request.Builder().url(requestURI).post(formParameters).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// DO something on FAIL
}
#Override
public void onResponse(Call call, Response response) throws IOException {
String jsonResponse = response.body().string();
Log.d("Net", jsonResponse);
try {
DataCollectClass.this.collected = new JSONObject(jsonResponse);
Log.d("Net", DataCollectClass.this.collected.getString("message"));
} catch (JSONException e) {
e.printStackTrace();
}
}
});
return collected;
}
}
This is working, it prints an expected line of JSON into the log.
It's called from the Activity as:
new DataCollectClass().execute(requestURI, formVars);
I've looked all over, and I can't seem to find a definitive answer on how (and where) to add a callback. Preferably, the callback code itself should be with the DataCollectClass so all related code is reusable in the same place.
Is there a way to create a custom event firing (similar to Javascript libraries) that the program can listen for?
I've been pulling my hair out over this!
UPDATE:
Since AsyncTask is redundant, I've removed it and rewrote the code (in case someone else has this same issue):
public class DataCollectClass {
private JSONObject collected;
public interface OnDataCollectedCallback {
void onDataCollected(JSONObject data);
}
private OnDataCollectedCallback mCallback;
public DataCollectClass(OnDataCollectedCallback callback, String requestURI, RequestBody formParameters){
mCallback = callback;
this.collect(requestURI, formParameters);
}
public JSONObject collect(String requestURI, RequestBody formParameters) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(requestURI).post(formParameters).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
//TODO Add what happens when shit fucks up...
}
#Override
public void onResponse(Call call, Response response) throws IOException {
String jsonResponse = response.body().string();
Log.d("Net", jsonResponse);
try {
DataCollectClass.this.collected = new JSONObject(jsonResponse);
if(mCallback != null)
mCallback.onDataCollected(DataCollectClass.this.collected);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
return collected;
}
}
Called from Activity:
new DataCollectClass(new DataCollectClass.OnDataCollectedCallback() {
#Override
public void onDataCollected(JSONObject data) {
if(data != null) {
try {
// Do Something //
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}, requestURI, formVars);
All working perfectly!
Thanks!
If you want to utilize a callback for an AsyncTask you can handle it via the following.
Do something like this (modifying your code to add what is below)
public class DataCollectClass extends AsyncTask<Object, Void, JSONObject> {
public interface OnDataCollectedCallback{
void onDataCollected(JSONObject data);
}
private OnDataCollectedCallback mCallback;
public DataCollectClass(OnDataCollectedCallback callback){
mCallback = callback;
}
// your code that is already there
...
#Override
public onPostExecute(JSONObject response){
if(mCallback != null)
mCallback.onDataCollected(response);
}
}
Then to make the magic happen
new DataCollectClass(new OnDataCollectedCallback() {
#Override
public void onDataCollected(JSONObject data) {
if(data != null)
// DO something with your data
}
}).execute(requestURI, formVars);
However, it is worth noting, most networking libraries, including OkHttp, handle background threads internally, and include callbacks to utilize with the requests.
This also implements a custom interface, so others may be able to see how you could use this for any AsyncTask.
There is a asynchronous get in OkHttp, so you don't need an AsyncTask, but as a learning exercise, you could define your callback as a parameter something like so.
new DataCollectClass(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// DO something on FAIL
}
#Override
public void onResponse(Call call, Response response) throws IOException {
JSONObject collected = null;
String jsonResponse = response.body().string();
Log.d("Callback - Net", jsonResponse);
try {
collected = new JSONObject(jsonResponse);
Log.d("Callback - Net", collected.getString("message"));
} catch (JSONException e) {
e.printStackTrace();
}
}
}).execute(requestURI, formVars);
The AsyncTask
public class DataCollectClass extends AsyncTask<Object, Void, Call> {
private Callback mCallback;
private OkHttpClient client;
public DataCollectClass(Callback callback) {
this.mCallback = callback;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
this.client = new OkHttpClient();
}
#Override
protected void onPostExecute(Call response) {
if (response != null && this.mCallback != null) {
response.enqueue(this.mCallback);
}
}
#Override
protected Call doInBackground(Object... params) {
// Get Parameters //
String requestURI = (String) params[0];
RequestBody formParameters = (RequestBody) params[1];
Request request = new Request.Builder().url(requestURI).post(formParameters).build();
return client.newCall(request); // returns to onPostExecute
}
}
Call Webservice using asynctask is an old fashioned. You can use Volley or retrofit.
But you can use this process to call Webservice . Here is steps:
Create an Interface and implements it in your Activity/Fragment
public interface IAsynchronousTask {
public void showProgressBar();
public void hideProgressBar();
public Object doInBackground();
public void processDataAfterDownload(Object data);
}
Create Class DownloadableAsyncTask . This class is:
import android.os.AsyncTask;
import android.util.Log;
public class DownloadableAsyncTask extends AsyncTask<Void, Void, Object> {
IAsynchronousTask asynchronousTask;
public DownloadableAsyncTask(IAsynchronousTask activity) {
this.asynchronousTask = activity;
}
#Override
protected void onPreExecute() {
if (asynchronousTask != null)
asynchronousTask.showProgressBar();
}
#Override
protected Object doInBackground(Void... params) {
try {
if (asynchronousTask != null) {
return asynchronousTask.doInBackground();
}
} catch (Exception ex) {
Log.d("BSS", ex.getMessage()==null?"":ex.getMessage());
}
return null;
}
#Override
protected void onPostExecute(Object result) {
if (asynchronousTask != null) {
asynchronousTask.hideProgressBar();
asynchronousTask.processDataAfterDownload(result);
}
}
}
Now in your Activity you will find this methods.
DownloadableAsyncTask downloadAsyncTask;
ProgressDialog dialog;
private void loadInformation() {
if (downloadAsyncTask != null)
downloadAsyncTask.cancel(true);
downloadAsyncTask = new DownloadableAsyncTask(this);
downloadAsyncTask.execute();
}
#Override
public void showProgressBar() {
dialog = new ProgressDialog(this, ProgressDialog.THEME_HOLO_LIGHT);
dialog.setMessage(" Plaese wait...");
dialog.setCancelable(false);
dialog.show();
}
#Override
public void hideProgressBar() {
dialog.dismiss();
}
#Override
public Object doInBackground() {
// Call your Web service and return value
}
#Override
public void processDataAfterDownload(Object data) {
if (data != null) {
// data is here
}else{
//"Internal Server Error!!!"
}
}
Now just call loadInformation() method then you will get your response on processDataAfterDownload().
I've created a component which downloads an Image (in the Android side) and I want to send back size details about that image (to the JS side) of my app.
Now, I can safely say that I can call my component from JS and it will respond back with data but as soon as I add in the Async element to download the image from an URL, read it and respond I get a NullpointerException as soon as I call my callback.invoke("response text");
My problematic code is:
public void loadImage(final String url, final Callback onLoadCallback) {
...
new AsyncTask<String, Void, Void>() {
#Override
protected Void doInBackground(String... url) {
try {
theImage = Glide.with(getReactApplicationContext()).load(url[0]).asBitmap().into(-1, -1).get();
}
catch ...
return null;
}
#Override
protected void onPostExecute(Void dummy) {
if (null != theImage) {
onLoadCallback.invoke("Success"); //<== THIS LINE HERE
}
}
}.execute(url);
...
}
Now, I get that it's because I'm trying to return on a sep thread back to the main thread via the callback but I'm not sure how in the heck I'm supposed to get the info I want back to the JS side?! This is my first attempt at a component in RN so be kind! :)
Extra Info - My React module:
var MY_Image = require('NativeModules').MYImage;
var myimage = {
loadImage(url, onLoad) {
MY_Image.loadImage(url, onLoad)
},
};
module.exports = myimage;
Then in my React app view:
...
componentDidMount: function() {
myImage.loadImage('[URL to Image]',onLoad=> {
console.log('Success: '+onLoad);
});
}
Thanks for the input everyone. I've managed to sort this. I needed a class-wide variable to hold the callback in and a callback handler. Here's my code:
public class MyClass extends ReactContextBaseJavaModule {
private Bitmap mTheImage;
private Callback mCallback;
private WritableMap mResults;
public MyClass(ReactApplicationContext reactContext) {
super(reactContext);
this.mContext = reactContext;
}
private void consumeCallback(String type, WritableMap obj) {
if(mCallback!=null) {
obj.putString("type", type);
mCallback.invoke(obj);
mCallback = null;
}
}
#ReactMethod
public void doMyStuff(final String input, final Callback callback) {
if(mCallback==null) {
mResults = Arguments.createMap();
}
mCallback = callback;
new AsyncTask<String, Void, Void>() {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(String... params) {
try {
String myValue = params[0];
mResults.putString("myValue", myValue);
mTheImage = [method to get the image]
}
catch(Exception e) {
}
return null;
}
protected void onPostExecute(Void dummy) {
if(null!=mTheImage && null!=mCallback) {
mResults.putInt("width", mImage.getWidth());
mResults.putInt("height",mImage.getHeight());
consumeCallback("success", mResults);
}
else {
consumeCallback("error", mResults);
}
}
}.execute(url);
}
}
I am following the documentation given by IBM (https://developer.ibm.com/mobilefirstplatform/documentation/getting-started-7-0/hello-world/creating-first-native-android-mobilefirst-application/)
After calling request.send(new MyInvokeListener()); there is no sucess or failure call back. Receiving an error message "Android Prototype stopped working."
Adapter is working fine when i right click on the adapter --> Run As --> Call Mobile First Adapter
Below is my android native code.
public class TaskFeed extends AsyncTask<Void, Void, String> {
ProgressDialog Dialog = new ProgressDialog(TaskActivity.this);
#Override
protected void onPreExecute() {
Dialog.setMessage("Establishing connection...");
Dialog.show();
}
#Override
protected String doInBackground(Void... params) {
try {
final WLClient client = WLClient.createInstance(TaskActivity.this);
client.connect(new MyConnectListener());
URI adapterPath = new URI("/adapters/TaskAdapter/getAllTasks");
WLResourceRequest request = new WLResourceRequest(adapterPath,WLResourceRequest.GET);
request.send(new MyInvokeListener());
} catch (Exception e) {
e.printStackTrace();
}
// Dialog.setMessage("Loading Tasks..");
return "test";
}
#Override
protected void onPostExecute(String r) {
Dialog.dismiss();
ArrayList<ListViewModel> result = AssignAndGetCurrentTaskResults();
tvListCount.setText(GetActionBarString());
adapter = new ArrayDataAdapter(taContext, R.layout.task_row_item, result);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
My InvokeListner Class
public class MyInvokeListener implements WLResponseListener {
public void onSuccess(WLResponse response) {
try {
allTaskResults= ParseData(response.getResponseJSON().getJSONArray("array"));
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
public void onFailure(WLFailResponse response) {
}
}
Taking out the code which creates and call to mobile first adapter from async task solved my problem.
There is a window leakage in android doing so.