android downloading multiple files and mapping to activity - java

I have an XML which contains an array of books. The array will contain url to download bimpat image. book id and book name.
e.g.
<result>
<books>
<book>
<bookId>101</bookId>
<bookname>java</bookname>
<bookurl><http://bookimage.jpg>
</book>
<book>
<bookId>102</bookId>
<bookname>c++</bookname>
<bookurl><http://bookimage.jpg>
</book>
<book>
<bookId>103</bookId>
<bookname>.net</bookname>
<bookurl><http://bookimage.jpg>
</book>
</books>
The above xml is downloaded and stored in array and individual image is downloaded from each URL. Mean while book data is set in activity.
If the image download is done serially it takes a lot of time. If done parallely , i do not come to know which image corresponds to which book.
public class myasync extends asynctask
{
doInBackground(String url)
{
Bitmap bitmap = getfilefromurl;
return bitmap;
}
}
public class activity1 extends Activity
{
OnCreate()
{
myasync myasyncobj = new myasync(this);
myasyncobj .execute("http://bookimage.jpg");
}
}
The image is returned to activity. But it is not known to which book id it correspond.
EDIT: I am planning to use hashmap to return URL and bitmap. But returning hashmap containing only one key value pair, might not be a good idea. Is there any construct for storing one string and bitmap?
EDIT2: I am sharing activity object. Can create an interface also.
Interface.setBitmap() , Interface.setURL(). But creating new interface for sharing one bitmap and one url, will be good programming practice?
EDIT 3: The solution I have used is to create an class for storing bitmap and url. This will be called from OnPostExecute.
class BitmapURLMap
{
Bitmap bmp;
String URL;
}
Class Book
{
String URL;
String BookID;
String IconImage;
//getter and setter methods.
}
Class model //model for mvp
{
ArrayList<Book> arrBook = null;
public void setBookArr(ArrayList<Book> arrBook )
{
this.arrBook = arrBook ;
}
public ArrayList<Book> arrBook getBookArr( )
{
return arrBook ;
}
}
public class myasync extends asynctask
{
Activity activity;
public myasync(Activity activity)
{
this.activity = activity;
}
doInBackground(String url)
{
Bitmap bitmap = getfilefromurl;
BitmapURLMap bmpURL = new BitmapURLMap(bitmap,url);
return bmp;
}
OnPostExecute()
{
activity.setMap(bmpURL);
}
public class activity1 extends Activity
{
OnCreate()
{
myasync myasyncobj = new myasync(this);
myasyncobj .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"http://bookimage.jpg");
}
setMap(BitmapURLMap bmpURL)
{
ArrayList<Book> arrBook= model.getBookArr();
for(Book book : arrBook)
String URL = book.getURL();
if(URL.equals( bmpURL.URL))
{
book.setIconImage(bmpURL.bmp);
break;
}
model.setBookArr(arrBook);
}
}
But I am sure this solution is not the right way to do it. Though it works.
Can someone tell me how to implement it the right way.

Related

Android asks for not to query on main thread, but asyc queries are delaying

