First, I have to say I'm kinda new to native Android development. I put the following code inside onCreate in my MainActivity class.
I never used AsyncTask, so I don't know if this is the best practice to do it. Should I create a new AsyncTask-class and load it somehow in the onCreate method? I put it in the MainActivity onCreate method, because of the scope of variables like bp (billing purchase class), pollfish, counting app-launches, etc.
Also I don't know what to return? Because I have multiple results. There are many tutorials how to create an AsyncTask like this and also suggestions to store SharedPreferences there, but I want to know the best practice for it. For example to get values later in my onResume method or similiar operations.
new AsyncTask<Context, Void, String>()
{
#Override
protected String doInBackground(Context... params)
{
Context context = params[0];
SharedPreferences pref = context.getSharedPreferences("Pref",Context.MODE_PRIVATE);
SharedPreferences.Editor editor = pref.edit();
// Count App Launches
totalCount = pref.getInt("counter", 0);
totalCount++;
editor.putInt("counter", totalCount);
editor.apply();
// Pollfish ADS
int pollfishPref = pref.getInt("pollfish", 0);
Log.d(TAG,"Application launch count: " + totalCount);
Log.d(TAG,"pollfish count: " + pollfishPref);
if (!bp.isPurchased(PRODUCT_ID) && (pollfishPref == 0 || (pollfishPref != 0 && pollfishPref <= totalCount)))
{
PollFish.ParamsBuilder paramsBuilder = new PollFish.ParamsBuilder("xxx")
.releaseMode(false)
.customMode(false)
.indicatorPosition(Position.MIDDLE_RIGHT)
.indicatorPadding(0)
.build();
PollFish.initWith(MainActivity.this, paramsBuilder);
mTracker.setScreenName("Pollfish Count: " + pollfishPref + " Applaunch Count: " + totalCount);
mTracker.send(new HitBuilders.ScreenViewBuilder().build());
}
Boolean consumed = pref.getBoolean("consumed", Boolean.parseBoolean(null));
Log.d(TAG,"consumedPref: " + consumed);
if (!consumed)
{
editor.putBoolean("consumed", true);
editor.apply();
bp.consumePurchase(PRODUCT_ID);
Log.d(TAG,"consumed now!");
}
return xxx;
}
protected void onPostExecute(String result)
{
Log.d(TAG, "Preference received in background: " + result);
};
}.execute(this);
Generally, the best practice is for re-use of the AsyncTask and cleaner code by having a separate class. If you are concerned about variable scope, you can pass those into a constructor for your AsyncTask.
class MyAsyncTask extends AsyncTask<Params, Progress, Result> {
private Object arg1;
public MyAsyncTask(Object arg1) {
this.arg1 = arg1;
}
#Override
public Result doInBackground(Params... params) { }
#Override
public void onPostExecute(Result result) { }
}
Of course, update the necessary object types, and use it like so
new MyAsyncTask(arg1).execute();
You may want to update and retrieve values from the SharedPreferences outside of the AsyncTask then pass in those respective values to the constructor as shown.
I don't know what to return? Because I have multiple results.
Sadly, you can only return one type of class. If you are concerned with how to get a result back from the AsyncTask back to where you called it from, then I would recommend callbacks.
You should separate the async task into a separate class and make it extend AsyncTask. You can pass variables you need to use through the constructor. This is the best practice because it can make the code easier to read and doesn't block the main thread
Related
I'm making an Android App with Google Spreadsheets. I'm scanning a barcode from a book, looking the info with the Google Books API and then I need to save this info into a sheet.
The following code is inside MainActivity:
private class GetBookInfo extends AsyncTask <String, Void, String> {
#Override
protected String doInBackground(String... urls) {
// make Call to the url
makeCall("https://www.googleapis.com/books/v1/volumes?" +
"q=isbn:" + ean_content + "&AIzaSyCpYez5556X4UzPV6rF4kkspj9DsCs_Q_c");
//print the call in the console
System.out.println("https://www.googleapis.com/books/v1/volumes?" +
"q=isbn:" + ean_content + "&AIzaSyCpYez5556X4UzPV6rF4kkspj9DsCs_Q_c");
return null;
}
#Override
protected void onPreExecute() {
// we can start a progress bar here
}
#Override
protected void onPostExecute(String result) {
String ruta = save_cover(getApplicationContext(), title, book_cover);
Intent intent = new Intent(MainActivity.this, Spreadsheets.class);
finish();
startActivity(intent);
}
}
After this code I have public void makeCall(String stringURL). It searchs the book's title, author, date, description and category of the book.
I have another Activity:
public class Spreadsheets extends Activity implements EasyPermissions.PermissionCallbacks
In this activity, I have the following code to write into a Google Sheet:
public void setDataToApi (String title, String author, String date, String category, String description) throws IOException {
String spreadsheetId = "1Gg121IjABekfSTKXg_TQzJgg4boxvYIQnmf_K4YDboo";
String range = "Sheet1!A2:E2";
List<List<Object>> values = new ArrayList<>();
List<Object> data1 = new ArrayList<>();
data1.add(title);
data1.add(author);
data1.add(date);
data1.add(category);
data1.add(description);
values.add(data1);
ValueRange valueRange = new ValueRange();
valueRange.setMajorDimension("ROWS");
valueRange.setRange(range);
valueRange.setValues(values);
UpdateValuesResponse response = this.mService.spreadsheets().values().update(spreadsheetId, range, valueRange)
.setValueInputOption("RAW")
.execute();
System.out.printf("%d cells updated.", response.getUpdatedCells());
}
My problem is that I'm trying to call the setDataToApi method from the onPostExecute method, before the intent, but it does not work at all. Thank you very much!
You cannot call the method of another Activity from outside that Activity. Before the Intent is actually dispatched, the other Activity is not yet running. You either need to
Move GetBookInfo to the SpreadSheets Activity so that it can perform the onPostExecute directly in the Activity. This requires passing the book's ID via an extra on the Intent. Or...
Keep GetBookInfo where it is in MainActivity and pass all the looked up details of the book through extras on the Intent.
If you have a method being called from 2 different activities, I would recommend putting that method in a separate class. So move setDataToApi into it's own Java class, instantiate an object of your new class and use that method in both of your activities that call it.
Or, as Elan said in their answer, use intents and pass information to your other activity using putExtra and write your own logic in the onCreate method to call setDataToApi. Hope this helps!
lately i have been researching about memory leaks in java/android and pretty much everywhere it says that instead of anonymous classes i should use static inner classes with weak references.
so, in my android app i started doing that but very quickly got tired of it because it's a lot of boilerplate code... i think have an alternative solution which i would prefer to use, but i'm juts not sure that it is a valid alternative to static inner classes in terms of preventing memory leaks. as i said before, i haven't seen this solution suggested anywhere else (all say to use static inner classes) so thats why im not sure my alternative will work.
ill use a simple example from my app:
i have a class called WebClient which handles asynchronous web requests and it accepts an interface called iCallback which returns the response from the server to the caller, and in my activity once i get this callback i need to dismiss a dialog, and maybe perform some activity related things (like trigger onBackPressed() and setResult()).
so here is my static inner class i have created:
private static class CallBack implements WebClient.ICallback
{
private WeakReference<ProgressDialog> mProgDiag;
private WeakReference<BaseActivity> mActivity;
public CallBack(BaseActivity activity, ProgressDialog progDiag)
{
this.mProgDiag = new WeakReference<>(progDiag);
this.mActivity = new WeakReference<>(activity);
}
#Override
public void onCallback(String data)
{
String responseAsString = Utils.extractStringFromResponse(...);
final BaseActivity parentActivity = mActivity.get();
ProgressDialog dialog = mProgDiag.get();
if(dialog != null)
{
dialog.dismiss();
}
if (responseAsString == null)
{
if(parentActivity != null)
{
Utils.makeServerErrorDialog(parentActivity,
new iDialogButtonClickedListener()
{
#Override
public void onDialogButtonClicked()
{
parentActivity.onBackPressed();
}
});
}
return;
}
//everything is ok
if (responseAsString.equals("1"))
{
if(parentActivity != null)
{
Intent result = new Intent();
result.putExtra(...);
parentActivity.setResult(Activity.RESULT_OK, result);
}
}
else
{
Utils.reportErrorToServer(...);
if(parentActivity != null)
{
parentActivity.setResult(Activity.RESULT_CANCELED);
}
}
if(parentActivity != null)
{
parentActivity.onBackPressed();
}
}
}
so for every variable i need in this static inner class i have to create a new weak reference, then retrieve the object itself, and then every time i want to access it i need to check whether it's null... that seems like a lot of code to me.
and here is my suggested alternative:
public abstract class BaseActivity extends AppCompatActivity
implements WebClient.ICallback
{
private static final String TAG = "BaseActivity";
WebClient.ICallback mCallBack;
ProgressDialog mProgDiag;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(...);
mCallBack = this;
//some code to invoke a server request on button click
//and passing mCallBack to the request
}
#Override
public void onCallback(String data)
{
String responseAsString = Utils.extractStringFromResponse(...);
mProgDiag.dismiss();
if (responseAsString == null)
{
Utils.makeServerErrorDialog(this,
new iDialogButtonClickedListener()
{
#Override
public void onDialogButtonClicked()
{
onBackPressed();
}
});
return;
}
//everything is ok
if (responseAsString.equals("1"))
{
Intent result = new Intent();
result.putExtra(...);
setResult(Activity.RESULT_OK, result);
}
else
{
Utils.reportErrorToServer(...);
setResult(Activity.RESULT_CANCELED);
}
onBackPressed();
}
#Override
protected void onPause()
{
mCallBack = null;
super.onPause();
}
#Override
protected void onResume()
{
super.onResume();
mCallBack = this;
}
}
to me this seems much cleaner: no creating and retrieving instances of weak references for every variable i need access to, i can directly invoke activity methods (e.g. onBackPressed()), and no checking for null everywhere.
the only place i would now have to check for null is inside WebClient class before invoking the callBack method.
so my question is, does this approach achieve the same result in terms of preventing memory leaks? is it a "worthy" alternative to static inner classes?
Unfortunately, your approach does not work. By implementing the WebClient.ICallback in your activity, rather than an inner class, you don't get rid of the leak. The leak happens not because the references to activity and dialog are implicit in an anonymous class, or in lambda, or in a non-static inner class instance; the happens when the WebClient keeps this reference while the activity is gone (it is not destroyed, because there is a strong reference to it).
The special mCallBack that you set to null when the activity is paused, gains nothing. Just as well, you can simply pass your activity instance to the WebClient. Now there is a strong reference to your activity, which is managed by someone (async handlers of the WebClient), who is not under your control. If you are unlucky, the async handler will get stuck somewhere and will never release this reference.
Please read this detailed explanation.
Note that WebView itself can cause a memory leak, if special measures are not undertaken!
This question already has answers here:
AsyncTask Android example
(21 answers)
Closed 6 years ago.
I am new to Android and having a hard time to implement the following code without UI freeze. I tried to use a new thread, but it doesn't give feedback (at least I don't know how to get it) when loop is finished. My code looks like this (excerpt):
public class FilterSettingsActivity extends AppCompatActivity {
private String SMSMsgs = "";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_filter_settings);
listSMSs(false);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.action_preview:
listSMSs(true);
//execute after loop completetion
Intent i = new Intent(FilterSettingsActivity.this, PreviewSmsActivity.class);
i.putExtra("SMSMsgs",SMSMsgs);
startActivity(i);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void listSMSs(Boolean write){
Cursor cur = getContentResolver().query(uriSMS, null, getSelection(fromDate,toDate,thread_id),getArgs(fromDate,toDate,thread_id),"thread_id");
String sms = cur.getCount()+" SMS found";
if(write==true) {
// this code stucks UI
while (cur.moveToNext()) {
Long d = Long.parseLong(cur.getString(cur.getColumnIndex("date"))); //for retrieve readable date, time
String PhoneNumber = cur.getString(cur.getColumnIndex("address"));
SMSMsgs += "At :"+ millisToDate(d) +" From :" + getContactName(FilterSettingsActivity.this,PhoneNumber)+" "+ cur.getString(cur.getColumnIndex("address"))+" : " + "\n"
+ cur.getString(cur.getColumnIndex("body"))+"\n"+"\n";
}
}
final TextView textView = (TextView) findViewById(R.id.tvStatus);
textView.setText(sms);
}
}
Can anyone help on how to implement AsyncTask or new Thread on Cursor queries? Thank you.
Edit: The problem here is that cursor has to be inside the asynctask class and has to receive several arguments (strings, arrays of strings) from outside functions and activities to do its job. These functions and activities are not presented in code example. If arrays of strings can be passed as params in doInBackground function, then quoted solution is applicable, otherwise this question is not a duplicate.
You can use CursorLoader to implement this which takes care of loading data asynchronously
refer this https://developer.android.com/reference/android/content/CursorLoader.html
http://www.theappguruz.com/blog/use-android-cursorloader-example
I know there are already some questions on global methods and variables in android but I'm running into problems with static methods probably due to my less experience with objectoriented programming. So here is what I want to have:
I am writing an app which counts points which the user can earn for certain things he does. Because of that I want to call the method addPoints from different activities and services. This method should also set the points textview in the main activity and some other things.
I realized it by adding a static variable
static int sPoints;
in the MainActivity, that I use as a "global" variable in each activity.
However, with the addPoints method I have some problems. If I use a non-static method, I have to create an instance of MainActivity in the other activities, which is not very nice and changing the values of that instance does not have an effect on the actual MainActivity.
If I use a static function it works fine as long as I don't want to use non-static methods like in this example:
public static void addPoints(Context context, int points){
int levelBefore, levelAfter;
levelBefore = getLevelFromPoints(sPoints);
sPoints = sPoints + points;
levelAfter = getLevelFromPoints(sPoints);
if(levelBefore!=levelAfter){
String rank = getRankFromLevel(levelAfter);
levelTextView.setText("Lvl. " + String.valueOf(levelAfter));
Toast.makeText(context, "Congrats! You reached the next level!", Toast.LENGTH_LONG).show();
}
}
Here I can't easily use levelTextView.setText and I run into this problem in many other cases. Moreover, I've read that using static methods is not good, anyway.
So would the correct way be creating an instance of MainActivity each time and then call addPoints on it which has to return the new number of points? Or is there another way (I hope so, because both above ways seem to be not very satisfying).
a. Static methods can safely be used in case your work can not be accomplished by ShardPreferences and requires the use of same code at multiple classes like in your case.
b. First create an interface that will pass the updated rank to respective activities or classes
public interface ScoreUpdater {
void updateScore (String rank);
}
c. then implement it in all activities where required to use, MainActivity in this case
public class MainActivity extends AppCompatActivity implements ScoreUpdater{
//
//other methods and codes
//
#Override
public void updateScore(String rank) {
runOnUiThread(new Runnable() {
#Override
public void run() {
levelTextView.setText("Lvl. " + String.valueOf(levelAfter));
Toast.makeText(MainActivity.this.getApplicationContext(), "Congrats! You reached the next level!", Toast.LENGTH_LONG).show();
}
});
}
}
d. then implement your static methods. not sure where you have declared few variables. so my method below is on guess work.
public static void addPoints(Context context, int points){
//not sure where you are declaring sPoints
int levelBefore, levelAfter;
levelBefore = getLevelFromPoints(sPoints);
sPoints = sPoints + points;
levelAfter = getLevelFromPoints(sPoints);
if(levelBefore!=levelAfter){
String rank = getRankFromLevel(levelAfter);
if(context instanceof ScoreUpdater){
((ScoreUpdater)context).updateScore(rank);
}
}
}
private static int getLevelFromPoints(int points){
//your operations
return points;
}
i have two class in my project.the first one is" public class Schtimetable extends Activity" in which there is a method i need to call in Class B :"public class ClassMode" .The method is
public int calculateWeeks() {
SharedPreferences preferences = getSharedPreferences("currentWeek",
Context.MODE_PRIVATE);
int csweek = preferences.getInt("CSweek", 1);
int weekofyear = preferences.getInt("currentWeek", 0);
int now_weekofyear = Calendar.getInstance().get(Calendar.WEEK_OF_YEAR);
return (csweek + now_weekofyear - weekofyear);// return current week
}
i find i can't use it just like this:
Schtimetable s = new Schtimetable();
int oddOReven = cursor.getInt(cursor.getColumnIndex("oddOReven"));
cursor.close();
if ((s.calculateWeeks() % 2 == oddOReven) || (oddOReven == 2)) {
Log.i(TAG, "has Class is true");
return true;
} else {
Log.i(TAG, "has Class is false");
return false;
}
in all,i want to have a data by the method calculateweeks() returned,how can i get it.
Thanks !
Don't call methods from activities. Activities are meant to be started, not instantiated like a plain old java object. Place the method elsewhere (in some other helper class) and have it take a Context as an argument (so you can get the SharedPreferences). Then, in any activity you can call that method. Also, you should probably make the method static.
use like this
YourActivtyClass activityclassobject = new YourActivtyClass();
activityclassobject.activityclassmethod();
use this in your non activity class
which is called from another activity class...
i hope you understand process to call method..