Design pattern about file uploading list and service/activity - android

I have a ListActivity to let user pick photo(s) to be upload
For upload, I'll put photos inside a static ArrayList called PhotoList
After that, I'll start a Service to upload those photos
Now, user may switch to another activity to do some other things
Later, user will back to this ListActivity to check upload status. Also in here they can pick more photos to be upload.
So, my PhotoList is actually a kind of Queue, but it also a data to be display at ListActivity.
My problem is, when the Service is running, and user picked more photo(s), I would like put those photo(s) inside the PhotoList.
(I don't want to start Service again since the service is already running...)
Now I just get stuck here.

okey I will try to give you a solution based on my understanding to your problem :
You have a list with photos and you want the user to be eligible to upload those images and update it the user of it status ( uploaded/ uploading / upload/failed) and you dont want to start the Service every time you upload .
a Simple working solution is to use an IntnetService i will be running only if there is tasks assigned to it and will atomically shut down when finish the work and of course the job will be in a sperate thread when working with IntentService
step 1
make a database table contains data about the images
_id integer
_image_uri
_image_status :
_image_status will hold one of these values ( 1-uploaded : finish_uploaded , 2- uploading: service is uploading the image , 3-upload : the user can upload the image 4-failed: failed to upload you can retry )
step2
now in the UploadIntentService try to upload the image to server and when if the upload competes successfully or an error happened while upload update the database
public class UploadIntentService extends IntentService {
private static final String TAG = UploadIntentService.class.getSimpleName();
private static final int STATUS_UPLOAD = 0x01; //can be uploaded
public static final int STATUS_FAILED_TO_UPLOAD = 0x02; // tried to upload but failed
public static final int STATUS_UPLOADING = 0x03; // self explanied
public static final int STATUS_SUCCESSFULLY_UPLOADED = 0x04; // the image uploaded to server
public UploadIntentService() {
super(TAG);
}
#Override
protected void onHandleIntent(Intent intent) {
int status = intent.getIntExtra("status", -1);
String imageUri = intent.getStringExtra("image_path");
long imageDatabaseid = intent.getLongExtra("image_db_address",-1);
if(status != STATUS_SUCCESSFULLY_UPLOADED && status != STATUS_UPLOADING){
try{
//update _image_status column with value of STATUS_UPLOADING with the image_id = imageDatabaseid;
//upload code
//successfully uploaded
//update _image_status column with value of STATUS_SUCCESSFULLY_UPLOADED with the image_id = imageDatabaseid;
}catch(Exception ex){
ex.printStackTrace();
//update _image_status column with value of STATUS_FAILED_TO_UPLOAD with the image_id = imageDatabaseid;
}
}
}
}
......
step3
and you ListActivity if you want to upload any image use this code
Intent intent = new Intent(context, UploadIntentService.class);
Bundle uploadExtras = new Bundle(3);
uploadExtras.putLong("image_db_address", PUT HERE THE IMAGE DATABASE ID );
uploadExtras.putInt("status", PUT HERE THE IMAGE STATUS );
uploadExtras.putString("image_path", PUT HERE THE IMAGE PATH IN FILE SYSTEM);
intent.putExtras(uploadExtras);
context.startService(intent);
.......
step 4
make sure you declare the Service in the manifest.xml , and enjoy.

Your Service should override the onResume() function. onResume() is called whenever the user does other tasks, opens other activities etc and finally returns back to your application's activity. That is just the gist.
You can learn more about Runtime Changes . The concepts that apply for Activity apply for Service also, in that Service is just not "shown" to the user.
In the above link, you will learn when an activity is recreated, and when it is actually resumed by calling onResume(). You also need to override onSaveInstanceState() and onRestoreInstanceState() so as to pass state objects between your old Activity instance and the new one.
What you can do is, when the user goes leaves your activity, in your onSaveInstanceState(), you should save your state variables eg. your list of selected photos, in a Bundle. And pass this Bundle to onRestoreInstanceState()
Question: Are you actually starting your service as a valid Service ? I mean, are you extending the Service class? I've used Service for my projects, and they always resumed where they left without overriding anything. The reason: Services run in background, resources are not released until the service is done doing it's job. So if you have not used Service , then I recommend you do that. Your problems will be taken care of.

You can make a BlockingQueue in which you will store photos to upload. This BlockingQueue is your PhotoList. In Service, in loop, you will look into this BlockingQueue. Two cases are possible:
There is some photo. Take it and upload.
Queue is empty. So the thread will go to sleep, BlockingQueue assures this.

You can use this :
Copy On Write Array List
This will be a thread-safe random-access list.
Check this link for good explanation and example.
Difference between ArrayList and CopyOnWriteArrayList
Iterators of this list never throw ConcurrentModificationException. When an iterator is created, it keeps a copy of the list's contents. It is always safe to iterate this list.

Related

How to change textviews & imageviews of another activity in Android

I have an activity which can take a few seconds to load its content (mainly pictures) from the cloud. Let's say this content is pictures & description from a person. The user can go to pictures & description from another person by clicking on the next button. I'd like to avoid the loading time When this button is clicked.
To do this I have two activities : firstPersonActivity for the first person content, and secondPersonActivity for the second person content.
What I try to do is to load the content of the secondPersonActivity when the user is in the firstPersonActivity, so that the secondPersonActivity can be displayed "directly" (= without needing to load content from the cloud when the next button is clicked in the firstPersonActivity).
But I do not succeed in doing this, I don't know how to modify the views of the secondPersonActivity layout from the firstPersonActivity class.
I tested the following code in my firstPersonActivity but it doesn't work (there is no connexion to the cloud, it's just a test to better understand how it works). R.id.first_image_second_person is the id of my imageview in the secondPersonLayout (= the layout used in the secondPersonActivity).
ImageView firstImageSecondPerson = (ImageView) findViewById(R.id.first_image_second_person);
firstImageSecondPerson.setImageResource(R.drawable.mypicture);
When I click on the next button to go from the firstPersonActivity to the secondPersonActivity, my firstImageSecondPerson imageview is not filled.
Do you see what's wrong ?
Is there a better way to avoid the loading time when the user click on the next button ?
It is not possible to change activity if activity instance is not created. In my opinion to do what You need I would go to single activity with hidden content of second person ( VIEW.INVISIBLE ) and show/hide it when it is needed.
But if second activity must be there, then create some structure for saving bitmaps in memory. In Your code sample You get picture from drawable so we are not talking about some delays, but if images are downloaded from some server then You can create some class which will have those bitmaps created from url and instance of this class can be used on any activity.
So for example Your class for caching pictures would have some method for creating bitmaps like -How to load an ImageView by URL in Android?. And those bitmaps should be saved in some Array or HashMap to have access to it, for example ( pseudo code ):
//some structure for cashing pictures in app
class PictureCache {
static PictureCache instance;
final HashMap<Integer, Bitmap> bitmaps;
//singleton
static PictureCache getInstance(){
if (instance==null)
instance = new PictureCache();
return instance;
}
public PictureCache(){
bitmaps = new HashMap<>;
}
private Bitmap loadBitmap(String url);//#1
public addPicture(Integer personId, String href){
//add to hashMap
bitmaps.put(personId, loadBitmap(href));
}
public Bitmap getPicture(Integer personId){
return bitmaps.get(personId);
}
}
#1 - method from How to load an ImageView by URL in Android?
Using it in first activity:
PictureCache.getInstance().addPicture(12,"http://url.to.bitmap");
Using it in second activity:
Bitmap pic = PictureCache.getInstance().getPicture(12);
Important note - above code was written here and was not tested, it shows solution concept.
Important second note - using such approach with bitmaps in memory can cause to much memory usage
You cannot access the views of SecondActivity before its creation. If you called this activity once only then you are able to access its views by making them static.
One more Solution for this is..
Access the whole data at once and save it in static arraylist with the help of getter-setters. Then on SecondActivity set data from that arraylist.
Hope this will work.
I don't think you can access the view before creating the activity.
You can try to use Glide for caching your images and minimizing loading time.
Try this
Picasso.with(getContext()).load(R.drawable.generatedId).into(imageView);
or
imageView.setImageDrawable(ActivityCompat.getDrawable(getContext(),
R.drawable.generatedID));`

Static Bitmap set to null doesn't stay null

In my app I have a profile pic of the user. If he previously logged in, I download it from my server, otherwise he takes a pic from the camera or gallery. Either way, I store it in a class called DataStorage in a static public Bitmap variable thus:
public class DataStorage {
.
.
public static Bitmap origProfilePic; //not saved on app close
.
.
Now, if the user decides to re-do his profile, I log him out send him back to the profile creation screen, and I call:
DataStorage.origProfilePic = null;
(By the way, null is a legitimate value for this picture - we allow users to have no picture). The profile creation is through 2 activities - first choosing a username and password, then choosing a picture and other details. What's weird is that although I set the picture variable to null, somehow when I get to the end of the profile creation, it has gone back to it's original value, and I somehow have a user with the old profile picture (this is in the case where he did not select a new picture)
I've logged it n the OnCreate functions of the two activities:
onCreate SignUpActivity1, origPic = null
then
onCreate SignUpActivity2, origPic = android.graphics.Bitmap#42ba7438
But SignupActivity1 calls the intent for SignUpActivity2 and nowhere in SiugnupActivity1 is there a reference to DataStorage.origProfilePic except in my log statements.
I decided to check one more time in SignUpActivity1 if the value changes and I found...just as I invoke SignupActivity2:
Intent intent = new Intent(SignUpActivity1.this, SignUpActivity2.class);
intent.putExtra("EMAIL", email.toString());
intent.putExtra("PASSWORD", password.toString());
startActivity(intent);
Log.e("Kibi", "End SignUpActivity1, origPic = " + DataStorage.origProfilePic);
finish();
This somehow has:
End SignUpActivity1, origPic = android.graphics.Bitmap#42ba7438
This is quite repeatable in this specific case, though I certainly set the bitmap to null in other places and don't see it popping back up.
I saw a few things on SO about static Bitmaps being a danger for memory leaks, though I don't see a memory leak right now, I worry that I must be doing something very wrong if setting to null cannot be relied upon.
Can anyone explain what I am doing wrong and what the best way is to store this Bitmap in a basically volatile way?
It makes sense that both values change when you use DataStorage.origProfilePic in both activities. Since it's a static value, both activities are pointing to the same memory object.
DataStorage.origProfilePic is pointing to a piece of memory where your bitmap is stored. Assuming you use something like:
#SignUpActivity1
Bitmap origPic = DataStorage.origProfilePic;
#SignUpActivity2
Bitmap origPic = DataStorage.origProfilePic;
What happens here is that you create a reference to DataStorage.origProfilePic. That means you practically have the same value in SignUpActivity1.origPic and SignUpActivity2.origPic.
When you call this rule:
startActivity(intent);
The OnCreate method in SignUpActivity2 will be invoked. You probably will set here (or somewhere else) the value of DataStorage.origProfilePic, which means the value of SignUpActivity1.origPic and SignUpActivity2.origPic also changed (not entire true, but will have a different output).
Also be carefull with DataStorage.origProfilePic = null;. This only set the reference to null, but doesn't clear the bitmap from memory. Use DataStorage.origProfilePic.recycle(); to clear the bitmap from memory.

Collect device data on android regularly

I managed to create an application that collects basic info from the device like android version,CPU,total RAM,free RAM,etc. Some of the data i need to collect is dynamic so i want my application to collect that data every minute.I also want my application to do so in the background. Here is how my code is setup:
public class Sniffer extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sniffer);
model_device=(TextView) findViewById(R.id.model1);
versiune_android=(TextView) findViewById(R.id.versiune1);
total_RAM=(TextView) findViewById(R.id.total_RAM1);
free_RAM=(TextView) findViewById(R.id.free_RAM1);
total_disk=(TextView) findViewById(R.id.total_disk1);
free_disk=(TextView) findViewById(R.id.free_disk1);
CPU=(TextView) findViewById(R.id.CPU1);
nivel_baterie=(TextView) findViewById(R.id.baterie1);
//get the requested data and set the corresponding text
getDeviceData();
}
Inside getDeviceData i read the /proc files and whatnot to obtain everything. Can i create a service that can run in the background and every x minutes update the data by calling getDeviceData again ? Let's take this scenario : start my app,check the data, leave app on, go check my email, listen to a song on youtube then i come back to my app (without closing it)and the collected data should have changed. How would i go about doing so using a service, i think this is the best solution. I need just a bump in the right direction, maybe a skeleton code on where my stuff should go. Thanks

Passing Data from multiple Activities to a single activity

Hello Everybody,
&nbsp&nbsp&nbsp&nbspI have an app that a couple of classmates and I did for a class project. We are now continuing the app to improve and streamline it. Here is what I have so far:
SplashScreen
HomePage
Hole 1 - 18
ScorePage
AboutPage
Home Screen Hole 1 - 18 Score Page About Page
I have pretty much got it all figured out except for a few small things. The issue that I am working on right now though is:
Passing data from each Hole Page to the Score Page.
I know how to pass it from page to page and I could brute force it, because that is how I initially had it, but it looks sloppy and I would like to not do that if possible.
//Code (partial)
//(From Hole 1)
#Override
public void onClick(View v)
{
TextView tvScoreLbl = (TextView) findViewById(R.id.scoreLbl);
tvScoreLbl.setText(String.valueOf(count));
if(v == findViewById(R.id.btnAdd))
{
count++;
tvScoreLbl.setText(String.valueOf(count));
}
else if(v == findViewById(R.id.btnMinus))
{
count--;
tvScoreLbl.setText(String.valueOf(count));
}
else if(v == findViewById(R.id.btnPrev))
{
Intent i_prev = new Intent(Hole_01.this, HomePage.class);
startActivity(i_prev);
}
else if(v == findViewById(R.id.btnNext))
{
Intent i_pass = new Intent(Hole_01.this, ScorePage.class);
i_pass.putExtra("score1", tvScoreLbl.getText().toString());
Intent i_next = new Intent(Hole_01.this, Hole_02.class);
startActivity(i_next);
}
//(From ScorePage)
String score;
TextView tvScore1 = (TextView) findViewById(R.id.tvScore1);
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.scorepage);
Button btnHome = (Button) findViewById(R.id.btnHome);
btnHome.setOnClickListener(this);
score = getIntent().getExtras().getString("score1");
tvScore1.setText(score);
}
Thanks in advance.
The best answer depends on how long you need your data to persist.
If you need it to persist across a long period of time (days/weeks) then store the data to the internal or external storage using either SQLite or creating a custom object that represents all the data from the 'hole page' and serialize that object and write it to disk.
If you don't need the data to persist then I'd do as you currently are and continue passing the data as you are, from Activity to Activity. It may seem 'sloppy' to you but it is how you are supposed to pass data between Activities.
Perhaps creating an object for the data you want to pass and have it implement the Parcelable interface would make it less 'sloppy' in your eyes. Here's a link to a tutorial for Parcelable objects.
Another option is to use the global Application context as suggested already BUT be warned it comes with a big problem - if the app is killed in the background any data stored in the Application class is lost, so you'll have to account for that happening.
A DB is excessive if you're only storing a tiny bit of info, the Application context will lose data if the app is killed by the OS.
So if you want to persist data for a period of time, use serialization to write the object ( if it's just the score then a simple File IO stuff would do the job with no need for serialization). Or if you only need it to persist while the app is running (foreground or background) then pass the data between Activities.
Rather than passing data between all your Intents with putExtra why not use a little SQLite DB to store the result of each activity that the score screen could read.
SQLite Docs
Android Notepad Tutorial is good for SQLite example as well
Hold all the state in an Application Class.
Write to it as needed, from whatever Activity.
Read from it in the ScorePage Activity.
More details and example available here: (Using the Android Application class to persist data)

User directed to other activity if already filled up his profile android

I'd like to ask the logic for first asking the user to fill up a profile form so it is filled, and then directed to the main activity. After closing the app and then re open it again, the profile activity should not be first launched but now the main activity. How can I do this? I'm in need of help. Thanks.
This is what I have tried so far:
private void doThread(){
Thread timer = new Thread(){
public void run(){
try{
sleep(5000); // sleeps/delays for 3 seconds
} // end try
catch(InterruptedException e){
e.printStackTrace();
}finally{
// this is going to create new intent activity for
// based on the action name (com.fps.ihealthfirst.IHEALTHFIRSACTIVITY)
boolean firstTime = mPreferences.getBoolean("user_prefs", true);
if (firstTime) {
Intent myProfile = new Intent( Disclaimer.this, Profile_Pref.class );
startActivity(myProfile);
}
else{
Intent openIHealthFirst = new Intent( "com.fps.iHealthFirst.IHEALTHFIRSTACTIVITY" );
startActivity( openIHealthFirst );
finish();
}
}// end finally
} // end run method
}; // end thread
timer.start();
}
Depending on your choice, you can choose to save the information collected from the Profile Form in either a Database or a SharedPreferences file. This part is rather subjective and if you are already employing a Database in your application, you might consider it.
Here are a few suggestions on handling the logic / flow after the user has setup / entered his Profile details:
First: If you aren't already employing a Splash Screen, you must consider creating one. This will give you a small buffer time to check if the user has already entered his profile details.
Second: If he hasn't, you can open the Profile Form Activity. If, after checking either of the Database or SharedPreferences file, you find data indicating a filled form, you can display the main activity directly.
I personally, would be more inclined towards using SharedPreferences for this task.
Here are a few tutorials to get started with SharedPreferences:
http://android-er.blogspot.in/2011/01/example-of-using-sharedpreferencesedito.html
http://saigeethamn.blogspot.in/2009/10/shared-preferences-android-developer.html
http://myandroidsolutions.blogspot.in/2012/06/android-store-persistent-data-using.html
http://moorandroid.blogspot.in/p/shared-preferences-on-android.html
They may not be specific to your question, but will give you the logic to save values. Retrieving the saved values would be fairly simple.
Hope any of this helps.
One way is to save the form information on to SD card, then load and check for the information, if the information is present there, then you can move to next activity. Check this answer for explanation to it.
Can I have an android activity run only on the first time an application is opened?
The other is to check for a specific shared preference in the main activity, if that shared preference is missing, then launch the single run activity again. Check the following answer for an explanation to it.
How to launch activity only once when app is opened for first time?
Determine if android app is the first time used
You can use SharedPreferences.I had this same question with a good answer here. Check it out.

Categories

Resources