As the title says, android needs queries out of main thread since it will trhow java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time otherwise. So I managed to make async queries as many tutorials explain, but it doesn't make so much sense (so far) as I could achieve.
public class NewDetalleDiarioActivity extends AppCompatActivity {
#Override
protected void onStart() {
super.onStart();
db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database").build();
findPeriodo();
findDiario();
}
private void findPeriodo() {
periodo = Diarios.getPeriodo(db);
if (periodo == null) {
Intent intent = new Intent(NewDetalleDiarioActivity.this, NewPeriodoActivity.class);
startActivity(intent);
}
}
PROBLEM/ERROR:
If periodo is null, another activity is started, otherwise this one continues its thread.
The problem is that, when I debug it (which slows proceses, of course) periodo returns an instance from the database, but when I run the code without debugging, periodo is null.
public class Diarios {
public static Periodo getPeriodo(AppDatabase db) {
return Factory.getIntPeriodo().getPeriodo(db);
}
}
.
public class Factory {
private static IntPeriodo intPeriodo;
public static IntPeriodo getIntPeriodo() {
return (intPeriodo == null) ? intPeriodo = new BusPeriodo() : intPeriodo;
}
}
.
public class BusPeriodo implements IntPeriodo {
// I don't think it's necessary to post the interface...
#Override
public Periodo getPeriodo(final AppDatabase db) {
final Periodo[] periodo = new Periodo[1];
AsyncTask.execute(new Runnable() {
#Override
public void run() { //the async query that is driving me mad.
periodo[0] = db.periodoDao().getPeriodo(new Date());
}
});
return periodo[0];
}
}
What's the proper way to make select queries without getting them delayed?
The select query is indeed working, I don't think is necessary to post it (because it is returning an unique result when I debug), but it returns null when I run the code without debugging!! Please help.
SOLUTION:
As #user7041125 suggested, but instead I made a new class with an interface to call methods back to the activity, like this:
public class PeriodoBridge extends AsyncTask<Void, Void, Periodo> implements IntPeriodoBridge {
private WeakReference<Activity> weakActivity;
private IntPeriodoBridge caller; //implement this interface in the activity which needs to query
private AppDatabase db;
private Periodo periodo;
public PeriodoBridge(Activity activity, IntPeriodoBridge caller, AppDatabase db) {
weakActivity = new WeakReference<>(activity);
this.caller = caller; // assign activity instance to the local interface instance
this.db = db;
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
#Override
protected Periodo doInBackground(Void... voids) {
periodo = Diarios.getPeriodo(db);
return periodo;
}
#Override
protected void onPostExecute(Periodo periodo) {
Activity activity = weakActivity.get();
if (activity == null) {
return;
}
if (periodo == null) {
Intent intent = new Intent(activity, NewPeriodoActivity.class);
activity.startActivity(intent);
} else {
setPeriodo(periodo);
}
}
#Override //this is an interface method (IntPeriodoBridge)
public void setPeriodo(Periodo periodo) {
caller.setPeriodo(periodo); //I can set the query result back to the activity class with this
}
Call the init method of this class. The activity implements IntPeriodoBridge and in that way I can set the query result object to the activity class.

Android: Pass information from one activity to another [duplicate]

This question already has answers here:
How to pass an object from one activity to another on Android
(35 answers)
Closed 5 years ago.
I am trying to pass information (more specifically a class with information) from one activity to another. In my application I have a splash screen that is responsible for loading and initializing variables. The goal is to get this information to the actual game itself so it may be used but I don't know how to do so. In my splash screen class I have this method that is responsible for moving from the splash screen to the game once everything is loaded:
private void moveToGame() {
loop.setRunning(false);
act.startActivity(new Intent(splash, MainActivity.class));
act.finish();
return;
}
The main activity class then has this line of code to get to the actual game:
setContentView(new Environment(this, this));
The constructor for the Environment class is Environment(Context context, Activity act)
The goal is to change the constuctor to Environment(Context context, ActivityAct, LoadInfo li) but how do I pass the information all the way to the Environment constructor?
EDIT 1 - LoadInfo Class
public class LoadInfo {
private HashMap<String, Typeface> fonts;
private HashMap<String, Image> images;
private File logFile;
private File settingsFile;
private File gameDir;
public LoadInfo() {}
public LoadInfo(HashMap<String, Typeface> fonts, HashMap<String, Image> images, File logFile, File settingsFile, File gameDir) {
this.fonts = fonts;
this.images = images;
this.logFile = logFile;
this.settingsFile = settingsFile;
this.gameDir = gameDir;
}
public HashMap<String, Typeface> getFonts() {
return fonts;
}
public HashMap<String, Image> getImages() {
return images;
}
public File getLogFile() {
return logFile;
}
public File getSettingsFile() {
return settingsFile;
}
public File getGameDir() {
return gameDir;
}
public void setFonts(HashMap<String, Typeface> fonts) {
this.fonts = fonts;
}
public void setImages(HashMap<String, Image> images) {
this.images = images;
}
public void setLogFile(File logFile) {
this.logFile = logFile;
}
public void setGameDir(File gameDir) {
this.gameDir = gameDir;
}
public void setSettingsFile(File settingsFile) {
this.settingsFile = settingsFile;
}
public boolean fullyLoaded() {
return fonts != null && images != null && logFile != null && gameDir != null && settingsFile != null;
}
public String toString() {
if(logFile == null)
return "well no file to load";
return logFile.toString();
}
}
You can make your LoadInfo as Serializable like below,
public class LoadInfo implements Serializable {
// your code,
}
and in you Splash Activity you can send like this,
//LoadInfo loadInfo = new LoadInfo(); this may be your loadInfo object
Intent intent = new Intent(act, MainActivity.class);
intent.putExtra("load_info", loadInfo); // Add your LoadInfo object here
act.startActivity(intent);
In your MainActvity you can get like this,
LoadInfo loadInfo = (LoadInfo) getIntent().getSerializableExtra("load_info");
setContentView(new Environment(this, this, loadInfo));
Warning: Intent extra has a limit of 1Mb:
To pass information from one Activity to another it's normal to use Intent extra, but it has a limitation of 1MB of data. In your question, you are using LoadInfo class and I believe it can easily pass 1MB in size because it loads Game information.
Suggestion: You can choose to implement Application or Service (i.e. Bound Service) to store this instance of LoadInfo and all your activities can access this instance.
More Tip: You can also use a Singleton class that stores this instance ofLoaderInfo, but you should remove it after closing all activities of your game.
Obs: Splash Screen as an Activity, you must remember to remove it from Back Stack, otherwise, when the user clicks back, it will return to the splash Activity.

How to get data stored in ListArray

I want to store my API result in a Array List, need to store, ID and ImageURL.
I am able to store the data using my class ImgModel. But I can't figureout how to access it later on.
public class ImgModel{
private String url, id;
public ImgModel(String id, String url) {
this.id = id;
this.url = url;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getId() {
return id;
}
public void setId(String photoId) {
this.id = photoId;
}
}
in MainActivity I call the API
public class MainActivity ....{
...
List<ImgModel> photosList = new ArrayList<ImgModel>();
....
//>>in the result API... after parse the json
String id = imgOgj.getString("id");
String url = imgOgj.getString("url");
ImgModelp p = new ImgModel(id, url);
photosList.add(p); //THIS WORKS
}
This Part I don't know how to implement - pls help
Now in the ImagePreview Activity I want to access these images and Id to display in Image view.
public class ImagePreviewActivity ....{
//List<ImgModel> mProcessedImg= new ArrayList<ImgModel>(); //If I do this, that means I am creating a new list, and not accessing the store data right ?
ProcessedImg mProcessedImg;
ImageView mImageView;
onCreate{
....
mProcessedImg.size(); //Get the size .i.e how make images url
mImageView.setImage(mProcessedImg.getUrl(0);//sample how can I get the url of position 0 ?
}
}
The photosList variable that you have declared in MainActivity is a local variable, which means that its scope is limited to only the code block in which it has been declared. This is the reason that you cannot access the data you have stored in that variable elsewhere in your code.
In order to use and access that same variable again outside of the code block in which it was declared you could instead use an instance variable of the MainActivity class, by amending your class declaration as follows:
public class MainActivity extends Activity {
List<ImgModel> mPhotosList;
...
// Override the OnCreate method of Activity
#Override
public void onCreate(Bundle savedInstanceState) {
// Create the mPhotosList instance variable
mPhotosList = new ArrayList<ImgModel>;
...
}
// other methods where you call the API and store the data in mPhotosList
...
}
These pages may help to explain the differences between the types of variables that you can use in Java:
what is the difference between local and instance variables in Java
http://www.cs.umd.edu/~clin/MoreJava/Objects/local.html
In terms of the next part of your problem, to access the mPhotosList member variable from another Activity, the following post may help:
Passing a Bundle on startActivity()?
If you neeed to share a list between lots of activities,putting it into MyApp's instance maybe a solution.
Create constructor inside ImagePreviewActivity.class which allows one List parameter.
public class ImagePreviewActivity ....{
List<ImgModel> imgList;
ImageView mImageView;
public ImagePreviewActivity(List<ImgModel> imageList){
this.imgList = imageList;
}
onCreate{
mImageView.setImage(imageList.get(0).getUrl();
}
}
Creating a object of ImagePreviewActivity.class
public class MainActivity ....{
...
List<ImgModel> photosList = new ArrayList<ImgModel>();
....
String id = imgOgj.getString("id");
String url = imgOgj.getString("url");
ImgModelp p = new ImgModel(id, url);
photosList.add(p);
//Craeate Object of ImagePreviewActivity
ImagePreviewActivity ipa = new ImagePreviewActivity(photosList);
}

Android, get data from web and update UI (Multi-threading and MVC design pattern)

Currently developing a weather application . I wanna strictly observe 2 rules:
MVC Design pattern
Multithreading when I deals with network.
The problem is combining these things into a whole, here are parts of my code:
Weather Class (represent a Weather object) :
public class Weather {
private int mTimeInSeconds;
private String mTimeZone;
private int mTemperature;
private String mSummary;
public Weather(int timeInSeconds, String timeZone, int temperature, String summary) {
mTimeInSeconds = timeInSeconds;
mTimeZone = timeZone;
mTemperature = temperature;
mSummary = summary;
}
public String getTime() {
SimpleDateFormat formatter = new SimpleDateFormat("h:mm a");
formatter.setTimeZone(TimeZone.getTimeZone(mTimeZone));
Date dateTime = new Date(mTimeInSeconds * 1000);
String timeAsString = formatter.format(dateTime);
return timeAsString;
}
public int getTemperature() {
return mTemperature;
}
public String getSummary() {
return mSummary;
}
}
Worker Class (do all "dirty" work):
public class Worker {
private final OkHttpClient mClient = new OkHttpClient();
private String apiKey = "Secret!11";
private double latitude = 37.8267;
private double longitude = -122.423;
private String forecastUrl = "https://api.forecast.io/forecast/"
+apiKey
+"/"
+latitude
+","
+longitude;
public void getCurrentWeather() throws Exception {
final Request request = new Request.Builder()
.url(forecastUrl)
.build();
mClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
Log.v(MainActivity.TAG, e.getMessage());
}
#Override
public void onResponse(Response response) throws IOException {
try {
JSONObject weatherData = new JSONObject(response.body().string());
JSONObject currentlyWeather = weatherData.getJSONObject("currently");
Log.v(MainActivity.TAG, currentlyWeather.toString());
} catch (JSONException e) {
Log.v(MainActivity.TAG, e.getMessage());
}
}
});
}
}
Based on my understanding of MVC I put all data and logic around that data in Model (Worker and Weather classes). I wanna to achieve something like this in
MainActivity.java:
...
Worker mWorker = new Worker();
Weather mWeather = mWorker.getWeatherData();
...
2 questions:
Is this the correct design of MVC? (I mean, that all code which somehow work with data separated from the controller which only update view's)
If yes, how I can implement this? I need to return Weather object
from Worker, but I can't do this because it's happen on separate thread, I wanna return Weather object to main thread, but have no idea how to implement this.
As far as I know, your Model is your Weather.java class, your MainActivity (and additional added Activities) are your Controller and
finally your layout.xml files are your View. So AFAIK yes,
this is correctly implemented followed the MVC pattern.
It sounds as your Worker.java class should be of an AsyncTask
implementation.
An example using AsyncTask (This is just a stub implementation, see the links below to see fully working examples):
private class WorkerTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
//Network calls go here. This will be done in the background,
//in a separate thread
//Finally, you return your result here to onPostExecute
return result;
}
#Override
protected void onPostExecute(String result) {
//This method runs on the main UI thread. Here you update your UI o return data to your caller class.
}
#Override
protected void onPreExecute() {
//Called before execution
}
#Override
protected void onProgressUpdate(Void... values) {
//If you want to update your UI with your current progress, that code goes here
}
}
Finally, you will execute your AsyncTask like this:
new WorkerTask().execute("your_url");
More examples using AsyncTask here, here and here.
Official documentation is found here.
Regarding AsyncTask and the MVC design pattern
A downside of this is that the AsyncTask has to be implemented in your MainActivity (or wherever you want to make use of it) which contradicts the MVC design.
A solution to this would be to create a "HTTP caller class" which does the network calls, and which is called in your AsyncTask doInBackground method.
Using this approach, you would be good to go. You would have your controller (MainActivity) executing the AsyncTask (but not doing any actual network calls in MainActivity) creating a new HttpCallerClass, then calling a method which handles the networking. Hence, MVC is preserved.

Java (Android): calling a function from Context without cast

First off - I'm rather novice at Java so if the question makes no sense do let me know.
Basically I'm making an Android app which communicates with my web service and so I've made a separate class to deal with the communication, which also includes the AsyncTask (I've removed a lot from the code here just for preview):
public class api {
private String caller = null;
Context that = null;
api(Context that) {
this.that = that;
this.caller = that.getClass().getSimpleName();
}
void call(String action) {
/* .... */
}
new back().execute(param1, param2);
}
void callback(String action, String result){
that.callback(action, result);
}
public class back extends AsyncTask<String, Void, String> {
public String response = null;
protected String doInBackground(String... params) {
response = connection.executeRequest(params[1]);
return response;
}
protected void onPostExecute(String result) {
callback("a", "b");
}
}
}
And when I use the class from some part of the app (let's say SomeClass.class), I do:
api WS = new api(this);
WS.call("....");
And it's supposed to execute the function 'callback' which is in SomeClass.
But the key problem here is this line:
that.callback(action, result);
Eclipse makes me add the name of the "caller" class in the cast:
(SomeClass) that.callback(action, result);
But that doesn't work for me, because I use the 'api' class from many different classes, so ideally I need to put a variable in the cast. I do get the name of the "caller" class here:
this.caller = that.getClass().getSimpleName();
//obviously this won't work:
(this.caller) that.callback(action, result);
Is there anyway to do that, or am I doing something fundamentally wrong?
Thank you.
Currently your api class accepts a Context object in its default constructor. It would make more sense to extend Context with a new class which contains a callback method which you can then override in subclasses such as SomeClass, that would negate the need for casting in your api class. e.g:
public class APIContext extends Context
{
public void callback( String action, String result )
{
/* ... */
}
}
public class SomeClass extends APIContext
{
#Override
public void callback( String action, String result )
{
/* ... */
}
}
public class api
{
private APIContext callerContext = null;
public api( APIContext context )
{
this.callerContext = context;
}
public void callback( String action, String result )
{
callerContext.callback( action, result );
}
}

Categories

Resources